文章目录

  • 1. FFmpeg解码流程
  • 2. 代码
  • 3. 解释
    • 3.1 sws_getContext
    • 3.2 sws_scale
  • 4. 参考资料

1. FFmpeg解码流程

2. 代码

    std::string input_file = "1.mp4";std::string output_file = "1.yuv";// 创建输出文件FILE *out_fd = nullptr;out_fd = fopen(output_file.c_str(), "wb");if (!out_fd){printf("can't open output file");return;}AVFormatContext *fmt_ctx = nullptr;fmt_ctx = avformat_alloc_context();// 打开输入视频文件int ret = avformat_open_input(&fmt_ctx, input_file.c_str(), nullptr, nullptr);if(ret < 0){av_log(nullptr, AV_LOG_ERROR, "can not open input: %s \n", err2str(ret).c_str());return;}// 获取视频文件信息ret = avformat_find_stream_info(fmt_ctx, nullptr);if(ret < 0){av_log(nullptr, AV_LOG_ERROR, "avformat_find_stream_info failed: %s \n", err2str(ret).c_str());return;}// 打印视频信息//av_dump_format(fmt_ctx, 0, input_file.c_str(), 0);   // 第四个参数,输入流为0, 输出流为1// 查找视频流序号int video_index = -1;for (int i = 0; i < fmt_ctx->nb_streams; ++i){if(fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO){video_index = i;}}if(video_index == -1){av_log(nullptr, AV_LOG_ERROR, "can not find video \n");return;}// 找视频流解码器const AVCodec *video_codec = avcodec_find_decoder(fmt_ctx->streams[video_index]->codecpar->codec_id);AVCodecContext *codec_ctx = avcodec_alloc_context3(video_codec);avcodec_parameters_to_context(codec_ctx, fmt_ctx->streams[video_index]->codecpar);// 打开视频解码器ret = avcodec_open2(codec_ctx, video_codec, nullptr);if(ret < 0){av_log(nullptr, AV_LOG_ERROR, "avcodec_open2 failed: %s \n", err2str(ret).c_str());return;}// 其他YUV格式转换成YUV420PSwsContext *img_convert_ctx = nullptr;img_convert_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);// 创建packet,用于存储解码前的数据AVPacket packet;av_init_packet(&packet);// 创建Frame,用于存储解码后的数据AVFrame *frame = av_frame_alloc();frame->width = fmt_ctx->streams[video_index]->codecpar->width;frame->height = fmt_ctx->streams[video_index]->codecpar->height;frame->format = fmt_ctx->streams[video_index]->codecpar->format;av_frame_get_buffer(frame, 32);// 创建YUV Frame,用于存储解码后的数据AVFrame *yuv_frame = av_frame_alloc();yuv_frame->width = fmt_ctx->streams[video_index]->codecpar->width;yuv_frame->height = fmt_ctx->streams[video_index]->codecpar->height;yuv_frame->format = AV_PIX_FMT_YUV420P;av_frame_get_buffer(yuv_frame, 32);// while循环,每次读取一帧,并转码while (av_read_frame(fmt_ctx, &packet) >= 0){if(packet.stream_index == video_index){// 开始解码// 发送数据到解码队列// 旧API:avcodec_decode_video2// 新API:avcodec_send_packet与avcodec_receive_frameret = avcodec_send_packet(codec_ctx, &packet);if (ret < 0){av_log(nullptr, AV_LOG_ERROR, "avcodec_send_packet failed: %s \n", err2str(ret).c_str());break;}while (avcodec_receive_frame(codec_ctx, frame) >= 0){// sws_scale(img_convert_ctx,(const uint8_t **)frame->data,frame->linesize,0,codec_ctx->height,yuv_frame->data,yuv_frame->linesize);// 数据写入到yuv文件中int y_size = codec_ctx->width * codec_ctx->height;fwrite(yuv_frame->data[0], 1, y_size, out_fd);fwrite(yuv_frame->data[1], 1, y_size/4, out_fd);fwrite(yuv_frame->data[2], 1, y_size/4, out_fd);}}av_packet_unref(&packet);}if (out_fd){fclose(out_fd);}avcodec_free_context(&codec_ctx);avformat_close_input(&fmt_ctx);avformat_free_context(fmt_ctx);

