视频转码 via FFmpeg

  • FFmpeg 简介
  • FFmpeg 命令行转码
  • FFmpeg API 转码
    • Transcoding 流程图
    • Transcoding 代码
      • open_input_file 函数
      • open_output_video_file 函数
      • HW_dec_helper::init 函数
      • init_cvt_frame_and_sws 函数
      • video_transcode 函数
        • decode_av_frame 函数
          • HW_dec_helper::convert_frame 函数
        • encode_av_frame 函数
        • flush_encoder 函数
  • 其他框架的转码

转码(transcoding)其实就是把音频从一种编码转换成另一种编码的过程,如 mpg2 → h.264。基本流程如下图:

FFmpeg 简介

FFmpeg 是一套可以用来记录、转换数字音频、视频,并能将其转化为流的开源计算机程序。采用 LGPL 或 GPL 许可证。它提供了录制、转换以及流化音视频的完整解决方案。它包含了非常先进的音频 / 视频编解码库 libavcodec,为了保证高可移植性和编解码质量,libavcodec 里很多 code 都是从头开发的。

FFmpeg 在 Linux 平台下开发,但它同样也可以在其它操作系统环境中编译运行,包括 Windows、Mac OS X 等。这个项目最早由 Fabrice Bellard 发起,2004 年至 2015 年间由 Michael Niedermayer 主要负责维护。许多 FFmpeg 的开发人员都来自 MPlayer 项目,而且当前 FFmpeg 也是放在 MPlayer 项目组的服务器上。项目的名称来自 MPEG 视频编码标准,前面的 “FF” 代表 “Fast Forward”。

FFmpeg 命令行转码

FFmpeg 提供了命令行的方式对视频(含音频)进行转码:

>ffmpeg.exe -i test.mp4 -s 640*360 test.mpg
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'test.mp4':Metadata:Duration: 00:00:01.90, start: 0.000000, bitrate: 14050 kb/sStream #0:0(eng): Video: h264 (Main) (avc1 / 0x31637661), yuv420p(tv, bt709), 1920x1080 [SAR 1:1 DAR 16:9], 13912 kb/s, 30 fps, 30 tbr, 30k tbn, 60 tbc (default)Metadata:encoder         : AVC CodingStream #0:1(eng): Audio: aac (LC) (mp4a / 0x6134706D), 32000 Hz, stereo, fltp, 130 kb/s (default)
Stream mapping:Stream #0:0 -> #0:0 (h264 (native) -> mpeg1video (native))Stream #0:1 -> #0:1 (aac (native) -> mp2 (native))
Output #0, mpeg, to 'test.mpg':Metadata:encoder         : Lavf58.20.100Stream #0:0(eng): Video: mpeg1video, yuv420p(progressive), 640x360 [SAR 1:1 DAR 16:9], q=2-31, 200 kb/s, 30 fps, 90k tbn, 30 tbc (default)Metadata:encoder         : Lavc58.35.100 mpeg1videoSide data:cpb: bitrate max/min/avg: 0/0/200000 buffer size: 0 vbv_delay: -1Stream #0:1(eng): Audio: mp2, 32000 Hz, stereo, s16, 384 kb/s (default)Metadata:encoder         : Lavc58.35.100 mp2
frame=56 fps=0.0 q=31.0 Lsize=230kB time=00:00:01.89 bitrate=993.2kbits/s speed=3.79x
video:134kB audio:91kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: 2.332372%

FFmpeg API 转码

Transcoding 流程图

Transcoding 代码

以下是整个转码过程的概要代码,略去各个函数的具体实现和资源释放:

本文中的代码基于 FFmpeg 4.1。

int video_index = open_input_file(in_file, AVMEDIA_TYPE_VIDEO, &in_fmt_ctx, &dec_ctx);
double frame_rate = av_q2d(av_guess_frame_rate(in_fmt_ctx, in_fmt_ctx->streams[video_index], NULL));hr = open_output_video_file(out_file, dec_ctx, 400 * 1000, (int)ceil(frame_rate), &out_fmt_ctx, &enc_ctx);if (is_hw_dec) hr = hw_decoder.init(hw_type_name, dec_ctx);hr = init_cvt_frame_and_sws(AV_PIX_FMT_YUV420P, dec_ctx, &yuv420p_frame, &yuv420p_buffer, &sws_ctx);
hr = avformat_write_header(out_fmt_ctx, NULL);while (_kbhit() == 0) {int finished = 0;hr = video_transcode(in_fmt_ctx, dec_ctx, out_fmt_ctx, enc_ctx,video_index, yuv420p_frame, sws_ctx, &finished );if (finished)break;
}flush_encoder(out_fmt_ctx, enc_ctx, false, false);
hr = av_write_trailer(out_fmt_ctx);

