博客园第一篇——SDL2+FFmpeg 制作简单播放器&同步

SDL 2.0.3; FFmpeg 20140402 shared;
部分来自http://blog.csdn.net/jiqiujia/article/details/22449131的第二个例子,修正了一些问题。
原来的代码在第555行,SDL_Delay(50) 应该替换为 av_free_packet(&packet),这样既解决了迟滞问题还解决了一个坑爹的内存泄露问题。(我才不会告诉你我播放一个稍微大点的视频在8秒之后 2 GB 内存就被吃掉了呢!)
https://github.com/phamquy/FFmpeg-tutorial-samples/blob/master/tutorial06.c也有一些帮助。
功能:
1、简单播放器;
2、音频重采样,大部分视频的音频部分都播放正常(也有少部分仍不正常的);
3、简单的同步。当前以音频流为基准,对于比较小(<120 MB)的 .mpg/.flv 文件还正常,其他的有各种问题,包括视频仍然过快,或者严重丢帧(建议打开控制台自己看)。
下面上代码。部分地方有注释。

  1 #define _CRT_SECURE_NO_WARNINGS
  2 
  3 extern "C"{
  4 #include "libavutil/opt.h"
  5 #include "libavcodec/avcodec.h"
  6 #include "libavformat/avformat.h"
  7 #include "libswscale/swscale.h"
  8 #include "libswresample/swresample.h"
  9 }
 10 #include "SDL.h"
 11 #include "SDL_image.h"
 12 #include "SDL_thread.h"
 13 
 14 #include <iostream>
 15 #include <queue>
 16 using namespace std;
 17 
 18 #pragma warning(disable: 4996)
 19 
 20 #pragma comment(lib,"avutil.lib")
 21 #pragma comment(lib,"avcodec.lib")
 22 #pragma comment(lib,"avformat.lib")
 23 #pragma comment(lib,"swscale.lib")
 24 #pragma comment(lib,"swresample.lib")
 25 
 26 #pragma comment(lib,"sdl2.lib")
 27 
 28 #define SDL_AUDIO_BUFFER_SIZE            (1152)
 29 #define AVCODEC_MAX_AUDIO_FRAME_SIZE    (192000)
 30 
 31 #if (defined main && defined __MINGW__)
 32 #undef main
 33 #endif
 34 
 35 static Uint8 *audio_chunk;
 36 static Uint32 audio_len;
 37 static Uint8 *audio_pos;
 38 
 39 static int64_t audio_pts = 0;
 40 static int64_t audio_dts = 0;
 41 static int64_t video_pts = 0;
 42 static int64_t video_dts = 0;
 43 
 44 static AVFrame *g_pFrameYUV;
 45 
 46 SDL_Thread *g_pVideoThread;
 47 SDL_mutex *g_pVideoMutex;
 48 
 49 static int quit = 0;
 50 static int video_quit = 0;
 51 
 52 typedef struct video_thread_params
 53 {
 54     SwsContext *sws_context;
 55     SDL_Texture *texture;
 56     SDL_Renderer *renderer;
 57     AVCodecContext *vid_codec_context;
 58     SDL_mutex *video_mutex;
 59 }
 60 video_thread_params;
 61 
 62 int video_thread_proc(void *data);
 63 
 64 void fill_audio(void *udata, Uint8 *stream, int len){
 65     if (audio_len == 0)
 66         return;
 67     len = (len > audio_len ? audio_len : len);
 68     SDL_MixAudio(stream, audio_pos, len, SDL_MIX_MAXVOLUME);
 69     audio_pos += len;
 70     audio_len -= len;
 71 }
 72 
 73 int AudioResampling(AVCodecContext * audio_dec_ctx,
 74                     AVFrame * pAudioDecodeFrame,
 75                     int out_sample_fmt,
 76                     int out_channels,
 77                     int out_sample_rate,
 78                     uint8_t* out_buf)
 79 {
 80     SwrContext * swr_ctx = NULL;
 81     int data_size = 0;
 82     int ret = 0;
 83     int64_t src_ch_layout = audio_dec_ctx->channel_layout;
 84     int64_t dst_ch_layout = AV_CH_LAYOUT_STEREO;
 85     int dst_nb_channels = 0;
 86     int dst_linesize = 0;
 87     int src_nb_samples = 0;
 88     int dst_nb_samples = 0;
 89     int max_dst_nb_samples = 0;
 90     uint8_t **dst_data = NULL;
 91     int resampled_data_size = 0;
 92 
 93     swr_ctx = swr_alloc();
 94     if (!swr_ctx)
 95     {
 96         printf("swr_alloc error \n");
 97         return -1;
 98     }
 99 
100     src_ch_layout = (audio_dec_ctx->channels ==
101                      av_get_channel_layout_nb_channels(audio_dec_ctx->channel_layout)) ?
102                      audio_dec_ctx->channel_layout :
103                      av_get_default_channel_layout(audio_dec_ctx->channels);
104 
105     if (out_channels == 1)
106     {
107         dst_ch_layout = AV_CH_LAYOUT_MONO;
108         //printf("dst_ch_layout: AV_CH_LAYOUT_MONO\n");
109     }
110     else if (out_channels == 2)
111     {
112         dst_ch_layout = AV_CH_LAYOUT_STEREO;
113         //printf("dst_ch_layout: AV_CH_LAYOUT_STEREO\n");
114     }
115     else
116     {
117         dst_ch_layout = AV_CH_LAYOUT_SURROUND;
118         //printf("dst_ch_layout: AV_CH_LAYOUT_SURROUND\n");
119     }
120 
121     if (src_ch_layout <= 0)
122     {
123         printf("src_ch_layout error \n");
124         return -1;
125     }
126 
127     src_nb_samples = pAudioDecodeFrame->nb_samples;
128     if (src_nb_samples <= 0)
129     {
130         printf("src_nb_samples error \n");
131         return -1;
132     }
133 
134     av_opt_set_int(swr_ctx, "in_channel_layout", src_ch_layout, 0);
135     av_opt_set_int(swr_ctx, "in_sample_rate", audio_dec_ctx->sample_rate, 0);
136     av_opt_set_sample_fmt(swr_ctx, "in_sample_fmt", audio_dec_ctx->sample_fmt, 0);
137 
138     av_opt_set_int(swr_ctx, "out_channel_layout", dst_ch_layout, 0);
139     av_opt_set_int(swr_ctx, "out_sample_rate", out_sample_rate, 0);
140     av_opt_set_sample_fmt(swr_ctx, "out_sample_fmt", (AVSampleFormat)out_sample_fmt, 0);
141 
142     if ((ret = swr_init(swr_ctx)) < 0) {
143         printf("Failed to initialize the resampling context\n");
144         return -1;
145     }
146 
147     max_dst_nb_samples = dst_nb_samples = av_rescale_rnd(src_nb_samples,
148                                                          out_sample_rate, audio_dec_ctx->sample_rate, AV_ROUND_UP);
149     if (max_dst_nb_samples <= 0)
150     {
151         printf("av_rescale_rnd error \n");
152         return -1;
153     }
154 
155     dst_nb_channels = av_get_channel_layout_nb_channels(dst_ch_layout);
156     ret = av_samples_alloc_array_and_samples(&dst_data, &dst_linesize, dst_nb_channels,
157                                              dst_nb_samples, (AVSampleFormat)out_sample_fmt, 0);
158     if (ret < 0)
159     {
160         printf("av_samples_alloc_array_and_samples error \n");
161         return -1;
162     }
163 
164 
165     dst_nb_samples = av_rescale_rnd(swr_get_delay(swr_ctx, audio_dec_ctx->sample_rate) +
166                                     src_nb_samples, out_sample_rate, audio_dec_ctx->sample_rate, AV_ROUND_UP);
167     if (dst_nb_samples <= 0)
168     {
169         printf("av_rescale_rnd error \n");
170         return -1;
171     }
172     if (dst_nb_samples > max_dst_nb_samples)
173     {
174         av_free(dst_data[0]);
175         ret = av_samples_alloc(dst_data, &dst_linesize, dst_nb_channels,
176                                dst_nb_samples, (AVSampleFormat)out_sample_fmt, 1);
177         max_dst_nb_samples = dst_nb_samples;
178     }
179 
180     if (swr_ctx)
181     {
182         ret = swr_convert(swr_ctx, dst_data, dst_nb_samples,
183                           (const uint8_t **)pAudioDecodeFrame->data, pAudioDecodeFrame->nb_samples);
184         if (ret < 0)
185         {
186             printf("swr_convert error \n");
187             return -1;
188         }
189 
190         resampled_data_size = av_samples_get_buffer_size(&dst_linesize, dst_nb_channels,
191                                                          ret, (AVSampleFormat)out_sample_fmt, 1);
192         if (resampled_data_size < 0)
193         {
194             printf("av_samples_get_buffer_size error \n");
195             return -1;
196         }
197     }
198     else
199     {
200         printf("swr_ctx null error \n");
201         return -1;
202     }
203 
204     memcpy(out_buf, dst_data[0], resampled_data_size);
205 
206     if (dst_data)
207     {
208         av_freep(&dst_data[0]);
209     }
210     av_freep(&dst_data);
211     dst_data = NULL;
212 
213     if (swr_ctx)
214     {
215         swr_free(&swr_ctx);
216     }
217     return resampled_data_size;
218 }
219 
220 //创建一个全局的结构体变量以便于我们从文件中得到的声音包有地方存
221 //放同时也保证SDL中的声音回调函数audio_callback 能从这个地方得到声音数据
222 typedef struct PacketQueue{
223     AVPacketList *first_pkt, *last_pkt;
224     int nb_packets;
225     int size;
226     SDL_mutex *mutex;//因为SDL 是在一个独立的线程中来进行音频处理的。如果我们没有正确的锁定这个队列,我们有可能把数据搞乱。
227     SDL_cond *cond;
228 }PacketQueue;
229 
230 PacketQueue audioq;
231 PacketQueue videoq;
232 queue<AVFrame *> frameq;
233 
234 void packet_queue_init(PacketQueue *pq){
235     memset(pq, 0, sizeof(PacketQueue));
236     pq->mutex = SDL_CreateMutex();
237     pq->cond = SDL_CreateCond();
238 }
239 
240 int packet_queue_put(PacketQueue *q, AVPacket *pkt){
241     AVPacketList *pkt1;
242     /*(
243     if (av_dup_packet(pkt) < 0){
244         printf("error");
245         return -1;
246     }
247     */
248 
249     pkt1 = (AVPacketList*)av_malloc(sizeof(AVPacketList));
250     if (!pkt1){
251         printf("error");
252         return -1;
253     }
254 
255     av_copy_packet(&pkt1->pkt, pkt);
256     av_free_packet(pkt);
257     pkt1->next = NULL;
258 
259     //函数SDL_LockMutex()锁定队列的互斥量以便于我们向队列中添加东西,然后函
260     //数SDL_CondSignal()通过我们的条件变量为一个接 收函数(如果它在等待)发
261     //出一个信号来告诉它现在已经有数据了,接着就会解锁互斥量并让队列可以自由
262     //访问。
263     SDL_LockMutex(q->mutex);
264 
265     if (!q->last_pkt)//队列为空
266         q->first_pkt = pkt1;
267     else//队列不为空
268         q->last_pkt->next = pkt1;
269     q->last_pkt = pkt1;
270     q->nb_packets++;
271     q->size += pkt1->pkt.size;
272     SDL_CondSignal(q->cond);
273 
274     SDL_UnlockMutex(q->mutex);
275 
276     return 0;
277 }
278 
279 int decode_interrupt_cb(void){
280     return quit;
281 }
282 
283 static int packet_queue_get(PacketQueue *q, AVPacket *pkt, int block){
284     AVPacketList *pkt1;
285     int ret;
286 
287     SDL_LockMutex(q->mutex);
288 
289     for (;;){
290         if (quit){
291             ret = -1;
292             break;
293         }
294 
295         pkt1 = q->first_pkt;
296         if (pkt1){
297             q->first_pkt = pkt1->next;
298             if (!q->first_pkt)
299                 q->last_pkt = NULL;
300             q->nb_packets--;
301             q->size -= pkt1->pkt.size;
302             //*pkt = pkt1->pkt;
303             av_copy_packet(pkt, &pkt1->pkt);
304             av_free_packet(&pkt1->pkt);
305             av_free(pkt1);
306             ret = 1;
307             break;
308         }
309         else if (!block){
310             ret = 0;
311             break;
312         }
313         else{
314             SDL_CondWait(q->cond, q->mutex);
315         }
316     }
317     SDL_UnlockMutex(q->mutex);
318     return ret;
319 }
320 
321 int audio_decode_frame(AVCodecContext *aCodecCtx, uint8_t *audio_buf, int buf_size){
322     static AVPacket pkt;
323     static uint8_t *audio_pkt_data = NULL;
324     static int audio_pkt_size = 0;
325 
326     int len1, data_size, ret = 0;
327 
328     static AVFrame *pFrame;
329     pFrame = av_frame_alloc();
330 
331     /*if (packet_queue_get(&audioq, &pkt, 1) < 0){//从这里开始,取得main线程放入队列的包
332     printf("error, can't get packet from the queue");
333     return -1;
334     }
335 
336     len1 = avcodec_decode_audio4(aCodecCtx, pFrame, &ret, &pkt);
337     if (len1 < 0)
338     return -1;
339 
340     return AudioResampling(aCodecCtx, pFrame, AV_SAMPLE_FMT_S16, 2, 44100, audio_buf);*/
341     for (;;){
342         while (audio_pkt_size > 0){
343             data_size = buf_size;
344             len1 = avcodec_decode_audio4(aCodecCtx, pFrame, &ret, &pkt);
345 
346             //len1 = avcodec_decode_audio3(aCodecCtx, (int16_t *)audio_buf,
347             //    &data_size, &pkt);
348             if (len1 < 0){//if error, skip frame
349                 printf("error\n");
350                 audio_pkt_size = 0;
351                 break;
352             }
353             data_size = AudioResampling(aCodecCtx, pFrame, AV_SAMPLE_FMT_S16, 2, 44100, audio_buf);
354             audio_pkt_data += len1;
355             audio_pkt_size -= len1;
356             if (data_size <= 0)//No data yet, get more frames
357                 continue;
358             return data_size;
359         }
360         //if (pkt.data)
361             av_free_packet(&pkt);
362         if (quit)
363             return -1;
364         if (packet_queue_get(&audioq, &pkt, 1) < 0){//从这里开始,取得main线程放入队列的包
365             printf("error, can't get packet from the queue");
366             return -1;
367         }
368 
369         //SDL_LockMutex(g_pVideoMutex);
370         audio_pts = pkt.pts;
371         audio_dts = pkt.dts;
372         //SDL_UnlockMutex(g_pVideoMutex);
373 
374         audio_pkt_data = pkt.data;
375         audio_pkt_size = pkt.size;
376     }
377 }
378 //声音回调函数
379 //userdata是输入,stream是输出,len是输入,len的值一般为4096(调试中发现的),
380 //audio_callback函数的功能是调用audio_decode_frame函数,把解码后数据块audio_buf追加在stream的后面,
381 //通过SDL库对audio_callback的不断调用,不断解码数据,然后放到stream的末尾,
382 //SDL库认为stream中数据够播放一帧音频了,就播放它, 
383 //第三个参数len是向stream中写数据的内存分配尺度,是分配给audio_callback函数写入缓存大小。
384 void audio_callback(void *userdata, Uint8 *stream, int len){
385     //SDL_memset(stream, 0, len);
386     AVCodecContext *aCodecCtx = (AVCodecContext*)userdata;
387     int len1, audio_size;
388 
389     //audio_buf 的大小为 1.5 倍的声音帧的大    小以便于有一个比较好的缓冲
390     static uint8_t audio_buf[(AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 2];
391     static unsigned int audio_buf_size = 0;
392     static unsigned int audio_buf_index = 0;
393 
394     while (len > 0){
395         if (audio_buf_index >= audio_buf_size){//already send all our data, get more
396             audio_size = audio_decode_frame(aCodecCtx, audio_buf, sizeof(audio_buf));
397             if (audio_size < 0){//error, output silence
398                 printf("error, output silence\n");
399                 audio_buf_size = SDL_AUDIO_BUFFER_SIZE;
400                 memset(audio_buf, 0, audio_buf_size);
401             }
402             else
403                 audio_buf_size = audio_size;
404             audio_buf_index = 0;
405         }
406         len1 = audio_buf_size - audio_buf_index;
407         if (len1>len){
408             len1 = len;
409         }
410         memcpy(stream, (uint8_t *)audio_buf + audio_buf_index, len1);
411         len -= len1;
412         stream += len1;
413         audio_buf_index += len1;
414     }
415 }
416 
417 // 视频流处理线程
418 // 这里对齐是以音频流为准的
419 // 所以如果音频流(相对于视频流)过于稀疏的话,就会失去作用,效果不好
420 
421 int video_thread_proc(void *data)
422 {
423     video_thread_params *params = (video_thread_params *)data;
424     AVFrame *pFrame = NULL;
425     AVFrame *pNextFrame = NULL;
426     AVPacket packet = {0};
427     AVPacket nextpacket;
428 
429     // 注意,以下代码比较的都是 DTS(解压缩时间戳)而不是 PTS(显示时间戳)!!!
430     // 实际视频显示是以 PTS 为准的,但是 PTS 大多没有规律(只要是在解压之后就好),难以处理
431     // 所幸在相当一部分视频中 DTS 与 PTS 相差较小,所以这里就用 DTS 了
432 
433     while (!video_quit)
434     {
435         while (!frameq.empty())
436         {
437             if (pFrame == NULL)
438             {
439                 SDL_LockMutex(params->video_mutex);
440 
441                 // 这里采用了“连续读取”的方法,也即如果下一帧的 DTS 小于当前基准(目前采用音频流),则循环直至找到第一个 DTS 比基准大的
442                 // 然后播放小于基准而且离基准最近的帧,并缓存下一帧
443                 // 由于处理时间较长,而且使用了互斥体(看,音频部分也用到了),所以可能有极少数的音频帧有异常(噪声啊之类的)
444                 // 当然也可以将这一段注释掉,采用 pFrame = frameq.count(); frameq.pop();,同时解除 if (packet_queue_get()) 的注释(和下面的一对大括号)
445                 // 但是无跳过的方法会导致视频严重滞后
446 
447                 if (pNextFrame != NULL)
448                 {
449                     pFrame = pNextFrame;
450                     SDL_memcpy(&packet, &nextpacket, sizeof(AVPacket));
451                     pNextFrame = NULL;
452                 }
453                 else
454                 {
455                     pFrame = frameq.front();
456                     frameq.pop();
457                 }
458                 while (!frameq.empty())
459                 {
460                     pNextFrame = frameq.front();
461                     frameq.pop();
462                     packet_queue_get(&videoq, &nextpacket, 1);
463 
464                     if (nextpacket.dts <= audio_dts)
465                     {
466                         av_free_packet(&packet);
467                         av_frame_free(&pFrame);
468                         SDL_memcpy(&packet, &nextpacket, sizeof(AVPacket));
469                         pFrame = pNextFrame;
470                         pNextFrame = NULL;
471                     }
472                     else
473                     {
474                         break;
475                     }
476                 }
477 
478                 
479                 //pFrame = frameq.front();
480                 //frameq.pop();
481                 
482 
483                 //cout << "vdts: " << packet.dts << " adts: " << audio_dts << endl;
484 
485                 //if (packet_queue_get(&videoq, &packet, 1) >= 0)
486                 //{
487                     sws_scale(params->sws_context, (const uint8_t* const*)pFrame->data,
488                               pFrame->linesize, 0, params->vid_codec_context->height, g_pFrameYUV->data, g_pFrameYUV->linesize);
489 
490                     SDL_UpdateYUVTexture(params->texture, NULL, g_pFrameYUV->data[0], g_pFrameYUV->linesize[0],
491                                          g_pFrameYUV->data[1], g_pFrameYUV->linesize[1], g_pFrameYUV->data[2], g_pFrameYUV->linesize[2]);
492 
493                     //SDL_RenderClear(params->renderer);
494                     SDL_RenderCopy(params->renderer, params->texture, NULL, NULL);
495                     SDL_RenderPresent(params->renderer);
496                     // 可以使用 av_frame_clone() + 队列 实现“远程”dts 读取
497                     //if (params->vid_codec_context->refcounted_frames)
498                     //{
499                     //    av_frame_unref(pFrame);
500                     //}
501                 //}
502 
503                 //cout << "--------------------------------------------------" << endl;
504                 //cout << "vidpts: " << packet.pts << " audpts: " << audio_pts << endl;
505                 //cout << "viddts: " << packet.dts << " auddts: " << audio_dts << endl;
506 
507                 SDL_UnlockMutex(params->video_mutex);
508             }
509             else
510             {
511                 //cout << "vdts: " << packet.dts << " adts: " << audio_dts << endl;
512 
513                 // 如果当前帧应该用被缓存的帧,那就用,不要重新读取了
514 
515                 sws_scale(params->sws_context, (const uint8_t* const*)pFrame->data,
516                           pFrame->linesize, 0, params->vid_codec_context->height, g_pFrameYUV->data, g_pFrameYUV->linesize);
517 
518                 SDL_UpdateYUVTexture(params->texture, NULL, g_pFrameYUV->data[0], g_pFrameYUV->linesize[0],
519                                      g_pFrameYUV->data[1], g_pFrameYUV->linesize[1], g_pFrameYUV->data[2], g_pFrameYUV->linesize[2]);
520 
521                 //SDL_RenderClear(params->renderer);
522                 SDL_RenderCopy(params->renderer, params->texture, NULL, NULL);
523                 SDL_RenderPresent(params->renderer);
524                 // 可以使用 av_frame_clone() + 队列 实现“远程”dts 读取
525                 if (params->vid_codec_context->refcounted_frames)
526                 {
527                     av_frame_unref(pFrame);
528                 }
529 
530                 // 如果该帧是在音频帧之前的,那就销毁它,和数据包
531                 if (packet.dts <= audio_dts)
532                 {
533                     av_frame_free(&pFrame);
534                     av_free_packet(&packet);
535                     pFrame = NULL;
536                 }
537             }
538 
539         }
540     }
541 
542     return 0;
543 }
544 
545 int main(int argc, char *argv[])
546 {
547     av_register_all();    //注册了所有的文件格式和编解码的库,它们将被自动的使用在被打开的合适格式的文件上
548     AVFormatContext *pFormatCtx;
549     pFormatCtx = avformat_alloc_context();
550 
551     if (argc < 2) return 0;
552     //Open an input stream and read the header
553     if (avformat_open_input(&pFormatCtx, argv[1], NULL, NULL) != 0){
554         printf("Can't open the file\n");
555         return -1;
556     }
557     //Retrieve stream information
558     if (avformat_find_stream_info(pFormatCtx, NULL) < 0){
559         printf("Couldn't find stream information.\n");
560         return -1;
561     }
562 
563     //output file information
564     cout << "文件信息----------------------------------" << endl;
565     av_dump_format(pFormatCtx, 0, argv[1], 0);
566     cout << "--------------------------------------------" << endl;
567 
568     int i, videoIndex, audioIndex;
569 
570     //Find the first video stream
571     videoIndex = -1;
572     audioIndex = -1;
573     for (i = 0; i < pFormatCtx->nb_streams; i++){//视音频流的个数
574         if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO
575             && videoIndex < 0){
576             videoIndex = i;
577         }
578         if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO
579             && audioIndex < 0)
580             audioIndex = i;
581     }
582 
583     if (videoIndex == -1)
584         return -1;
585     if (audioIndex == -1)
586         return -1;
587 
588     AVCodecContext *pCodecCtx, *paCodecCtx;
589     AVCodec *pCodec, *paCodec;
590     //Get a pointer to the codec context for the video stream
591     //流中关于编解码器的信息就是被我们叫做"codec context"(编解码器上下文)
592     //的东西。这里面包含了流中所使用的关于编解码器的所有信
593     pCodecCtx = pFormatCtx->streams[videoIndex]->codec;
594     // 帧引用:打开,见 AVFrame 的注释
595     pCodecCtx->refcounted_frames = 1;
596     paCodecCtx = pFormatCtx->streams[audioIndex]->codec;
597     //Find the decoder for the video stream
598     pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
599     paCodec = avcodec_find_decoder(paCodecCtx->codec_id);
600 
601     if (pCodec == NULL || paCodecCtx == NULL){
602         printf("Unsupported codec!\n");
603         return -1;
604     }
605     //Open codec
606     if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0){
607         printf("Could not open video codec.\n");
608         return -1;
609     }
610     if (avcodec_open2(paCodecCtx, paCodec, NULL) < 0){
611         printf("Could not open audio codec.\n");
612         return -1;
613     }
614 
615     //--------------------------------------------------------//
616 
617     printf("比特率 %3d\n", pFormatCtx->bit_rate);
618     printf("解码器名称 %s\n", paCodecCtx->codec->long_name);
619     printf("time_base  %d \n", paCodecCtx->time_base);
620     printf("声道数  %d \n", paCodecCtx->channels);
621     printf("sample per second  %d \n", paCodecCtx->sample_rate);
622     //--------------------------------------------------------//
623 
624     //allocate video frame and set its fileds to default value
625     AVFrame *pFrame;
626     //AVFrame *pFrameYUV;
627     pFrame = av_frame_alloc();
628     g_pFrameYUV = av_frame_alloc();
629 
630     //即使我们申请了一帧的内存,当转换的时候,我们仍然需要一个地方来放置原始
631     //的数据。我们使用avpicture_get_size 来获得我们需要的大小, 然后手工申请
632     //内存空间:
633     uint8_t *out_buffer;
634     int numBytes;
635     numBytes = avpicture_get_size(PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height);
636     //av_malloc 是ffmpeg 的malloc,用来实现一个简单的malloc 的包装,这样来保
637     //证内存地址是对齐的(4 字节对齐或者2 字节对齐)。它并不能保 护你不被内
638     //存泄漏,重复释放或者其它malloc 的问题所困扰。
639     out_buffer = (uint8_t *)av_malloc(numBytes*sizeof(uint8_t));
640     //Assign appropriate parts of buffer to image planes in pFrameYUV
641     //Note that pFrameYUV is an AVFrame, but AVFrame is a superset of AVPicture
642     avpicture_fill((AVPicture*)g_pFrameYUV, out_buffer, PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height);
643 
644     //----------------SDL--------------------------------------//
645     if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)){
646         printf("Could not initialize SDL -%s\n", SDL_GetError());
647         exit(1);
648     }
649     //先设置声音的选项:采样率,声音通道数和其它的参 数,然后我们
650     //设置一个回调函数和一些用户数据userdata。当开始播放音频的时候,SDL 将不
651     //断地调用这个回调函数并且要求它来向声音缓冲填入一个特定的数量的字节。
652     //当我们把这些信息放到SDL_AudioSpec 结构体中后,我们调用函数
653     //SDL_OpenAudio()就会打开声音设备并且给我们送 回另外一个AudioSpec 结构
654     //体。这个结构体是我们实际上用到的--因为我们不能保证得到我们所要求的。
655     SDL_AudioSpec wanted_spec;
656     wanted_spec.freq = paCodecCtx->sample_rate;
657     wanted_spec.format = AUDIO_S16SYS;
658     wanted_spec.channels = paCodecCtx->channels;    //声音的通道数
659     wanted_spec.silence = 0;    //用来表示静音的值
660     wanted_spec.samples = SDL_AUDIO_BUFFER_SIZE;    //声音缓冲区的大小
661     wanted_spec.callback = audio_callback;
662     wanted_spec.userdata = paCodecCtx;
663 
664     if (SDL_OpenAudio(&wanted_spec, NULL) < 0){
665         printf("SDL_OpenAudio error: %s\n", SDL_GetError());
666         return -1;
667     }
668 
669     packet_queue_init(&audioq);
670     packet_queue_init(&videoq);
671     SDL_PauseAudio(0);
672 
673     SDL_Window *window = nullptr;
674     window = SDL_CreateWindow("MPlayer", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
675                               pCodecCtx->width, pCodecCtx->height, SDL_WINDOW_SHOWN);
676     if (!window){
677         cout << SDL_GetError() << endl;
678         return 1;
679     }
680 
681     SDL_Renderer *ren = nullptr;
682     ren = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
683     if (ren == nullptr){
684         cout << SDL_GetError() << endl;
685         return -1;
686     }
687 
688     SDL_Texture *texture = nullptr;
689     texture = SDL_CreateTexture(ren, SDL_PIXELFORMAT_YV12,
690                                 SDL_TEXTUREACCESS_STREAMING, pCodecCtx->width, pCodecCtx->height);
691 
692     //*************************************************************//
693     //通过读取包来读取整个视频流,然后把它解码成帧,最后转换格式并且保存
694     int frameFinished;
695     //int psize = pCodecCtx->width * pCodecCtx->height;
696     AVPacket packet;
697     av_new_packet(&packet, numBytes);
698 
699     i = 0;
700     int ret;
701     static struct SwsContext *img_convert_ctx;
702     img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height,
703                                      pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height, PIX_FMT_YUV420P,
704                                      SWS_BICUBIC, NULL, NULL, NULL);
705 
706     SDL_Event ev;
707 
708     video_thread_params vtp;
709     vtp.renderer = ren;
710     vtp.texture = texture;
711     vtp.sws_context = img_convert_ctx;
712     vtp.vid_codec_context = pCodecCtx;
713     vtp.video_mutex = SDL_CreateMutex();
714     g_pVideoMutex = vtp.video_mutex;
715     g_pVideoThread = SDL_CreateThread(video_thread_proc, "video_thread", &vtp);
716 
717     double v_a_ratio;            // 视频帧数/音频帧数
718     int frame_queue_size;
719 
720     //Read the next frame of a stream
721     while ((!quit) && (av_read_frame(pFormatCtx, &packet) >= 0 || (!frameq.empty())))
722     {
723         //Is this a packet from the video stream?
724         if (packet.stream_index == videoIndex)
725         {
726             //decode video frame of size packet.size from packet.data into picture
727             ret = avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &packet);
728             //Did we get a video frame?
729             if (ret >= 0)
730             {
731                 //Convert the image from its native format to YUV
732                 if (frameFinished)
733                 {
734                     /*
735                     sws_scale(img_convert_ctx, (const uint8_t* const*)pFrame->data,
736                     pFrame->linesize, 0, pCodecCtx->height, g_pFrameYUV->data, g_pFrameYUV->linesize);
737 
738                     SDL_UpdateYUVTexture(texture, NULL, g_pFrameYUV->data[0], g_pFrameYUV->linesize[0],
739                     g_pFrameYUV->data[1], g_pFrameYUV->linesize[1], g_pFrameYUV->data[2], g_pFrameYUV->linesize[2]);
740 
741                     AVFrame *pNewFrame = av_frame_clone(pFrame);
742                     frameq.push(pNewFrame);
743                     packet_queue_put(&videoq, &packet);
744 
745                     SDL_RenderClear(ren);
746                     SDL_RenderCopy(ren, texture, NULL, NULL);
747                     SDL_RenderPresent(ren);
748                     // 可以使用 av_frame_clone() + 队列 实现远程 dts 读取
749                     if (pCodecCtx->refcounted_frames)
750                     {
751                     av_frame_unref(pFrame);
752                     }
753                     cout << "<<<<<<" << endl;
754                     cout << "vpts: " << packet.pts << ", apts: " << audio_pts << endl;
755                     cout << "vdts: " << packet.dts << ", adts: " << audio_dts << endl;
756                     */
757                     // 用互斥体保持同步,将包送入队列,让另外的线程自己处理
758                     SDL_LockMutex(vtp.video_mutex);
759                     packet_queue_put(&videoq, &packet);
760                     AVFrame *pNewFrame = av_frame_clone(pFrame);
761                     frameq.push(pNewFrame);
762                     //cout << "Pushing vpacket." << endl;
763                     SDL_UnlockMutex(vtp.video_mutex);
764                 }
765                 // 注意这里也必须要 free packet,否则会导致严重内存泄露
766                 // 我修改了 packet_queue_put() 函数,它会复制 packet,所以可以放心释放上
767                 av_free_packet(&packet);
768             }
769             else{
770                 av_free_packet(&packet);
771                 cout << "decode error" << endl;
772                 return -1;
773             }
774         }
775         else if (packet.stream_index == audioIndex){
776             //packet_queue_put(&audioq, &packet);
777             /*ret = avcodec_decode_audio4(paCodecCtx, pFrame, &frameFinished, &packet);
778             cout << pFrame->format << endl;
779 
780             if (ret < 0){
781             printf("Error in decoding audio frame\n");
782             exit(0);
783             }
784             if (frameFinished){
785             printf("pts %5d\n", packet.pts);
786             printf("dts %5d\n", packet.dts);
787             printf("packet_size %5d\n", packet.size);
788             }
789             audio_chunk = (Uint8*)pFrame->data[0];
790             audio_len = pFrame->linesize[0];
791 
792             audio_pos = audio_chunk;
793             //SDL_PauseAudio(0);
794             while (audio_len>0)
795             SDL_Delay(1);*/
796             packet_queue_put(&audioq, &packet);
797         }
798         else
799         {
800             av_free_packet(&packet);
801         }
802 
803 process_sdl_events:
804         if (SDL_PollEvent(&ev))
805         {
806             switch (ev.type){
807                 case SDL_QUIT:
808                 {
809                     quit = 1;
810                     video_quit = 1;
811                     SDL_Quit();
812                     goto exit_line;
813                     break;
814                 }
815                 case SDL_KEYDOWN:
816                     if (ev.key.keysym.scancode == SDL_SCANCODE_ESCAPE)
817                     {
818                         quit = 1;
819                         video_quit = 1;
820                         SDL_Quit();
821                         goto exit_line;
822                         break;
823                     }
824                 default:
825                     break;
826             }
827         }
828 
829         //cout << "vframes: " << pCodecCtx->frame_number << " aframes: " << paCodecCtx->frame_number << endl;
830         
831         // 设置一个缓冲区大小,如果超过此大小则暂停处理(视频和音频)帧
832         // 这里采用限制 frameq 的大小的方法
833         // 如果采用以下的动态代码,有可能导致结尾“崩坏”,囧
834         /*
835         if (paCodecCtx->frame_number == 0)
836         {
837             v_a_ratio = 300;        // 一个很大的值,基本上能保证所有视频都能解码
838         }
839         else
840         {
841             v_a_ratio = pCodecCtx->frame_number / (double)paCodecCtx->frame_number;
842             if (v_a_ratio < 10.0) v_a_ratio = 10.0;
843         }
844 
845         frame_queue_size = (int)v_a_ratio * 2;
846         if (frameq.size() > frame_queue_size) goto process_sdl_events;
847         */
848         if (frameq.size() > 50) goto process_sdl_events;
849     }
850 
851 exit_line:
852 
853     SDL_DestroyMutex(vtp.video_mutex);
854 
855     SDL_DestroyTexture(texture);
856 
857     av_frame_free(&pFrame);
858     av_frame_free(&g_pFrameYUV);
859 
860     avcodec_close(pCodecCtx);
861 
862     avformat_close_input(&pFormatCtx);
863 
864     return 0;
865 }

 

posted @ 2014-04-03 16:54  Grid Science  阅读(4561)  评论(0编辑  收藏  举报