其中:

std::string err2str(int err)
{char errStr[1024] = {0};av_strerror(err, errStr, sizeof(errStr));return errStr;
}

转码后,使用pplay播放:

转换成yuv后,播放: ffplay -s 640x352 -pix_fmt yuv420p 1.yuv

-s 640x352 为视频 宽 x 高

3. 解释

3.1 sws_getContext

struct SwsContext* sws_getContext(int srcW,int srcH,enum AVPixelFormat srcFormat,int dstW,int dstH,enum AVPixelFormat dstFormat,int flags,SwsFilter *srcFilter,SwsFilter *dstFilter,const double *param )

参数:

  • srcW 源视频帧的width;
  • srcH 源视频帧的height;
  • srcFormat 源视频帧的像素格式format;
  • dstW 转换后视频帧的width;
  • dstH 转换后视频帧的height;
  • dstFormat 转换后视频帧的像素格式format;
  • flags 转换的算法
  • srcFilter、dstFilter 分别定义输入/输出图像滤波器信息,如果不做前后图像滤波,输入NULL;
  • param 定义特定缩放算法需要的参数,默认为NULL

函数返回SwsContext结构体,定义了基本变换信息

例子:

sws_getContext(w, h, YV12, w, h, NV12, 0, NULL, NULL, NULL);      // YV12->NV12 色彩空间转换
sws_getContext(w, h, YV12, w/2, h/2, YV12, 0, NULL, NULL, NULL);  // YV12图像缩小到原图1/4
sws_getContext(w, h, YV12, 2w, 2h, YN12, 0, NULL, NULL, NULL);    // YV12图像放大到原图4倍,并转换为NV12结构

3.2 sws_scale

int sws_scale(struct SwsContext *c,const uint8_t *const srcSlice[],const int srcStride[],int srcSliceY,int srcSliceH,uint8_t *const dst[],const int dstStride[] )

参数:

  • c 是由 sws_getContext 所取得的参数;
  • srcSlice[] 输入数据buffer;
  • srcStride[] 每一列的byte数,比实际width值要大;
  • srcSliceY 第一列要处理的位置;这里我是从头处理,所以直接填0;
  • srcSliceH 高度;
  • dst[] 目标数据buffer;
  • dstStride[] 同srcStride[]

解码后YUV格式的视频像素数据保存在AVFrame的data[0]、data[1]、data[2]中,但是这些像素值并不是连续存储的,每行有效像素之后存储了一些无效像素。
以亮度Y数据为例,data[0]中一共包含了linesize[0]*height个数据。但是处于优化等方面的考虑,linesize[0]实际上并不等于宽度width,而是一个比宽度大一些的值。因而需要使用sws_scale()进行转换,转换后去除了无效数据,width与linesize[0]取值相等。

4. 参考资料

https://ffmpeg.org/doxygen/trunk/group__libsws.html#gae531c9754c9205d90ad6800015046d74
https://www.cnblogs.com/cyyljw/p/8676062.html
ffmpeg代码实现h264转yuv
《基于 FFmpeg + SDL 的视频播放器的制作》课程的视频

