FFmpeg+SDL2音频播放器

基于雷神最简单的音频播放器修改。

/**
* 最简单的基于FFmpeg的音频播放器 2
* Simplest FFmpeg Audio Player 2
*
* 雷霄骅 Lei Xiaohua
* leixiaohua1020@126.com
* 中国传媒大学/数字电视技术
* Communication University of China / Digital TV Technology
* http://blog.csdn.net/leixiaohua1020
*
* 本程序实现了音频的解码和播放。
* 是最简单的FFmpeg音频解码方面的教程。
* 通过学习本例子可以了解FFmpeg的解码流程。
*
* 该版本使用SDL 2.0替换了第一个版本中的SDL 1.0。
* 注意:SDL 2.0中音频解码的API并无变化。唯一变化的地方在于
* 其回调函数的中的Audio Buffer并没有完全初始化,需要手动初始化。
* 本例子中即SDL_memset(stream, 0, len);
*
* This software decode and play audio streams.
* Suitable for beginner of FFmpeg.
*
* This version use SDL 2.0 instead of SDL 1.2 in version 1
* Note:The good news for audio is that, with one exception,
* it's entirely backwards compatible with 1.2.
* That one really important exception: The audio callback
* does NOT start with a fully initialized buffer anymore.
* You must fully write to the buffer in all cases. In this
* example it is SDL_memset(stream, 0, len);
*
* Version 2.0
*/
#include "stdafx.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define __STDC_CONSTANT_MACROS


extern "C"
{
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswresample/swresample.h"
#include "SDL2/SDL.h"
#include "libavutil/audio_fifo.h"
};



Uint32  audio_len;
Uint8  *audio_pos;
AVAudioFifo* audiofifo = NULL;

void  fill_audio(void *udata, Uint8 *stream, int len){
    //SDL 2.0
    SDL_memset(stream, 0, len);
    if (audio_len == 0)
        return;

    len = (len>audio_len ? audio_len : len);    /*  Mix  as  much  data  as  possible  */

    SDL_MixAudio(stream, audio_pos, len, SDL_MIX_MAXVOLUME);
    audio_pos += len;
    audio_len -= len;
}



