哎,喜欢偷懒,这边直接抄袭下雷神的代码。雷神是个值得敬佩的程序员。
vs代码下载链接:
https://pan.baidu.com/s/1c2dIuYk 密码:ld4b
/*
*最简单的基于FFmpeg的音频播放器
*Simplest FFmpeg Audio Player
*
*雷霄骅 Lei Xiaohua
*leixiaohua1020@126.com
*中国传媒大学/数字电视技术
*Communication University of China / Digital TV Technology
*http://blog.csdn.net/leixiaohua1020
*
*本程序实现了音频的解码和播放。
*
*/
#include <stdlib.h>
#include <string.h>
extern "C"
{
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswresample/swresample.h"
//SDL
#include "sdl/SDL.h"
#include "sdl/SDL_thread.h"
};
#include "decoder.h"
//#define _WAVE_
//全局变量---------------------
static Uint8 *audio_chunk;
static Uint32 audio_len;
static Uint8 *audio_pos;
//-----------------
/* The audio function callback takes the following parameters:
stream: A pointer to the audio buffer to be filled
len: The length (in bytes) of the audio buffer (这是固定的4096?)
回调函数
注意:mp3为什么播放不顺畅?
len=4096;audio_len=4608;两个相差512!为了这512,还得再调用一次回调函数。。。
m4a,aac就不存在此问题(都是4096)!
*/
void fill_audio(void *udata,Uint8 *stream,int len){
/* Only play if we have data left */
if(audio_len==0)
return;
/* Mix as much data as possible */
len=(len>audio_len?audio_len:len);
SDL_MixAudio(stream,audio_pos,len,SDL_MIX_MAXVOLUME);
audio_pos += len;
audio_len -= len;
}
//-----------------
int decode_audio(char* no_use)
{
AVFormatContext *pFormatCtx;
int i, audioStream;
AVCodecContext *pCodecCtx;
AVCodec *pCodec;
char url[300]={0};
strcpy(url,no_use);
av_register_all();
//支持网络流输入ff
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;
}
// Retrieve stream information
if(av_find_stream_info(pFormatCtx)<0)
{
printf("Couldn't find stream information.\n");
return -1;
}
// Dump valid information onto standard error
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;
}
FILE *pFile;
#ifdef _WAVE_
pFile=fopen("output.wav", "wb");
fseek(pFile, 44, SEEK_SET); //预留文件头的位置
#else
pFile=fopen("output1.pcm", "wb");
#endif
AVPacket *packet=(AVPacket *)malloc(sizeof(AVPacket));
av_init_packet(packet);
AVFrame *pFrame;
pFrame=avcodec_alloc_frame();
//输出音频数据大小,一定小于输出内存。
int out_linesize;
//输出内存大小
int out_buffer_size=av_samples_get_buffer_size(&out_linesize, pCodecCtx->channels,pCodecCtx->frame_size,pCodecCtx->sample_fmt, 1);
uint8_t *out_buffer=new uint8_t[out_buffer_size];
//---------SDL--------------------------------------
//初始化
if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) {
printf( "Could not initialize SDL - %s\n", SDL_GetError());
exit(1);
}
//结构体,包含PCM数据的相关信息
SDL_AudioSpec wanted_spec;
wanted_spec.freq = pCodecCtx->sample_rate;
wanted_spec.format = AUDIO_S16SYS;
wanted_spec.channels = pCodecCtx->channels;
wanted_spec.silence = 0;
//wanted_spec.samples = 1024; //播放AAC,M4a,缓冲区的大小
wanted_spec.samples = 1152; //播放MP3,WMA时候用
wanted_spec.callback = fill_audio;
wanted_spec.userdata = pCodecCtx;
if (SDL_OpenAudio(&wanted_spec, NULL)<0)//步骤(2)打开音频设备
{
printf("can't open audio.\n");
return 0;
}
//-----------------------------------------------------
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);
uint32_t ret,len = 0;
int got_picture;
int index = 0;
struct SwrContext *au_convert_ctx;
au_convert_ctx = swr_alloc();
au_convert_ctx=swr_alloc_set_opts(au_convert_ctx,AV_CH_LAYOUT_STEREO, AV_SAMPLE_FMT_S16, 44100,
pCodecCtx->channel_layout,pCodecCtx->sample_fmt , pCodecCtx->sample_rate,0, NULL);
swr_init(au_convert_ctx);
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");
exit(0);
}
if ( got_picture > 0 )
{
#if 1
swr_convert(au_convert_ctx,&out_buffer, out_linesize,(const uint8_t **)pFrame->data , pFrame->nb_samples);
printf("index:%5d\t pts %5d\n", index,packet->pts);
#endif
//直接写入
#if 1
fwrite(out_buffer, 1, out_linesize, pFile);
#endif
index++;
}
#if 1
//---------------------------------------
//printf("begin....\n");
//设置音频数据缓冲,PCM数据
audio_chunk = (Uint8 *) out_buffer;
//设置音频数据长度
audio_len = out_linesize;
audio_len = 4096;
//播放mp3的时候改为audio_len = 4096
//则会比较流畅,但是声音会变调!MP3一帧长度4608
//使用一次回调函数(4096字节缓冲)播放不完,所以还要使用一次回调函数,导致播放缓慢。。。
//设置初始播放位置
audio_pos = audio_chunk;
//回放音频数据
SDL_PauseAudio(0);
//printf("don't close, audio playing...\n");
while(audio_len>0)//等待直到音频数据播放完毕!
SDL_Delay(1);
//---------------------------------------
#endif
}
av_free_packet(packet);
}
#ifdef _WAVE_
fseek(pFile, 0, SEEK_SET);
struct WAVE_HEADER wh;
memcpy(wh.header.RiffID, "RIFF", 4);
wh.header.RiffSize = 36 + len;
memcpy(wh.header.RiffFormat, "WAVE", 4);
memcpy(wh.format.FmtID, "fmt ", 4);
wh.format.FmtSize = 16;
wh.format.wavFormat.FormatTag = 1;
wh.format.wavFormat.Channels = pCodecCtx->channels;
wh.format.wavFormat.SamplesRate = pCodecCtx->sample_rate;
wh.format.wavFormat.BitsPerSample = 16;
calformat(wh.format.wavFormat); //Calculate AvgBytesRate and BlockAlign
memcpy(wh.data.DataID, "data", 4);
wh.data.DataSize = len;
fwrite(&wh, 1, sizeof(wh), pFile);
#endif
SDL_CloseAudio();//关闭音频设备
// Close file
fclose(pFile);
// Close the codec
avcodec_close(pCodecCtx);
// Close the video file
av_close_input_file(pFormatCtx);
return 0;
}
相关文章
Android 音视频学习基础–1.1 音视频基础知识
Android 音视频学习基础–1.2 需要认识的一些工具
Android 音视频学习基础–1.3 主流的开源项目
Android 音视频学习基础–1.4 ffmpeg pcm输出
Android 音视频学习基础–1.5 ffmpeg yuv输出
Android 音视频学习基础–1.6 ffmpeg 简单视频播放器
Android 音视频学习基础–1.7 Android最简单的音频播放器
Android 音视频学习基础–1.8 Android最简单的音频播放器
Android 音视频学习基础–1.9 Android最简单的视频播放器
Android 音视频学习基础–1.10 Android自制简单音视频播放器
欢迎大家批评指正