ffmpeg+sdl教程----编写一个简单的播放器4(让程序更模块化)

来源:http://blog.csdn.net/mu399/article/details/5815444

音频和视频之间的同步,再那之前需要先做一些准备工作。

为了让程序更模块化,便于扩展,需要把原来main函数中的各个功能模块代码分离出来放在相应的函数中。该教程和上个教程相比代码量和难度都增加很多,比上个教程使用了更多的线程,一定要理解清楚各个函数和数据结构之间的关联以及线程之间如何协同工作。

[c-sharp] view plaincopy

  1. // ffmpegExe.cpp: 主项目文件。
  2. #include "libavformat/avformat.h"
  3. #include "libswscale/swscale.h"
  4. //#include <windows.h>
  5. #include <stdlib.h>
  6. #include <stdio.h>
  7. #include <string.h>
  8. #include <math.h>
  9. #include <SDL/SDL.h>
  10. #include <SDL/SDL_thread.h>
  11. #ifdef main
  12. #undef main
  13. #endif
  14. #define SDL_AUDIO_BUFFER_SIZE 1024
  15. #define MAX_AUDIOQ_SIZE (5 * 16 * 1024)
  16. #define MAX_VIDEOQ_SIZE (5 * 256 * 1024)
  17. #define FF_ALLOC_EVENT   (SDL_USEREVENT)
  18. #define FF_REFRESH_EVENT (SDL_USEREVENT + 1)
  19. #define FF_QUIT_EVENT (SDL_USEREVENT + 2)
  20. #define VIDEO_PICTURE_QUEUE_SIZE 1
  21. #define SDL_AUDIO_BUFFER_SIZE 1024
  22. static int sws_flags = SWS_BICUBIC;
  23. typedef struct PacketQueue
  24. {
  25. AVPacketList *first_pkt, *last_pkt;
  26. int nb_packets;
  27. int size;
  28. SDL_mutex *mutex;
  29. SDL_cond *cond;
  30. } PacketQueue;
  31. typedef struct VideoPicture
  32. {
  33. SDL_Overlay *bmp;
  34. int width, height; /* source height & width */
  35. int allocated;
  36. } VideoPicture;
  37. typedef struct VideoState
  38. {
  39. AVFormatContext *pFormatCtx;
  40. int             videoStream, audioStream;
  41. AVStream        *audio_st;
  42. PacketQueue     audioq;
  43. uint8_t         audio_buf[(AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 2];
  44. unsigned int    audio_buf_size;
  45. unsigned int    audio_buf_index;
  46. AVPacket        audio_pkt;
  47. uint8_t         *audio_pkt_data;
  48. int             audio_pkt_size;
  49. AVStream        *video_st;
  50. PacketQueue     videoq;
  51. VideoPicture    pictq[VIDEO_PICTURE_QUEUE_SIZE];
  52. int             pictq_size, pictq_rindex, pictq_windex;
  53. SDL_mutex       *pictq_mutex;
  54. SDL_cond        *pictq_cond;
  55. SDL_Thread      *parse_tid;
  56. SDL_Thread      *video_tid;
  57. char            filename[1024];
  58. int             quit;
  59. } VideoState;
  60. SDL_Surface     *screen;
  61. /* Since we only have one decoding thread, the Big Struct
  62. can be global in case we need it. */
  63. VideoState *global_video_state;
  64. void packet_queue_init(PacketQueue *q)
  65. {
  66. memset(q, 0, sizeof(PacketQueue));
  67. q->mutex = SDL_CreateMutex();
  68. q->cond = SDL_CreateCond();
  69. }
  70. int packet_queue_put(PacketQueue *q, AVPacket *pkt)
  71. {
  72. AVPacketList *pkt1;
  73. if(av_dup_packet(pkt) < 0)
  74. {
  75. return -1;
  76. }
  77. pkt1 = (AVPacketList *)av_malloc(sizeof(AVPacketList));
  78. if (!pkt1)
  79. return -1;
  80. pkt1->pkt = *pkt;
  81. pkt1->next = NULL;
  82. SDL_LockMutex(q->mutex);
  83. if (!q->last_pkt)
  84. q->first_pkt = pkt1;
  85. else
  86. q->last_pkt->next = pkt1;
  87. q->last_pkt = pkt1;
  88. q->nb_packets++;
  89. q->size += pkt1->pkt.size;
  90. SDL_CondSignal(q->cond);
  91. SDL_UnlockMutex(q->mutex);
  92. return 0;
  93. }
  94. static int packet_queue_get(PacketQueue *q, AVPacket *pkt, int block)
  95. {
  96. AVPacketList *pkt1;
  97. int ret;
  98. SDL_LockMutex(q->mutex);
  99. for(;;)
  100. {
  101. if(global_video_state->quit)
  102. {
  103. ret = -1;
  104. break;
  105. }
  106. pkt1 = q->first_pkt;
  107. if (pkt1)
  108. {
  109. q->first_pkt = pkt1->next;
  110. if (!q->first_pkt)
  111. q->last_pkt = NULL;
  112. q->nb_packets--;
  113. q->size -= pkt1->pkt.size;
  114. *pkt = pkt1->pkt;
  115. av_free(pkt1);
  116. ret = 1;
  117. break;
  118. }
  119. else if (!block)
  120. {
  121. ret = 0;
  122. break;
  123. }
  124. else
  125. {
  126. SDL_CondWait(q->cond, q->mutex);
  127. }
  128. }
  129. SDL_UnlockMutex(q->mutex);
  130. return ret;
  131. }
  132. int audio_decode_frame(VideoState *is, uint8_t *audio_buf, int buf_size)
  133. {
  134. int len1, data_size;
  135. AVPacket *pkt = &is->audio_pkt;
  136. for(;;)
  137. {
  138. while(is->audio_pkt_size > 0)
  139. {
  140. data_size = buf_size;
  141. len1 = avcodec_decode_audio2(is->audio_st->codec,
  142. (int16_t *)audio_buf, &data_size,
  143. is->audio_pkt_data, is->audio_pkt_size);
  144. if(len1 < 0)
  145. {
  146. /* if error, skip frame */
  147. is->audio_pkt_size = 0;
  148. break;
  149. }
  150. is->audio_pkt_data += len1;
  151. is->audio_pkt_size -= len1;
  152. if(data_size <= 0)
  153. {
  154. /* No data yet, get more frames */
  155. continue;
  156. }
  157. /* We have data, return it and come back for more later */
  158. return data_size;
  159. }
  160. if(pkt->data)
  161. av_free_packet(pkt);
  162. if(is->quit)
  163. {
  164. return -1;
  165. }
  166. /* next packet */
  167. if(packet_queue_get(&is->audioq, pkt, 1) < 0)
  168. {
  169. return -1;
  170. }
  171. is->audio_pkt_data = pkt->data;
  172. is->audio_pkt_size = pkt->size;
  173. }
  174. }
  175. void audio_callback(void *userdata, Uint8 *stream, int len)
  176. {
  177. VideoState *is = (VideoState *)userdata;
  178. int len1, audio_size;
  179. while(len > 0)
  180. {
  181. if(is->audio_buf_index >= is->audio_buf_size)
  182. {
  183. /* We have already sent all our data; get more */
  184. audio_size = audio_decode_frame(is, is->audio_buf, sizeof(is->audio_buf));
  185. if(audio_size < 0)
  186. {
  187. /* If error, output silence */
  188. is->audio_buf_size = 1024;
  189. memset(is->audio_buf, 0, is->audio_buf_size);
  190. }
  191. else
  192. {
  193. is->audio_buf_size = audio_size;
  194. }
  195. is->audio_buf_index = 0;
  196. }
  197. len1 = is->audio_buf_size - is->audio_buf_index;
  198. if(len1 > len)
  199. len1 = len;
  200. memcpy(stream, (uint8_t *)is->audio_buf + is->audio_buf_index, len1);
  201. len -= len1;
  202. stream += len1;
  203. is->audio_buf_index += len1;
  204. }
  205. }
  206. static Uint32 sdl_refresh_timer_cb(Uint32 interval, void *opaque)
  207. {
  208. //printf("sdl_refresh_timer_cb called:interval--%d/n",interval);
  209. SDL_Event event;
  210. event.type = FF_REFRESH_EVENT;
  211. event.user.data1 = opaque;
  212. SDL_PushEvent(&event);      //派发FF_REFRESH_EVENT事件
  213. return 0; /* 0 means stop timer */
  214. }
  215. /* schedule a video refresh in ‘delay‘ ms */
  216. static void schedule_refresh(VideoState *is, int delay)
  217. {
  218. //printf("schedule_refresh called:delay--%d/n",delay);
  219. SDL_AddTimer(delay, sdl_refresh_timer_cb, is);      //sdl_refresh_timer_cb函数在延时delay毫秒后,只会被执行一次,is是sdl_refresh_timer_cb的参数
  220. }
  221. void video_display(VideoState *is)
  222. {
  223. //printf("video_display called/n");
  224. SDL_Rect rect;
  225. VideoPicture *vp;
  226. AVPicture pict;
  227. float aspect_ratio;
  228. int w, h, x, y;
  229. int i;
  230. vp = &is->pictq[is->pictq_rindex];
  231. if(vp->bmp)
  232. {
  233. if(is->video_st->codec->sample_aspect_ratio.num == 0)
  234. {
  235. aspect_ratio = 0;
  236. }
  237. else
  238. {
  239. aspect_ratio = av_q2d(is->video_st->codec->sample_aspect_ratio) *
  240. is->video_st->codec->width / is->video_st->codec->height;
  241. }
  242. if(aspect_ratio <= 0.0)      //aspect_ratio 宽高比
  243. {
  244. aspect_ratio = (float)is->video_st->codec->width /
  245. (float)is->video_st->codec->height;
  246. }
  247. h = screen->h;
  248. w = ((int)(h * aspect_ratio)) & -3;
  249. if(w > screen->w)
  250. {
  251. w = screen->w;
  252. h = ((int)(w / aspect_ratio)) & -3;
  253. }
  254. x = (screen->w - w) / 2;
  255. y = (screen->h - h) / 2;
  256. rect.x = x;
  257. rect.y = y;
  258. rect.w = w;
  259. rect.h = h;
  260. SDL_DisplayYUVOverlay(vp->bmp, &rect);
  261. }
  262. }
  263. void video_refresh_timer(void *userdata)
  264. {
  265. VideoState *is = (VideoState *)userdata;
  266. VideoPicture *vp;
  267. if(is->video_st)
  268. {
  269. if(is->pictq_size == 0)
  270. {
  271. schedule_refresh(is, 1);
  272. }
  273. else
  274. {
  275. vp = &is->pictq[is->pictq_rindex];
  276. /* Now, normally here goes a ton of code
  277. about timing, etc. we‘re just going to
  278. guess at a delay for now. You can
  279. increase and decrease this value and hard code
  280. the timing - but I don‘t suggest that ;)
  281. We‘ll learn how to do it for real later.
  282. */
  283. schedule_refresh(is, 80);
  284. /* show the picture! */
  285. video_display(is);
  286. /* update queue for next picture! */
  287. if(++is->pictq_rindex == VIDEO_PICTURE_QUEUE_SIZE)
  288. {
  289. is->pictq_rindex = 0;
  290. }
  291. SDL_LockMutex(is->pictq_mutex);
  292. is->pictq_size--;
  293. SDL_CondSignal(is->pictq_cond);
  294. SDL_UnlockMutex(is->pictq_mutex);
  295. }
  296. }
  297. else
  298. {
  299. schedule_refresh(is, 100);
  300. }
  301. }
  302. void alloc_picture(void *userdata)
  303. {
  304. VideoState *is = (VideoState *)userdata;
  305. VideoPicture *vp;
  306. vp = &is->pictq[is->pictq_windex];
  307. if(vp->bmp)
  308. {
  309. // we already have one make another, bigger/smaller
  310. SDL_FreeYUVOverlay(vp->bmp);
  311. }
  312. // Allocate a place to put our YUV image on that screen
  313. vp->bmp = SDL_CreateYUVOverlay(is->video_st->codec->width,
  314. is->video_st->codec->height,
  315. SDL_YV12_OVERLAY,
  316. screen);
  317. vp->width = is->video_st->codec->width;
  318. vp->height = is->video_st->codec->height;
  319. SDL_LockMutex(is->pictq_mutex);
  320. vp->allocated = 1;
  321. SDL_CondSignal(is->pictq_cond);
  322. SDL_UnlockMutex(is->pictq_mutex);
  323. }
  324. int queue_picture(VideoState *is, AVFrame *pFrame)
  325. {
  326. //printf("queue_picture called/n");
  327. VideoPicture *vp;
  328. int dst_pix_fmt;
  329. AVPicture pict;
  330. static struct SwsContext *img_convert_ctx;
  331. if (img_convert_ctx == NULL)
  332. {
  333. img_convert_ctx = sws_getContext(is->video_st->codec->width, is->video_st->codec->height,
  334. is->video_st->codec->pix_fmt,
  335. is->video_st->codec->width, is->video_st->codec->height,
  336. PIX_FMT_YUV420P,
  337. sws_flags, NULL, NULL, NULL);
  338. if (img_convert_ctx == NULL)
  339. {
  340. fprintf(stderr, "Cannot initialize the conversion context/n");
  341. exit(1);
  342. }
  343. }
  344. /* wait until we have space for a new pic */
  345. SDL_LockMutex(is->pictq_mutex);
  346. while(is->pictq_size >= VIDEO_PICTURE_QUEUE_SIZE &&
  347. !is->quit)
  348. {
  349. SDL_CondWait(is->pictq_cond, is->pictq_mutex);
  350. }
  351. SDL_UnlockMutex(is->pictq_mutex);
  352. if(is->quit)
  353. return -1;
  354. // windex is set to 0 initially
  355. vp = &is->pictq[is->pictq_windex];
  356. /* allocate or resize the buffer! */
  357. if(!vp->bmp ||
  358. vp->width != is->video_st->codec->width ||
  359. vp->height != is->video_st->codec->height)
  360. {
  361. SDL_Event event;
  362. vp->allocated = 0;
  363. /* we have to do it in the main thread */
  364. event.type = FF_ALLOC_EVENT;
  365. event.user.data1 = is;
  366. SDL_PushEvent(&event);
  367. /* wait until we have a picture allocated */
  368. SDL_LockMutex(is->pictq_mutex);
  369. while(!vp->allocated && !is->quit)
  370. {
  371. SDL_CondWait(is->pictq_cond, is->pictq_mutex);    //没有得到消息时解锁,得到消息后加锁,和SDL_CondSignal配对使用
  372. }
  373. SDL_UnlockMutex(is->pictq_mutex);
  374. if(is->quit)
  375. {
  376. return -1;
  377. }
  378. }
  379. /* We have a place to put our picture on the queue */
  380. if(vp->bmp)
  381. {
  382. SDL_LockYUVOverlay(vp->bmp);
  383. dst_pix_fmt = PIX_FMT_YUV420P;
  384. /* point pict at the queue */
  385. pict.data[0] = vp->bmp->pixels[0];
  386. pict.data[1] = vp->bmp->pixels[2];
  387. pict.data[2] = vp->bmp->pixels[1];
  388. pict.linesize[0] = vp->bmp->pitches[0];
  389. pict.linesize[1] = vp->bmp->pitches[2];
  390. pict.linesize[2] = vp->bmp->pitches[1];
  391. // Convert the image into YUV format that SDL uses
  392. sws_scale(img_convert_ctx, pFrame->data, pFrame->linesize,
  393. 0, is->video_st->codec->height, pict.data, pict.linesize);
  394. SDL_UnlockYUVOverlay(vp->bmp);
  395. /* now we inform our display thread that we have a pic ready */
  396. if(++is->pictq_windex == VIDEO_PICTURE_QUEUE_SIZE)
  397. {
  398. is->pictq_windex = 0;
  399. }
  400. SDL_LockMutex(is->pictq_mutex);
  401. is->pictq_size++;
  402. SDL_UnlockMutex(is->pictq_mutex);
  403. }
  404. return 0;
  405. }
  406. int video_thread(void *arg)
  407. {
  408. //printf("video_thread called");
  409. VideoState *is = (VideoState *)arg;
  410. AVPacket pkt1, *packet = &pkt1;
  411. int len1, frameFinished;
  412. AVFrame *pFrame;
  413. pFrame = avcodec_alloc_frame();
  414. for(;;)
  415. {
  416. if(packet_queue_get(&is->videoq, packet, 1) < 0)
  417. {
  418. // means we quit getting packets
  419. break;
  420. }
  421. // Decode video frame
  422. len1 = avcodec_decode_video(is->video_st->codec, pFrame, &frameFinished,
  423. packet->data, packet->size);
  424. // Did we get a video frame?
  425. if(frameFinished)
  426. {
  427. if(queue_picture(is, pFrame) < 0)
  428. {
  429. break;
  430. }
  431. }
  432. av_free_packet(packet);
  433. }
  434. av_free(pFrame);
  435. return 0;
  436. }
  437. int stream_component_open(VideoState *is, int stream_index)
  438. {
  439. AVFormatContext *pFormatCtx = is->pFormatCtx;
  440. AVCodecContext *codecCtx;
  441. AVCodec *codec;
  442. SDL_AudioSpec wanted_spec, spec;
  443. if(stream_index < 0 || stream_index >= pFormatCtx->nb_streams)
  444. {
  445. return -1;
  446. }
  447. // Get a pointer to the codec context for the video stream
  448. codecCtx = pFormatCtx->streams[stream_index]->codec;
  449. if(codecCtx->codec_type == CODEC_TYPE_AUDIO)
  450. {
  451. // Set audio settings from codec info
  452. wanted_spec.freq = codecCtx->sample_rate;
  453. wanted_spec.format = AUDIO_S16SYS;
  454. wanted_spec.channels = codecCtx->channels;
  455. wanted_spec.silence = 0;
  456. wanted_spec.samples = SDL_AUDIO_BUFFER_SIZE;
  457. wanted_spec.callback = audio_callback;
  458. wanted_spec.userdata = is;
  459. if(SDL_OpenAudio(&wanted_spec, &spec) < 0)
  460. {
  461. fprintf(stderr, "SDL_OpenAudio: %s/n", SDL_GetError());
  462. return -1;
  463. }
  464. }
  465. codec = avcodec_find_decoder(codecCtx->codec_id);
  466. if(!codec || (avcodec_open(codecCtx, codec) < 0))
  467. {
  468. fprintf(stderr, "Unsupported codec!/n");
  469. return -1;
  470. }
  471. switch(codecCtx->codec_type)
  472. {
  473. case CODEC_TYPE_AUDIO:
  474. is->audioStream = stream_index;
  475. is->audio_st = pFormatCtx->streams[stream_index];
  476. is->audio_buf_size = 0;
  477. is->audio_buf_index = 0;
  478. memset(&is->audio_pkt, 0, sizeof(is->audio_pkt));
  479. packet_queue_init(&is->audioq);
  480. SDL_PauseAudio(0);
  481. break;
  482. case CODEC_TYPE_VIDEO:
  483. is->videoStream = stream_index;
  484. is->video_st = pFormatCtx->streams[stream_index];
  485. packet_queue_init(&is->videoq);
  486. is->video_tid = SDL_CreateThread(video_thread, is);
  487. break;
  488. default:
  489. break;
  490. }
  491. }
  492. int decode_interrupt_cb(void)
  493. {
  494. return (global_video_state && global_video_state->quit);
  495. }
  496. int decode_thread(void *arg)
  497. {
  498. VideoState *is = (VideoState *)arg;
  499. AVFormatContext *pFormatCtx;
  500. AVPacket pkt1, *packet = &pkt1;
  501. int video_index = -1;
  502. int audio_index = -1;
  503. int i;
  504. is->videoStream=-1;
  505. is->audioStream=-1;
  506. global_video_state = is;
  507. // will interrupt blocking functions if we quit!
  508. url_set_interrupt_cb(decode_interrupt_cb);
  509. // Open video file
  510. if(av_open_input_file(&pFormatCtx, is->filename, NULL, 0, NULL)!=0)
  511. return -1; // Couldn‘t open file
  512. is->pFormatCtx = pFormatCtx;
  513. // Retrieve stream information
  514. if(av_find_stream_info(pFormatCtx)<0)
  515. return -1; // Couldn‘t find stream information
  516. // Dump information about file onto standard error
  517. dump_format(pFormatCtx, 0, is->filename, 0);
  518. // Find the first video stream
  519. for(i=0; i<pFormatCtx->nb_streams; i++)
  520. {
  521. if(pFormatCtx->streams[i]->codec->codec_type==CODEC_TYPE_VIDEO &&
  522. video_index < 0)
  523. {
  524. video_index=i;
  525. }
  526. if(pFormatCtx->streams[i]->codec->codec_type==CODEC_TYPE_AUDIO &&
  527. audio_index < 0)
  528. {
  529. audio_index=i;
  530. }
  531. }
  532. if(audio_index >= 0)
  533. {
  534. stream_component_open(is, audio_index);
  535. }
  536. if(video_index >= 0)
  537. {
  538. stream_component_open(is, video_index);
  539. }
  540. if(is->videoStream < 0 || is->audioStream < 0)
  541. {
  542. fprintf(stderr, "%s: could not open codecs/n", is->filename);
  543. goto fail;
  544. }
  545. // main decode loop
  546. for(;;)
  547. {
  548. if(is->quit)
  549. {
  550. break;
  551. }
  552. // seek stuff goes here
  553. if(is->audioq.size > MAX_AUDIOQ_SIZE ||
  554. is->videoq.size > MAX_VIDEOQ_SIZE)
  555. {
  556. SDL_Delay(10);
  557. continue;
  558. }
  559. if(av_read_frame(is->pFormatCtx, packet) < 0)
  560. {
  561. if(url_ferror(pFormatCtx->pb) == 0)
  562. {
  563. SDL_Delay(100); /* no error; wait for user input */
  564. continue;
  565. }
  566. else
  567. {
  568. break;
  569. }
  570. }
  571. // Is this a packet from the video stream?
  572. if(packet->stream_index == is->videoStream)
  573. {
  574. packet_queue_put(&is->videoq, packet);
  575. }
  576. else if(packet->stream_index == is->audioStream)
  577. {
  578. packet_queue_put(&is->audioq, packet);
  579. }
  580. else
  581. {
  582. av_free_packet(packet);
  583. }
  584. }
  585. /* all done - wait for it */
  586. while(!is->quit)
  587. {
  588. SDL_Delay(100);
  589. }
  590. fail:
  591. SDL_Event event;
  592. event.type = FF_QUIT_EVENT;
  593. event.user.data1 = is;
  594. SDL_PushEvent(&event);
  595. return 0;
  596. }
  597. int main(int argc, char *argv[])
  598. {
  599. SDL_Event       event;
  600. VideoState      *is;
  601. is = (VideoState *)av_mallocz(sizeof(VideoState));
  602. if(argc < 2)
  603. {
  604. fprintf(stderr, "Usage: test <file>/n");
  605. exit(1);
  606. }
  607. // Register all formats and codecs
  608. av_register_all();
  609. if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER))
  610. {
  611. fprintf(stderr, "Could not initialize SDL - %s/n", SDL_GetError());
  612. exit(1);
  613. }
  614. // Make a screen to put our video
  615. #ifndef __DARWIN__
  616. screen = SDL_SetVideoMode(640, 480, 0, 0);
  617. #else
  618. screen = SDL_SetVideoMode(640, 480, 24, 0);
  619. #endif
  620. if(!screen)
  621. {
  622. fprintf(stderr, "SDL: could not set video mode - exiting/n");
  623. exit(1);
  624. }
  625. //pstrcpy(is->filename, sizeof(is->filename), argv[1]);
  626. strcpy(is->filename,argv[1]);
  627. is->pictq_mutex = SDL_CreateMutex();
  628. is->pictq_cond = SDL_CreateCond();
  629. schedule_refresh(is, 40);
  630. is->parse_tid = SDL_CreateThread(decode_thread, is);
  631. if(!is->parse_tid)
  632. {
  633. av_free(is);
  634. return -1;
  635. }
  636. for(;;)
  637. {
  638. SDL_WaitEvent(&event);
  639. switch(event.type)
  640. {
  641. case FF_QUIT_EVENT:
  642. printf("FF_QUIT_EVENT recieved");
  643. case SDL_QUIT:
  644. printf("SDL_QUIT recieved");
  645. is->quit = 1;
  646. SDL_Quit();
  647. return 0;
  648. break;
  649. case FF_ALLOC_EVENT:
  650. alloc_picture(event.user.data1);
  651. break;
  652. case FF_REFRESH_EVENT:
  653. video_refresh_timer(event.user.data1);
  654. break;
  655. default:
  656. break;
  657. }
  658. }
  659. return 0;
  660. }

