dimanche 12 février 2017

Playing MP3 in with FFMPEG Library

Vote count: 0

I am attempting to make a C program that will play an MP3 file (Using FFMPEG & SDL). I haven't been able to find many conclusive resources for learning how to do this. Generally they seem to go about similar tasks in different ways and the code is generally highly outdated and doesn't even compile.

Before I put up code, here's the general gist of what I'm try to do, based on my understanding thus far.

1) Open the file + load 'container' level data into a AVFormatContext

2) Find the stream inside the container that you want to work with. In my case I believe there is only a single audio steam so format_context->streams[0] references this.

3) Fetch a decoder(Stored in AVcodec), by the Codec ID found in the stream and populate AVCodecContext

4) Set up your desired SDL_AudioSpec struct. Notably here you pass it a callback function for filling up an audio buffer provided by SDL itself and your AVCodecContext which is passed to the callback.

5) Open an SDL audio device using your wanted spec and call SDL_PauseAudioDevice() which creates a new thread to continually call the callback function (start the audio playback).

6) The main thread continues and continually reads encoded packets/frames and puts them on a global queue until the stream data has been completely processed.

7) The callback function dequeues the encoded packets, decodes them and writes them to the buffer passed by SDL audio as a parameter.

Questions..

1) What exactly is the difference between a frame and a packet in the context of audio?

My understanding is that frames are encapsulated packets that are easier to feed as raw data since they are more uniform in size. A frame may require multiple packets to fill it but multiple frames cannot be made from a single packet(I could have mixed that up, as I don't remember where I read that). Are packets always decoded into frames or are the terms somewhat interchangeable?

2) Where is the encoded stream of packets stored? In my code I'm using av_read_frame(AVFormatContext*, AVPacket) to somehow fetch a packet from somewhere so I can put the packet on my queue.

3) Lots of things about the callback function..

void audio_callback(void *userdata, Uint8 *stream, int len)

3A) Just to confirm, stream and len are decided by SDL audio? The programmer has no way of passing values for these arguments?

3B) Len is the size, in bytes of decoded audio data that SDL Audio is requesting?

3C) How do I know how many frames is enough to write len data to the buffer? To my knowledge a frame is an encapsulation of a FIXED amount of decoded audio data, so how do I know how much data each frame contains?

3D) avcodec_send_packet(AVCodecContext *, AVPacket *) and avcodec_receive_frame(AVCodecContext *, AVFrame *) are the new ways to decode data, how exactly do they work?

So it seems like you pass in a valid packet to avcodec_send_packet(), it decodes it and then to get the corresponding frame you call the avcodec_receive_frame(). Issues that I can see with this is that size-wise a packet mightn't correspond to a frame, a number of packets might make up a single frame.

OK here's the stripped down version of my code, I can link to the full thing if required.

I omitted all error checking, clean up and the implementation of the PacketQueue structure since it's pretty intuitive. I haven't tested this specifically but my original program is segfaulting when it gets to avcodec_send_packet()

// Includes etc ommited

// My own type, just assume it works and was allocated in main
PacketQueue *audioq;

void audio_callback(void *userdata, Uint8 *stream, int requestedLen)
{
    AVCodecContext *codec_context = (AVCodecContext *)userdata;
    AVPacket * nextPacket;
    AVFrame outputFrame;

    // Take packet off of audioq Queue and store in nextPacket
    deQueue(audioq, nextPacket);
    avcodec_send_packet(codec_context, nextPacket);
    avcodec_receive_frame(codec_context, &outputFrame)'

    memcpy(stream, (uint8_t *)outputFrame.data, outputFrame.sample_rate);
}

int main(int argc, char *argv[])
{

    AVCodec * audio_codec;
    AVCodecContext *codec_context;
    AVFormatContext *format_context = NULL;
    SDL_AudioSpec want;
    SDL_AudioSpec have;
    SDL_AudioDeviceID audio_device_id;
    const char* audio_device_name = NULL;

    SDL_Init(SDL_INIT_AUDIO | SDL_INIT_TIMER);
    av_register_all();

    format_context = avformat_alloc_context();
    avformat_open_input(&format_context, *(argv + 1), NULL, NULL);
    avformat_find_stream_info(format_context, NULL);

    audio_codec = avcodec_find_decoder(format_context->streams[0]->codecpar->codec_id);
    codec_context = avcodec_alloc_context3(audio_codec);

    avcodec_open2(codec_context, audio_codec, NULL);

    want.freq = codec_context->sample_rate;
    want.format = AUDIO_S16SYS;
    want.channels = 2;
    want.samples = SDL_AUDIO_BUFFER_SIZE;
    want.callback = audio_callback;
    want.userdata = codec_context;
    want.silence = 0;

    audio_device_id = SDL_OpenAudioDevice(NULL, 0, &want, &have, SDL_AUDIO_ALLOW_FORMAT_CHANGE);
    audio_device_name = SDL_GetAudioDeviceName(0, 0);

    SDL_PauseAudioDevice(audio_device_id, 0);

    while(quit == 0)
    {
        AVPacket* packet = malloc(sizeof(AVPacket));
        av_read_frame(format_context, packet);
        // Put packet onto audioq Queue
        enQueue(audioq, packet);

        if(packet)
            free(packet);
    }
    // Cleanup

    return 0;
}

I'm sure my callback function is highly simplistic and somewhat naive but after I replaced the old avcodec_decode_audio4() much of the code I had seem irrelevant(Don't worry the program wasn't working at that point either..)

If you can think of anything that might help clear up my confusion please let me know, I'm aware this is a rather broad topic and I don't have a single specific question to ask.

Thanks in advance, Keith.

asked 54 secs ago

Let's block ads! (Why?)



Playing MP3 in with FFMPEG Library

Aucun commentaire:

Enregistrer un commentaire