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

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

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

上个教程实现了视频的简单播放,但那是个哑巴电影,完全没有声音。

      这个教程第一次用到了SDL的线程,涉及到了两个线程间的同步协调,有几个地方需要特别留意,SDL_OpenAudio库函数会打开音频设备(0是恢 复,其他的是暂停),SDL_PauseAudio库函数可以暂停或者恢复audio_callback函数的执行,程序中的这行代码 “SDL_PauseAudio(0);”执行后,让audio_callback函数开始反复的被调用。在这之前audio_callback回调函数 还没有被调用。

audio_callback函数

     原型为void callback(void *userdata, Uint8 *stream, int len),userdata是输入,stream是输出,len是输入,len的值一般为4096(调试中发现的),audio_callback函数的 功能是调用audio_decode_frame函数,把解码后数据块audio_buf追加在stream的后面,通过SDL库对 audio_callback的不断调用,不断解码数据,然后放到stream的末尾,SDL库认为stream中数据够播放一帧音频了,就播放它,第三 个参数len是向stream中写数据的内存分配尺度,是分配给audio_callback函数写入缓存大小。

    假设len=4096,解码后数据块audio_buf的大小为4608,那么一次audio_callback调用不能把audio_buf中全部数据 写入stream末尾,就分两次,第一次先把audio_buf的前4096个字节写入stream末尾,第二次调用audio_callback函数 时,由于写缓存用光了,又分配4096个字节的缓存,再写剩余的512个字节到stream末尾,写缓存还剩余3584个字节留给下次 audio_callback调用使用。

audio_decode_frame函数

     原型:int audio_decode_frame(AVCodecContext *aCodecCtx, uint8_t *audio_buf, int buf_size)

     返回值:解码完一帧音频到缓存后,缓存占用的实际大小,以字节为单位,为负数表示失败

     aCodecCtx:输入,解码上下文

     audio_buf:输出,解码成功后,输出到的缓存的首地址

     buf_size:输入,audio_buf的预留空间

     该函数是实际上是从尾部开始执行的,先取得main线程放入队列的包,再用库函数avcodec_decode_audio2处理,如果一次调用没有处理 完一个包的数据,记录下处理到包的那个位置了,下次接着处理(这种情况可能是因为一个音频包,包含多个音频帧的数据引起)

库函数avcodec_decode_audio2

    原型:int avcodec_decode_audio2(AVCodecContext *avctx, int16_t *samples,
                         int *frame_size_ptr,
                         const uint8_t *buf, int buf_size);
    avctx : 解码器上下文
    samples: 输出参数  输出数据的缓存首地址.
    frame_size_ptr:既是输入又是输出,无帧可解返回0,解码失败返回负数,解码成功返回,解码后一帧音频所占空间,以字节为单位
    buf: 输入参数,输入数据的缓存
    buf_size:输入参数,buf的大小
    返回值:无帧可解返回0,解码失败返回负数,解码成功返回解码前一帧音频所占空间


SDL_CondWait库函数

    等待消息时解锁,等到消息后加锁,该函数可以阻塞代码的执行,一般和SDL_CondSignal库函数(或SDL_CondBroadcast库函数)配对使用

 