程序结构分析:

main函数主要做了三件事:

1.创建了一个线程decode_Thread

2.调用了schedule_refresh函数

3.创建了一个无限循环,处理程序运行过程中派发的退出播放事件,内存分配事件,屏幕刷新事件

先从简单地schedule_refresh函数说起

这个函数调用了SDL库函数SDL_AddTimer(delay, sdl_refresh_timer_cb, is);这个函数会让sdl_refresh_timer_cb函数延迟delay毫秒后只执行一次,is参数是大结构体VideoState类型,包含 了视频播放的各种信息,作为参数传递给回调函数sdl_refresh_timer_cb,sdl_refresh_timer_cb函数又会派发事件 FF_REFRESH_EVENT,大结构体类型的数据也跟着派发出去,让main的事件监听模块监听到,然后调用 video_refresh_timer函数。

video_refresh_timer函数

video_refresh_timer函数主要是负责播放视频的每一帧,如果帧队列is->pictq中有可以播放的图片帧,就调用 video_display函数播放它,然后改变is->pictq_rindex(影响video_display的播放),改变 is->pictq_size(帧队列大小)影响queue_picture函数地执行。

video_refresh_timer函数只要被调用,就会让schedule_refresh函数执行一次,又会派发屏幕刷新事件 FF_REFRESH_EVENT,让video_refresh_timer又有机会执行,实际上是个隐形的循环不断地调用 video_refresh_timer函数,虽然没有for或while关键字。

