本代码可以用于播放音视频,同时播放声音和视频图像。
整体工程连接为:基于FFMPEG和SDL的音视频播放器
在写代码的时候发现,音频线程和视频线程delay的时间不同,那么怎样才能设置一个控制线程来控制两个线程的暂停播放呢?各位大神请多多指教。
另外由于直接从一个FormatContext读取音频和视频时,会出现访问不到,主要是因为共享的问题。我是采用直接再读取另外一个formatContext解决的,有没有更好的办法,请多多指教。
下面是代码的介绍:
此函数主要是对视频播放前的准备工作。
int video_set(AVFormatContext *pFormatCtx){
for(int i=0; i<pFormatCtx->nb_streams; i++) {
if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO){
videoindex=i;
break;
}
}
if(videoindex==-1){
printf("Didn't find a video stream.\n");
return -1;
}
AVCodecContext *pCodecCtx=pFormatCtx->streams[videoindex]->codec;
AVCodec *pCodec=avcodec_find_decoder(pCodecCtx->codec_id);
avcodec_open2(pCodecCtx, pCodec,NULL);
return 0;
}
此函数主要是对音频播放前的准备工作。
int audio_set(AVFormatContext *pFormatCtx){
for(int i=0; i<pFormatCtx->nb_streams; i++) {
if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_AUDIO){
audioindex=i;
break;
}
}
AVCodecContext *pAudioCodecCtx=pFormatCtx->streams[audioindex]->codec;
AVCodec *pAudioCodec=avcodec_find_decoder(pAudioCodecCtx->codec_id);
if(pAudioCodec==NULL){
printf("Codec not found.\n");
return -1;
}
// Open codec
if(avcodec_open2(pAudioCodecCtx, pAudioCodec,NULL)<0){
printf("Could not open codec.\n");
return -1;
}
//SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER);
//Out Audio Param
uint64_t out_channel_layout=AV_CH_LAYOUT_STEREO;
//nb_samples: AAC-1024 MP3-1152
int out_nb_samples=pAudioCodecCtx->frame_size;
AVSampleFormat out_sample_fmt=AV_SAMPLE_FMT_S16;
int out_sample_rate=44100;
int out_channels=av_get_channel_layout_nb_channels(out_channel_layout);
//Out Buffer Size
out_buffer_size=av_samples_get_buffer_size(NULL,out_channels ,out_nb_samples,out_sample_fmt, 1);
//SDL_Event event;
//结构体,包含PCM数据的相关信息
//SDL_AudioSpec
SDL_AudioSpec wanted_spec;
wanted_spec.freq = out_sample_rate;
wanted_spec.format = AUDIO_S16SYS;
wanted_spec.channels = out_channels;
wanted_spec.silence = 0;
wanted_spec.samples = out_nb_samples;
wanted_spec.callback = fill_audio;
wanted_spec.userdata = pAudioCodecCtx;
if (SDL_OpenAudio(&wanted_spec, NULL)<0){
printf("can't open audio.\n");
return -1;
}
int64_t in_channel_layout=av_get_default_channel_layout(pAudioCodecCtx->channels);
//Swr
au_convert_ctx = swr_alloc();
au_convert_ctx=swr_alloc_set_opts(au_convert_ctx,out_channel_layout, out_sample_fmt, out_sample_rate,
in_channel_layout,pAudioCodecCtx->sample_fmt , pAudioCodecCtx->sample_rate,0, NULL);
swr_init(au_convert_ctx);
return 0;
}
下面是视频播放函数
int video_player(void *opaque){
AVFormatContext *pFormatCtx=(AVFormatContext *)opaque;
AVCodecContext *pCodecCtx=pFormatCtx->streams[videoindex]->codec;
SDL_Rect sdlRect;
sdlRect.x = 0;
sdlRect.y = 0;
AVFrame *pFrame=av_frame_alloc();
AVFrame *pFrameYUV=av_frame_alloc();
unsigned char *out_buffer=(unsigned char *)av_malloc(av_image_get_buffer_size(AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height,1));
av_image_fill_arrays(pFrameYUV->data, pFrameYUV->linesize,out_buffer,
AV_PIX_FMT_YUV420P,pCodecCtx->width, pCodecCtx->height,1);
struct SwsContext *img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt,
pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);
int screen_w,screen_h;
SDL_Window *screen;
SDL_Renderer* sdlRenderer;
SDL_Texture* sdlTexture;
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER);
screen_w = pCodecCtx->width;
screen_h = pCodecCtx->height;
screen = SDL_CreateWindow("Simplest ffmpeg player's Window", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
screen_w, screen_h,
SDL_WINDOW_OPENGL|SDL_WINDOW_RESIZABLE);
sdlRenderer = SDL_CreateRenderer(screen, -1, 0);
sdlTexture = SDL_CreateTexture(sdlRenderer, SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STREAMING,pCodecCtx->width,pCodecCtx->height);
AVPacket *packet=(AVPacket *)av_malloc(sizeof(AVPacket));
int got_picture,ret;
int clicked=1;
SDL_Event event;
while(av_read_frame(pFormatCtx, packet)>=0){
if(packet->stream_index==videoindex){
ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, packet);
//int y_size=pCodecCtx->width*pCodecCtx->height;
//fwrite(pFrameYUV->data[0],1,y_size,fp_yuv); //Y
// fwrite(pFrameYUV->data[1],1,y_size/4,fp_yuv); //U
// fwrite(pFrameYUV->data[2],1,y_size/4,fp_yuv); //V
if(got_picture){
sws_scale(img_convert_ctx, (const uint8_t* const*)pFrame->data, pFrame->linesize, 0,
pCodecCtx->height, pFrameYUV->data, pFrameYUV->linesize);
SDL_UpdateTexture( sdlTexture, NULL, pFrameYUV->data[0], pFrameYUV->linesize[0] );
/* sdlRect.w = screen_w;
sdlRect.h = screen_h;
*/
SDL_RenderClear( sdlRenderer );
SDL_RenderCopy( sdlRenderer, sdlTexture, NULL, NULL);
SDL_RenderPresent( sdlRenderer );
SDL_Delay(40);
}
}
av_free_packet(packet);
}
sws_freeContext(img_convert_ctx);
//--------------
av_frame_free(&pFrameYUV);
av_frame_free(&pFrame);
avcodec_close(pCodecCtx);
av_free(out_buffer);
return 0;
}
下面是音频播放函数:
int audio_player(void *opaque){
AVFormatContext *pFormatCtx=(AVFormatContext *)opaque;
AVFrame *pAudioFrame =av_frame_alloc();
uint8_t *out_buffer_audio=(uint8_t *)av_malloc(MAX_AUDIO_FRAME_SIZE*2);
AVCodecContext *pAudioCodecCtx=pFormatCtx->streams[audioindex]->codec;
//Play
SDL_PauseAudio(0);
int index = 0;
AVPacket *packet=(AVPacket *)av_malloc(sizeof(AVPacket));
int ret,got_picture;
SDL_Event event;
while(av_read_frame(pFormatCtx, packet)>=0){
if(packet->stream_index==audioindex){
ret = avcodec_decode_audio4( pAudioCodecCtx, pAudioFrame,&got_picture, packet);
if ( ret < 0 ) {
printf("Error in decoding audio frame.\n");
return -1;
}
if (got_picture > 0 ){
swr_convert(au_convert_ctx,&out_buffer_audio, MAX_AUDIO_FRAME_SIZE,(const uint8_t **)pAudioFrame->data , pAudioFrame->nb_samples);
//printf("index:%5d\t pts:%lld\t packet size:%d\n",index,packet->pts,packet->size);
index++;
}
while(audio_len>0)//Wait until finish
SDL_Delay(1);
//Set audio buffer (PCM data)
audio_chunk = (Uint8 *) out_buffer_audio;
//Audio buffer length
audio_len =out_buffer_size;
audio_pos = audio_chunk;
}
av_free_packet(packet);
}
swr_free(&au_convert_ctx);
av_free(out_buffer_audio);
SDL_CloseAudio();//Close SDL
av_frame_free(&pAudioFrame);
avcodec_close(pAudioCodecCtx);
//SDL_Quit();
return 0;
}
主函数:
int main(int argc, char* argv[]){
AVFormatContext *pFormatCtx;
AVFormatContext *pFormatCtx2;
av_register_all();
avformat_network_init();
pFormatCtx=avformat_alloc_context();
pFormatCtx2=avformat_alloc_context();
char filepath[]="E:\\workspace\\c_primer\\小学期课程资料 - 基于FFmpeg+SDL的视频播放器的制作\\工具\\testvideo\\ds.mov";
//音频播放线程
avformat_open_input(&pFormatCtx,filepath,NULL,NULL);
avformat_find_stream_info(pFormatCtx,NULL);
audio_set(pFormatCtx);
SDL_Thread *audio_thread = SDL_CreateThread(audio_player,NULL,pFormatCtx);
//视频播放线程
avformat_open_input(&pFormatCtx2,filepath,NULL,NULL);
avformat_find_stream_info(pFormatCtx2,NULL);
video_set(pFormatCtx2);
video_player(pFormatCtx2);
SDL_Thread *video_thread = SDL_CreateThread(video_player,NULL,pFormatCtx2);
SDL_WaitThread(video_thread,NULL);
SDL_WaitThread(audio_thread,NULL);
avformat_close_input(&pFormatCtx);
avformat_close_input(&pFormatCtx2);
SDL_Quit();
return 0;
}