open_input_file 函数

请看 这里,一模一样。

open_output_video_file 函数

请看 这里,一模一样。

HW_dec_helper::init 函数

硬解码器的初始化。

int HW_dec_helper::init(char* hw_type_name, AVCodecContext *decoder_ctx, AVCodec *decoder = NULL)
{ AVHWDeviceType hw_type = av_hwdevice_find_type_by_name(hw_type_name);RETURN_IF_FALSE(hw_type != AV_HWDEVICE_TYPE_NONE);if (decoder == NULL)decoder = (AVCodec*)decoder_ctx->codec;for (int i = 0; ; i++) {const AVCodecHWConfig *config = avcodec_get_hw_config(decoder, i);RETURN_IF_NULL(config);if (config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX &&config->device_type == hw_type) {m_hw_pix_fmt = config->pix_fmt;break;}}decoder_ctx->get_format = get_hw_format;int hr = av_hwdevice_ctx_create(&m_hw_device_ctx, hw_type, NULL, NULL, 0);RETURN_IF_FAILED(hr);decoder_ctx->hw_device_ctx = av_buffer_ref(m_hw_device_ctx);return 0;
}

init_cvt_frame_and_sws 函数

请看 这里,一模一样。

video_transcode 函数

解码 → 色彩空间转换 → 编码 的过程。

    const int output_frame_size = enc_ctx->frame_size;std::vector<AVFrame*> decoded_frames;while (true) {hr = decode_av_frame(in_fmt_ctx, dec_ctx, video_index, decoded_frames, finished);   for (size_t dec_frame_idx = 0; dec_frame_idx < decoded_frames.size(); ++dec_frame_idx) {AVFrame* frame = decoded_frames[dec_frame_idx];// convert to YUV420P formatint height = sws_scale(sws_ctx, (const uint8_t* const*)frame->data, frame->linesize, 0, dec_ctx->height, yuv420p_frame->data, yuv420p_frame->linesize);    av_frame_copy_props(yuv420p_frame, frame);yuv420p_frame->pict_type = AV_PICTURE_TYPE_NONE;int data_written = 0;hr = encode_av_frame(yuv420p_frame, out_fmt_ctx, enc_ctx, &data_written, interleaved, init_pts);if (hr == AVERROR(EAGAIN))continue;GOTO_IF_FAILED(hr);}if (SUCCEEDED(hr))break;}if (*finished)flush_encoder(out_fmt_ctx, enc_ctx, interleaved, init_pts);

decode_av_frame 函数

这里和之前的解码函数不一样的地方就是增加了硬解码的处理。

init_packet(&input_packet);while (true) {hr = av_read_frame(in_fmt_ctx, &input_packet);if (hr == AVERROR_EOF)*finished = 1;elseav_packet_rescale_ts(&input_packet, in_fmt_ctx->streams[input_packet.stream_index]->time_base, in_codec_ctx->time_base);hr = avcodec_send_packet(in_codec_ctx, *finished ? NULL : &input_packet);if (SUCCEEDED(hr) || (hr == AVERROR(EAGAIN))) {while (true) {frame = av_frame_alloc();hr = avcodec_receive_frame(in_codec_ctx, frame);if (SUCCEEDED(hr)) {// decoded by hardwareAVFrame* sw_frame = HW_dec_helper::convert_frame(frame);frames.push_back(sw_frame);av_frame_free(&frame);} else if (hr == AVERROR_EOF) {*finished = 1;break;}else if (hr == AVERROR(EAGAIN)) // need more packetsbreak;}} else if (hr == AVERROR_EOF)*finished = 1;if (*finished || !frames.empty())break;
}
HW_dec_helper::convert_frame 函数

从显存中拷贝 frame 到内存中。

AVFrame* HW_dec_helper::convert_frame(AVFrame* frame)
{if (frame->format == m_hw_pix_fmt) {AVFrame* sw_frame = av_frame_alloc();int hr = av_hwframe_transfer_data(sw_frame, frame, 0);GOTO_LABEL_IF_FAILED(hr, OnErr);av_frame_copy_props(sw_frame, frame);return sw_frame;OnErr:if (NULL != sw_frame)av_frame_free(&sw_frame);return NULL;}elsereturn frame;
}

encode_av_frame 函数

请看 这里,一模一样。

flush_encoder 函数

请看 这里,一模一样。

其他框架的转码

关于 Media Foundation 的视频转码请参考 这里。


EOF

视频转码 via FFmpeg相关推荐

  1. ffplay flv mp4 转_手动视频转码教程,FFmpeg

    我相信很多人,在学习剪辑的时候都会在很多网站上下载影视素材.下载的素材有很多类型的格式,包括avi.rmvb.mkv.flv等等.得到这些格式的素材是无法直接进行编辑的,需要将其转换成MP4格式.这时 ...

  2. 基于ffmpeg实现音视频转码

    一.背景 偶然的机会接触了ffmpeg,当时是从B站下载的视频转移到笔记本上看.使用b站手机客户端下载的视频格式为m4s的两个文件(video.m4s和audio.m4s),需要转成普通播放器支持的m ...

  3. ffmpeg 调用 NVIDIA GPU 处理视频转码,笔记。和纯用CPU比起来,速度快5倍以上

    参考别人的文章 FFMPEG 使用显卡加速转码 ffmpeg 调用 NVIDIA GPU 处理视频转码 ffmpeg 硬件加速视频转码指南 ffmpeg 硬件加速 wmv 视频转码 自己的关于ffmp ...

  4. FFmpeg视频转码技巧之-crf参数(H.264篇)

    昨天,有个朋友给我出了个难题:他手上有一个视频,1080P的,49秒,200多兆:要求在确保质量的情况下把文件压缩到10M以内. 这是什么概念呢?按照文件大小10M来计算,码率是:10 x 8 / 4 ...

  5. 用FFMPEG SDK进行视频转码压缩时解决音视频不同步问题的方法(转) PTS DTS

    用FFMPEG SDK进行视频转码压缩的时候,转码成功后去看视频的内容,发现音视频是不同步的.这个的确是一个恼火的事情.我在用FFMPEG SDK做h264格式的FLV文件编码Filter的时候就碰到 ...

  6. 最简单的基于FFmpeg的移动端样例:IOS 视频转码器

    ===================================================== 最简单的基于FFmpeg的移动端样例系列文章列表: 最简单的基于FFmpeg的移动端样例:A ...

  7. ffmpeg 截图 java_Java Web 中使用ffmpeg实现视频转码、视频截图

    视频网站中提供的在线视频播放功能,播放的都是FLV格式的文件,它是Flash动画文件,可通过Flash制作的播放器来播放该文件.项目中用制作的player.swf播放器. 多媒体视频处理工具FFmpe ...

  8. ffmpeg 参数_使用FFMPEG进行视频转码

    Note: 此文章首发于我的个人博客zhi-hua.wang ,开放转载但需附上出处. 使用FFMPEG进行视频转码 - 日落孤城​www.zhi-hua.wang FFMPEG 是一款开源的视频处理 ...

  9. 最简单的基于FFmpeg的移动端例子:IOS 视频转码器

    ===================================================== 最简单的基于FFmpeg的移动端例子系列文章列表: 最简单的基于FFmpeg的移动端例子:A ...

最新文章

  1. c# 之Web.config
  2. 4.IT-解决方案-4-Cluster-Win2K3
  3. 一年成为emacs高手
  4. mysql 授权是哪一个表_MySQL授权系统的五个表
  5. Kubernetes系统架构简介--转
  6. jquery选择器连续选择_JQuery中的选择器
  7. python的while分支
  8. 使用 matlab 求解多元非线性方程组
  9. 教程:Visual Studio 连接 MySQL 数据库(包含常见错误及解决方法)
  10. RoadRunner安装与使用教程
  11. 免匙SSH登录失败问题(非常规)
  12. ESP8266WiFi模块资料整理
  13. mysql dump 转excel_使用mysqldump备份单表数据,并使用navicat导出单表中部分字段到excel...
  14. Linux关闭防火墙并设置开机启动/不启动
  15. 报错,Uncaught TypeError: Cannot read properties of undefined (reading ‘inputValue‘) at <anonymous>
  16. vimdiff解决git merge冲突
  17. 【好数推荐】数据堂平均音色语音库
  18. [java]自动生成指定长度的英文名字
  19. 加薪引发的难题 穆穆-movno1
  20. s32k118CAN通信问题

热门文章

  1. Spring注解大全详情,
  2. Export常见的15种认证
  3. OPPO A57t怎么刷机 OPPO A57t的刷机教程 OPPO A57t完美解除账号锁
  4. Spark操作Kudu
  5. XP系统测试显示器软件在哪,WinXP系统下如何检测显示器白点
  6. 什么是疲劳试验基础之材料力学
  7. 联想小新潮7000黑苹果教程_小新黑苹果-锐7000(10.13.6)双系统安装_小新笔记本-联想社区...
  8. 数据库的三级模式和两级映射--简单介绍
  9. 2022最新格创校园跑腿微信小程序V1.1.64+前端程序
  10. 输人一元二次方程的3个系数a、b、c,求方程的根。