decode_Thread线程函数

decode_Thread函数首先调用ffmpeg库函数av_open_input_file打开main函数参数中指定的视频文件,然后调用 stream_component_open函数分别创建了两个线程分别去播放音频和视频,最后又是一个无限循环不断地调用库函数 av_read_frame把数据读入包变量packet中,然后根据packet->stream_index来分流,分出视频包和音频包,分别 放到视频包队列is->videoq和音频包队列is->audioq中,其中包队列is->videoq中的数据包还有待进行解码和 格式转换,然后在放到帧队列is->pictq中,让video_refresh_timer函数有帧可读,才能播放出图像。

stream_component_open函数

会根据参数的不同分别创建两个线程audio_callback进行音频的解码和播放,和video_thread进行视频的解码和格式转换(视频的播放是在video_refresh_timer函数中进行)

video_thread函数

首先调用packet_queue_get函数得到视频数据包,然后调用库函数avcodec_decode_video解码数据包,当数据包中的数据解 码满一帧后,把该帧的数据pFrame送到queue_picture函数中进行格式转换,queue_picture函数把格式转换好后会把该帧存入帧 队列is->pictq中,应该存到is->pictq的队前还是队后是通过is->pictq_windex来指示的。

queue_picture函数

queue_picture函数在向帧队列is->pictq写入一帧之前会先判断帧队列的大小is->pictq_size,如果帧队列中 有多于一帧的数据,就通过库函数SDL_CondWait阻塞起来,等待video_refresh_timer函数去播放一帧,然后改变帧队列大小。

