当前位置: 首页 > news >正文

网站没收录手机优化专家

网站没收录,手机优化专家,wordpress注册邮箱验证收不到邮件,千鸟云网站建设基于ffmpeg和sdl2的简单视频播放器制作 前言一、视频播放器开发的基础1.1 视频播放原理1.2 开发所需的库 二、FFmpeg库详解2.1 FFmpeg库的组成2.2 关键数据结构2.3 打开视频文件并获取流信息2.4 查找视频流和解码器2.5 初始化解码器 三、SDL库详解3.1 SDL库的功能3.2 初始化SDL… 基于ffmpeg和sdl2的简单视频播放器制作 前言一、视频播放器开发的基础1.1 视频播放原理1.2 开发所需的库 二、FFmpeg库详解2.1 FFmpeg库的组成2.2 关键数据结构2.3 打开视频文件并获取流信息2.4 查找视频流和解码器2.5 初始化解码器 三、SDL库详解3.1 SDL库的功能3.2 初始化SDL3.3 创建窗口、渲染器和纹理3.4 事件处理 四、视频播放流程4.1 读取和解码视频帧4.2 时间同步4.3 图像格式转换与渲染 五、资源清理六、完整代码示例七、总结与展望 前言 本文将简单探讨视频播放器的开发过程通过一个完整的代码示例带你领略从打开视频文件到播放视频画面的每一个关键步骤。 一、视频播放器开发的基础 1.1 视频播放原理 视频本质上是一系列连续的图像帧快速播放所形成的视觉效果。在数字视频中这些图像帧被编码压缩以减小文件大小便于存储和传输。常见的视频编码格式有H.264、H.265等。同时视频文件通常还包含音频数据音频也经过特定的编码方式如AAC、MP3等。 当我们播放视频时播放器需要执行以下主要步骤 解封装从视频文件中分离出视频流和音频流。视频文件通常采用某种封装格式如MP4、AVI等这些格式将视频和音频数据按照一定的结构组织在一起。解码对分离出的视频流和音频流进行解码将压缩的数据还原为原始的图像帧和音频样本。这需要使用相应的解码器不同的编码格式需要不同的解码器。渲染将解码后的图像帧显示在屏幕上同时将音频样本通过音频设备播放出来。这涉及到图形渲染和音频输出的相关技术。 1.2 开发所需的库 在开发视频播放器时我们需要借助一些强大的开源库来简化开发过程。在本文的示例中我们主要使用了以下两个库 FFmpeg这是一个功能强大的开源多媒体框架提供了丰富的工具和函数用于处理多媒体文件的解封装、解码、编码等操作。它支持几乎所有常见的多媒体格式并且具有高效的性能。SDL (Simple DirectMedia Layer)这是一个跨平台的多媒体开发库专注于提供硬件抽象层用于创建窗口、渲染图形、播放音频以及处理输入事件等。它使得我们能够在不同的操作系统上轻松实现一致的多媒体交互功能。 二、FFmpeg库详解 2.1 FFmpeg库的组成 FFmpeg库由多个模块组成每个模块都有其特定的功能 libavformat负责处理多媒体文件的格式包括解封装和封装操作。它能够识别各种常见的视频和音频封装格式如MP4、AVI、FLV等并从中提取出视频流和音频流。libavcodec这是FFmpeg的核心编解码模块支持众多的音频和视频编码格式。它包含了各种解码器和编码器能够将压缩的多媒体数据进行解码或编码操作。libavutil提供了一系列通用的工具函数和数据结构如内存管理、错误处理、数学运算等。这些工具函数在整个FFmpeg库的其他模块中被广泛使用。libswscale用于图像的缩放和格式转换。在视频播放过程中由于解码后的图像格式可能与显示设备所需的格式不一致需要使用该模块进行转换。libswresample主要用于音频的重采样和格式转换。它可以将音频数据从一种采样率、声道数或样本格式转换为另一种以适应不同的音频输出设备。 2.2 关键数据结构 AVFormatContext这个结构体是FFmpeg中用于管理多媒体文件格式的上下文。它包含了文件的各种信息如流的数量、每个流的参数等。在打开视频文件时我们会创建一个AVFormatContext对象并使用它来读取文件的信息和解封装数据。AVCodecContext代表编解码器的上下文包含了编解码所需的各种参数如编码格式、分辨率、帧率等。在找到合适的解码器后我们需要创建一个AVCodecContext对象并将其与解码器进行关联。AVFrame用于存储解码后的音频或视频数据。对于视频来说它包含了一帧图像的像素数据对于音频来说它包含了音频样本数据。AVPacket用于存储从文件中读取的压缩数据这些数据在经过解封装后以AVPacket的形式存在然后被传递给解码器进行解码。 2.3 打开视频文件并获取流信息 在我们的代码示例中首先需要打开视频文件并获取其流信息 AVFormatContext* fmt_ctx NULL; std::string file_path F:/QT/mp4_flv/x.mp4;// 打开视频文件 int ret avformat_open_input(fmt_ctx, file_path.c_str(), NULL, NULL); if (ret 0) {handle_ffmpeg_error(ret, Failed to open video file.);return -1; }// 读取视频流信息 ret avformat_find_stream_info(fmt_ctx, NULL); if (ret 0) {handle_ffmpeg_error(ret, Error in obtaining video stream information.);avformat_close_input(fmt_ctx);return -1; }这里avformat_open_input函数用于打开指定路径的视频文件并将文件信息存储在fmt_ctx中。如果打开失败会调用handle_ffmpeg_error函数进行错误处理。接着avformat_find_stream_info函数用于读取视频文件中的流信息包括视频流和音频流的参数等。同样如果读取失败也会进行相应的错误处理。 2.4 查找视频流和解码器 const AVCodec* codec NULL; int video_stream_idx -1; for (unsigned int i 0; i fmt_ctx-nb_streams; i) {if (fmt_ctx-streams[i]-codecpar-codec_type AVMEDIA_TYPE_VIDEO) {video_stream_idx i;codec avcodec_find_decoder(fmt_ctx-streams[i]-codecpar-codec_id);if (!codec) {fprintf(stderr, Video decoder not found\n);avformat_close_input(fmt_ctx);return -1;}break;} }这段代码通过遍历fmt_ctx中的所有流找到类型为视频流AVMEDIA_TYPE_VIDEO的流并获取其索引video_stream_idx。然后根据流的编码ID使用avcodec_find_decoder函数查找对应的解码器。如果找不到解码器会输出错误信息并关闭文件。 2.5 初始化解码器 找到解码器后需要对其进行初始化 AVCodecContext* codec_ctx avcodec_alloc_context3(codec); if (!codec_ctx) {fprintf(stderr, Decoder context allocation failed\n);avformat_close_input(fmt_ctx);return -1; }ret avcodec_parameters_to_context(codec_ctx, fmt_ctx-streams[video_stream_idx]-codecpar); if (ret 0) {handle_ffmpeg_error(ret, Copying codec parameters failed!);avcodec_free_context(codec_ctx);avformat_close_input(fmt_ctx);return -1; }ret avcodec_open2(codec_ctx, codec, NULL); if (ret 0) {handle_ffmpeg_error(ret, Decoder open failed!);avcodec_free_context(codec_ctx);avformat_close_input(fmt_ctx);return -1; }首先使用avcodec_alloc_context3函数分配一个AVCodecContext对象。然后将流的编码参数复制到codec_ctx中最后使用avcodec_open2函数打开解码器。每一步操作都进行了错误处理确保解码器能够正确初始化。 三、SDL库详解 3.1 SDL库的功能 SDL库主要用于创建图形窗口、渲染图像以及处理用户输入事件。它提供了一系列简单易用的函数使得我们能够在不同的操作系统上实现一致的图形界面和交互功能。 窗口管理SDL可以创建和管理窗口设置窗口的大小、位置、标题等属性。它还支持窗口的最小化、最大化、关闭等操作。图形渲染提供了多种渲染方式包括软件渲染和硬件加速渲染。可以将图像数据渲染到窗口上实现视频画面的显示。事件处理能够捕获和处理各种用户输入事件如鼠标点击、键盘按键、窗口关闭等事件使得我们的程序能够响应用户的操作。 3.2 初始化SDL 在使用SDL之前需要先对其进行初始化 if (SDL_Init(SDL_INIT_VIDEO) 0) {fprintf(stderr, SDL could not initialize! SDL_Error: %s\n, SDL_GetError());av_frame_free(frame);avcodec_free_context(codec_ctx);avformat_close_input(fmt_ctx);return -1; }这里使用SDL_Init函数初始化SDL库的视频子系统。如果初始化失败会输出错误信息并释放之前分配的资源。 3.3 创建窗口、渲染器和纹理 SDL_Window* window SDL_CreateWindow(Video Player, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,codec_ctx-width, codec_ctx-height, SDL_WINDOW_SHOWN); if (!window) {fprintf(stderr, Window could not be created! SDL_Error: %s\n, SDL_GetError());SDL_Quit();av_frame_free(frame);avcodec_free_context(codec_ctx);avformat_close_input(fmt_ctx);return -1; }SDL_Renderer* renderer SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); if (!renderer) {fprintf(stderr, Renderer could not be created! SDL_Error: %s\n, SDL_GetError());SDL_DestroyWindow(window);SDL_Quit();av_frame_free(frame);avcodec_free_context(codec_ctx);avformat_close_input(fmt_ctx);return -1; }SDL_Texture* texture SDL_CreateTexture(renderer, SDL_PIXELFORMAT_YV12, SDL_TEXTUREACCESS_STREAMING,codec_ctx-width, codec_ctx-height); if (!texture) {fprintf(stderr, Texture could not be created! SDL_Error: %s\n, SDL_GetError());SDL_DestroyRenderer(renderer);SDL_DestroyWindow(window);SDL_Quit();av_frame_free(frame);avcodec_free_context(codec_ctx);avformat_close_input(fmt_ctx);return -1; }这段代码依次创建了窗口、渲染器和纹理。SDL_CreateWindow函数用于创建一个指定大小和标题的窗口。SDL_CreateRenderer函数创建一个渲染器用于将图像渲染到窗口上这里使用了硬件加速渲染SDL_RENDERER_ACCELERATED。最后SDL_CreateTexture函数创建一个纹理用于存储视频图像数据以便后续渲染。同样每一步创建操作都进行了错误处理如果创建失败会释放之前创建的资源。 3.4 事件处理 在视频播放过程中需要处理用户的输入事件如关闭窗口事件 SDL_Event event; while (SDL_PollEvent(event)) {if (event.type SDL_QUIT) {goto cleanup;} }这里使用SDL_PollEvent函数不断检查是否有事件发生。如果检测到SDL_QUIT事件即用户点击了窗口的关闭按钮则跳转到cleanup标签处进行资源清理操作。 四、视频播放流程 4.1 读取和解码视频帧 在初始化完成后进入视频播放的主循环不断从视频文件中读取数据包并进行解码 AVPacket pkt; while (av_read_frame(fmt_ctx, pkt) 0) {if (pkt.stream_index video_stream_idx) {ret avcodec_send_packet(codec_ctx, pkt);if (ret 0) {handle_ffmpeg_error(ret, send data decoder error.);av_packet_unref(pkt);continue;}while (ret 0) {ret avcodec_receive_frame(codec_ctx, frame);if (ret 0) {// 处理解码后的帧}else if (ret AVERROR(EAGAIN) || ret AVERROR_EOF) {break;}}}av_packet_unref(pkt); }在循环中使用av_read_frame函数从视频文件中读取一个数据包pkt。如果数据包属于视频流通过pkt.stream_index判断则将其发送给解码器进行解码。avcodec_send_packet函数将数据包发送给解码器avcodec_receive_frame函数从解码器中接收解码后的帧。如果解码成功ret 0则可以对解码后的帧进行进一步处理如果解码器需要更多数据ret AVERROR(EAGAIN)或已经到达文件末尾ret AVERROR_EOF则退出内层循环。每次处理完数据包后使用av_packet_unref函数释放数据包的引用。 4.2 时间同步 为了保证视频播放的流畅性和音频视频的同步需要进行时间同步 int64_t start_time av_gettime();// 在解码帧的处理部分 int64_t pts frame-pts; if (pts AV_NOPTS_VALUE) {pts av_rescale_q(pkt.dts, fmt_ctx-streams[video_stream_idx]-time_base, { 1, 1000000 }); } else {pts av_rescale_q(frame-pts, fmt_ctx-streams[video_stream_idx]-time_base, { 1, 1000000 }); }int64_t now av_gettime(); if (pts now - start_time) {SDL_Delay((pts - (now - start_time)) / 1000); }这里使用av_gettime函数获取当前时间。通过帧的显示时间戳pts和当前时间的比较计算出需要延迟的时间使用SDL_Delay函数进行延迟以确保视频帧按照正确的时间顺序显示。 4.3 图像格式转换与渲染 解码后的帧需要进行格式转换并渲染到窗口上 SDL_Rect rect { 0, 0, codec_ctx-width, codec_ctx-height }; sws_ctx sws_getContext(codec_ctx-width, codec_ctx-height, codec_ctx-pix_fmt,codec_ctx-width, codec_ctx-height, AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);// 在解码帧的处理部分 SDL_UpdateYUVTexture(texture, rect,frame-data[0], frame-linesize[0],frame-data[1], frame-linesize[1],frame-data[2], frame-linesize[2]);SDL_RenderClear(renderer); SDL_RenderCopy(renderer, texture, NULL, NULL); SDL_RenderPresent(renderer);首先使用sws_getContext函数创建一个图像格式转换上下文sws_ctx将解码后的帧格式转换为适合SDL渲染的格式这里是AV_PIX_FMT_YUV420P。然后使用SDL_UpdateYUVTexture函数将转换后的帧数据更新到纹理中。接着使用SDL_RenderClear函数清空渲染器SDL_RenderCopy函数将纹理数据复制到渲染器上最后使用SDL_RenderPresent函数将渲染器的内容显示在窗口上。 五、资源清理 在视频播放结束后需要释放所有分配的资源以避免内存泄漏 cleanup:sws_freeContext(sws_ctx);SDL_DestroyTexture(texture);SDL_DestroyRenderer(renderer);SDL_DestroyWindow(window);SDL_Quit();av_frame_free(frame);avcodec_free_context(codec_ctx);avformat_close_input(fmt_ctx);这里依次释放了图像格式转换上下文、纹理、渲染器、窗口、SDL库资源、视频帧、解码器上下文以及视频文件格式上下文。 六、完整代码示例 #include iostream #include string #include SDL2\SDL.h extern C { #include libavcodec\avcodec.h #include libavformat\avformat.h #include libavutil\avutil.h #include libswscale\swscale.h #include libswresample/swresample.h #include libavutil/channel_layout.h #include libavutil/opt.h #include libavutil\pixfmt.h #include libavutil/imgutils.h #include libavutil/time.h } #include chrono // 用于时间同步 #include threadvoid handle_ffmpeg_error(int ret, const char* msg) {char errbuf[AV_ERROR_MAX_STRING_SIZE];av_strerror(ret, errbuf, AV_ERROR_MAX_STRING_SIZE);fprintf(stderr, %s: %s\n, msg, errbuf); } #undef main int main() {AVFormatContext* fmt_ctx NULL;std::string file_path F:/QT/mp4_flv/x.mp4;// 打开视频文件int ret avformat_open_input(fmt_ctx, file_path.c_str(), NULL, NULL);if (ret 0) {handle_ffmpeg_error(ret, Failed to open video file.);return -1;}// 读取视频流信息ret avformat_find_stream_info(fmt_ctx, NULL);if (ret 0) {handle_ffmpeg_error(ret, Error in obtaining video stream information.);avformat_close_input(fmt_ctx);return -1;}// 查找视频流和解码器const AVCodec* codec NULL;int video_stream_idx -1;for (unsigned int i 0; i fmt_ctx-nb_streams; i) {if (fmt_ctx-streams[i]-codecpar-codec_type AVMEDIA_TYPE_VIDEO) {video_stream_idx i;codec avcodec_find_decoder(fmt_ctx-streams[i]-codecpar-codec_id);if (!codec) {fprintf(stderr, Video decoder not found\n);avformat_close_input(fmt_ctx);return -1;}break;}}AVCodecContext* codec_ctx avcodec_alloc_context3(codec);if (!codec_ctx) {fprintf(stderr, Decoder context allocation failed\n);avformat_close_input(fmt_ctx);return -1;}ret avcodec_parameters_to_context(codec_ctx, fmt_ctx-streams[video_stream_idx]-codecpar);if (ret 0) {handle_ffmpeg_error(ret, Copying codec parameters failed!);avcodec_free_context(codec_ctx);avformat_close_input(fmt_ctx);return -1;}ret avcodec_open2(codec_ctx, codec, NULL);if (ret 0) {handle_ffmpeg_error(ret, Decoder open failed!);avcodec_free_context(codec_ctx);avformat_close_input(fmt_ctx);return -1;}AVFrame* frame av_frame_alloc();SwsContext* sws_ctx NULL;if (SDL_Init(SDL_INIT_VIDEO) 0) {fprintf(stderr, SDL could not initialize! SDL_Error: %s\n, SDL_GetError());av_frame_free(frame);avcodec_free_context(codec_ctx);avformat_close_input(fmt_ctx);return -1;}SDL_Window* window SDL_CreateWindow(Video Player, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,codec_ctx-width, codec_ctx-height, SDL_WINDOW_SHOWN);if (!window) {fprintf(stderr, Window could not be created! SDL_Error: %s\n, SDL_GetError());SDL_Quit();av_frame_free(frame);avcodec_free_context(codec_ctx);avformat_close_input(fmt_ctx);return -1;}SDL_Renderer* renderer SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);if (!renderer) {fprintf(stderr, Renderer could not be created! SDL_Error: %s\n, SDL_GetError());SDL_DestroyWindow(window);SDL_Quit();av_frame_free(frame);avcodec_free_context(codec_ctx);avformat_close_input(fmt_ctx);return -1;}SDL_Texture* texture SDL_CreateTexture(renderer, SDL_PIXELFORMAT_YV12, SDL_TEXTUREACCESS_STREAMING,codec_ctx-width, codec_ctx-height);if (!texture) {fprintf(stderr, Texture could not be created! SDL_Error: %s\n, SDL_GetError());SDL_DestroyRenderer(renderer);SDL_DestroyWindow(window);SDL_Quit();av_frame_free(frame);avcodec_free_context(codec_ctx);avformat_close_input(fmt_ctx);return -1;}SDL_Rect rect { 0, 0, codec_ctx-width, codec_ctx-height };sws_ctx sws_getContext(codec_ctx-width, codec_ctx-height, codec_ctx-pix_fmt,codec_ctx-width, codec_ctx-height, AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);int64_t start_time av_gettime();AVPacket pkt;while (av_read_frame(fmt_ctx, pkt) 0) {if (pkt.stream_index video_stream_idx) {ret avcodec_send_packet(codec_ctx, pkt);if (ret 0) {handle_ffmpeg_error(ret, send data decoder error.);av_packet_unref(pkt);continue;}while (ret 0) {ret avcodec_receive_frame(codec_ctx, frame);if (ret 0) {int64_t pts frame-pts;if (pts AV_NOPTS_VALUE) {pts av_rescale_q(pkt.dts, fmt_ctx-streams[video_stream_idx]-time_base, { 1, 1000000 });}else {pts av_rescale_q(frame-pts, fmt_ctx-streams[video_stream_idx]-time_base, { 1, 1000000 });}int64_t now av_gettime();if (pts now - start_time) {SDL_Delay((pts - (now - start_time)) / 1000);}SDL_UpdateYUVTexture(texture, rect,frame-data[0], frame-linesize[0],frame-data[1], frame-linesize[1],frame-data[2], frame-linesize[2]);SDL_RenderClear(renderer);SDL_RenderCopy(renderer, texture, NULL, NULL);SDL_RenderPresent(renderer);}else if (ret AVERROR(EAGAIN) || ret AVERROR_EOF) {break;}}}av_packet_unref(pkt);SDL_Event event;while (SDL_PollEvent(event)) {if (event.type SDL_QUIT) {goto cleanup;}}}cleanup:sws_freeContext(sws_ctx);SDL_DestroyTexture(texture);SDL_DestroyRenderer(renderer);SDL_DestroyWindow(window);SDL_Quit();av_frame_free(frame);avcodec_free_context(codec_ctx);avformat_close_input(fmt_ctx);return 0; }七、总结与展望 通过本文的详细介绍和代码示例我们深入了解了视频播放器的开发过程。从视频播放的基本原理到使用FFmpeg库进行视频文件的解封装、解码再到利用SDL库进行窗口创建、图形渲染和事件处理每一个环节都紧密相扣共同构成了一个完整的视频播放系统。 然而这只是一个简单的视频播放器示例实际应用中的视频播放器还需要具备更多的功能和优化。例如支持更多的视频和音频格式、实现音频的播放和同步、添加播放控制功能如暂停、快进、快退等、优化性能以适应不同的硬件环境等。
http://www.hkea.cn/news/14452572/

