要開始播放是一個視頻,到音頻的輸出,大概經歷了這個幾個過程:解複用(抽取視音頻流)->解碼音頻->重採樣。最終從重採樣出來的數據,是可以從音頻驅動直接輸出的。
重採樣的話,應該不是必須的,然而我們項目是用安卓的架構,HAL層有了輸出參數的設定(採樣率48K,雙聲道,採樣格式AV_SAMPLE_FMT_S16)。
下面是代碼,寫得很爛,初始化和反初始化亂七八糟的,我也不想改了,反正是我自己用的demo而已,貼上來之前,我去掉了很多東西,不一定能編譯過,反正是這個邏輯了。幾個月前寫的東西,都幾乎忘光。
#include "libavformat/avformat.h"
#include "libavcodec/avcodec.h"
#include "libavutil/avutil.h"
#include <libswscale/swscale.h>
#include <libswresample/swresample.h>
#include "stdio.h"
#include "math.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define AVCODEC_MAX_AUDIO_FRAME_SIZE 192000 // 1 second of 48khz 32bit audio
int main(int argc ,char **argv)
{
if (argc !=2)
{
printf("useage:%s INPUTFILE\n", argv[0]);
return -1;
}
char *outdec="dec.pcm";
int fd_out = open(outdec, O_WRONLY, 0666);
if (fd_out < 0)
{
printf("open in file is failed\n");
return -1;
}
av_register_all();
AVFrame* frame = avcodec_alloc_frame();
if (!frame)
{
printf( "Error allocating the frame" );
return -1;
}
AVFormatContext* formatContext = NULL;
if (avformat_open_input(&formatContext, argv[1], NULL, NULL) != 0)
{
av_free(frame);
printf( "Error opening the file" );
return -1;
}
if (avformat_find_stream_info(formatContext, NULL) < 0)
{
av_free(frame);
//av_close_input_file(formatContext);
printf( "Error finding the stream info" );
return -1;
}
AVStream* audioStream = NULL;
unsigned int i = 0;
for (; i < formatContext->nb_streams; ++i)
{
if (formatContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO)//查找音頻流對應的ID
{
audioStream = formatContext->streams[i];
break;
}
}
if (audioStream == NULL)
{
av_free(frame);
////av_close_input_file(formatContext);
printf( "Could not find any audio stream in the file" );
return 1;
}
AVCodecContext* codecContext = audioStream->codec;
codecContext->codec = avcodec_find_decoder(codecContext->codec_id);
if (codecContext->codec == NULL)
{
av_free(frame);
////av_close_input_file(formatContext);
printf( "Couldn't find a proper decoder" );
return -1;
}
else if (avcodec_open2(codecContext, codecContext->codec, NULL) != 0)
{
av_free(frame);
////av_close_input_file(formatContext);
printf( "Couldn't open the context with the decoder" );
return 1;
}
/* 重採樣格式初始化 */
SwrContext* swrContext = NULL;
printf(" bit_rate = %d \r\n", codecContext->bit_rate);
printf(" sample_rate = %d \r\n", codecContext->sample_rate);
printf(" channels = %d \r\n", codecContext->channels);
printf(" channel_layout = %d \r\n", codecContext->channel_layout);
printf(" code_name = %s \r\n", codecContext->codec->name);
printf(" av_get_default_channel_layout(2) %d \r\n", av_get_default_channel_layout(2));
swrContext = swr_alloc_set_opts(NULL,
av_get_default_channel_layout(2), // out channel layout
AV_SAMPLE_FMT_S16, // out sample format
48000, // out sample rate
codecContext->channel_layout, // in channel layout
codecContext->sample_fmt, // in sample format
codecContext->sample_rate, // in sample rate
0, // log offset
NULL); // log context
if(swr_init(swrContext)<0)
{
printf("swr_init() for AV_SAMPLE_FMT_S16 fail");
swr_free(&swrContext);
av_free(frame);
////av_close_input_file(formatContext);
return -1;
}
if (!swrContext)
{
swr_free(&swrContext);
av_free(frame);
avcodec_close(codecContext);
////av_close_input_file(formatContext);
swrContext = NULL;
printf( "Couldn't allocate and set the resampling context" );
return -1;
}
// int bufSize = av_samples_get_buffer_size(NULL, codecContext->channels, codecContext->sample_rate, codecContext->sample_fmt, 0);
// unsigned char* buf = new unsigned char[bufSize];
AVPacket *packet=(AVPacket *)av_mallocz(sizeof(AVPacket));
av_init_packet(packet);
static unsigned char audio_buf[(AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 2];
unsigned char *out[]={audio_buf};
unsigned char *pktdata;
int pktsize,flush_complete=0;
int resampled_data_size = 0;
while (av_read_frame(formatContext, packet) == 0)
{
if (packet->stream_index == audioStream->index) //只處理音頻
{
int got_frame = 0;
int len = 0;
while (packet->size > 0)
{
len = avcodec_decode_audio4(codecContext, frame, &got_frame, packet); //音頻解碼
if (len < 0)
{
av_free_packet(packet);
swr_free(&swrContext);
av_free(frame);
avcodec_close(codecContext);
////av_close_input_file(formatContext);
swrContext = NULL;
printf( " audio decode error ! \n" );
return -1;
}
packet->data += len; //必須考慮一包解多次的情況
packet->size -= len;
if (got_frame)//獲取一幀的音頻
{
//const unsigned char **in = (const unsigned char **) src_pp;
const unsigned char **in = (const unsigned char **) frame->extended_data;
int len2 = swr_convert(swrContext, out,
sizeof(audio_buf)/codecContext->channels/av_get_bytes_per_sample(AV_SAMPLE_FMT_S16),
in, frame->nb_samples);
if (len2 < 0)
{
fprintf(stderr, "audio_resample() failed\n");
av_free_packet(packet);
swr_free(&swrContext);
av_free(frame);
avcodec_close(codecContext);
////av_close_input_file(formatContext);
swrContext = NULL;
printf( " audio decode error ! \n" );
return -1;
}
if (len2 == sizeof(audio_buf) / codecContext->channels / av_get_bytes_per_sample(AV_SAMPLE_FMT_S16))
{
fprintf(stderr, "warning: audio buffer is probably too small\n");
swr_init(swrContext);
}
resampled_data_size = len2 * (2) * av_get_bytes_per_sample(AV_SAMPLE_FMT_S16);
write(fd_out, *out, resampled_data_size);
}
}
}
av_init_packet(packet);
}
end:
av_free_packet(packet);
// delete [] buf;
swr_free(&swrContext);
av_free(frame);
avcodec_close(codecContext);
//av_close_input_file(formatContext);
close(fd);
}