[cpp] view plaincopy
  1. // ffmpegExe.cpp: 主项目文件。  
  2.   
  3. #include "libavformat/avformat.h"  
  4. #include "libswscale/swscale.h"  
  5. #include <windows.h>  
  6.   
  7. #include <stdlib.h>  
  8. #include <stdio.h>  
  9. #include <string.h>  
  10. #include <math.h>  
  11. #include <SDL/SDL.h>  
  12. #include <SDL/SDL_thread.h>  
  13.   
  14. #ifdef main  
  15. #undef main  
  16. #endif  
  17.   
  18. #define SDL_AUDIO_BUFFER_SIZE 1024  
  19. static int sws_flags = SWS_BICUBIC;  
  20.   
  21. typedef struct PacketQueue  
  22. {  
  23.     AVPacketList *first_pkt, *last_pkt;  
  24.     int nb_packets;  
  25.     int size;  
  26.     SDL_mutex *mutex;  
  27.     SDL_cond *cond;  
  28. } PacketQueue;  
  29. PacketQueue audioq;  
  30. int quit = 0;  
  31. void packet_queue_init(PacketQueue *q)  
  32. {  
  33.     memset(q, 0, sizeof(PacketQueue));  
  34.     q->mutex = SDL_CreateMutex();  
  35.     q->cond = SDL_CreateCond();  
  36. }  
  37. int packet_queue_put(PacketQueue *q, AVPacket *pkt)  
  38. {  
  39.     AVPacketList *pkt1;  
  40.     if(av_dup_packet(pkt) < 0)  
  41.     {  
  42.         return -1;  
  43.     }  
  44.     pkt1 = (AVPacketList *)av_malloc(sizeof(AVPacketList));  
  45.     if (!pkt1)  
  46.         return -1;  
  47.     pkt1->pkt = *pkt;  
  48.     pkt1->next = NULL;  
  49.     SDL_LockMutex(q->mutex);  
  50.     if (!q->last_pkt)  
  51.         q->first_pkt = pkt1;  
  52.     else  
  53.         q->last_pkt->next = pkt1;  
  54.     q->last_pkt = pkt1;  
  55.     q->nb_packets++;  
  56.     q->size += pkt1->pkt.size;  
  57.     SDL_CondSignal(q->cond);  
  58.     SDL_UnlockMutex(q->mutex);  
  59.     return 0;  
  60. }  
  61. static int packet_queue_get(PacketQueue *q, AVPacket *pkt, int block)  
  62. {  
  63.     AVPacketList *pkt1;  
  64.     int ret;  
  65.     SDL_LockMutex(q->mutex);  
  66.     for(;;)  
  67.     {  
  68.         if(quit)  
  69.         {  
  70.             ret = -1;  
  71.             break;  
  72.         }  
  73.         pkt1 = q->first_pkt;  
  74.         if (pkt1)  
  75.         {  
  76.             q->first_pkt = pkt1->next;  
  77.             if (!q->first_pkt)  
  78.                 q->last_pkt = NULL;  
  79.             q->nb_packets--;  
  80.             q->size -= pkt1->pkt.size;  
  81.             *pkt = pkt1->pkt;  
  82.             av_free(pkt1);  
  83.             ret = 1;  
  84.             break;  
  85.         }  
  86.         else if (!block)  
  87.         {  
  88.             ret = 0;  
  89.             break;  
  90.         }  
  91.         else  
  92.         {  
  93.             SDL_CondWait(q->cond, q->mutex);  
  94.         }  
  95.     }  
  96.     SDL_UnlockMutex(q->mutex);  
  97.     return ret;  
  98. }  
  99.   
  100. int audio_decode_frame(AVCodecContext *aCodecCtx, uint8_t *audio_buf, int buf_size)  
  101. {  
  102.     static AVPacket pkt;  
  103.     static uint8_t *audio_pkt_data = NULL;  
  104.     static int audio_pkt_size = 0;  
  105.     int len1, data_size;  
  106.     for(;;)  
  107.     {  
  108.         while(audio_pkt_size > 0)  
  109.         {  
  110.             data_size = buf_size;  
  111.             len1 = avcodec_decode_audio2(aCodecCtx, (int16_t *)audio_buf, &data_size, audio_pkt_data, audio_pkt_size);  
  112.             if(len1 < 0)  
  113.             { /* if error, skip frame */  
  114.                 audio_pkt_size = 0;  
  115.                 break;  
  116.             }  
  117.             audio_pkt_data += len1;  
  118.             audio_pkt_size -= len1;  
  119.             if(data_size <= 0)  
  120.             { /* No data yet, get more frames */  
  121.                 continue;  
  122.             } /* We have data, return it and come back for more later */  
  123.             return data_size;  
  124.         }  
  125.         if(pkt.data)  
  126.             av_free_packet(&pkt);  
  127.         if(quit)  
  128.         {  
  129.             return -1;  
  130.         }  
  131.         if(packet_queue_get(&audioq, &pkt, 1) < 0)  
  132.         {  
  133.             return -1;  
  134.         }  
  135.         audio_pkt_data = pkt.data;  
  136.         audio_pkt_size = pkt.size;  
  137.     }  
  138. }  
  139.   
  140. void audio_callback(void *userdata, Uint8 *stream, int len)  
  141. {  
  142.     AVCodecContext *aCodecCtx = (AVCodecContext *)userdata;  
  143.     int len1, audio_size;  
  144.     static uint8_t audio_buf[(AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 2];  
  145.     static unsigned int audio_buf_size = 0;  
  146.     static unsigned int audio_buf_index = 0;  
  147.     while(len > 0)  
  148.     {  
  149.         if(audio_buf_index >= audio_buf_size)  
  150.         { /* We have already sent all our data; get more */  
  151.             audio_size = audio_decode_frame(aCodecCtx, audio_buf, sizeof(audio_buf));  
  152.             if(audio_size < 0)  
  153.             { /* If error, output silence */  
  154.                 audio_buf_size = 1024; // arbitrary?  
  155.                 memset(audio_buf, 0, audio_buf_size);  
  156.             }  
  157.             else  
  158.             {  
  159.                 audio_buf_size = audio_size;  
  160.             }  
  161.             audio_buf_index = 0;  
  162.         }  
  163.         len1 = audio_buf_size - audio_buf_index;  
  164.         if(len1 > len)  
  165.             len1 = len;  
  166.         memcpy(stream, (uint8_t *)audio_buf + audio_buf_index, len1);  
  167.         len -= len1;  
  168.         stream += len1;  
  169.         audio_buf_index += len1;  
  170.     }  
  171. }  
  172.   
  173. int main(int argc, char *argv[])  
  174. {  
  175.     AVFormatContext *pFormatCtx;  
  176.     int i, videoStream(-1), audioStream(-1);  
  177.     AVCodecContext *pCodecCtx;  
  178.     AVCodec *pCodec;  
  179.     AVFrame *pFrame;  
  180.     AVPacket packet;  
  181.     int frameFinished;  
  182.     float aspect_ratio;  
  183.     AVCodecContext *aCodecCtx;  
  184.     AVCodec *aCodec;  
  185.     SDL_Overlay *bmp;  
  186.     SDL_Surface *screen;  
  187.     SDL_Rect rect;  
  188.     SDL_Event event;  
  189.     SDL_AudioSpec wanted_spec, spec;  
  190.     if(argc < 2)  
  191.     {  
  192.         fprintf(stderr, "Usage: test /n");  
  193.         exit(1);  
  194.     }  
  195.   
  196.     av_register_all();  
  197.     pFormatCtx = av_alloc_format_context();  
  198.     if (!pFormatCtx) {  
  199.         fprintf(stderr, "Memory error/n");  
  200.         exit(1);  
  201.     }  
  202.     if(av_open_input_file(&pFormatCtx, argv[1], NULL, 0, NULL)!=0)  
  203.         return -1; // Couldn‘t open file  
  204.     if(av_find_stream_info(pFormatCtx)<0)  
  205.         return -1; // Couldn‘t find stream information  
  206.     // Dump information about file onto standard error  
  207.     dump_format(pFormatCtx, 0, argv[1], 0);  
  208.   
  209.     // Find the first video stream  
  210.     for(i=0; i<pFormatCtx->nb_streams; i++)  
  211.     {  
  212.         if(pFormatCtx->streams[i]->codec->codec_type==CODEC_TYPE_VIDEO && videoStream<0)  
  213.         {  
  214.             videoStream=i;  
  215.         }  
  216.         if(pFormatCtx->streams[i]->codec->codec_type==CODEC_TYPE_AUDIO && audioStream<0)  
  217.         {  
  218.             audioStream=i;  
  219.         }  
  220.     }  
  221.     if(videoStream==-1||audioStream==-1)  
  222.       return -1; // Didn‘t find a video stream  
  223.   
  224.     // Get a pointer to the codec context for the video stream  
  225.   
  226.     aCodecCtx=pFormatCtx->streams[audioStream]->codec;  
  227.     wanted_spec.freq = aCodecCtx->sample_rate;  
  228.     wanted_spec.format = AUDIO_S16SYS;  
  229.     wanted_spec.channels = aCodecCtx->channels;  
  230.     wanted_spec.silence = 0;  
  231.     wanted_spec.samples = SDL_AUDIO_BUFFER_SIZE;  
  232.     wanted_spec.callback = audio_callback;  
  233.     wanted_spec.userdata = aCodecCtx;  
  234.     if(SDL_OpenAudio(&wanted_spec, &spec) < 0)  
  235.     {  
  236.         fprintf(stderr, "SDL_OpenAudio: %s/n", SDL_GetError());  
  237.         return -1;  
  238.     }  
  239.     aCodec = avcodec_find_decoder(aCodecCtx->codec_id);  
  240.     if(!aCodec)  
  241.     {  
  242.         fprintf(stderr, "Unsupported codec!/n"); return -1;  
  243.     }  
  244.     avcodec_open(aCodecCtx, aCodec); // audio_st = pFormatCtx->streams[index]  
  245.     packet_queue_init(&audioq);  
  246.     SDL_PauseAudio(0);  
  247.   
  248.     pCodecCtx=pFormatCtx->streams[videoStream]->codec;  
  249.     pCodec=avcodec_find_decoder(pCodecCtx->codec_id);  
  250.     if(pCodec==NULL)  
  251.     {  
  252.         fprintf(stderr, "Unsupported codec!/n");  
  253.         return -1; // Codec not found  
  254.     }  
  255.     // Open codec  
  256.     if(avcodec_open(pCodecCtx, pCodec)<0)  
  257.         return -1; // Could not open codec  
  258.   
  259.     // Allocate video frame  
  260.     pFrame=avcodec_alloc_frame();  
  261.     // Allocate an AVFrame structure  
  262.     uint8_t *buffer;  
  263.     int numBytes;  
  264.     // Determine required buffer size and allocate buffer  
  265.     numBytes=avpicture_get_size(PIX_FMT_RGB24, pCodecCtx->width,  
  266.         pCodecCtx->height);  
  267.     buffer=(uint8_t *)av_malloc(numBytes*sizeof(uint8_t));  
  268.   
  269.     // Assign appropriate parts of buffer to image planes in pFrameRGB  
  270.     if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER))  
  271.     {  
  272.         fprintf(stderr, "Could not initialize SDL - %s/n", SDL_GetError());  
  273.         exit(1);  
  274.     }  
  275.   
  276. #ifndef __DARWIN__  
  277.     screen = SDL_SetVideoMode(pCodecCtx->width, pCodecCtx->height, 0, 0);  
  278. #else  
  279.     screen = SDL_SetVideoMode(pCodecCtx->width, pCodecCtx->height, 24, 0);  
  280. #endif  
  281.     if(!screen)  
  282.     {  
  283.         fprintf(stderr, "SDL: could not set video mode - exiting/n");  
  284.         exit(1);  
  285.     }  
  286.   
  287.     bmp = SDL_CreateYUVOverlay(pCodecCtx->width, pCodecCtx->height,  
  288.         SDL_YV12_OVERLAY, screen);  
  289.   
  290.     static struct SwsContext *img_convert_ctx;  
  291.     if (img_convert_ctx == NULL)  
  292.     {  
  293.         img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height,  
  294.                                          pCodecCtx->pix_fmt,  
  295.                                          pCodecCtx->width, pCodecCtx->height,  
  296.                                          PIX_FMT_YUV420P,  
  297.                                          sws_flags, NULL, NULL, NULL);  
  298.         if (img_convert_ctx == NULL)  
  299.         {  
  300.             fprintf(stderr, "Cannot initialize the conversion context/n");  
  301.             exit(1);  
  302.         }  
  303.     }  
  304.     i=0;  
  305.     while(av_read_frame(pFormatCtx, &packet)>=0)  
  306.     {  
  307.         // Is this a packet from the video stream?  
  308.         if(packet.stream_index==videoStream)  
  309.         {  
  310.             // Decode video frame  
  311.             avcodec_decode_video(pCodecCtx, pFrame, &frameFinished,  
  312.                 packet.data, packet.size);  
  313.             // Did we get a video frame?  
  314.             if(frameFinished)  
  315.             {  
  316.                 SDL_LockYUVOverlay(bmp);  
  317.                 AVPicture pict;  
  318.                 pict.data[0] = bmp->pixels[0];  
  319.                 pict.data[1] = bmp->pixels[2];  
  320.                 pict.data[2] = bmp->pixels[1];  
  321.   
  322.                 pict.linesize[0] = bmp->pitches[0];  
  323.                 pict.linesize[1] = bmp->pitches[2];  
  324.                 pict.linesize[2] = bmp->pitches[1];  
  325.   
  326.                 // Convert the image into YUV format that SDL uses  
  327.                 /*img_convert(&pict, PIX_FMT_YUV420P, 
  328.                     (AVPicture *)pFrame, pCodecCtx->pix_fmt, 
  329.                     pCodecCtx->width, pCodecCtx->height);*/  
  330.                 sws_scale(img_convert_ctx, pFrame->data, pFrame->linesize,  
  331.                     0, pCodecCtx->height, pict.data, pict.linesize);  
  332.                 SDL_UnlockYUVOverlay(bmp);  
  333.                 rect.x = 0;  
  334.                 rect.y = 0;  
  335.                 rect.w = pCodecCtx->width;  
  336.                 rect.h = pCodecCtx->height;  
  337.                 SDL_DisplayYUVOverlay(bmp, &rect);  
  338.                 Sleep(60);  
  339.                 av_free_packet(&packet);  
  340.             }  
  341.         }  
  342.         else if(packet.stream_index==audioStream)  
  343.         {  
  344.             packet_queue_put(&audioq, &packet);  
  345.         }  
  346.         else  
  347.         {  
  348.             av_free_packet(&packet);  
  349.         }  
  350.         // Free the packet that was allocated by av_read_frame  
  351.         SDL_PollEvent(&event);  
  352.         switch(event.type)  
  353.         {  
  354.         case SDL_QUIT:  
  355.             quit = 1;  
  356.             SDL_Quit();  
  357.             exit(0);  
  358.             break;  
  359.         default: break;  
  360.         }  
  361.     }  
  362.     // Free the RGB image  
  363.     av_free(buffer);  
  364.     //av_free(pFrameRGB);  
  365.     // Free the YUV frame  
  366.     av_free(pFrame);  
  367.     // Close the codec  
  368.     avcodec_close(pCodecCtx);  
  369.     // Close the video file  
  370.     av_close_input_file(pFormatCtx);  
  371.     return 0;  
  372. }  

 

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