线程间的协调

整个程序有四个工作线程:main线程,decode_thread线程,audio_callback线程,video_thread线程,这四个线程 如果不加以协调肯定会乱套,让程序无法正常工作。协调这些线程用到的变量有VideoState结构体中的audioq,videoq来协调 decode_thread线程和他的两个子线程(线程audio_callback线程、video_thread线程)之间读包、取包。

用pictq,pictq_size, 来协调main线程与video_thread线程之间的写帧、读帧。

互斥量pictq_mutex和信号量pictq_cond,通过SDL_LockMutex函数、SDL_UnlockMutex函数保证线程协调变量的读写安全;SDL_CondWait函数,SDL_CondSignal函数来阻塞和解除阻塞

时间: 2024-10-05 05:01:44

ffmpeg+sdl教程----编写一个简单的播放器4(让程序更模块化)的相关文章

ffmpeg+sdl教程----编写一个简单的播放器5(同步视频到音频)

来源:http://blog.csdn.net/mu399/article/details/5816566 个人认为,这这部分教程的新增代码量虽然不是最多的,难度却是最大的,重复看了多次才明白,因为有两个问题的困扰,搞得还不清楚: 1.音频和视频既然都有各自的时间戳,各自按各自的时间戳来播放不就行了,为什么还需要同步呢? 2.如果要把视频同步到音频,怎么同步?或者说以什么标准来同步? 第一个问题的答案可能是,一是音频和视频的开始播放的时间是不一样,二是播放每帧音频或视频时可能必须把解码数据,视频