int main(int argc, char* argv[])
{
    AVFormatContext *pFormatCtx;
    int             i, audioStream;
    AVCodecContext  *pCodecCtx;
    AVCodec         *pCodec;
    AVPacket        *packet;

    AVFrame         *pFrame;
    SDL_AudioSpec wanted_spec;
    int ret;
    uint32_t len = 0;
    int got_picture;
    int index = 0;
    int64_t in_channel_layout;
    struct SwrContext *au_convert_ctx;

    FILE *pFile = NULL;
    char url[] = "rtmp://live.hkstv.hk.lxdns.com/live/hks";

    av_register_all();
    avformat_network_init();
    pFormatCtx = avformat_alloc_context();

    if (avformat_open_input(&pFormatCtx, url, NULL, NULL) != 0){
        printf("Couldn't open input stream.\n");
        return -1;
    }

    if (avformat_find_stream_info(pFormatCtx, NULL)<0){
        printf("Couldn't find stream information.\n");
        return -1;
    }

    av_dump_format(pFormatCtx, 0, url, false);

    // Find the first audio stream
    audioStream = -1;
    for (i = 0; i < pFormatCtx->nb_streams; i++)
    if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO){
        audioStream = i;
        break;
    }

    if (audioStream == -1){
        printf("Didn't find a audio stream.\n");
        return -1;
    }

    // Get a pointer to the codec context for the audio stream
    pCodecCtx = pFormatCtx->streams[audioStream]->codec;

    // Find the decoder for the audio stream
    pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
    if (pCodec == NULL){
        printf("Codec not found.\n");
        return -1;
    }

    // Open codec
    if (avcodec_open2(pCodecCtx, pCodec, NULL)<0){
        printf("Could not open codec.\n");
        return -1;
    }




    packet = (AVPacket *)av_malloc(sizeof(AVPacket));
    av_init_packet(packet);
    int out_framesize = 1024;
    //Out Audio Param
    uint64_t out_channel_layout = AV_CH_LAYOUT_STEREO;
    //nb_samples: AAC-1024 MP3-1152
    int out_nb_samples = out_framesize;
    AVSampleFormat out_sample_fmt = AV_SAMPLE_FMT_S16;
    int out_sample_rate = 44100;// pCodecCtx->sample_rate;
    int out_channels = av_get_channel_layout_nb_channels(out_channel_layout);
    //Out Buffer Size
    int out_buffer_size = av_samples_get_buffer_size(NULL, out_channels, out_nb_samples, out_sample_fmt, 1);

    uint8_t ** audio_data_buffer = NULL;

    audiofifo = av_audio_fifo_alloc(out_sample_fmt, out_channels, 1);

    pFrame = av_frame_alloc();

    if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) {
        printf("Could not initialize SDL - %s\n", SDL_GetError());
        return -1;
    }
    //SDL_AudioSpec
    wanted_spec.freq = out_sample_rate;
    wanted_spec.format = AUDIO_S16SYS;
    wanted_spec.channels = out_channels;
    wanted_spec.silence = 0;
    wanted_spec.samples = out_nb_samples;
    wanted_spec.callback = fill_audio;
    wanted_spec.userdata = pCodecCtx;

    if (SDL_OpenAudio(&wanted_spec, NULL)<0){
        printf("can't open audio.\n");
        return -1;
    }


    //FIX:Some Codec's Context Information is missing
    in_channel_layout = av_get_default_channel_layout(pCodecCtx->channels);
    //Swr

    au_convert_ctx = swr_alloc();
    au_convert_ctx = swr_alloc_set_opts(au_convert_ctx, out_channel_layout, out_sample_fmt, out_sample_rate,
        in_channel_layout, pCodecCtx->sample_fmt, pCodecCtx->sample_rate, 0, NULL);
    swr_init(au_convert_ctx);

    //Play
    SDL_PauseAudio(0);

    AVFrame* audio_frame = av_frame_alloc();

    while (av_read_frame(pFormatCtx, packet) >= 0){
        if (packet->stream_index == audioStream){
            ret = avcodec_decode_audio4(pCodecCtx, pFrame, &got_picture, packet);
            if (ret < 0) {
                printf("Error in decoding audio frame.\n");
                return -1;
            }
            if (got_picture > 0){
                av_samples_alloc_array_and_samples(&audio_data_buffer, NULL, out_channels, pFrame->nb_samples, out_sample_fmt, 1);

                int convert_size = swr_convert(au_convert_ctx, audio_data_buffer, pFrame->nb_samples,
                    (const uint8_t**)pFrame->data, pFrame->nb_samples);


                ret = av_audio_fifo_realloc(audiofifo, av_audio_fifo_size(audiofifo) + convert_size);
                if (ret < 0){
                    printf("av_audio_fifo_realloc error\n");
                    return -1;
                }
                if (av_audio_fifo_write(audiofifo, (void **)audio_data_buffer, convert_size) < convert_size){
                    printf("av_audio_fifo_write error\n");
                    return -1;
                }
                while (av_audio_fifo_size(audiofifo) >= out_framesize){
                    int frame_size = FFMIN(av_audio_fifo_size(audiofifo), out_framesize);
                    audio_frame->nb_samples = frame_size;
                    audio_frame->channel_layout =out_channel_layout;
                    audio_frame->format = out_sample_fmt;
                    audio_frame->sample_rate = out_sample_rate;
                    av_frame_get_buffer(audio_frame, 0);
                    if (av_audio_fifo_read(audiofifo, (void **)audio_frame->data, frame_size) < frame_size){
                        printf("av_audio_fifo_read error\n");
                        return -1;
                    }
                    if (wanted_spec.samples != frame_size){
                        SDL_CloseAudio();
                        out_nb_samples = frame_size;
                        out_buffer_size = av_samples_get_buffer_size(NULL, out_channels, out_nb_samples, out_sample_fmt, 1);

                        wanted_spec.samples = out_nb_samples;
                        SDL_OpenAudio(&wanted_spec, NULL);

                    }
                    while (audio_len>0)//Wait until finish
                        SDL_Delay(1);


                    audio_len = out_buffer_size;
                    audio_pos = *audio_frame->data;

                }


            }





        }
        av_free_packet(packet);
    }

    swr_free(&au_convert_ctx);


    SDL_CloseAudio();//Close SDL
    SDL_Quit();

    if (audiofifo)
        av_audio_fifo_free(audiofifo);

    avcodec_close(pCodecCtx);
    avformat_close_input(&pFormatCtx);

    return 0;
}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值