• 博客园第一篇——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 
    ");
     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
    ");
    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
    ");
    114     }
    115     else
    116     {
    117         dst_ch_layout = AV_CH_LAYOUT_SURROUND;
    118         //printf("dst_ch_layout: AV_CH_LAYOUT_SURROUND
    ");
    119     }
    120 
    121     if (src_ch_layout <= 0)
    122     {
    123         printf("src_ch_layout error 
    ");
    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 
    ");
    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
    ");
    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 
    ");
    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 
    ");
    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 
    ");
    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 
    ");
    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 
    ");
    195             return -1;
    196         }
    197     }
    198     else
    199     {
    200         printf("swr_ctx null error 
    ");
    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
    ");
    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
    ");
    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
    ");
    555         return -1;
    556     }
    557     //Retrieve stream information
    558     if (avformat_find_stream_info(pFormatCtx, NULL) < 0){
    559         printf("Couldn't find stream information.
    ");
    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!
    ");
    603         return -1;
    604     }
    605     //Open codec
    606     if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0){
    607         printf("Could not open video codec.
    ");
    608         return -1;
    609     }
    610     if (avcodec_open2(paCodecCtx, paCodec, NULL) < 0){
    611         printf("Could not open audio codec.
    ");
    612         return -1;
    613     }
    614 
    615     //--------------------------------------------------------//
    616 
    617     printf("比特率 %3d
    ", pFormatCtx->bit_rate);
    618     printf("解码器名称 %s
    ", paCodecCtx->codec->long_name);
    619     printf("time_base  %d 
    ", paCodecCtx->time_base);
    620     printf("声道数  %d 
    ", paCodecCtx->channels);
    621     printf("sample per second  %d 
    ", 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
    ", 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
    ", 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
    ");
    782             exit(0);
    783             }
    784             if (frameFinished){
    785             printf("pts %5d
    ", packet.pts);
    786             printf("dts %5d
    ", packet.dts);
    787             printf("packet_size %5d
    ", 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 }
  • 相关阅读:
    JS文本框下拉提示效果
    JS动态添加删除表格行
    JS验证 数字 电话号码 传真 邮箱 手机号码 邮编 日期
    TreeView 中CheckBox级联选中问题
    HashTable Dictionary
    JS操作Frame对象
    Winfrom 中怎样在回车时设置焦点
    Word 操作(未完待续)
    HTML5特性——prefetching预加载功能
    10个实用的 jQuery Mobile 插件推荐
  • 原文地址:https://www.cnblogs.com/GridScience/p/3643428.html
Copyright © 2020-2023  润新知