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

时间:2014-08-20 10:37:45

标签: 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;
}

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

文档says一些奇怪的事情,可以进行哪些调用继承(copy_context之前应调用get_context_defaults),当前代码尽可能靠近技术版本。如上所述,它导致avcodec_open2的非零返回,其中&#34;编解码器类型或ID不匹配&#34;信息。更改订单效果不大:现在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

2 个答案:

答案 0 :(得分:7)

首先,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

答案 1 :(得分:0)

如果avcodec_open2没有失败,并且您仍然可以看到宽度和高度为0,则可能会出现这种情况。请注意,在您实际开始解码之前,并不总是知道流(帧)维度。

在第一次AVFrame解码调用之后,您应该使用avcodec_decode_video2值来初始化解码缓冲区。