ffmpeg+sdl教程----编写一个简单的播放器3(为视频加入音频)

来源:http://blog.csdn.net/mu399/article/details/5814901 上个教程实现了视频的简单播放,但那是个哑巴电影,完全没有声音. 这个教程第一次用到了SDL的线程,涉及到了两个线程间的同步协调,有几个地方需要特别留意,SDL_OpenAudio库函数会打开音频设备(0是恢 复,其他的是暂停),SDL_PauseAudio库函数可以暂停或者恢复audio_callback函数的执行,程序中的这行代码 “SDL_PauseAudio(0);”执行后,让aud

ffmpeg+sdl教程----编写一个简单的播放器2(输出视频到屏幕)

来源:http://blog.csdn.net/mu399/article/details/5814859 下面完整代码,在vc2005下编译通过.可以看到,程序运行后视频播放出来了,但是由于没有加入播放延迟,视频简直跑疯了,为视频加入延迟将在教程五中实现,目前可以简单地让程序在播放完一帧后,sleep若干秒,改善一下运行状况. [cpp] view plaincopy // ffmpegExe.cpp: 主项目文件. #include "stdafx.h" #include &quo

ffmpeg+sdl教程----编写一个简单的播放器6(其他的时钟同步方式)

来源:http://blog.csdn.net/mu399/article/details/5818384 在理解上一个教程的基础上,这篇教程就稍微容易理解些了,不外乎多加了两种同步方式,同步音频到视频,同步音频视频到外部时钟. 这篇教程主要是新增了不少新变量,is->video_current_pts用于保存当前视频帧的时间戳(以秒为单位),只在 video_refresh_timer函数中播放一帧视频前改变,is->video_current_pts_time单位为毫秒,在 stream_

