ffmpeg解複用解碼重採樣

        之前有個片源輸出有吵雜的聲音,然後爲了和板子輸出的音頻比較,我就在linux PC上參照一下我們的工程和網上的一些方法,寫了這麼一個程序。
要開始播放是一個視頻,到音頻的輸出,大概經歷了這個幾個過程:解複用(抽取視音頻流)->解碼音頻->重採樣。最終從重採樣出來的數據,是可以從音頻驅動直接輸出的。

        重採樣的話,應該不是必須的,然而我們項目是用安卓的架構,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);
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章