相关文章:

  • 淮北手机网站建设公司求手机网址
  • 合肥比较好的网站制作网络推广公司名称
  • 网站新年特效网站建设业动态
  • 物联网网站开发公司信息产业部icp备案中心网站
  • 潍坊住房公积金个人账户查询网页优化公司
  • 长沙企业网站推广服务公司公司网站域名和空间使用费
  • 网站优化目录软件开发项目管理系统
  • 有什么做公众号封面图的网站网站可不可以不添加源码直接添加模板
  • 如何建设国外网站做一个小程序的步骤
  • 重庆网站优化排名软件方案太原网站设计排名
  • 网站权重到底是什么贺州建设网站
  • 网站教育机构排行前十名结构设计师之家官网
  • 企业网站页面宽哪里设置山东住房建设部官方网站
  • 做网站哪个行业比较有前景wordpress 文章访问量
  • 心理咨询中心网站模板软件开发文档管理规范
  • 网站建设不开单凡科网官网首页
  • 免费搭建业务网站建设网站的原则
  • 怎么用vscode做网站wordpress 4.2.2 漏洞
  • 创新创业项目计划书pptwin7优化教程
  • 北京通州网站设计公司seo和sem的区别是什么?
  • 网站建设最重要的因素wordpress 修改语言
  • 网站可以用中国二字做抬头吗大理网站建设滇icp备
  • 口碑好的网站推广价格做网站模板的海报尺寸多少
  • 在哪注册网站亚马逊网站开发
  • 下载168网站举报网站怎么做
  • 前端只是做网站吗中国互联网协会官网平台
  • 成都响应式网站建设做网站用js的好处
  • 合肥做网站公司有哪些rar在线解压缩网站
  • jsp网站建设项目实战课本内容益阳网站seo
  • 网站建设与管理基础及实训(php版)深圳专业做网站设计