i have been trying to decode an MP3 file to pcm, using ffmpeg API, but i keep getting an error
[mp3 @ 0x8553020]Header missing
this is the code i use :
#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;
}
when i use the same code to directly play the sound, like this :
if (out_size > 0) {
/* if a frame has been decoded, output it *
play_sound(outbuf, out_size);
}
i have no problem at all with some files, other mp3 files just gives an error without even starting ... is there any ideas ?
PS: this code is from libavcodec/api-example.c , modified as needed
I think i found my answer, the avpkt.data must have a header in front, without any garbage or previous frame bytes, or may be initial mp3 file data (name, gender, year ... etc).
so a little parser must be wrote, this is a useful link for mp3 headers (just search for the correct bytes in within the file, and increase avpkt.data pointer to match):
http://www.mp3-tech.org/programmer/frame_header.html
Use avformat for reading instead of fread(). For example, it can be customized (e.g. for buffering), it can detect and check formats automatically on opening and also has separated probe functions and other format-related stuff. And it works properly with headers. I came to following usage (warning, code can contain errors)
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);
After that you should basically prepare decoded_frame
from TC's example and pass packet used for reading to avcodec_decode_audio4
(instead of avpkt
).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With