avcodec_open2: PCM频道越界

avcodec_open2: PCM channels out of bounds

我试图在我的应用程序中读取音频 RTP 流,但出现此错误:

[pcm_mulaw @ 03390580] PCM channels out of bounds

我可以使用 ffplay 读取 RTP 流:

ffplay -i test.sdp -protocol_whitelist file,udp,rtp

我使用此命令生成 RTP 流:

ffmpeg -re -f lavfi -i aevalsrc="sin(400*2*PI*t)" -ar 8000 -f mulaw -f rtp rtp://127.0.0.1:8554

// SDP
v=0
o=- 0 0 IN IP4 127.0.0.1
s=No Name
c=IN IP4 127.0.0.1
t=0 0
a=tool:libavformat 57.25.101
m=audio 8554 RTP/AVP 0
b=AS:64

这是我的源代码:

#include "stdafx.h"
#include <math.h>
extern "C"
{
    #include <libavutil/opt.h>
    #include <libavcodec/avcodec.h>
    #include <libavutil/channel_layout.h>
    #include <libavutil/common.h>
    #include <libavutil/imgutils.h>
    #include <libavutil/mathematics.h>
    #include <libavutil/samplefmt.h>
    #include <libavformat/avformat.h>
}

#define AUDIO_INBUF_SIZE 20480
#define AUDIO_REFILL_THRESH 4096

#define ERRBUFFLEN 200
char errbuf[ERRBUFFLEN];
#define av_err2str(ret) av_strerror(ret, errbuf, ERRBUFFLEN)

/*
* Audio decoding.
*/
static void audio_decode_example(const char *outfilename, const char *filename)
{
    AVCodec *inCodec;
    AVCodecContext *inCodecCtx = NULL;
    int len;
    FILE *f, *outfile;
    uint8_t inbuf[AUDIO_INBUF_SIZE + AV_INPUT_BUFFER_PADDING_SIZE];
    AVPacket avpkt;
    AVFrame *decoded_frame = NULL;


    AVFormatContext *inFormatCtx = NULL;
    AVFrame *inFrame = NULL;
    AVFrame *outFrame = NULL;

    int ret;

    av_init_packet(&avpkt);

    AVDictionary *d = NULL;           // "create" an empty dictionary
    int listen = false;
    listen = true;
    if (listen)
    {
        av_dict_set(&d, "protocol_whitelist", "file,udp,rtp", 0); // add an entry
        printf("Listening mode.\n");
    }
    else {
        printf("Connecting mode.\n");
    }

    // Open video file
    ret = avformat_open_input(&inFormatCtx, filename, NULL, &d);
    if (ret <0)
    {
        printf_s("Failed: cannot open input.\n");
        av_strerror(ret, errbuf, ERRBUFFLEN);
        fprintf(stderr, "avformat_open_input() fail: %s\n", errbuf);
        exit(1);
    }

    printf_s("Retrieve stream information.\n");
    ret = avformat_find_stream_info(inFormatCtx, NULL);
    if (ret <0)
    {
        printf_s("Failed: cannot find stream.\n");
        av_strerror(ret, errbuf, ERRBUFFLEN);
        fprintf(stderr, "avformat_find_stream_info() fail: %s\n", errbuf);
        exit(1);
    }

    av_dump_format(inFormatCtx, 0, filename, 0);

    int stream_idx = -1;

    for (int i = 0; i < inFormatCtx->nb_streams; i++)
        if (inFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
            stream_idx = i;
            break;
        }
    if (stream_idx == -1)
    {
        fprintf(stderr, "Video stream not found\n");
        exit(1);
    }

    inCodec = avcodec_find_decoder(inFormatCtx->streams[stream_idx]->codec->codec_id);
    if (!inCodec) {
        fprintf(stderr, "Codec not found\n");
        exit(1);
    }

    inCodecCtx = avcodec_alloc_context3(inCodec);
    if (!inCodecCtx) {
        fprintf(stderr, "Could not allocate audio codec context\n");
        exit(1);
    }
    /* Error here */
    ret = avcodec_open2(inCodecCtx, inCodec, NULL);
    if (ret < 0) {
        fprintf(stderr, "Could not open codec: %s\n", av_err2str(ret));
        exit(1);
    }
    (...more code)

我知道出了点问题,但那是什么?非常感谢您的建议和提示。

我发现流属性没有自动设置,所以我必须在调用之前手动设置它们 avcodec_open2():

inCodecCtx->sample_rate = 8000;
inCodecCtx->sample_fmt = AV_SAMPLE_FMT_S16;
inCodecCtx->channels = 1;
inCodecCtx->channel_layout = AV_CH_LAYOUT_MONO;

希望这对遇到与我相同问题的人有所帮助。

谢谢,你帮了我:-D

只是想补充一下,如果你不知道它们,你可以从文件中获取这些参数。

这是我的代码,用于搜索音频流、为其获取解码器并设置 formatContext 中的参数。

请注意,我的代码是 Scala 调用 Java-CPP 包装器围绕 FFMEG

val formatContext = chkNull(avformat.avformat_alloc_context())

val inputFormat = avformat.av_find_input_format(format)
if (inputFormat == null) throw new Error(s"Format '${format}' is not supported")
formatContext.iformat(inputFormat)
chk(avformat.avformat_open_input(formatContext, "<path to audio file goes here>", null, null))

val audioStreams = (0 until formatContext.nb_streams) filter { i: Int =>
  formatContext.streams(i).codecpar().codec_type() == avutil.AVMEDIA_TYPE_AUDIO
}

val audioStream = audioStreams.size match {
  case 0 => throw new Error("No Audio Stream found")
  case 1 => audioStreams.head
  case _ => throw new Error("More than one Audio Streams found")
}

val codecParameters = formatContext.streams(audioStream).codecpar()
val decoder = chkNull(avcodec.avcodec_find_decoder(codecParameters.codec_id()))
val codecContext = chkNull(avcodec.avcodec_alloc_context3(decoder))

codecContext.sample_rate(codecParameters.sample_rate())
codecContext.sample_fmt(codecParameters.format())
codecContext.channels(codecParameters.channels())
codecContext.channel_layout(codecParameters.channel_layout())

chk(avcodec.avcodec_open2(codecContext, null, null.asInstanceOf[AVDictionary]))

avcodec.h中还有一个专用函数:

int error = avcodec_parameters_to_context(inCodecCtx, inFormatCtx->streams[stream_idx]->codecpar);
if (error < 0) {
    // something went wrong
}

(参见 https://www.ffmpeg.org/doxygen/3.2/demuxing__decoding_8c_source.html,第 182 行)。