使用ffmpeg API进行mp3解码(Header missing)

时间:2012-03-01 01:13:44

标签: ffmpeg mp3 decode

我一直在尝试使用ffmpeg API将MP3文件解码为pcm,但我一直收到错误

[mp3 @ 0x8553020]标题丢失

这是我使用的代码:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#ifdef HAVE_AV_CONFIG_H
#undef HAVE_AV_CONFIG_H
#endif

#include "libavcodec/avcodec.h"
#include "libavutil/mathematics.h"

#define INBUF_SIZE 4096
#define AUDIO_INBUF_SIZE 20480
#define AUDIO_REFILL_THRESH 4096


static void audio_decode_example(const char *outfilename, const char *filename)
{
    AVCodec *codec;
    AVCodecContext *c= NULL;
    int out_size, len;
    FILE *f, *outfile;
    uint8_t *outbuf;
    uint8_t inbuf[AUDIO_INBUF_SIZE + FF_INPUT_BUFFER_PADDING_SIZE];
    AVPacket avpkt;

    av_init_packet(&avpkt);

    printf("Audio decoding\n");

    /* find the mpeg audio decoder */
    codec = avcodec_find_decoder(CODEC_ID_MP3ON4);
    if (!codec) {
    fprintf(stderr, "codec not found\n");
    exit(1);
    }

    c= avcodec_alloc_context();

    /* open it */
    if (avcodec_open(c, codec) < 0) {
    fprintf(stderr, "could not open codec\n");
    exit(1);
    }

    outbuf = malloc(AVCODEC_MAX_AUDIO_FRAME_SIZE);

    f = fopen(filename, "rb");
    if (!f) {
    fprintf(stderr, "could not open %s\n", filename);
    exit(1);
    }
    outfile = fopen(outfilename, "wb");
    if (!outfile) {
    av_free(c);
    exit(1);
    }

    /* decode until eof */
    avpkt.data = inbuf;
    avpkt.size = fread(inbuf, 1, AUDIO_INBUF_SIZE, f);

    while (avpkt.size > 0) {
    out_size = AVCODEC_MAX_AUDIO_FRAME_SIZE;
    len = avcodec_decode_audio3(c, (short *)outbuf, &out_size, &avpkt);
    if (len < 0) {
        fprintf(stderr, "Error while decoding\n");
        exit(1);
    }
    if (out_size > 0) {
        /* if a frame has been decoded, output it */
        fwrite(outbuf, 1, out_size, outfile);
    }
    avpkt.size -= len;
    avpkt.data += len;
    if (avpkt.size < AUDIO_REFILL_THRESH) {
        /* Refill the input buffer, to avoid trying to decode
         * incomplete frames. Instead of this, one could also use
         * a parser, or use a proper container format through
         * libavformat. */
        memmove(inbuf, avpkt.data, avpkt.size);
        avpkt.data = inbuf;
        len = fread(avpkt.data + avpkt.size, 1,
                    AUDIO_INBUF_SIZE - avpkt.size, f);
        if (len > 0)
            avpkt.size += len;
    }
    }

    fclose(outfile);
    fclose(f);
    free(outbuf);

    avcodec_close(c);
    av_free(c);
}

int main(int argc, char **argv)
{
    const char *filename;

    /* must be called before using avcodec lib */
    avcodec_init();

    /* register all the codecs */
    avcodec_register_all();

    audio_decode_example("test.wav", argv[1]);

    return 0;
}

当我使用相同的代码直接播放声音时,如下所示:

if (out_size > 0) {
    /* if a frame has been decoded, output it *
    play_sound(outbuf, out_size);
}

我对某些文件没有任何问题,其他mp3文件只是在没有启动的情况下发出错误...有什么想法吗?

PS:此代码来自libavcodec / api-example.c,根据需要进行了修改

2 个答案:

答案 0 :(得分:2)

我想我找到了答案,avpkt.data必须在前面有一个标题,没有任何垃圾或前一帧字节,或者可能是初始mp3文件数据(名称,性别,年份等)。

所以必须编写一个小解析器,这是一个有用的mp3头文件链接(只需在文件中搜索正确的字节,并增加avpkt.data指针以匹配):

http://www.mp3-tech.org/programmer/frame_header.html

答案 1 :(得分:1)

使用avformat进行阅读而不是fread()。例如,它可以定制(例如用于缓冲),它可以在打开时自动检测和检查格式,并且还具有分离的探测功能和其他与格式相关的内容。它适用于标题。我来使用(警告,代码可能包含错误)

struct FormatCtx {
  inline FormatCtx(const char* filename)
  : ctx_(avformat_alloc_context()) {
    av_init_packet(&p);
    if (avformat_open_input(&ctx_, filename, 0, 0) < 0)
      abort();
    if (avformat_find_stream_info(ctx_, 0) < 0)
      abort();
  }

  inline ~FormatCtx() {
    av_free_packet(&p);
  }

  inline bool read() {
    return av_read_frame(ctx_, &p) >= 0;
  }

  AVFormatContext* ctx_;
  AVPacket p;
} formatCtx_;

AVCodec* findCodec(const char* filename) {
  AVCodec* codec = formatCtx_.ctx_->audio_codec;
  if (codec)
    return codec;
  codec = avcodec_find_decoder(formatCtx_.ctx_->audio_codec_id);
  if (codec)
    return codec;
  AVOutputFormat* fmt = av_guess_format(0, //const char *short_name,
      filename, 0); // const char *mime_type);;
  codec = fmt ? avcodec_find_decoder(fmt->audio_codec) : 0;
  if (codec)
    return codec;
  return 0;
}

//*** initialize all stuff ***

AVCodec* codec = findCodec(filename);
if (!codec)
  exit(1);
AVCodecContext* c; // class member for me, needed for following reading
int stream_index_; // class member for me, needed for extra stuff
for (size_t i = 0; i < formatCtx_.ctx_->nb_streams; ++i) {
  AVCodecContext* tc = formatCtx_.ctx_->streams[i]->codec;
  if (tc->codec_type == AVMEDIA_TYPE_AUDIO) {
    c = tc;
    stream_index_ = i;
    break;
  }
}
// for example, here we're know track length
l->onDurationDetected(double(formatCtx_.ctx_->streams[stream_index_]->duration)
    * av_q2d(formatCtx_.ctx_->streams[stream_index_]->time_base));

if (avcodec_open2(c, codec, &d.d_) < 0)
  exit(1);

c->channels = 2;
c->sample_rate = 48000;
c->strict_std_compliance = FF_COMPLIANCE_EXPERIMENTAL;
c->channel_layout = av_get_default_channel_layout(2);

之后,您应该基本上从TC的示例中准备decoded_frame,并将用于阅读的数据包传递给avcodec_decode_audio4(而不是avpkt)。