杭州网站排名优化工具,关于传媒的网站模板,传媒公司简介模板,网站建设实训步骤FFmpeg入门#xff1a;最简单的视频播放器
FFmpeg入门第一篇#xff0c;制作一个简单的MP4视频播放器。
整体流程
话不多说#xff0c;直接上流程图
视频播放速率控制
这里可以直接看图中的帧率同步模块#xff0c;可以分为如下几步
获取到当前帧的预期播放时间…FFmpeg入门最简单的视频播放器
FFmpeg入门第一篇制作一个简单的MP4视频播放器。
整体流程
话不多说直接上流程图
视频播放速率控制
这里可以直接看图中的帧率同步模块可以分为如下几步
获取到当前帧的预期播放时间根据时间基time_base和帧pts计算time_base*pts解码渲染过程中获取当前时间av_gettime计算和预期播放时间的时间差使用SDL_Delay函数延迟播放时间差。
源代码
tutorial01.h
//
// tutorial02.h
// tutorial02
//
// Created by chenhuaiyi on 2025/2/13.
//#ifndef tutorial02_h
#define tutorial02_h#include libavcodec/avcodec.h
#include libavformat/avformat.h
#include libswscale/swscale.h
#include libavutil/imgutils.h
#include libavutil/time.h#include SDL.h
#include SDL_thread.h#include stdio.h/**宏定义*/
#define SFM_REFRESH_EVENT (SDL_USEREVENT1)/**全局变量*/
extern int thread_exit;/**方法声明*/
char* get_frame_type(AVFrame* frame);#endif /* tutorial02_h */
tutorial02.c
// tutorial02.c
// A pedagogical video player that will stream through every video frame as fast as it can.#ifdef __MINGW32__
#undef main /* Prevents SDL from overriding main() */
#endif#include tutorial02.h// 控制程序是否结束
int thread_exit0;/**获取帧类型*/
char* get_frame_type(AVFrame* frame) {switch (frame-pict_type) {case AV_PICTURE_TYPE_I:return I;break;case AV_PICTURE_TYPE_P:return P;break;case AV_PICTURE_TYPE_B:return B;break;case AV_PICTURE_TYPE_S:return S;break;case AV_PICTURE_TYPE_SI:return SI;break;case AV_PICTURE_TYPE_SP:return SP;break;case AV_PICTURE_TYPE_BI:return BI;break;default:return N;break;}
}int main(int argc, char *argv[]) {AVFormatContext* pFormatCtx NULL;int i, videoStream;AVCodecContext* pCodecCtx avcodec_alloc_context3(NULL);;AVCodecParameters* pCodecParam NULL;const AVCodec* pCodec NULL;AVFrame* pFrame NULL;AVFrame* pFrame2 NULL;AVPacket packet;AVDictionary* optionsDict NULL;struct SwsContext* sws_ctx NULL;SDL_Window* window NULL;SDL_Renderer* renderer NULL;SDL_Texture* textureNULL;SDL_Rect rect;SDL_Event event;if(argc 2) {fprintf(stderr, Usage: test file\n);exit(1);}// SDL组件创建if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) {fprintf(stderr, Could not initialize SDL - %s\n, SDL_GetError());exit(1);}// 1. 打开视频文件获取格式上下文if(avformat_open_input(pFormatCtx, argv[1], NULL, NULL)!0)return -1; // Couldnt open file// 2. 对文件探测流信息if(avformat_find_stream_info(pFormatCtx, NULL)0)return -1; // Couldnt find stream information// 打印信息av_dump_format(pFormatCtx, 0, argv[1], 0);// 3.找到对应的视频流videoStream-1;for(i0; ipFormatCtx-nb_streams; i)if(pFormatCtx-streams[i]-codecpar-codec_typeAVMEDIA_TYPE_VIDEO) {videoStreami;break;}if(videoStream-1){return -1; // Didnt find a video stream}// 4. 将视频流编码参数写入上下文pCodecParam pFormatCtx-streams[videoStream]-codecpar;avcodec_parameters_to_context(pCodecCtx, pCodecParam);// 5. 查找流的编码器pCodecavcodec_find_decoder(pCodecCtx-codec_id);if(pCodecNULL) {fprintf(stderr, Unsupported codec!\n);return -1; // Codec not found}// 6. 打开流的编解码器if(avcodec_open2(pCodecCtx, pCodec, optionsDict)0)return -1; // Could not open codec// 7.申请缩放颜色空间格式的上下文sws_ctx sws_getContext(pCodecCtx-width,pCodecCtx-height,pCodecCtx-pix_fmt,pCodecCtx-width,pCodecCtx-height,AV_PIX_FMT_YUV420P,SWS_BILINEAR,NULL,NULL,NULL);// 8.帧分配pFrameav_frame_alloc();pFrame2av_frame_alloc();// 9.计算并分配frame帧所占内存空间int numBytesav_image_get_buffer_size(AV_PIX_FMT_YUV420P, pCodecCtx-width, pCodecCtx-height, 1);uint8_t* buffer(uint8_t*)av_malloc(numBytes*sizeof(uint8_t));av_image_fill_arrays(pFrame2-data, pFrame2-linesize, buffer, AV_PIX_FMT_YUV420P, pCodecCtx-width, pCodecCtx-height, 1);i0;Uint32 start_time SDL_GetTicks();// SDL2的最新创建和渲染窗口方式window SDL_CreateWindow(SDL2 window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, pCodecCtx-width, pCodecCtx-height, SDL_WINDOW_SHOWN);if (!window) {printf(SDL_CreateWindow Error: %s\n, SDL_GetError());SDL_Quit();return 1;}renderer SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);if (!renderer) {printf(SDL_CreateRenderer Error: %s\n, SDL_GetError());SDL_DestroyWindow(window);SDL_Quit();return 1;}texture SDL_CreateTexture(renderer, SDL_PIXELFORMAT_YV12, SDL_TEXTUREACCESS_STREAMING, pCodecCtx-width, pCodecCtx-height);AVRational time_base pFormatCtx-streams[videoStream]-time_base;int64_t av_start_time av_gettime(); // 播放开始时间int64_t frame_delay av_q2d(time_base) * AV_TIME_BASE; // pts单位(ms*1000)int64_t frame_start_time av_gettime();// 循环一对文件上下文持续读取packetwhile(av_read_frame(pFormatCtx, packet)0) {if(packet.stream_indexvideoStream) {// 将packet写入编解码器int ret avcodec_send_packet(pCodecCtx, packet);if (ret 0) {printf(packet resolve error!);break;}// 循环二从解码器中不断读取帧while(!avcodec_receive_frame(pCodecCtx, pFrame)) { // 解码得到数据frame// 帧格式转化转为YUV420Psws_scale(sws_ctx,(uint8_t const * const *)pFrame-data,pFrame-linesize,0,pCodecCtx-height,pFrame2-data,pFrame2-linesize);// 将AVFrame的数据写入到texture中然后渲染后windows上rect.x 0;rect.y 0;rect.w pCodecCtx-width;rect.h pCodecCtx-height;// 更新纹理SDL_UpdateYUVTexture(texture, rect,pFrame2-data[0], pFrame2-linesize[0], // YpFrame2-data[1], pFrame2-linesize[1], // UpFrame2-data[2], pFrame2-linesize[2]); // V// 渲染页面SDL_RenderClear(renderer);SDL_RenderCopy(renderer, texture, NULL, NULL);SDL_RenderPresent(renderer);int64_t pts pFrame-pts; // ptsint64_t actual_playback_time av_start_time pts * frame_delay; // 实际播放时间int64_t current_time av_gettime();if (actual_playback_time current_time) {SDL_Delay((Uint32)(actual_playback_time-current_time)/1000); // 延迟当前时间和实际播放时间}i;printf(第%i帧 | 属于%s | pts为%d | 时长为%.2fms | 实际播放点为%.2fs | 预期播放点为%.2fs\n ,i,get_frame_type(pFrame),(int)pFrame-pts,(double)(av_gettime() - frame_start_time)/1000,(double)(av_gettime() - av_start_time)/AV_TIME_BASE,pFrame-pts * av_q2d(time_base));frame_start_time av_gettime();}}// Free the packet that was allocated by av_read_frameav_packet_unref(packet);SDL_PollEvent(event);switch(event.type) {case SDL_QUIT:SDL_Quit();exit(0);break;default:break;}}Uint32 endTime SDL_GetTicks();/**打印一些关键参数*/printf(格式: %s\n, pFormatCtx-iformat-name);printf(时长: %lld us\n, pFormatCtx-duration);printf(编码器: %s (%s)\n, pCodec-name, avcodec_get_name(pCodecCtx-codec_id));printf(分辨率: %dx%d\n, pCodecCtx-width, pCodecCtx-height);printf(帧率: %.2f\n, av_q2d(pFormatCtx-streams[videoStream]-avg_frame_rate));printf(帧数: %lld\n, pFormatCtx-streams[videoStream]-nb_frames);printf(比特率: %lld\n, pFormatCtx-bit_rate);printf(pts单位(ms*1000): %.2f\n, av_q2d(pFormatCtx-streams[videoStream]-time_base) * AV_TIME_BASE);printf(视频持续时长为 %d视频帧总数为 %d\n, (endTime-start_time)/1000, i);// Free the YUV frameav_free(pFrame);av_free(pFrame2);// Close the codec paramavcodec_parameters_free(pCodecParam);// Close the codecavcodec_free_context(pCodecCtx);// Close the video fileavformat_close_input(pFormatCtx);return 0;
}
文章最后感谢音视频领域的大神雷霄骅雷神永垂不朽