基于雷神最简单的音频播放器修改。
/**
* 最简单的基于FFmpeg的音频播放器 2
* Simplest FFmpeg Audio Player 2
*
* 雷霄骅 Lei Xiaohua
* leixiaohua1020@126.com
* 中国传媒大学/数字电视技术
* Communication University of China / Digital TV Technology
* http://blog.youkuaiyun.com/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;
}