接上篇
ffmpeg命令分析【内容包括】-vf/ac/b:v/r/re/segment/t/ss/output_ts_offset/vn/acc/print/yuv420p/yuv封装mp4/FFmpeg硬件加速/pix_fmt/拉取TCP流/tee输出多路流/ffmpeg复杂滤镜filter_complex/map_channel/vframe
16、ffmpeg命令分析-pix_fmt
16、ffmpeg命令分析-pix_fmt
本系列 以 ffmpeg4.2 源码为准,下载地址:链接:百度网盘 提取码:g3k8
本系列主要分析各种 ffmpeg 命令参数 在代码里是如何实现的。a.mp4下载链接:百度网盘,提取码:nl0s 。
命令如下:
ffmpeg -vcodec h264 -i a.mp4 -pix_fmt nv12 -vcodec h264_mf -acodec copy a_h264_nvenc.mp4 -y
上面的命令是解码,然后编码,本文不关注编解码,主要分析 -pix_fmt nv12 参数,由于一些硬件编码器只支持特定的像素格式,所以需要 --pix_fmt 指定编码器的 pix_fmt,代码如下:
MATCH_PER_STREAM_OPT(frame_pix_fmts, str, frame_pix_fmt, oc, st);
//省略代码。
if (frame_pix_fmt && (video_enc->pix_fmt = av_get_pix_fmt(frame_pix_fmt)) == AV_PIX_FMT_NONE) {
av_log(NULL, AV_LOG_FATAL, "Unknown pixel format requested: %s.\n", frame_pix_fmt);
exit_program(1);
}
相关视频推荐
FFmpeg最佳学习方法,只讲一次!/FFmpeg/webRTC/rtmp/hls/rtsp/ffplay/srs
重点代码是 video_enc->pix_fmt = av_get_pix_fmt(frame_pix_fmt),命令行的 pix_fmt 最后解析给编码器的 pix_fmt 。
这里,还有一个重点,如果解码器输出的 pix_fmt 跟 video_enc->pix_fmt 不一致,就会创建 filter,进行格式转换。把像素格式转换好之后再丢给编码器。
代码如下:
configure_output_video_filter() 函数代码
if ((pix_fmts = choose_pix_fmts(ofilter))) {
AVFilterContext *filter;
snprintf(name, sizeof(name), "format_out_%d_%d",
ost->file_index, ost->index);
ret = avfilter_graph_create_filter(&filter,
avfilter_get_by_name("format"),
"format", pix_fmts, NULL, fg->graph);
av_freep(&pix_fmts);
if (ret < 0)
return ret;
if ((ret = avfilter_link(last_filter, pad_idx, filter, 0)) < 0)
return ret;
last_filter = filter;
pad_idx = 0;
}
17、ffmpeg命令分析-拉取TCP流
TCP推流命令如下:
ffmpeg.exe -re -i a.mp4 -c copy -f flv tcp://127.0.0.1:1234/live/stream
TCP拉流命令如下:
ffmpeg.exe -listen 1 -i tcp://127.0.0.1:1234/live/stream -c copy -f flv output.flv -y
ffmpeg 除了支持 RTMP,HTTP 等高层的协议,也支持直接拉取 TCP 流。本文主要分析 TCP 拉流的逻辑,推流不管。
-i tcp://127.0.0.1:1234/live/stream 这个参数其实没有太多需要分析的地方,在API调用层,跟 -i 本地文件是一样的,就是传递个字符串进去 avforamt_open_input 函数里面。avforamt_open_input 内部根据字符串类型 做不同的解封装处理,如图:
主要分析 -listen 1
会影响哪些逻辑。 listen 的定义是在 libavformat/tcp.c
里面,如下:
static const AVOption options[] = {
{ "listen","Listen for incoming connections", OFFSET(listen),AV_OPT_TYPE_INT, { .i64 = 0 },0, 2,.flags = D|E }
省略代码...
};
因为 listen 定义是在 libavformat 目录里面的,所以这个 -listen 的解析,是走的 opt_default() -> avformat_get_class() ,最后把 listen 解析到 o->g->format_opts ,然后丢进去 avformat_open_input() 函数,具体分析可以看专栏《FFmpeg源码分析-参数解析篇》
所以 -listen 1 对 ffmpeg.c 的代码逻辑并没有什么影响,他只是 tcp format 的一个 option 参数,传递进去 avformat_open_input() 函数,内部已经封装好,API调用层无感知。
分享一个音视频高级开发交流群,需要C/C++ Linux服务器架构师学习资料加企鹅群:788280672获取。资料包括(C/C++,Linux,FFmpeg webRTC rtmp hls rtsp ffplay srs 等等),免费分享。
18、ffmpeg命令分析-tee输出多路流
早期 FFmpeg 在 转 码 后 输出 直播 流 时并 不支持 编码 一次 之后 同时 输出 多路 直播 流, 需要 使用 管道 方式 进行 输出, 而在 新版本 的 FFmpeg 中 已经 支持 tee 文件 封装 及 协议 输出, 可以 使用 tee 进行 多路 流 输出, 本节 将 主要 讲解 管道 方式 输出 多路 流 与 tee 协议 输出 方式 输出 多路 流。
本文主要讲解 tee 方式 输出多路流 在 ffmpeg.c 里面的逻辑实现,命令如下:
ffmpeg.exe -re -i a.mp4 -vcodec h264_mf -acodec aac -map 0 -f tee "[f=flv]tcp://127.0.0.1:1234/live/stream | [f=flv]rtmp://192.168.0.122/live/livestream"
上面的命令 音频编码 为 AAC,视频编码为 H264,转成 flv 的封装,然后推了两路流。
1,tcp 流,把 flv 的数据放在 tcp 包里面进行传输。
2,rtmp 流,把flv 的数据放在 rtmp 上层进行传输。
这两路流的服务器请自行搭建。
首先分析 -map 0 参数在 ffmpeg.c 里面的逻辑,map 的定义在 ffmpeg_opt.c 里面。
{ "map", HAS_ARG | OPT_EXPERT | OPT_PERFILE |
OPT_OUTPUT, { .func_arg = opt_map },
"set input stream mapping",
"[-]input_file_id[:stream_specifier][,sync_file_id[:stream_specifier]]" },
"[-]input_file_id[:stream_specifier][,sync_file_id[:stream_specifier]]" 这句注释,我也没看出这个 map 的具体用法,所以直接分析代码逻辑,从代码逻辑推导出 map的 具体用法。