完整的音频播放示例
使用最新的SDL2.0版本,对于http://dranger.com/ffmpeg教程中示例改进,更新API
// tutorial_1.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
#include <windows.h>
extern "C"
{
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <libavutil/pixfmt.h>
#include <libavutil/error.h>
#include <libavutil/time.h>
#include <libswresample/swresample.h>
#include <SDL.h>
#include <SDL_thread.h>
#include "libavutil/audio_fifo.h"
}
#include <assert.h>
#ifdef __MINGW32__
#undef main /* Prevents SDL from overriding main() */
#endif
#define SDL_AUDIO_BUFFER_SIZE 1024
#define MAX_AUDIO_FRAME_SIZE 192000
typedef struct PacketQueue {
AVPacketList *first_pkt, *last_pkt;
int nb_packets;
int size;
SDL_mutex *mutex;
SDL_cond *cond;
} PacketQueue;
typedef struct AudioParams {
int freq;
int channels;
int64_t channel_layout;
enum AVSampleFormat fmt;
int frame_size;
int nb_samples;
int buffer_size;
} ST_AudioParams;
PacketQueue audioq;
SwrContext * g_swrCtx;
ST_AudioParams g_audio_param_dst;
//解码缓冲区
AVAudioFifo* audiofifo = NULL;//
int ct = 0;
int quit = 0;
DWORD start_time, end_time;
FILE *g_file;
//全局解码缓冲区
uint8_t audio_decode[(MAX_AUDIO_FRAME_SIZE * 3) / 2];
Uint32 audio_decodeLen;//每音频帧的长度
Uint8 *audio_decode_pos;//目前接收播放位置
bool b_finish_packet = false;
void packet_queue_init(PacketQueue *q);
int packet_queue_put(PacketQueue *q, AVPacket *pkt);
static int packet_queue_get(PacketQueue *q, AVPacket *pkt, int block);
int audio_decode_frame(AVCodecContext *aCodecCtx, AVPacket *pPacket, uint8_t *audio_buf, int buf_size);
void audio_callback(void *udata, Uint8 *stream, int len);
static DWORD WINAPI Thread_Decode_Audio(LPVOID lpParam);
int main(int argc, char *argv[])
{
AVFormatContext * pFormatCtx = NULL;
int audioStream;
AVCodecContext * pAudioCodecCtx;
AVCodec * pAudioCodec = NULL;
AVPacket * packet;
AVDictionary * pAudioOptionsDict = NULL;
uint8_t ** audio_data_buffer = NULL;//存储转换后的数据,再编码AAC 二维数组data[0],data[1]
SDL_Event event;
SDL_AudioSpec wanted_spec, real_spec;
if (argc < 2)
{
std::cout << "please provide a movie file" << std::endl;
return -1;
}
g_file = fopen("1.pcm", "wb");
if (g_file == NULL)
{
printf("create file failed.\n");
}
start_time = GetTickCount();
//注册所有的文件格式和编码器
av_register_all();
//打开视频文件
if (avformat_open_input(&pFormatCtx, argv[1], NULL, NULL) != 0)
{
return -1;
}
//检索获取流信息
if (avformat_find_stream_info(pFormatCtx, NULL) < 0)
{
return -1;
}
av_dump_format(pFormatCtx, 0, argv[1], 0);
//寻找第一个音频流
audioStream = -1;
int i;
for (i = 0; i < pFormatCtx->nb_streams; i++)
{
if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO && audioStream < 0)
{
audioStream = i;
}
}
//未找到音频流
if (audioStream == -1)
return -1;
pAudioCodecCtx = pFormatCtx->streams[audioStream]->codec;
//打印一些信息:
printf("音频流标识符:%d\n", pFormatCtx->streams[audioStream]->index);
printf("音频流长度:%d微秒\n", pFormatCtx->streams[audioStream]->duration);
printf("音频时长:%d微秒\n", pFormatCtx->streams[audioStream]->duration);
printf("音频采样率:%d\n", pFormatCtx->streams[audioStream]->codecpar->sample_rate);
printf("音频信道数目:%d\n", pFormatCtx->streams[audioStream]->codecpar->channels);
//获取音频编码结构
pAudioCodec = avcodec_find_decoder(pAudioCodecCtx->codec_id);
if (pAudioCodec == NULL)
{
std::cout << "Unsupported codec!\n";
return -1;
}
if (avcodec_open2(pAudioCodecCtx, pAudioCodec, &pAudioOptionsDict) < 0)
{
return -1;
}
packet = (AVPacket *)av_malloc(sizeof(AVPacket));
av_init_packet(packet);
//g_audio_param_dst为输出音频参数
g_audio_param_dst.frame_size = SDL_AUDIO_BUFFER_SIZE;
g_audio_param_dst.fmt = AV_SAMPLE_FMT_S16;
g_audio_param_dst.freq = 44100;
g_audio_param_dst.channel_layout = AV_CH_LAYOUT_STEREO;
g_audio_param_dst.channels = av_get_channel_layout_nb_channels(AV_CH_LAYOUT_STEREO);
g_audio_param_dst.nb_samples = 1024;
g_audio_param_dst.buffer_size = av_samples_get_buffer_size(NULL, g_audio_param_dst.channels, g_audio_param_dst.nb_samples, g_audio_param_dst.fmt, 1);
audiofifo = av_audio_fifo_alloc(g_audio_param_dst.fmt, g_audio_param_dst.channels, 1);
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) {
fprintf(stderr, "Could not initialize SDL - %s\n", SDL_GetError());
exit(1);
}
wanted_spec.freq = g_audio_param_dst.freq;
wanted_spec.format = AUDIO_S16SYS;
wanted_spec.channels = g_audio_param_dst.channels;
wanted_spec.silence = 0;
wanted_spec.samples = g_audio_param_dst.nb_samples;
wanted_spec.callback = audio_callback;
wanted_spec.userdata = pAudioCodecCtx;
if (SDL_OpenAudio(&wanted_spec, NULL) < 0) {
fprintf(stderr, "SDL_OpenAudio: %s\n", SDL_GetError());
return -1;
}
g_swrCtx = swr_alloc();
g_swrCtx = swr_alloc_set_opts(
g_swrCtx,
g_audio_param_dst.channel_layout,
g_audio_param_dst.fmt,
g_audio_param_dst.freq,
pAudioCodecCtx->channels,
pAudioCodecCtx->sample_fmt,
pAudioCodecCtx->sample_rate,
0,
nullptr
);
if (g_swrCtx == NULL || swr_init(g_swrCtx) < 0)
{
printf("Cannot create sample rate converter for conversion of %d Hz %s %d channels to %d Hz %s %d channels!\n",
pAudioCodecCtx->sample_rate, av_get_sample_fmt_name(pAudioCodecCtx->sample_fmt), pAudioCodecCtx->channels,
g_audio_param_dst.freq, av_get_sample_fmt_name(g_audio_param_dst.fmt), g_audio_param_dst.channels);
swr_free(&g_swrCtx);
return -1;
}
SDL_PauseAudio(0);
packet_queue_init(&audioq);
AVFrame *pFrame = av_frame_alloc();
AVFrame* audio_frame = av_frame_alloc();
int out_buffer_size = 0;
while (av_read_frame(pFormatCtx, packet) >= 0)
{
if (packet->stream_index == audioStream) {
packet_queue_put(&audioq, packet);
printf("audioq size is %d\n", audioq.nb_packets);
}
else {
av_packet_unref(packet);
}
}
HANDLE hThread = CreateThread(NULL, 0, Thread_Decode_Audio, (LPVOID)pAudioCodecCtx, NULL, NULL);
printf("test1..\n");
WaitForSingleObject(hThread, INFINITE);
printf("test2..\n");
//getchar();
avcodec_close(pAudioCodecCtx);
avformat_close_input(&pFormatCtx);
return 0;
}
void packet_queue_init(PacketQueue *q) {
memset(q, 0, sizeof(PacketQueue));
q->mutex = SDL_CreateMutex();
q->cond = SDL_CreateCond();
}
int packet_queue_put(PacketQueue *q, AVPacket *pkt) {
AVPacketList *pkt1;
//if (av_dup_packet(pkt) < 0) {
// return -1;
//}
pkt1 = (AVPacketList *)av_malloc(sizeof(AVPacketList));
if (!pkt1)
return -1;
if (av_packet_ref(&pkt1->pkt, pkt) < 0)
return -1;
//pkt1->pkt = *pkt;
pkt1->next = NULL;
SDL_LockMutex(q->mutex);
if (!q->last_pkt)
q->first_pkt = pkt1;
else
q->last_pkt->next = pkt1;
q->last_pkt = pkt1;
q->nb_packets++;
q->size += pkt1->pkt.size;
SDL_CondSignal(q->cond);
SDL_UnlockMutex(q->mutex);
return 0;
}
static int packet_queue_get(PacketQueue *q, AVPacket *pkt, int block)
{
AVPacketList *pkt1;
int ret;
SDL_LockMutex(q->mutex);
for (;;) {
if (quit) {
ret = -1;
break;
}
pkt1 = q->first_pkt;
if (pkt1) {
q->first_pkt = pkt1->next;
if (!q->first_pkt)
q->last_pkt = NULL;
q->nb_packets--;
q->size -= pkt1->pkt.size;
//*pkt = pkt1->pkt;
if (av_packet_ref(pkt, &pkt1->pkt) < 0)
{
ret = 0;
break;
}
av_free(pkt1);
ret = 1;
break;
}
else if (!block) {
ret = 0;
break;
}
else {
SDL_CondWait(q->cond, q->mutex);
}
}
SDL_UnlockMutex(q->mutex);
return ret;
}
/*
*
*第一个参数 aCodecCtx,用于解码获取帧数据
*第二个参数 audio_buf,用户存储解码后的音频数据
*第三个参数 buf_size是audio_buf的大小
*/
int audio_decode_frame(AVCodecContext *aCodecCtx, AVPacket *pPacket, uint8_t *audio_buf, int buf_size) {
static int ct = 0;
AVPacket pkt;
uint8_t *audio_data = NULL;
AVFrame * pFrame = av_frame_alloc();
AVFrame* audio_frame = av_frame_alloc();
int len1, data_size = 0;
bool need_newPacket = false;
int ret = 0;
int res = 0;
int nb_samples = 0; // 重采样输出样本数
uint8_t ** audio_data_buffer = NULL;
while (audioq.nb_packets) {
need_newPacket = false;
ret = avcodec_receive_frame(aCodecCtx, pFrame);
if (ret != 0)
{
if (ret == AVERROR_EOF)
{
printf("audio avcodec_receive_frame(): the decoder has been fully flushed\n");
res = 0;
goto exit;
}
else if (ret == AVERROR(EAGAIN))
{
need_newPacket = true;
res = 0;
}
else if (ret == AVERROR(EINVAL))
{
printf("audio avcodec_receive_frame(): codec not opened, or it is an encoder\n");
res = -1;
goto exit;
}
else
{
printf("audio avcodec_receive_frame(): legitimate decoding errors\n");
res = -1;
goto exit;
}
}
else
{
av_samples_alloc_array_and_samples(
&audio_data_buffer,// 此时data[0],data[1]分别指向audio_data_buffer数组起始、中间地址
NULL,
g_audio_param_dst.channels,
pFrame->nb_samples,
g_audio_param_dst.fmt,
1
);
int convert_size = swr_convert(
g_swrCtx,// 转换数据,令各自声道的音频数据存储在不同的数组(分别由不同指针指向)
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) >= g_audio_param_dst.frame_size) {
int frame_size = 0;
frame_size = FFMIN(av_audio_fifo_size(audiofifo), g_audio_param_dst.frame_size);
//申请AVFrame成员的 buffer空间,并设置属性
audio_frame->nb_samples = frame_size;
audio_frame->channel_layout = g_audio_param_dst.channel_layout;
audio_frame->format = g_audio_param_dst.fmt;
audio_frame->sample_rate = g_audio_param_dst.freq;
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;
}
//同步回调函数,等待回调函数取完数据,audio_decodeLen全局变量
while (audio_decodeLen > 0)//Wait until finish
SDL_Delay(1);
//audio_decode_pos保存每一帧的数据
audio_decode_pos = *audio_frame->data;
//audio_decodeLen每一帧的数据字节数
audio_decodeLen = g_audio_param_dst.buffer_size;
res = audio_decodeLen;
if (++ct == 30)
{
ct = 0;
end_time = GetTickCount();
printf("音频帧率 %f \n", 30000.0 / (end_time - start_time));
start_time = end_time;
}
}
}
if (need_newPacket)
{
if (packet_queue_get(&audioq, pPacket, 1) <= 0)
{
av_packet_unref(pPacket);
res = -1;
goto exit;
}
else
{
printf("解码 packet.size %d\n", audioq.nb_packets);
}
ret = avcodec_send_packet(aCodecCtx, pPacket);
if (ret != 0)
{
printf("avcodec_send_packet() failed %d\n", ret);
av_packet_unref(pPacket);
res = -1;
goto exit;
}
}
}
exit:
av_frame_unref(pFrame);
av_frame_unref(audio_frame);
av_packet_unref(pPacket);
b_finish_packet = true;
return res;
}
static DWORD WINAPI Thread_Decode_Audio(LPVOID lpParam)
{
DWORD dwRet = 0;
AVCodecContext *aCodecCtx = (AVCodecContext *)lpParam;
AVPacket * pPacket = NULL;
pPacket = (AVPacket *)av_malloc(sizeof(AVPacket));
//取packet
av_init_packet(pPacket);
//解码
audio_decode_frame(aCodecCtx, pPacket, audio_decode, sizeof(audio_decode));
return dwRet;
}
/*
*
第一个参数userdata是AVCodecContext,为了获取AVPacket传入AVCodecContext结构体,用于解码
第二个参数stream指向需要填充的音频缓冲区
第三个参数len,表示音频缓存区的大小
*
*/
void audio_callback(void *udata, Uint8 *stream, int len) {
SDL_memset(stream, 0, len);//初始化缓存区
if (audio_decodeLen == 0)
return;
len = (len > audio_decodeLen ? audio_decodeLen : len);
SDL_MixAudio(stream, audio_decode_pos, len, SDL_MIX_MAXVOLUME);
//输出pcm文件
fwrite((uint8_t *)audio_decode_pos, 1, len, g_file);
audio_decode_pos += len;
audio_decodeLen -= len;
}