декодирование mp3 с использованием ffmpeg API (отсутствует заголовок) - PullRequest
7 голосов
/ 01 марта 2012

Я пытался декодировать MP3-файл в pcm, используя ffmpeg API, но получаю сообщение об ошибке

[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 ]

2 голосов
/ 04 марта 2012

Я думаю, что нашел свой ответ, avpkt.data должен иметь заголовок впереди, без мусора или предыдущих байтов кадра, или может быть исходными данными файла mp3 (имя, пол, год ... и т. Д.).*

поэтому нужно написать небольшой анализатор, это полезная ссылка для заголовков mp3 (просто найдите правильные байты внутри файла и увеличьте указатель avpkt.data до соответствия):

http://www.mp3 -tech.org / программист / frame_header.html

1 голос
/ 10 января 2015

Используйте 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);

После этого вы должны в основном подготовить decoded_frame из примера TC и передать пакет, используемый для чтения, в avcodec_decode_audio4 (вместо avpkt).).

...