接上篇博文代码实现:
#define TEST_H264 1 #define TEST_HEVC 0 int _tmain(int argc, _TCHAR* argv[]) { AVCodec *pCodec; //编解码器结构体 AVCodecContext *pCodecCtx= NULL;//编解码器信息头 AVCodecParserContext *pCodecParserCtx=NULL;//解析输入的数据流信息头 AVPacket packet; //存储一帧压缩编码数据包 AVFrame *pFrame; //存储一帧解码后像素数据 //==========SDL相关部分========== int screen_w = 0; int screen_h = 0; SDL_Window *screen; //窗体对象指针用于保存窗体创建返回结果 SDL_Renderer *sdlRenderer; //2D渲染环境指针,用于保存返回创建环境结果 SDL_Texture *sdlTexture; //创建表面纹理,用于显示图片 SDL_Rect sdlRect; //窗体大小 FILE *fp_in; FILE *fp_out; const int in_buffer_size=4096; //一次文件读取长度 uint8_t in_buffer[in_buffer_size + FF_INPUT_BUFFER_PADDING_SIZE]={0};//FF_INPUT_BUFFER_PADDING_SIZE:在解码的输入比特流的末尾所需的额外分配字节数 uint8_t *cur_ptr; //数据指针 int cur_size; int ret, got_picture; int y_size; #if TEST_HEVC enum AVCodecID codec_id=AV_CODEC_ID_HEVC; char filepath_in[]="bigbuckbunny_480x272.hevc"; #elif TEST_H264 AVCodecID codec_id=AV_CODEC_ID_H264; //解码器设置为h264 //char filepath_in[]="../bigbuckbunny_480x272.h264"; char filepath_in [1024]; printf("please Input videofile:"); scanf("%s",filepath_in); #else AVCodecID codec_id=AV_CODEC_ID_MPEG2VIDEO; char filepath_in[]="bigbuckbunny_480x272.m2v"; #endif char filepath_out[]="../bigbuckbunny_480x272.yuv";//输出文件 int first_time=1; #if USE_SWSCALE struct SwsContext *img_convert_ctx; AVFrame *pFrameYUV; uint8_t *out_buffer; #endif //av_log_set_level(AV_LOG_DEBUG); avcodec_register_all(); //注册所有编解码器 pCodec = avcodec_find_decoder(codec_id); //查找编解码器 if (!pCodec) { printf("Codec not found\n"); return -1; } pCodecCtx = avcodec_alloc_context3(pCodec);//为编解码信息头结构体分配内存 if (!pCodecCtx){ printf("Could not allocate video codec context\n"); return -1; } pCodecParserCtx=av_parser_init(codec_id); //初始化AVCodecParserContext。 if (!pCodecParserCtx){ printf("Could not allocate video parser context\n"); return -1; } //if(pCodec->capabilities&CODEC_CAP_TRUNCATED) // pCodecCtx->flags|= CODEC_FLAG_TRUNCATED; /* we do not send complete frames */ if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {//打开编解码器 printf("Could not open codec\n"); return -1; } //Input File fp_in = fopen(filepath_in, "rb"); if (!fp_in) { printf("Could not open input stream\n"); return -1; } //Output File fp_out = fopen(filepath_out, "wb"); if (!fp_out) { printf("Could not open output YUV file\n"); return -1; } pFrame = av_frame_alloc(); //分配像素存储内存 av_init_packet(&packet); //初始化压缩数据包 //==SDL部分==== //初始化SDL if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) { printf( "Could not initialize SDL - %s\n", SDL_GetError()); return -1; } #if 0 //这部分依赖于视频相关信息的创建需要在解码一帧视频后创建 或者采用FFMPEG 格式处理工具sws_getContext等函数 //获取视频分辨率 screen_w = pCodecCtx->width; ///这里直接调用 无法获取视频宽高 screen_h = pCodecCtx->height; //SDL 2.0 支持多窗口 (创建播放窗口) SDL_WINDOWPOS_UNDEFINED(默认屏幕中间显示) screen = SDL_CreateWindow("Simplest ffmpeg player's Window", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, screen_w, screen_h, SDL_WINDOW_OPENGL); if(!screen) { printf("SDL: could not create window - exiting:%s\n",SDL_GetError()); return -1; } // 基于窗口创建2d渲染器 sdlRenderer = SDL_CreateRenderer(screen, -1, 0); // 创建纹理(Texture) sdlTexture = SDL_CreateTexture(sdlRenderer, SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STREAMING, pCodecCtx->width,pCodecCtx->height); //SDL窗体位置 与大小 sdlRect.x=0; sdlRect.y=0; sdlRect.w=screen_w; sdlRect.h=screen_h; #endif while (1) { cur_size = fread(in_buffer, 1, in_buffer_size, fp_in);//读取文件 if (cur_size == 0) break; cur_ptr=in_buffer; while (cur_size>0){ //主要是通过av_parser_parse2拿到AVPaket数据,跟av_read_frame类似。 //输入必须是只包含视频编码数据“裸流”(例如H.264、HEVC码流文件),而不能是包含封装格式的媒体数据(例如AVI、MKV、MP4)。 int len = av_parser_parse2( //获取编码数据包 pCodecParserCtx, pCodecCtx, &packet.data, &packet.size, cur_ptr , cur_size , AV_NOPTS_VALUE, AV_NOPTS_VALUE, AV_NOPTS_VALUE); cur_ptr += len; cur_size -= len; if(packet.size==0) continue; #if 0 //相关视频信息输出 //Some Info from AVCodecParserContext printf("[Packet]Size:%6d\t",packet.size); switch(pCodecParserCtx->pict_type){ case AV_PICTURE_TYPE_I: printf("Type:I\t");break; case AV_PICTURE_TYPE_P: printf("Type:P\t");break; case AV_PICTURE_TYPE_B: printf("Type:B\t");break; default: printf("Type:Other\t");break; } printf("Number:%4d\n",pCodecParserCtx->output_picture_number); #endif ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, &packet); //解码像素数据 if (ret < 0) { printf("Decode Error.\n"); return ret; } if (got_picture) { //这里解码出来的像素数据可以用SDL直接刷新显示播放出来 if(first_time) { //获取到相应的视频信息 长宽等 //初始化视频显示窗体 screen_w = pCodecCtx->width; ///这里直接调用 无法获取视频宽高 screen_h = pCodecCtx->height; //SDL 2.0 支持多窗口 (创建播放窗口) SDL_WINDOWPOS_UNDEFINED(默认屏幕中间显示) screen = SDL_CreateWindow("Simplest ffmpeg player's Window", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, screen_w, screen_h, SDL_WINDOW_OPENGL); if(!screen) { printf("SDL: could not create window - exiting:%s\n",SDL_GetError()); return -1; } // 基于窗口创建2d渲染器 sdlRenderer = SDL_CreateRenderer(screen, -1, 0); // 创建纹理(Texture) sdlTexture = SDL_CreateTexture(sdlRenderer, SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STREAMING, pCodecCtx->width,pCodecCtx->height); //SDL窗体位置 与大小 sdlRect.x=0; sdlRect.y=0; sdlRect.w=screen_w; sdlRect.h=screen_h; //只进入一次窗体创建 first_time=0; } //显示视频 SDL_UpdateYUVTexture(sdlTexture, &sdlRect, pFrame->data[0], pFrame->linesize[0], //Y行的长度 pFrame->data[1], pFrame->linesize[1], //U行的长度 pFrame->data[2], pFrame->linesize[2]); //V行的长度 SDL_RenderClear( sdlRenderer ); //清理一下渲染器 SDL_RenderCopy( sdlRenderer, sdlTexture, NULL, &sdlRect); //纹理复制给渲染器 srcrect:选择输入纹理的一块矩形区域作为输入。设置为NULL的时候整个纹理作为输入 SDL_RenderPresent( sdlRenderer ); //显示图片 //SDL End----------------------- //Delay 40ms //一秒钟25帧 SDL_Delay(40); } } } //Flush Decoder //清理解码器中的剩余数据 packet.data = NULL; packet.size = 0; while(1){ ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, &packet); if (ret < 0) { printf("Decode Error.\n"); return ret; } if (!got_picture) break; if (got_picture) { SDL_UpdateYUVTexture(sdlTexture, &sdlRect, pFrame->data[0], pFrame->linesize[0], //Y行的长度 pFrame->data[1], pFrame->linesize[1], //U行的长度 pFrame->data[2], pFrame->linesize[2]); //V行的长度 SDL_RenderClear( sdlRenderer ); //清理一下渲染器 SDL_RenderCopy( sdlRenderer, sdlTexture, NULL, &sdlRect); //纹理复制给渲染器 srcrect:选择输入纹理的一块矩形区域作为输入。设置为NULL的时候整个纹理作为输入 SDL_RenderPresent( sdlRenderer ); //显示图片 //SDL End----------------------- //Delay 40ms //一秒钟25帧 SDL_Delay(40); } printf("Flush Decoder: Succeed to decode 1 frame!\n"); } fclose(fp_in); fclose(fp_out); #if USE_SWSCALE sws_freeContext(img_convert_ctx); av_frame_free(&pFrameYUV); #endif //清理工作 SDL_Quit(); av_parser_close(pCodecParserCtx); av_frame_free(&pFrame); avcodec_close(pCodecCtx); av_free(pCodecCtx); system("pause"); return 0; }SDL窗口创建需要一定时间,解码一帧后再创建其窗口其实不是最好的选择,可以采用创建固定窗口大小,解码视频帧后将图片进行相应转换显示,才是正确思路。