视频播放器(二):视频解码相关推荐

  1. Android本地视频播放器开发--视频解码

    在上一章Android本地视频播放器开发--SDL编译编译中编译出sdl的支持库,当时我们使用的2.0,但是有些api被更改了,所以在以下的使用者中我们使用SDL1.3的库,这个库我会传上源码以及编译 ...

  2. Android 视频播放器 (二):使用MediaPlayer播放视频

    在 Android 视频播放器 (一):使用VideoView播放视频 我们讲了一下如何使用VideoView播放视频,了解了基本的播放器的一些知识和内容.也知道VideoView内部封装的就是Med ...

  3. 【Android视频 之 阿里云视频播放器 二】

    说明一下:写文章时,我用的最新sdk版本是 4.7.0 哦,这里也只贴出关键代码,更多详细代码在文章末尾有地址链接的. [Android视频 之 阿里云视频播放器 一] 1 视频试看功能的实现 效果图 ...

  4. 视频播放器(二)——播放列表

    这一篇主要来讲视频的播放,上一篇说主页面是Indicator+ViewPager+Fragment的布局,所以,在线视频的数据是展示在OnlineFragment中,本地视频的数据是展示在LocalF ...

  5. C++ Qt高仿QQ影音视频播放器 (二)

      本篇介绍QQ影音标题栏的实现.   一般在Qt客户端开发时,都会采用无边框窗口,去掉窗口边框后就没有标题栏了,此时就得自定义标题栏,自己实现窗口的移动.拉伸.缩放等功能.   我是在VS2019里 ...

  6. Android FFmpeg视频播放器三 音频封装格式解码播放

    Android FFmpeg视频播放器一解封装 Android Android FFmpeg视频播放器二 视频封装格式解码播放 视频解封装之后就会得到音频流和视频流,解封状得到的数据是AVPackag ...

  7. Android课程设计之视频播放器

    CSDN下载:https://download.csdn.net/download/eseszb/10463442 移动互联网开发   课程设计报告 学生姓名:学 号: 专业:计算机科学与技术 班级: ...

  8. 【Android视频 之 阿里云视频播放器 一】

    说到视频,现在短视频,小视频真是火的不行,抖音,快手,更是很多人手机上必会安装的一款app.那如何开发一款类似于抖音,集视频播放,直播,点播,拍摄,滤镜于一体的app呢,想想那都是相当的复杂.说到这里 ...

  9. C语言基于GTK+Libvlc实现的简易视频播放器(二)

    简易视频播放器-全屏播放 一.课程说明 上一次我们使用gtk+libvlc实现了一个最简单的视频播放器,可以实现点击按钮暂定和停止播放视频,以及同步显示视频播放进度,但即使作为一个视频播放器,只有这些 ...

最新文章

  1. Linux设备中文件系统位置,【linux之设备,分区,文件系统】
  2. 【错误记录】Android Studio 编译报错 ( Could not determine java version from ‘11.0.8‘. | Android Studio 降级 )
  3. mysql带LIKE 关键字的查询
  4. Windows内核工具Win64AST初步使用
  5. 59. 螺旋矩阵 II(模拟)
  6. python xlutils教程_Python基于xlutils修改表格内容过程解析
  7. Chapter1-4_Speech_Recognition(HMM)
  8. 筛法求素数c 语言,位筛法求素数,有段代码看不懂,有大佬可以来说一下
  9. linux中的基础正则表达式
  10. java反编译工具_推荐Java反编译工具luyten、jd-gui
  11. cms 移动根节点到根节点中的子节点,根节点消失解决办法
  12. Python编码规范和Python风格规范
  13. VB6的后期绑定和前期绑定
  14. 腾达n3_腾达n3迷你路由参数
  15. Git版本控制:Git查阅、撤销文件修改和撤销文件追踪
  16. 13. CSS 链接
  17. C#学习笔记(三):值类型、引用类型及参数传递
  18. MVC中使用Tuple完成匿名类数据存储
  19. oracle常用查询语句
  20. 什么是商业智能(BI),就看这篇文章足够了

热门文章

  1. <C语言>简单表白代码小❤❤
  2. 重阳节,让我们祝天下的老年人生活幸福快乐。
  3. 大家以后不要说百毒的坏话了?
  4. nsf5隐写算法 matlab,基于纹理复杂度的JPEG图像自适应隐写
  5. 软件开发:项目设计思路(流程、功能方法和数据结构)
  6. 电商系统购物车设计思路
  7. java思路_java编程的思路
  8. MATALB APP DESIGNER 回调函数创建及StartupFcn函数
  9. Ubuntu18.04 编译Android 10源码 并烧录源码到pixel3的避坑指南
  10. glassfish简单介绍