ffmpeg - 使用 FFMpeg 库读取 RTSP 流 - 如何使用 avcodec_open2?

标签 ffmpeg rtsp

在尝试读取 rtsp 流时,我遇到了一些问题,包括代码和文档。简短描述:无论我做什么,avcodec_open2要么失败(说“编解码器类型或 id 不匹配”)要么 widthheight调用后的编解码器上下文为 0(从而使进一步的代码无用)。流本身可以通过VLC播放器和av_dump_format()正常打开显示正确的信息。我的代码基于 技术回复 this question .

详细描述:我的代码是 C#,但这里是 C++ 等效的 FFMpeg 调用(我实际上将我的代码减少到这个最小值并且问题仍然存在):

av_register_all();
avformat_network_init(); //return code ignored

AVFormatContext* formatContext = avformat_alloc_context();
if (avformat_open_input(&formatContext, stream_path, null, null) != 0) {
    return;
}

if (avformat_find_stream_info(formatContext, null) < 0) {
    return;
}

int videoStreamIndex = 0;
for (int i = 0; i < formatContext->nb_streams; ++i) {
    AVStream* s = formatContext->streams[i];
    if (s->codec == null) continue;
    AVCodecContext c = *(s->codec);
    if (c.codec_type == AVMEDIA_TYPE_VIDEO) videoStreamIndex = i;
}

//start reading packets from stream and write them to file
//av_read_play(formatContext); //return code ignored
//this call would print "method PLAY failed: 455 Method Not Valid in This State"
//seems to be the case that for rtsp stream it isn't needed

AVCodec* codec = null;
codec = avcodec_find_decoder(AV_CODEC_ID_H264);
if (codec == null) {
    return;
}

AVCodecContext* codecContext = avcodec_alloc_context3(null);
avcodec_get_context_defaults3(codecContext, codec);//return code ignored
avcodec_copy_context(codecContext, formatContext->streams[videoStreamIndex]->codec); //return code ignored

av_dump_format(formatContext, videoStreamIndex, stream_path, 0);

if (avcodec_open2(codecContext, codec, null) < 0) {
    return;
}

该代码实际上使用了 DLL 版本的 FFMpeg 库;使用了 avcodec-55.dll 和 avformat-55.dll。

文档 says关于可以在哪个连续中进行哪些调用的奇怪之处(应该在 copy_context 之前调用 get_context_defaults ),当前代码尽可能接近 技术版本。如所写,它会导致 avcodec_open2 的非零返回。带有“编解码器类型或 ID 不匹配”消息。更改顺序没有什么好处:现在 avcodec_open2成功执行,但 codecContext->widthcodecContext->height之后为 0。

文档也没有提到 avcodec_open2 的第三个参数的默认值。应该是,但源代码似乎考虑到 options可以为 NULL。
av_dump_format 的输出如下:
Input #0, rtsp, from 'rtsp://xx.xx.xx.xx:xx/video.pro1':
  Metadata:
    title           : QStream
    comment         : QStreaming Media
  Duration: N/A, start: 0.000000, bitrate: 64 kb/s
    Stream #0:0: Video: h264 (Baseline), yuvj420p(pc), 1920x1080, 30 fps, 25 tbr, 90k tbn, 60 tbc
    Stream #0:1: Audio: pcm_mulaw, 8000 Hz, 1 channels, s16, 64 kb/s

最佳答案

首先,av_dump_format 是什么意思?显示?您确定您的视频流编解码器是 h264,因为您尝试像打开 H264 一样打开编解码器。

要打开任何编解码器,请更改您的 avcodec_find_decoder将源编解码器 ID 传递给它:

codec = avcodec_find_decoder(formatContext->streams[videoStreamIndex]->codec->codec_id);

顺便说一句,(如果您不使用 c++ 代码但坚持使用 c#,请忽略这一点):您不需要复制初始 AVCodecContext当您正在寻找视频流时。您可以这样做:(请注意,您可能希望保留指向初始编解码器上下文的指针,见下文)。
AVCodecContext* c = s->codec;
if (c->codec_type == AVMEDIA_TYPE_VIDEO) {
    videoStreamIndex = i;
    initialVideoCodecCtx = c;
}

下一点,在这种情况下并不真正相关:FFmpeg 没有循环遍历所有流,而是为其提供了一个辅助函数:
int videoStreamIndex = av_find_best_stream(formatContext, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);

最后一点:我认为只有第一点才能做到 avcodec_open2工作,但您可能无法解码您的流。您为新的编解码器上下文打开了编解码器,但没有为初始上下文打开编解码器。为什么要复制初始编解码器上下文?如果您想在另一个文件中记录您的流(即转码),这很有用,但如果您只想解码您的流,使用初始上下文会更容易,并使用它而不是新上下文作为参数avcodec_decode_video2 .

总结一下,在avformat_find_stream_info之后替换你的代码通过(警告:没有错误检查):
int videoStreamIndex = av_find_best_stream(formatContext, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
AVCodecContext* codecCtx = formatContext->streams[videoStreamIndex]->codec;
AVCodec* codec = avcodec_find_decoder(codecCtx->codec_id);
// tune codecCtx if you want special decoding options. See FFmpeg docs for a list of members
if (avcodec_open2(codecCtx, codec, null) < 0) {
    return;
}

// use av_read_frame(formatContext, ...) to read packets
// use avcodec_decode_video2(codecCtx, ...) to decode packets

关于ffmpeg - 使用 FFMpeg 库读取 RTSP 流 - 如何使用 avcodec_open2?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25402590/

相关文章:

android - FFMPEG Android 库增加大小

ffmpeg - 如何在ffmpeg中同时在视频上添加字幕和水印?

tcp - 如果 wireshark 无法识别某些类型的协议(protocol)(RTSP、RTP、RTCP),我如何强制其破译这些协议(protocol)?

javascript - 停止或终止 Node 媒体服务器

c++ - OpenCV VideoCapture 在 Windows 7 32 位中无法读取任何视频文件

ffmpeg - 如何使用 FFMPEG 从文件夹的选定图像创建视频?

python - 使用 OpenCV + Python 从 Elp H264 读取 H264 流

java-me - J2ME 中用于播放 Youtube 视频的 Flv 解码器

video - 如何使用 rtsp 流中的 ffmpeg 在循环中输出图像序列

gstreamer - gstreamer gst-rtsp-server 是否接受 udpsrc (RTP)?