使用 libx264 将 FFMPEG RTSP 流式传输到 MPEG4/H264 文件
FFMPEG RTSP stream to MPEG4/H264 file using libx264
大家好,
我正在尝试将 H264 格式的 RTSP 流 transcode/remux 放入 MPEG4 容器中,仅包含 H264 视频流。基本上,网络摄像头输出到 MP4 容器中。
我可以使用以下代码生成编码不佳的 MP4:
// Variables here for demo
AVFormatContext * video_file_output_format = nullptr;
AVFormatContext * rtsp_format_context = nullptr;
AVCodecContext * video_file_codec_context = nullptr;
AVCodecContext * rtsp_vidstream_codec_context = nullptr;
AVPacket packet = {0};
AVStream * video_file_stream = nullptr;
AVCodec * rtsp_decoder_codec = nullptr;
int errorNum = 0, video_stream_index = 0;
std::string outputMP4file = "D:\somemp4file.mp4";
// begin
AVDictionary * opts = nullptr;
av_dict_set(&opts, "rtsp_transport", "tcp", 0);
if ((errorNum = avformat_open_input(&rtsp_format_context, uriANSI.c_str(), NULL, &opts)) < 0) {
errOut << "Connection failed: avformat_open_input failed with error " << errorNum << ":\r\n" << ErrorRead(errorNum);
TacticalAbort();
return;
}
rtsp_format_context->max_analyze_duration = 50000;
if ((errorNum = avformat_find_stream_info(rtsp_format_context, NULL)) < 0) {
errOut << "Connection failed: avformat_find_stream_info failed with error " << errorNum << ":\r\n" << ErrorRead(errorNum);
TacticalAbort();
return;
}
video_stream_index = errorNum = av_find_best_stream(rtsp_format_context, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
if (video_stream_index < 0) {
errOut << "Connection in unexpected state; made a connection, but there was no video stream.\r\n"
"Attempts to find a video stream resulted in error " << errorNum << ": " << ErrorRead(errorNum);
TacticalAbort();
return;
}
rtsp_vidstream_codec_context = rtsp_format_context->streams[video_stream_index]->codec;
av_init_packet(&packet);
if (!(video_file_output_format = av_guess_format(NULL, outputMP4file.c_str(), NULL))) {
TacticalAbort();
throw std::exception("av_guess_format");
}
if (!(rtsp_decoder_codec = avcodec_find_decoder(rtsp_vidstream_codec_context->codec_id))) {
errOut << "Connection failed: connected, but avcodec_find_decoder returned null.\r\n"
"Couldn't find codec with an AV_CODEC_ID value of " << rtsp_vidstream_codec_context->codec_id << ".";
TacticalAbort();
return;
}
video_file_format_context = avformat_alloc_context();
video_file_format_context->oformat = video_file_output_format;
if (strcpy_s(video_file_format_context->filename, sizeof(video_file_format_context->filename), outputMP4file.c_str())) {
errOut << "Couldn't open video file: strcpy_s failed with error " << errno << ".";
std::string log = errOut.str();
TacticalAbort();
throw std::exception("strcpy_s");
}
if (!(video_file_encoder_codec = avcodec_find_encoder(video_file_output_format->video_codec))) {
TacticalAbort();
throw std::exception("avcodec_find_encoder");
}
// MARKER ONE
if (!outputMP4file.empty() &&
!(video_file_output_format->flags & AVFMT_NOFILE) &&
(errorNum = avio_open2(&video_file_format_context->pb, outputMP4file.c_str(), AVIO_FLAG_WRITE, nullptr, &opts)) < 0) {
errOut << "Couldn't open video file \"" << outputMP4file << "\" for writing : avio_open2 failed with error " << errorNum << ": " << ErrorRead(errorNum);
TacticalAbort();
return;
}
// Create stream in MP4 file
if (!(video_file_stream = avformat_new_stream(video_file_format_context, video_file_encoder_codec))) {
TacticalAbort();
return;
}
AVCodecContext * video_file_codec_context = video_file_stream->codec;
// MARKER TWO
// error -22/-21 in avio_open2 if this is skipped
if ((errorNum = avcodec_copy_context(video_file_codec_context, rtsp_vidstream_codec_context)) != 0) {
TacticalAbort();
throw std::exception("avcodec_copy_context");
}
//video_file_codec_context->codec_tag = 0;
/*
// MARKER 3 - is this not needed? Examples suggest not.
if ((errorNum = avcodec_open2(video_file_codec_context, video_file_encoder_codec, &opts)) < 0)
{
errOut << "Couldn't open video file codec context: avcodec_open2 failed with error " << errorNum << ": " << ErrorRead(errorNum);
std::string log = errOut.str();
TacticalAbort();
throw std::exception("avcodec_open2, video file");
}*/
//video_file_format_context->flags |= AVFMT_FLAG_GENPTS;
if (video_file_format_context->oformat->flags & AVFMT_GLOBALHEADER)
{
video_file_codec_context->flags |= CODEC_FLAG_GLOBAL_HEADER;
}
if ((errorNum = avformat_write_header(video_file_format_context, &opts)) < 0) {
errOut << "Couldn't open video file: avformat_write_header failed with error " << errorNum << ":\r\n" << ErrorRead(errorNum);
std::string log = errOut.str();
TacticalAbort();
return;
}
但是,有几个问题:
- 我无法将任何 x264 选项传递给输出文件。输出 H264 与输入 H264 匹配 profile/level - 将摄像机切换到不同型号会切换 H264 级别。
- 输出文件的时间明显不对。
- 输出文件的持续时间大量关闭。几秒钟的镜头变成了几个小时,尽管播放时间不匹配。 (FWIW,我正在使用 VLC 来播放它们。)
传递 x264 选项
如果我手动增加每个数据包的 PTS,并将 DTS 设置为 PTS,播放速度太快,一秒播放时间约为 2-3 秒的镜头,持续时间长达数小时。画面也模糊了好几秒,一秒钟大概有10秒的画面。
如果我让 FFMPEG 决定(带或不带 GENPTS 标志),该文件具有可变帧率(可能如预期的那样),但它会立即播放整个文件并且持续时间也很长(超过四十小时)几秒钟)。持续时间不是 "real",因为文件会立即播放。
在一号标记处,我尝试通过将选项传递给 avio_open2
来设置配置文件。 libx264 会简单地忽略这些选项。我试过:
av_dict_set(&opts, "vprofile", "main", 0);
av_dict_set(&opts, "profile", "main", 0); // error, missing '('
// FF_PROFILE_H264_MAIN equals 77, so I also tried
av_dict_set(&opts, "vprofile", "77", 0);
av_dict_set(&opts, "profile", "77", 0);
它似乎确实读取了配置文件设置,但没有使用它们。在标记二,我尝试将其设置在 avio_open2
之后、avformat_write_header
之前。
// I tried all 4 av_dict_set from earlier, passing it to avformat_write_header.
// None had any effect, they weren't consumed.
av_opt_set(video_file_codec_context, "profile", "77", 0);
av_opt_set(video_file_codec_context, "profile", "main", 0);
video_file_codec_context->profile = FF_PROFILE_H264_MAIN;
av_opt_set(video_file_codec_context->priv_data, "profile", "77", 0);
av_opt_set(video_file_codec_context->priv_data, "profile", "main", 0);
搞乱 privdata 使程序不稳定,但我当时正在尝试任何事情。
我想通过传递设置来解决问题 1,因为我想它会阻碍任何解决问题 2 或 3 的尝试。
大半个月以来我一直在摆弄这个。我浏览了数十份文档、问答和示例。有不少已经过时了,这于事无补。
如有任何帮助,我们将不胜感激。
干杯
好的,首先,我没有使用 ffmpeg, but a fork of ffmpeg called libav。不要混淆,ffmpeg 是更新的,而 libav 在 Linux.
的一些发行版中使用
为 Visual Studio
编译
一旦我升级到主分支,我不得不再次手动编译它,因为我在 Visual Studio 中使用它并且唯一的静态库是 G++,所以链接不能很好地工作。
官方指南是https://trac.ffmpeg.org/wiki/CompilationGuide/MSVC。
首先,这样编译工作正常:
确保 VS 在路径中。您的 PATH 应按此顺序读取 :
C:\Program Files (x86)\Microsoft Visual Studio XX.0\VC\bin
D:\MinGW\msys64\mingw32\bin
D:\MinGW\msys64\usr\bin
D:\MinGW\bin
然后 运行 Visual Studio x86 Native Tools 提示。应该在您的开始菜单中。
在 CMD 中,运行
(your path to MinGW)\msys64\msys2_shell.cmd -full-path
在创建的 MinGW window, 运行:
$ cd /your dev path/
$ git clone https://git.ffmpeg.org/ffmpeg.git ffmpeg
大约五分钟后,您将在子文件夹 ffmpeg 中获得 FFMPEG 源代码。
通过以下方式访问源代码:
$ cd ffmpeg
然后 运行:
$ which link
如果它不提供来自 PATH 的 VS 路径,而是 usr/link 或 usr/bin/link,类似地重命名:
$ mv /usr/bin/link.exe /usr/bin/msys-link.exe
如果确实跳过 $ mv 步骤。
最后,运行 这个命令:
$ ./configure --toolchain=msvc
以及您想要的任何其他命令行
(您可以通过 ./configure --help
查看命令行)
它可能会长时间显示为不活动。一旦完成,您将获得几页输出。
然后运行:
$ make
$ make install
注意静态构建(使用 --enable-static
配置),虽然你得到 Windows 个静态库文件,它会生成扩展名为 *.a 的文件。只需重命名为 .lib.
(你可以使用命令:ren *.a *.lib
)
使用 FFMPEG
要使用源配置文件、级别等从 FFMPEG RTSP 复制 到文件,只需使用:
- 读取网络帧
av_read_frame(rtsp_format_context)
- 传递到 MP4
av_write_frame(video_file_format_context)
您不需要打开 AVCodecContext、解码器或编码器;只是 avformat_open_input
,以及视频文件 AVFormatContext 和 AVIOContext。
如果你想重新编码,你必须:
- 读取网络帧
av_read_frame(rtsp_format_context)
- 将数据包传递给解码器
avcodec_send_packet(rtsp_decoder_context)
- 从解码器读取帧(循环)
avcodec_receive_frame(rtsp_decoder_context)
- 将每个解码帧发送到编码器
avcodec_send_frame(video_file_encoder_context)
- 从编码器读取数据包(循环)
avcodec_receive_packet(video_file_encoder_context)
- 将每个编码数据包发送到输出视频
av_write_frame(video_file_format_context)
一些陷阱
手动复制宽度、高度和像素格式。对于 H264,它是 YUV420P。
例如,对于级别 3.1,配置文件高:
AVCodecParameters * video_file_codec_params = video_file_stream->codecpar;
video_file_codec_params->profile = FF_PROFILE_H264_HIGH;
video_file_codec_params->format = AV_PIX_FMT_YUV420P;
video_file_codec_params->level = 31;
video_file_codec_params->width = rtsp_vidstream->codecpar->width;
video_file_codec_params->height = rtsp_vidstream->codecpar->height;
libx264 通过 avcodec_open2
中的 opts 参数接受 H264 预设。 “非常快”预设示例:
AVDictionary * mydict;
av_dict_set(&mydict, "preset", "veryfast", 0);
avcodec_open2(video_file_encoder_context, video_file_encoder_codec, &opts)
// avcodec_open2 returns < 0 for errors.
// Recognised options will be removed from the mydict variable.
// If all are recognised, mydict will be NULL.
输出时间是一个易变的东西。在 ``.
之前使用它
video_file_stream->avg_frame_rate = rtsp_vidstream->avg_frame_rate;
video_file_stream->r_frame_rate = rtsp_vidstream->r_frame_rate;
video_file_stream->time_base = rtsp_vidstream->time_base;
video_file_encoder_context->time_base = rtsp_vidstream_codec_context->time_base;
// Decreasing GOP size for more seek positions doesn't end well.
// libx264 forces the new GOP size.
video_file_encoder_context->gop_size = rtsp_vidstream_codec_context->gop_size;
if ((errorNum = avcodec_open2(video_file_encoder_context,...)) < 0) {
// an error...
}
H264可能会以双倍速度写入文件,因此播放速度会加倍。要更改此设置,请手动使用编码数据包的时间:
packet->pts = packet->dts = frameNum++;
av_packet_rescale_ts(packet, video_file_encoder_context->time_base, video_file_stream->time_base);
packet->pts *= 2;
packet->dts *= 2;
av_interleaved_write_frame(video_file_format_context, packet)
// av_interleaved_write_frame returns < 0 for errors.
注意我们将av_write_frame
切换为av_interleaved_write_frame
,同时设置了PTS和DTS。 frameNum
应该是 int64_t,并且应该从 0 开始(尽管这不是必需的)。
另请注意 av_rescale_ts
调用的参数是视频文件编码器上下文和视频文件流 - 不涉及 RTSP。
VLC 媒体播放器无法播放 FPS 为 4 或更低的 H.264 流编码。
因此,如果您的 RTSP 流显示第一个解码帧并且从不进行,或者在视频结束之前显示纯绿色,请确保您的 FPS 足够高。 (即 VLC v2.2.4)
大家好,
我正在尝试将 H264 格式的 RTSP 流 transcode/remux 放入 MPEG4 容器中,仅包含 H264 视频流。基本上,网络摄像头输出到 MP4 容器中。
我可以使用以下代码生成编码不佳的 MP4:
// Variables here for demo
AVFormatContext * video_file_output_format = nullptr;
AVFormatContext * rtsp_format_context = nullptr;
AVCodecContext * video_file_codec_context = nullptr;
AVCodecContext * rtsp_vidstream_codec_context = nullptr;
AVPacket packet = {0};
AVStream * video_file_stream = nullptr;
AVCodec * rtsp_decoder_codec = nullptr;
int errorNum = 0, video_stream_index = 0;
std::string outputMP4file = "D:\somemp4file.mp4";
// begin
AVDictionary * opts = nullptr;
av_dict_set(&opts, "rtsp_transport", "tcp", 0);
if ((errorNum = avformat_open_input(&rtsp_format_context, uriANSI.c_str(), NULL, &opts)) < 0) {
errOut << "Connection failed: avformat_open_input failed with error " << errorNum << ":\r\n" << ErrorRead(errorNum);
TacticalAbort();
return;
}
rtsp_format_context->max_analyze_duration = 50000;
if ((errorNum = avformat_find_stream_info(rtsp_format_context, NULL)) < 0) {
errOut << "Connection failed: avformat_find_stream_info failed with error " << errorNum << ":\r\n" << ErrorRead(errorNum);
TacticalAbort();
return;
}
video_stream_index = errorNum = av_find_best_stream(rtsp_format_context, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
if (video_stream_index < 0) {
errOut << "Connection in unexpected state; made a connection, but there was no video stream.\r\n"
"Attempts to find a video stream resulted in error " << errorNum << ": " << ErrorRead(errorNum);
TacticalAbort();
return;
}
rtsp_vidstream_codec_context = rtsp_format_context->streams[video_stream_index]->codec;
av_init_packet(&packet);
if (!(video_file_output_format = av_guess_format(NULL, outputMP4file.c_str(), NULL))) {
TacticalAbort();
throw std::exception("av_guess_format");
}
if (!(rtsp_decoder_codec = avcodec_find_decoder(rtsp_vidstream_codec_context->codec_id))) {
errOut << "Connection failed: connected, but avcodec_find_decoder returned null.\r\n"
"Couldn't find codec with an AV_CODEC_ID value of " << rtsp_vidstream_codec_context->codec_id << ".";
TacticalAbort();
return;
}
video_file_format_context = avformat_alloc_context();
video_file_format_context->oformat = video_file_output_format;
if (strcpy_s(video_file_format_context->filename, sizeof(video_file_format_context->filename), outputMP4file.c_str())) {
errOut << "Couldn't open video file: strcpy_s failed with error " << errno << ".";
std::string log = errOut.str();
TacticalAbort();
throw std::exception("strcpy_s");
}
if (!(video_file_encoder_codec = avcodec_find_encoder(video_file_output_format->video_codec))) {
TacticalAbort();
throw std::exception("avcodec_find_encoder");
}
// MARKER ONE
if (!outputMP4file.empty() &&
!(video_file_output_format->flags & AVFMT_NOFILE) &&
(errorNum = avio_open2(&video_file_format_context->pb, outputMP4file.c_str(), AVIO_FLAG_WRITE, nullptr, &opts)) < 0) {
errOut << "Couldn't open video file \"" << outputMP4file << "\" for writing : avio_open2 failed with error " << errorNum << ": " << ErrorRead(errorNum);
TacticalAbort();
return;
}
// Create stream in MP4 file
if (!(video_file_stream = avformat_new_stream(video_file_format_context, video_file_encoder_codec))) {
TacticalAbort();
return;
}
AVCodecContext * video_file_codec_context = video_file_stream->codec;
// MARKER TWO
// error -22/-21 in avio_open2 if this is skipped
if ((errorNum = avcodec_copy_context(video_file_codec_context, rtsp_vidstream_codec_context)) != 0) {
TacticalAbort();
throw std::exception("avcodec_copy_context");
}
//video_file_codec_context->codec_tag = 0;
/*
// MARKER 3 - is this not needed? Examples suggest not.
if ((errorNum = avcodec_open2(video_file_codec_context, video_file_encoder_codec, &opts)) < 0)
{
errOut << "Couldn't open video file codec context: avcodec_open2 failed with error " << errorNum << ": " << ErrorRead(errorNum);
std::string log = errOut.str();
TacticalAbort();
throw std::exception("avcodec_open2, video file");
}*/
//video_file_format_context->flags |= AVFMT_FLAG_GENPTS;
if (video_file_format_context->oformat->flags & AVFMT_GLOBALHEADER)
{
video_file_codec_context->flags |= CODEC_FLAG_GLOBAL_HEADER;
}
if ((errorNum = avformat_write_header(video_file_format_context, &opts)) < 0) {
errOut << "Couldn't open video file: avformat_write_header failed with error " << errorNum << ":\r\n" << ErrorRead(errorNum);
std::string log = errOut.str();
TacticalAbort();
return;
}
但是,有几个问题:
- 我无法将任何 x264 选项传递给输出文件。输出 H264 与输入 H264 匹配 profile/level - 将摄像机切换到不同型号会切换 H264 级别。
- 输出文件的时间明显不对。
- 输出文件的持续时间大量关闭。几秒钟的镜头变成了几个小时,尽管播放时间不匹配。 (FWIW,我正在使用 VLC 来播放它们。)
传递 x264 选项
如果我手动增加每个数据包的 PTS,并将 DTS 设置为 PTS,播放速度太快,一秒播放时间约为 2-3 秒的镜头,持续时间长达数小时。画面也模糊了好几秒,一秒钟大概有10秒的画面。
如果我让 FFMPEG 决定(带或不带 GENPTS 标志),该文件具有可变帧率(可能如预期的那样),但它会立即播放整个文件并且持续时间也很长(超过四十小时)几秒钟)。持续时间不是 "real",因为文件会立即播放。
在一号标记处,我尝试通过将选项传递给 avio_open2
来设置配置文件。 libx264 会简单地忽略这些选项。我试过:
av_dict_set(&opts, "vprofile", "main", 0);
av_dict_set(&opts, "profile", "main", 0); // error, missing '('
// FF_PROFILE_H264_MAIN equals 77, so I also tried
av_dict_set(&opts, "vprofile", "77", 0);
av_dict_set(&opts, "profile", "77", 0);
它似乎确实读取了配置文件设置,但没有使用它们。在标记二,我尝试将其设置在 avio_open2
之后、avformat_write_header
之前。
// I tried all 4 av_dict_set from earlier, passing it to avformat_write_header.
// None had any effect, they weren't consumed.
av_opt_set(video_file_codec_context, "profile", "77", 0);
av_opt_set(video_file_codec_context, "profile", "main", 0);
video_file_codec_context->profile = FF_PROFILE_H264_MAIN;
av_opt_set(video_file_codec_context->priv_data, "profile", "77", 0);
av_opt_set(video_file_codec_context->priv_data, "profile", "main", 0);
搞乱 privdata 使程序不稳定,但我当时正在尝试任何事情。 我想通过传递设置来解决问题 1,因为我想它会阻碍任何解决问题 2 或 3 的尝试。
大半个月以来我一直在摆弄这个。我浏览了数十份文档、问答和示例。有不少已经过时了,这于事无补。
如有任何帮助,我们将不胜感激。
干杯
好的,首先,我没有使用 ffmpeg, but a fork of ffmpeg called libav。不要混淆,ffmpeg 是更新的,而 libav 在 Linux.
的一些发行版中使用为 Visual Studio
编译一旦我升级到主分支,我不得不再次手动编译它,因为我在 Visual Studio 中使用它并且唯一的静态库是 G++,所以链接不能很好地工作。
官方指南是https://trac.ffmpeg.org/wiki/CompilationGuide/MSVC。
首先,这样编译工作正常:
确保 VS 在路径中。您的 PATH 应按此顺序读取 :
C:\Program Files (x86)\Microsoft Visual Studio XX.0\VC\bin
D:\MinGW\msys64\mingw32\bin
D:\MinGW\msys64\usr\bin
D:\MinGW\bin
然后 运行 Visual Studio x86 Native Tools 提示。应该在您的开始菜单中。
在 CMD 中,运行
(your path to MinGW)\msys64\msys2_shell.cmd -full-path
在创建的 MinGW window, 运行:
$ cd /your dev path/
$ git clone https://git.ffmpeg.org/ffmpeg.git ffmpeg
大约五分钟后,您将在子文件夹 ffmpeg 中获得 FFMPEG 源代码。
通过以下方式访问源代码:
$ cd ffmpeg
然后 运行:
$ which link
如果它不提供来自 PATH 的 VS 路径,而是 usr/link 或 usr/bin/link,类似地重命名:
$ mv /usr/bin/link.exe /usr/bin/msys-link.exe
如果确实跳过 $ mv 步骤。
最后,运行 这个命令:
$ ./configure --toolchain=msvc
以及您想要的任何其他命令行
(您可以通过 ./configure --help
查看命令行)
它可能会长时间显示为不活动。一旦完成,您将获得几页输出。
然后运行:
$ make
$ make install
注意静态构建(使用 --enable-static
配置),虽然你得到 Windows 个静态库文件,它会生成扩展名为 *.a 的文件。只需重命名为 .lib.
(你可以使用命令:ren *.a *.lib
)
使用 FFMPEG
要使用源配置文件、级别等从 FFMPEG RTSP 复制 到文件,只需使用:
- 读取网络帧
av_read_frame(rtsp_format_context)
- 传递到 MP4
av_write_frame(video_file_format_context)
您不需要打开 AVCodecContext、解码器或编码器;只是 avformat_open_input
,以及视频文件 AVFormatContext 和 AVIOContext。
如果你想重新编码,你必须:
- 读取网络帧
av_read_frame(rtsp_format_context)
- 将数据包传递给解码器
avcodec_send_packet(rtsp_decoder_context)
- 从解码器读取帧(循环)
avcodec_receive_frame(rtsp_decoder_context)
- 将每个解码帧发送到编码器
avcodec_send_frame(video_file_encoder_context)
- 从编码器读取数据包(循环)
avcodec_receive_packet(video_file_encoder_context)
- 将每个编码数据包发送到输出视频
av_write_frame(video_file_format_context)
一些陷阱
手动复制宽度、高度和像素格式。对于 H264,它是 YUV420P。
例如,对于级别 3.1,配置文件高:AVCodecParameters * video_file_codec_params = video_file_stream->codecpar; video_file_codec_params->profile = FF_PROFILE_H264_HIGH; video_file_codec_params->format = AV_PIX_FMT_YUV420P; video_file_codec_params->level = 31; video_file_codec_params->width = rtsp_vidstream->codecpar->width; video_file_codec_params->height = rtsp_vidstream->codecpar->height;
libx264 通过
avcodec_open2
中的 opts 参数接受 H264 预设。 “非常快”预设示例:AVDictionary * mydict; av_dict_set(&mydict, "preset", "veryfast", 0); avcodec_open2(video_file_encoder_context, video_file_encoder_codec, &opts) // avcodec_open2 returns < 0 for errors. // Recognised options will be removed from the mydict variable. // If all are recognised, mydict will be NULL.
输出时间是一个易变的东西。在 ``.
之前使用它video_file_stream->avg_frame_rate = rtsp_vidstream->avg_frame_rate; video_file_stream->r_frame_rate = rtsp_vidstream->r_frame_rate; video_file_stream->time_base = rtsp_vidstream->time_base; video_file_encoder_context->time_base = rtsp_vidstream_codec_context->time_base; // Decreasing GOP size for more seek positions doesn't end well. // libx264 forces the new GOP size. video_file_encoder_context->gop_size = rtsp_vidstream_codec_context->gop_size; if ((errorNum = avcodec_open2(video_file_encoder_context,...)) < 0) { // an error... }
H264可能会以双倍速度写入文件,因此播放速度会加倍。要更改此设置,请手动使用编码数据包的时间:
packet->pts = packet->dts = frameNum++; av_packet_rescale_ts(packet, video_file_encoder_context->time_base, video_file_stream->time_base); packet->pts *= 2; packet->dts *= 2; av_interleaved_write_frame(video_file_format_context, packet) // av_interleaved_write_frame returns < 0 for errors.
注意我们将
av_write_frame
切换为av_interleaved_write_frame
,同时设置了PTS和DTS。frameNum
应该是 int64_t,并且应该从 0 开始(尽管这不是必需的)。另请注意
av_rescale_ts
调用的参数是视频文件编码器上下文和视频文件流 - 不涉及 RTSP。VLC 媒体播放器无法播放 FPS 为 4 或更低的 H.264 流编码。 因此,如果您的 RTSP 流显示第一个解码帧并且从不进行,或者在视频结束之前显示纯绿色,请确保您的 FPS 足够高。 (即 VLC v2.2.4)