http://dranger.com/ffmpeg/tutorial04.html
1. 流程
- main中创建线程decode_thread,作用是从视频文件中分离出vidoe与sound帧
- decode_thread
- {
- stream_component_open(is, audio_index); --> audio_callback
- stream_component_open(is, video_index); --> video_thread
- while(1)
- {
- av_read_frame();
- 将video的数据 put_queue
- 将sound的数据 put_queue
- }
- }
-
- audio_callback
- {
- audio的流程不变
- }
-
- video_thread
- {
- a.从video的帧队列中packet_queue_get
- b. decode_video2解码
- c. queue_picture显示
- }
- queue_picture
- {
- a. 等侍信号pictq_cond
- b. 如果是第1次需要初始化SDL_screeen
- c. 转换图片格式sws_scale
- }
-
- video_refresh_timer
- {
- a. SDL_DisplayYUVOverlay显示图像
- b. 显示完成后发送信号pictq_cond
- }
- cong@msi:/work/ffmpeg/test/4spawn$ cat spawn.c
- #include "utils.h"
- #include <libavcodec/avcodec.h>
- #include <libavformat/avformat.h>
- #include <libswscale/swscale.h>
- #include <libswresample/swresample.h>
- #include <libavutil/avstring.h>
- #include <libavutil/pixfmt.h>
- #include <libavutil/log.h>
- #include <SDL/SDL.h>
- #include <SDL/SDL_thread.h>
- #include <stdio.h>
- #include <math.h>
-
- #define SDL_AUDIO_BUFFER_SIZE 4096
- #define AVCODEC_MAX_AUDIO_FRAME_SIZE 192000
-
- #define MAX_AUDIOQ_SIZE (5 * 16 * 1024)
- #define MAX_VIDEOQ_SIZE (5 * 256 * 1024)
-
-
- #define FF_REFRESH_EVENT (SDL_USEREVENT)
- #define FF_QUIT_EVENT (SDL_USEREVENT + 1)
-
- typedef struct PacketQueue
- {
- AVPacketList * first_pkt, *last_pkt;
- int nb_packets;
- int size;
- SDL_mutex *mutex;
- SDL_cond * cond;
- }PacketQueue;
-
- static int signal_quit = 0;
- SDL_mutex *affmutex;
- SDL_Event sdlevent;
-
- #define VIDEO_PICTURE_QUEUE_SIZE 1
-
- typedef struct VideoState {
- int videoindex;
- int sndindex;
- int frameFinished;
- int wanted_freq;
- int wanted_samples;
- int wanted_channels;
- char filename[1024];
- int pictq_size, pictq_rindex, pictq_windex;
- SDL_Thread* video_tid;
- AVFormatContext* pFormatCtx;
- AVCodecContext* vdoCodecCtx;
- AVCodecContext* sndCodecCtx;
- AVCodec* vdoCodec;
- AVCodec* sndCodec;
- AVFrame* pFrameYUV;
- AVPacket* packet;
- struct SwsContext *img_convert_ctx;
- struct SwrContext *swr_ctx;
- SDL_mutex *pictq_mutex;
- SDL_cond *pictq_cond;
- SDL_Surface* psscreen;
- SDL_Overlay* overlay;
- SDL_Rect rect;
- SDL_mutex *screen_mutex;
- enum AVSampleFormat wanted_fmt;
- int64_t wanted_channel_layout;
- PacketQueue audioq;
- PacketQueue videoq;
- DECLARE_ALIGNED(16,uint8_t,audio_buf2) [AVCODEC_MAX_AUDIO_FRAME_SIZE * 4];
- }VideoState;
-
- void video_refresh_timer(void* arg);
-
- static int sdl_event_thread(void* data)
- {
- while((0==signal_quit))
- {
- SDL_LockMutex(affmutex);
- while(SDL_PollEvent(&sdlevent))
- {
- switch(sdlevent.type)
- {
- case FF_QUIT_EVENT:
- case SDL_QUIT:
- {
- dbmsg("next change signal_quit to 1");
- signal_quit = 1;
- SDL_Quit();
- }
- break;
- case FF_REFRESH_EVENT:
- video_refresh_timer(sdlevent.user.data1);
- break;
- default:
- break;
- }
- }
- SDL_UnlockMutex(affmutex);
- }
- }
-
- void packet_queue_init(PacketQueue *q)
- {
- memset(q, 0, sizeof(PacketQueue));
- q->mutex = SDL_CreateMutex();
- q->cond = SDL_CreateCond();
- }
-
- int packet_queue_put(PacketQueue *q, AVPacket *pkt)
- {
- AVPacketList *pkt1;
- if(av_dup_packet(pkt) < 0)
- return -1;
- pkt1 = av_malloc(sizeof(AVPacketList));
- if (!pkt1)
- return -1;
- pkt1->pkt = *pkt;
- pkt1->next = NULL;
-
- SDL_LockMutex(q->mutex);
- if (!q->last_pkt)
- q->first_pkt = pkt1;
- else
- q->last_pkt->next = pkt1;
- q->last_pkt = pkt1;
- q->nb_packets++;
- q->size += pkt1->pkt.size;
- //dbmsg("put_queue and send singal");
- SDL_CondSignal(q->cond);
- SDL_UnlockMutex(q->mutex);
- return 0;
- }
-
- static int packet_queue_get(PacketQueue *q, AVPacket *pkt, int block)
- {
- AVPacketList *pkt1;
- int ret;
- SDL_LockMutex(q->mutex);
- for(;;)
- {
- pkt1 = q->first_pkt;
- if (pkt1)
- {
- q->first_pkt = pkt1->next;
- if (!q->first_pkt)
- q->last_pkt = NULL;
- q->nb_packets--;
- q->size -= pkt1->pkt.size;
- *pkt = pkt1->pkt;
- av_free(pkt1);
- ret = 1;
- break;
- } else if (!block) {
- ret = 0;
- break;
- } else {
- SDL_CondWait(q->cond, q->mutex);
- dbmsg("get_queue and get singal");
- }
- }
- SDL_UnlockMutex(q->mutex);
- return ret;
- }
-
- int audio_decode_frame(VideoState* is)
- {
- int i;
- int len1,len2, data_size, got_frame;
- int new_packet;
- int64_t dec_channel_layout;
- uint8_t *out[] = { is->audio_buf2 };
- AVPacket *pkt = av_mallocz(sizeof(AVPacket));
- AVPacket *pkt_temp = av_mallocz(sizeof(AVPacket));
- AVFrame *frame = av_frame_alloc();
- for(;;)
- {
- while(pkt_temp->size>0 || (!pkt_temp->data && new_packet))
- {
- if(frame)
- {
- //dbmsg("av_get default frame");
- av_frame_unref(frame); //reset frame
- }
- new_packet = 0;
- len1 = avcodec_decode_audio4(is->sndCodecCtx, frame, &got_frame, pkt_temp); //decode data is store in frame
- if(len1 < 0)
- {
- pkt_temp->size = 0;
- break;
- }
- //dbmsg("len1=%d, linesize=%d",len1, frame->linesize[0]);
-
- pkt_temp->data += len1;
- pkt_temp->size -= len1;
-
- if(got_frame <= 0) /* No data yet, get more frames */
- continue;
- data_size = av_samples_get_buffer_size(NULL, is->sndCodecCtx->channels, frame->nb_samples, is->sndCodecCtx->sample_fmt, 1);
- dec_channel_layout = (frame->channel_layout && frame->channels==av_get_channel_layout_nb_channels(frame->channel_layout))?
- frame->channel_layout: is->wanted_channel_layout;
- #if 0
- dbmsg("format=%d:%d,layout=%lld:%lld,rate=%d:%d,samples=%d:%d",frame->format,is->wanted_fmt,
- (long long) dec_channel_layout,(long long)av_get_default_channel_layout(is->wanted_channels),
- frame->sample_rate,is->wanted_freq,frame->nb_samples,is->wanted_samples );
-
- dbmsg("wanted_channel_layout=%lld", (long long)is->wanted_channel_layout);
- dbmsg("wanted_fmt=%d", is->wanted_fmt);
- dbmsg("spec.freq=%d", is->wanted_freq);
- #endif
- //check: format,channel_layout,rate,sample
- if((NULL==is->swr_ctx) && ( (frame->format!=is->wanted_fmt) || (dec_channel_layout!=av_get_default_channel_layout(is->wanted_channels)) ||
- (frame->sample_rate!=is->wanted_freq) || (frame->nb_samples!=is->wanted_samples)) )
- {
- if(is->swr_ctx != NULL)
- swr_free(&is->swr_ctx);
- #if 0
- dbmsg("wanted_channel_layout=%lld, wanted_fmt=%d, wanted_sample=%d, dec_channel_layout=%lld, frame->format=%d, frame->sample_rate=%d",
- (long long)is->wanted_channel_layout, is->wanted_fmt, is->wanted_samples, (long long)dec_channel_layout, frame->format, frame->sample_rate);
- #endif
- is->swr_ctx = swr_alloc_set_opts(NULL, is->wanted_channel_layout, is->wanted_fmt, is->wanted_freq, dec_channel_layout, frame->format, frame->sample_rate, 0, NULL);
- if(is->swr_ctx == NULL)
- {
- dbmsg("swr_ctx == NULL");
- }
- swr_init(is->swr_ctx);
- }
- if(is->swr_ctx)
- {
- len2 = swr_convert(is->swr_ctx, out, sizeof(is->audio_buf2)/is->wanted_channels/av_get_bytes_per_sample(frame->format),(const uint8_t **)frame->extended_data, frame->nb_samples);
- data_size = len2 * is->wanted_channels * av_get_bytes_per_sample(is->wanted_fmt);
- }else {
- memcpy(is->audio_buf2, frame->data[0], frame->linesize[0]);
- }
-
- return data_size;
- }
- if(pkt->data)
- av_free_packet(pkt);
- memset(pkt_temp, 0, sizeof(*pkt_temp));
- if(signal_quit)
- return -1;
- if((new_packet = packet_queue_get(&is->audioq, pkt, 1)) < 0)
- {
- dbmsg("get packet=%d", new_packet);
- return -1;
- }
-
- *pkt_temp = *pkt;
- }
- }
-
- void audio_callback(void *userdata, Uint8 *stream, int len)
- {
- int i;
- VideoState* is = (VideoState*)userdata;
- int len1, audio_size;
-
- static unsigned int audio_buf_size = 0;
- static unsigned int audio_buf_index = 0;
-
- while(len > 0)
- {
- if(audio_buf_index >= audio_buf_size)
- {
- audio_size = audio_decode_frame(is); //decode data is store in is->audio_buf2
- //dbmsg("audio_size=%d", audio_size);
- if(audio_size < 0)
- {
- /* If error, output silence */
- audio_buf_size = 1024;
- memset(is->audio_buf2, 0, audio_buf_size);
- } else {
- audio_buf_size = audio_size;
- }
- audio_buf_index = 0;
- }
- //dbmsg("len=%d, audio_buf_size=%d, audio_buf_index=%d", len, audio_buf_size, audio_buf_index);
- len1 = audio_buf_size - audio_buf_index;
- //dbmsg("len1=%d", len1);
- if(len1 > len)
- len1 = len;
- //dbmsg("len1 = %d", len1);
- memcpy(stream, (uint8_t *)is->audio_buf2 + audio_buf_index, len1);
- len -= len1;
- stream += len1;
- audio_buf_index += len1;
- }
- }
-
- static Uint32 sdl_refresh_timer_cb(Uint32 interval, void *opaque) {
- SDL_Event event;
- event.type = FF_REFRESH_EVENT;
- event.user.data1 = opaque;
- SDL_PushEvent(&event);
- return 0; /* 0 means stop timer */
- }
-
- /* schedule a video refresh in 'delay' ms */
- static void schedule_refresh(VideoState *is, int delay) {
- SDL_AddTimer(delay, sdl_refresh_timer_cb, is);
- }
-
- void video_display(VideoState *is) {
- SDL_LockMutex(is->screen_mutex);
- SDL_DisplayYUVOverlay(is->overlay, &is->rect);
- SDL_UnlockMutex(is->screen_mutex);
- }
-
- void video_refresh_timer(void *data)
- {
- VideoState *is = (VideoState *)data;
- if(is->pictq_size == 0) {
- schedule_refresh(is, 1);
- } else {
- schedule_refresh(is, 40);
- video_display(is);
-
- SDL_LockMutex(is->pictq_mutex);
- is->pictq_size--;
- SDL_CondSignal(is->pictq_cond);
- SDL_UnlockMutex(is->pictq_mutex);
- }
- }
-
- int queue_picture(VideoState *is, AVFrame *pFrame)
- {
- /* wait until we have space for a new pic */
- SDL_LockMutex(is->pictq_mutex);
- while(is->pictq_size >= VIDEO_PICTURE_QUEUE_SIZE && (0==signal_quit)) {
- SDL_CondWait(is->pictq_cond, is->pictq_mutex);
- }
- SDL_UnlockMutex(is->pictq_mutex);
-
- if(signal_quit)
- return -1;
-
- /* We have a place to put our picture on the queue */
- SDL_LockYUVOverlay(is->overlay);
- is->pFrameYUV->data[0] = is->overlay->pixels[0];
- is->pFrameYUV->data[1] = is->overlay->pixels[2];
- is->pFrameYUV->data[2] = is->overlay->pixels[1];
- is->pFrameYUV->linesize[0] = is->overlay->pitches[0];
- is->pFrameYUV->linesize[1] = is->overlay->pitches[2];
- is->pFrameYUV->linesize[2] = is->overlay->pitches[1];
- sws_scale(is->img_convert_ctx, (const uint8_t* const*)pFrame->data, pFrame->linesize, 0,
- is->vdoCodecCtx->height, is->pFrameYUV->data, is->pFrameYUV->linesize);
- SDL_UnlockYUVOverlay(is->overlay);
- SDL_LockMutex(is->pictq_mutex);
- is->pictq_size++;
- SDL_UnlockMutex(is->pictq_mutex);
- return 0;
- }
-
- static int video_thread(void* data)
- {
- VideoState *is = (VideoState *)data;
- AVPacket pkt1, *packet = &pkt1;
- int frameFinished;
- AVFrame *pFrame;
- int ret;
- pFrame = av_frame_alloc();
-
- for(;;)
- {
- if(signal_quit)
- break;
- if(packet_queue_get(&is->videoq, packet, 1) < 0) {
- // means we quit getting packets
- dbmsg("packet_queue_get < 0");
- break;
- }
- // Decode video frame
- if((ret=avcodec_decode_video2(is->vdoCodecCtx, pFrame, &is->frameFinished, packet)) < 0)
- {
- dbmsg("decocode video error");
- continue;
- }
- if(is->frameFinished) {
- if(queue_picture(is, pFrame) < 0) {
- break;
- }
- }
- av_free_packet(packet);
- }
- av_frame_free(&pFrame);
- return 0;
- }
-
- static int decode_thread(void* data)
- {
- VideoState* is = (VideoState*)data;
- is->packet = (AVPacket*)av_malloc(sizeof(AVPacket));
- while( (av_read_frame(is->pFormatCtx, is->packet)>=0) && (signal_quit==0))
- {
- if(is->packet->stream_index == is->videoindex)
- {
- packet_queue_put(&is->videoq, is->packet);
- }
-
- if(is->packet->stream_index == is->sndindex)
- {
- packet_queue_put(&is->audioq, is->packet);
- }
- }
- return 0;
- }
-
- int init_ffmpeg_and_SDL(VideoState* is, char* video_file)
- {
- int i=0;
- int ret;
- SDL_AudioSpec wanted_spec, real_spec;
- is->videoindex = -1;
- is->sndindex = -1;
- if(NULL == video_file)
- {
- dbmsg("input file is NULL");
- return -1;
- }
- SDL_Init(SDL_INIT_EVERYTHING);
- avcodec_register_all();
- avfilter_register_all();
- av_register_all();
-
- is->pFormatCtx = avformat_alloc_context();
-
- if(avformat_open_input(&is->pFormatCtx, video_file, NULL, NULL)!=0)
- return -1;
-
- if(avformat_find_stream_info(is->pFormatCtx, NULL)<0)
- return -1;
- av_dump_format(is->pFormatCtx,0, 0, 0);
-
- for(i=0; i<is->pFormatCtx->nb_streams; i++)
- {
- if(is->pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
- {
- is->videoindex= i;
- }
- if(is->pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO)
- {
- is->sndindex= i;
- }
- }
- if(is->videoindex== -1)
- dbmsg("no video stream found!");
- if(is->sndindex== -1)
- dbmsg("no sound stream found!");
- dbmsg("videoindex=%d, sndindex=%d", is->videoindex, is->sndindex);
-
- if(is->videoindex != -1)
- {
- is->vdoCodecCtx = is->pFormatCtx->streams[is->videoindex]->codec;
- is->vdoCodec = avcodec_find_decoder(is->vdoCodecCtx->codec_id);
- if(is->vdoCodec == NULL)
- {
- dbmsg("Codec not found");
- return -1;
- }
- if(avcodec_open2(is->vdoCodecCtx, is->vdoCodec, NULL) < 0)
- return -1;
- is->pFrameYUV = av_frame_alloc();
- is->img_convert_ctx = sws_getContext(is->vdoCodecCtx->width, is->vdoCodecCtx->height, is->vdoCodecCtx->pix_fmt,
- is->vdoCodecCtx->width, is->vdoCodecCtx->height, PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);
- if(is->img_convert_ctx == NULL)
- {
- dbmsg("img_convert error");
- return -1;
- }
- is->psscreen = SDL_SetVideoMode(is->vdoCodecCtx->width, is->vdoCodecCtx->height, 0, SDL_SWSURFACE);
- SDL_WM_SetCaption( "FFMPEG Window", NULL);
- is->overlay = SDL_CreateYUVOverlay(is->vdoCodecCtx->width, is->vdoCodecCtx->height, SDL_YV12_OVERLAY, is->psscreen);
- is->rect.x = 0;
- is->rect.y = 0;
- is->rect.w = is->vdoCodecCtx->width;
- is->rect.h = is->vdoCodecCtx->height;
- packet_queue_init(&is->videoq);
- is->video_tid = SDL_CreateThread(video_thread, is);
- }
- if(is->sndindex != -1)
- {
- is->sndCodecCtx = is->pFormatCtx->streams[is->sndindex]->codec;
- is->sndCodec = avcodec_find_decoder(is->sndCodecCtx->codec_id);
- if(is->sndCodec == NULL)
- {
- dbmsg("Codec not found");
- return -1;
- }
- if(avcodec_open2(is->sndCodecCtx, is->sndCodec, NULL) < 0)
- return -1;
-
- wanted_spec.freq = is->sndCodecCtx->sample_rate;
- wanted_spec.format = AUDIO_S16SYS;
- wanted_spec.channels = is->sndCodecCtx->channels;
- wanted_spec.silence = 0;
- wanted_spec.samples = 2048;
- wanted_spec.callback = audio_callback;
- wanted_spec.userdata = is;
- if(SDL_OpenAudio(&wanted_spec, &real_spec) < 0)
- {
- dbmsg("SDL_OpenAudio:%s", SDL_GetError());
- return -1;
- }
- //store infomation for audio swr_convert
- is->wanted_fmt = AV_SAMPLE_FMT_S16; //AUDIO_S16SYS;
- is->wanted_channel_layout = av_get_default_channel_layout(is->sndCodecCtx->channels);
- is->wanted_freq = real_spec.freq;
- is->wanted_samples = real_spec.samples;
- is->wanted_channels = real_spec.channels;
- //dbmsg("freq=%d, channels=%d, samples=%d", is->wanted_freq, is->wanted_channels, is->wanted_samples);
- packet_queue_init(&is->audioq);
- SDL_PauseAudio(0);
- }
- return 0;
- }
-
- int main(int argc, char **argv)
- {
- int ret;
- SDL_Thread* sdl_thread;
- SDL_Thread* decode_thread_id;
- VideoState* is = (VideoState*) av_mallocz(sizeof(VideoState));
-
- if( (ret=init_ffmpeg_and_SDL(is, argv[1])) != 0)
- {
- dbmsg("init_ffmpeg_and SDL error");
- return -1;
- }
-
- is->pictq_mutex = SDL_CreateMutex();
- is->pictq_cond = SDL_CreateCond();
-
- //sdl get signal quit
- affmutex = SDL_CreateMutex();
- decode_thread_id = SDL_CreateThread(decode_thread, is);
- sdl_thread = SDL_CreateThread(sdl_event_thread, NULL);
-
- schedule_refresh(is, 40);
-
- SDL_WaitThread(sdl_thread, &ret);
- SDL_WaitThread(decode_thread_id, &ret);
- SDL_WaitThread(is->video_tid,&ret);
- SDL_DestroyMutex(affmutex);
- av_free_packet(is->packet);
- av_free(is->pFrameYUV);
- avcodec_close(is->vdoCodecCtx);
- avcodec_close(is->sndCodecCtx);
- avformat_close_input(&is->pFormatCtx);
- return 0;
- }
- cong@msi:/work/ffmpeg/test/4spawn$
