本文代码在雷博的基础上进行了略微修改,实现播放音频的功能。
使用ffmpeg-3.1.5 + SDL2
雷博的代码链接:http://blog.csdn.net/leixiaohua1020/article/details/38979615
修改了两个地方:(1)使用了新版本的ffmpeg API。(2)更改了部分变量的赋值,使程序能够适应各种音频序列。
代码还不是完全理解,先记录下来。
/*
*实现音频解码和播放
*/
#include "stdafx.h"
#include <stdio.h>
#include <iostream>
using namespace std;
extern "C"
{
#include "include\libavcodec\avcodec.h"
#include "include\libavformat\avformat.h"
#include "include\libswresample\swresample.h"
#include "include\SDL2\SDL.h"
};
#define __STDC_CONSTANT_MACROS
#define OUTPUT_PCM 0
//Buffer
static Uint8 *audio_chunk;
static Uint32 audio_len;
static Uint8 *audio_pos;
//callback func
void fill_audio(void *udata, Uint8 *stream, int len){
SDL_memset(stream, 0, len);
if (audio_len == 0) /* Only play if we have data left */
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 _tmain(int argc, _TCHAR* argv[])
{
AVFormatContext *pFormatCtx;
AVCodecContext *pCodecCtx;
AVCodec *pCodec;
int audioStreamIndex;
//input audio
char url[] = "test.mp3";
#if OUTPUT_PCM
//output file
FILE *outputFile = NULL;
char *outputFileName = "output.aac";
fopen_s(&outputFile, outputFileName, "wb");
#endif
//init ffmpeg
av_register_all();
avformat_network_init();
pFormatCtx = avformat_alloc_context();
//open input audio
if (avformat_open_input(&pFormatCtx, url, NULL, NULL) != 0){
cout << "could not open input stream." << endl;
return -1;
}
if (avformat_find_stream_info(pFormatCtx,NULL) < 0){
cout << "could not find stream information." << endl;
return -1;
}
//print input audio info
cout << "--------------------- audio info ---------------------" << endl;
av_dump_format(pFormatCtx, 0, url, false);
cout << "------------------------------------------------------" << endl;
//find audioStreamIndex
audioStreamIndex = av_find_best_stream(pFormatCtx, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, NULL);
if (audioStreamIndex < 0){
cout << "could not found a audio stream." << endl;
return -1;
}
//find codec
pCodecCtx = avcodec_alloc_context3(NULL);
if (pCodecCtx == NULL){
cout << "could not allocate audio AVCCodecContext." << endl;
return -1;
}
avcodec_parameters_to_context(pCodecCtx, pFormatCtx->streams[audioStreamIndex]->codecpar);
pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
if (pCodec == NULL){
cout << "Codec not found." << endl;
return -1;
}
//open codec
if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0){
cout << "could not open codec." << endl;
return -1;
}
//out audio param
uint64_t out_channel_layout = AV_CH_LAYOUT_STEREO;
int out_nb_samples = pCodecCtx->frame_size;//number of samples per channels in an audio frame
AVSampleFormat out_sample_fmt = AV_SAMPLE_FMT_S16;
int out_sample_rate = pCodecCtx->sample_rate;
int out_channels = av_get_channel_layout_nb_channels(out_channel_layout);//the number of channels
//out buffer size
int out_buffer_size = av_samples_get_buffer_size(NULL, out_channels, out_nb_samples, out_sample_fmt, 1);
uint8_t *out_buffer = (uint8_t *)av_malloc(out_buffer_size);
//SDL
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)){
cout << "could not initialize SDL - " << SDL_GetError << endl;
return -1;
}
SDL_AudioSpec wanted_spec;
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;
//open the audio device with the desired parameters
if (SDL_OpenAudio(&wanted_spec, NULL) < 0){
cout << "could not open audio." << endl;
return -1;
}
printf("Bitrate:\t %3d\n", pFormatCtx->bit_rate);
printf("Decoder Name:\t %s\n", pCodecCtx->codec->long_name);
printf("Channels:\t %d\n", pCodecCtx->channels);
printf("Sample per Second\t %d \n", pCodecCtx->sample_rate);
//FIX:some codec's context information is missing
int64_t in_channel_layout = av_get_default_channel_layout(pCodecCtx->channels);
//Swr
struct SwrContext *au_convert_ctx;
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);
//pre decode
AVPacket *packet = (AVPacket*)malloc(sizeof(AVPacket));
av_init_packet(packet);
AVFrame *pFrame;
pFrame = av_frame_alloc();
//Play
SDL_PauseAudio(0);
int index = 0;
while (av_read_frame(pFormatCtx, packet) >= 0){
if (packet->stream_index == audioStreamIndex){
if (avcodec_send_packet(pCodecCtx, packet) != 0){
printf("input AVPacket to audio decoder failed!\n");
return -1;
}
while (0 == avcodec_receive_frame(pCodecCtx, pFrame)){
swr_convert(au_convert_ctx, &out_buffer, out_buffer_size, (const uint8_t **)pFrame->data, pFrame->nb_samples);
printf("index:%5d\t pts:%lld\t packet size:%d\n", index, packet->pts, packet->size);
index++;
audio_chunk = (Uint8*)out_buffer;
audio_len = out_buffer_size;
audio_pos = audio_chunk;
while (audio_len > 0)
SDL_Delay(1);
#if OUTPUT_PCM
//wirte file
fwrite(out_buffer, 1, out_buffer_size, outputFile);
#endif OUTPUT_PCM
}
}
av_packet_unref(packet);
}
//free
swr_free(&au_convert_ctx);
SDL_CloseAudio();
SDL_Quit();
av_free(out_buffer);
avcodec_close(pCodecCtx);
avformat_close_input(&pFormatCtx);
#if OUTPUT_PCM
fclose(outputFile);
#endif
return 0;
}