ffmpeg+sdl教程----编写一个简单的播放器7(处理快进快退命令)

来源:http://blog.csdn.net/mu399/article/details/5818970 这篇教程例子中的程序,让右方向按键为快进10秒,上方向按键为快进60秒,左方向按键为快退10秒,上方向按键为快退60秒,程序中的 av_seek_frame函数可能是用错了,或者函数本身的问题导致按上和右都没反应;按左和下让画面暂停,声音在很短区间内不停播放,这时再按右和下 才正常. [cpp] view plaincopy #include "libavformat/avformat.h

可视化程序设计基础(三)——一个简单的播放器(并不)

本次的作业是制作一个简单的播放器,功能仅限于播放视频和音频,虽说是简单的播放器,但其中还是有很多细节需要注意的. 问题一:布局 本来这个问题不应该是一个问题了,之前老师讲过的Stackpanel和Grid等对于布局一个播放器来说绰绰有余,但上次上课老师提到的NavigationView令我十分感兴趣,这是一个uwp应用程序中随处可见的一种布局,节省了开发者很多的时间. 所以我就着手于建立这个NavigationView了,首先我看了一下XAML Controls Gallery,然而其中关于Na

[SimplePlayer] 实现一个简单的播放器

简单的播放器需要实现一个最基本的功能:播放视频文件. 实现这个功能需要包含以下几个步骤: 从视频文件中提取视频图像 在屏幕上显示视频图像 视频帧的同步,也就是保证视频图像在合适的时间在屏幕上显示 从视频文件中提取音频 向音频设备输出音频 音频同步,也就是保证合适的时间输出合适的音频 多线程处理 音视频同步 本实现是通过ffmpeg来实现音视频的提取,通过sdl2来实现音视频的输出,版本如下: libavutil 56. 19.100 / 56. 19.100 libavcodec 58. 23.

qt实现一个简单的播放器

ui界面设计如下: 2.代码结构 3.dialog.cpp #include "dialog.h" #include "ui_dialog.h" #include<QStringList> #include<QDebug> Dialog::Dialog(QWidget *parent) : QDialog(parent), ui(new Ui::Dialog) { ui->setupUi(this); ui->lb_dis->

Android开发6:Service的使用(简单音乐播放器的实现)

前言 啦啦啦~各位好久不见啦~博主最近比较忙,而且最近一次实验也是刚刚结束~ 好了不废话了,直接进入我们这次的内容~ 在这篇博文里我们将学习Service(服务)的相关知识,学会使用 Service 进行后台工作, 学会使用 Service 与 Activity 进行通信,并在此知识基础上学会使用 MediaPlayer和简单的多线程编程.使用 Handle 更新 UI,并设计成功一个简单的音乐播放器. 是不是很高大上呢~一起来学习~ 基础知识 Service作为Android四大组件之一,在每