1. 程式人生 > >最簡單的基於FFMPEG+SDL的音訊播放器:拆分-解碼器和播放器

最簡單的基於FFMPEG+SDL的音訊播放器:拆分-解碼器和播放器

=====================================================

最簡單的基於FFmpeg的音訊播放器系列文章列表:

=====================================================


本文補充記錄《最簡單的基於FFMPEG+SDL的音訊播放器》中的兩個例子:FFmpeg音訊解碼器和SDL音訊取樣資料播放器。這兩個部分是從音訊播放器中拆分出來的兩個例子。FFmpeg音訊解碼器實現了視訊資料到PCM取樣資料的解碼,而SDL音訊取樣資料播放器實現了PCM資料到音訊裝置的播放。簡而言之,原先的FFmpeg+SDL音訊播放器實現了:

音訊資料->PCM->音訊裝置

FFmpeg音訊解碼器實現了:

音訊資料->PCM

SDL音訊取樣資料播放器實現了:

PCM->音訊裝置

FFmpeg音訊解碼器

原始碼

/**
 * 最簡單的基於FFmpeg的音訊解碼器
 * Simplest FFmpeg Audio Decoder
 *
 * 雷霄驊 Lei Xiaohua
 * [email protected]
 * 中國傳媒大學/數字電視技術
 * Communication University of China / Digital TV Technology
 * http://blog.csdn.net/leixiaohua1020
 *
 * 本程式可以將音訊碼流(mp3,AAC等)解碼為PCM取樣資料。
 * 是最簡單的FFmpeg音訊解碼方面的教程。
 * 通過學習本例子可以瞭解FFmpeg的解碼流程。
 *
 * This software decode audio streams (AAC,MP3 ...) to PCM data.
 * Suitable for beginner of FFmpeg.
 *
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define __STDC_CONSTANT_MACROS

#ifdef _WIN32
//Windows
extern "C"
{
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswresample/swresample.h"
};
#else
//Linux...
#ifdef __cplusplus
extern "C"
{
#endif
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswresample/swresample.h>
#ifdef __cplusplus
};
#endif
#endif

#define MAX_AUDIO_FRAME_SIZE 192000 // 1 second of 48khz 32bit audio

int main(int argc, char* argv[])
{
	AVFormatContext	*pFormatCtx;
	int				i, audioStream;
	AVCodecContext	*pCodecCtx;
	AVCodec			*pCodec;
	AVPacket		*packet;
	uint8_t			*out_buffer;
	AVFrame			*pFrame;
    int ret;
	uint32_t len = 0;
	int got_picture;
	int index = 0;
	int64_t in_channel_layout;
	struct SwrContext *au_convert_ctx;

	FILE *pFile=fopen("output.pcm", "wb");
	char url[]="skycity1.mp3";

	av_register_all();
	avformat_network_init();
	pFormatCtx = avformat_alloc_context();
	//Open
	if(avformat_open_input(&pFormatCtx,url,NULL,NULL)!=0){
		printf("Couldn't open input stream.\n");
		return -1;
	}
	// Retrieve stream information
	if(avformat_find_stream_info(pFormatCtx,NULL)<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;
	}

	packet=(AVPacket *)av_malloc(sizeof(AVPacket));
	av_init_packet(packet);

	//Out Audio Param
	uint64_t out_channel_layout=AV_CH_LAYOUT_STEREO;
	//nb_samples: AAC-1024 MP3-1152
	int out_nb_samples=pCodecCtx->frame_size;
	AVSampleFormat out_sample_fmt=AV_SAMPLE_FMT_S16;
	int out_sample_rate=44100;
	int out_channels=av_get_channel_layout_nb_channels(out_channel_layout);
	//Out Buffer Size
	int out_buffer_size=av_samples_get_buffer_size(NULL,out_channels ,out_nb_samples,out_sample_fmt, 1);

	out_buffer=(uint8_t *)av_malloc(MAX_AUDIO_FRAME_SIZE*2);
	pFrame=av_frame_alloc();

	//FIX:Some Codec's Context Information is missing
	in_channel_layout=av_get_default_channel_layout(pCodecCtx->channels);
	//Swr
	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);

	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");
                return -1;
            }
			if ( got_picture > 0 ){
				swr_convert(au_convert_ctx,&out_buffer, MAX_AUDIO_FRAME_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);
				//Write PCM
				fwrite(out_buffer, 1, out_buffer_size, pFile);
				index++;
			}
		}
		av_free_packet(packet);
	}

	swr_free(&au_convert_ctx);

	fclose(pFile);

	av_free(out_buffer);
	// Close the codec
	avcodec_close(pCodecCtx);
	// Close the video file
	avformat_close_input(&pFormatCtx);

	return 0;
}


執行結果

程式執行後,會解碼下面的音訊檔案。
 解碼後的PCM取樣資料被儲存成了一個檔案。使用Adobe Audition設定取樣率等資訊後可以檢視PCM的內容。
 

SDL音訊取樣資料播放器

原始碼

/**
 * 最簡單的SDL2播放音訊的例子(SDL2播放PCM)
 * Simplest Audio Play SDL2 (SDL2 play PCM) 
 *
 * 雷霄驊 Lei Xiaohua
 * [email protected]
 * 中國傳媒大學/數字電視技術
 * Communication University of China / Digital TV Technology
 * http://blog.csdn.net/leixiaohua1020
 *
 * 本程式使用SDL2播放PCM音訊取樣資料。SDL實際上是對底層繪圖
 * API(Direct3D,OpenGL)的封裝,使用起來明顯簡單于直接呼叫底層
 * API。
 *
 * 函式呼叫步驟如下: 
 *
 * [初始化]
 * SDL_Init(): 初始化SDL。
 * SDL_OpenAudio(): 根據引數(儲存於SDL_AudioSpec)開啟音訊裝置。
 * SDL_PauseAudio(): 播放音訊資料。
 *
 * [迴圈播放資料]
 * SDL_Delay(): 延時等待播放完成。
 *
 * This software plays PCM raw audio data using SDL2.
 * SDL is a wrapper of low-level API (DirectSound).
 * Use SDL is much easier than directly call these low-level API.
 *
 * The process is shown as follows:
 *
 * [Init]
 * SDL_Init(): Init SDL.
 * SDL_OpenAudio(): Opens the audio device with the desired 
 *					parameters (In SDL_AudioSpec).
 * SDL_PauseAudio(): Play Audio.
 *
 * [Loop to play data]
 * SDL_Delay(): Wait for completetion of playback.
 */

#include <stdio.h>
#include <tchar.h>

extern "C"
{
#include "sdl/SDL.h"
};

//Buffer:
//|-----------|-------------|
//chunk-------pos---len-----|
static  Uint8  *audio_chunk; 
static  Uint32  audio_len; 
static  Uint8  *audio_pos; 

/* Audio Callback
 * 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 
 * 
*/ 
void  fill_audio(void *udata,Uint8 *stream,int len){ 
	//SDL 2.0
	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 main(int argc, char* argv[])
{
	//Init
	if(SDL_Init(SDL_INIT_AUDIO | SDL_INIT_TIMER)) {  
		printf( "Could not initialize SDL - %s\n", SDL_GetError()); 
		return -1;
	}
	//SDL_AudioSpec
	SDL_AudioSpec wanted_spec;
	wanted_spec.freq = 44100; 
	wanted_spec.format = AUDIO_S16SYS; 
	wanted_spec.channels = 2; 
	wanted_spec.silence = 0; 
	wanted_spec.samples = 1024; 
	wanted_spec.callback = fill_audio; 

	if (SDL_OpenAudio(&wanted_spec, NULL)<0){ 
		printf("can't open audio.\n"); 
		return -1; 
	} 

	FILE *fp=fopen("NocturneNo2inEflat_44.1k_s16le.pcm","rb+");
	if(fp==NULL){
		printf("cannot open this file\n");
		return -1;
	}

	int pcm_buffer_size=4096;
	char *pcm_buffer=(char *)malloc(pcm_buffer_size);
	int data_count=0;

	//Play
	SDL_PauseAudio(0);

	while(1){
		if (fread(pcm_buffer, 1, pcm_buffer_size, fp) != pcm_buffer_size){
			// Loop
			fseek(fp, 0, SEEK_SET);
			fread(pcm_buffer, 1, pcm_buffer_size, fp);
			data_count=0;
		}
		printf("Now Playing %10d Bytes data.\n",data_count);
		data_count+=pcm_buffer_size;
		//Set audio buffer (PCM data)
		audio_chunk = (Uint8 *) pcm_buffer; 
		//Audio buffer length
		audio_len =pcm_buffer_size;
		audio_pos = audio_chunk;

		while(audio_len>0)//Wait until finish
			SDL_Delay(1); 
	}
	free(pcm_buffer);
	SDL_Quit();
	return 0;
}


執行結果

程式執行後,會讀取程式資料夾下的一個PCM取樣資料檔案,內容如下所示。
接下來會將PCM資料輸出到系統的音訊裝置上(音響、耳機)。

下載


Simplest FFmpeg Audio Player

SourceForge:https://sourceforge.net/projects/simplestffmpegaudioplayer/
本程式實現了音訊的解碼和播放。是最簡單的FFmpeg音訊解碼方面的教程。
通過學習本例子可以瞭解FFmpeg的解碼流程。
專案包含3個工程:
simplest_ffmpeg_audio_player:基於FFmpeg+SDL的音訊解碼器
simplest_ffmpeg_audio_decoder:音訊解碼器。使用了libavcodec和libavformat。
simplest_audio_play_sdl2:使用SDL2播放PCM取樣資料的例子。

相關推薦

簡單基於FFMPEG+SDL音訊播放拆分-解碼播放

=====================================================最簡單的基於FFmpeg的音訊播放器系列文章列表:=====================================================本文補充記錄《

簡單基於FFMPEG+SDL的視訊播放拆分-解碼播放

=====================================================最簡單的基於FFmpeg的視訊播放器系列文章列表:=====================================================本文補充記錄《

簡單基於FFMPEG+SDL音訊播放 ver2 (採用SDL2 0)

                =====================================================最簡單的基於FFmpeg的音訊播放器系列文章列表:=====================================================簡介之前做過一個

100行代碼實現簡單基於FFMPEG+SDL的視頻播放(SDL1.x)【轉】

工程 全屏 升級版 gin avcodec ive 系列文章 相同 hello 轉自:http://blog.csdn.net/leixiaohua1020/article/details/8652605 版權聲明:本文為博主原創文章,未經博主允許不得轉載。

100行程式碼實現簡單基於FFMPEG+SDL的視訊播放(SDL1.x)

                =====================================================最簡單的基於FFmpeg的視訊播放器系列文章列表:=====================================================簡介FFMPEG

簡單基於FFMPEG+SDL的視訊播放 ver2 (採用SDL2.0

                =====================================================最簡單的基於FFmpeg的視訊播放器系列文章列表:=====================================================簡介之前做過一個

簡單基於FFMPEG+SDL的音視訊播放

一、概述         在《最簡單的基於FFMPEG+SDL的音訊播放器》記錄一中,我們實現了音訊的播放。更早前,我們在《最簡單的基於FFMPEG+SDL的視訊播放器》記錄一和二中,實現了視訊的播放。在實現視訊播放的時候,我們設定了一個延遲40ms,否則視訊就會以解碼的速

XCode版【100行程式碼實現簡單基於FFMPEG+SDL的視訊播放

【來自】 1.新建XCode工程後,發現即使安裝了SDL和FFMPEG也編譯不成功,需要修改各種環境。經過我的不懈努力加百穀啥的...貼個能編譯通過的過程出來。謹記! 2.首先需要編譯好ffmpeg原始碼,然後還需要安裝SDL(ffmpeg直接編譯,SDL我是通過brew安

簡單基於FFMPEG音訊編碼(PCM編碼為AAC

                本文介紹一個最簡單的基於FFMPEG的音訊編碼器。該編碼器實現了PCM音訊取樣資料編碼為AAC的壓縮編碼資料。編碼器程式碼十分簡單,但是每一行程式碼都很重要。通過看本編碼器的原始碼,可以瞭解FFMPEG音訊編碼的流程。本程式使用最新版的類庫(編譯時間為2014.5.6),開發平

簡單基於FFmpeg的封裝格式處理音訊分離簡化版(demuxer-simple)

=====================================================最簡單的基於FFmpeg的封裝格式處理系列文章列表:=====================================================簡介打算記錄

史上全的基於ffmpeg+sdl網路攝像頭編解碼播放資料(包含交叉編譯過程,附帶完整原始碼)

原創博文,嚴禁私自轉載,轉載請註明出處!!! 近期,由於工作需要,要在開發板上跑一個攝像頭,攝像頭款式比較老,不支援rtsp格式,所以選擇編譯ffmpeg+sdl實現軟解碼播放攝像頭,特此記錄整個編譯過程(非常之艱辛,發文留念) 在ubuntu上交叉編譯環境的搭建:因為開發板上搭建的程式的執

基於 FFmpeg + SDL 的視訊播放的製作》課程的視訊

這兩天開始帶廣播電視工程大二的暑假小學期的課程設計了。本次小學期課程內容為《基於 FFmpeg + SDL 的視訊播放器的製作》,其中主要講述了視音訊開發的入門知識。由於感覺本課程的內容不但適合本科生,而且也比較適合無視音訊基礎的開發者入門使用,所以在講課的同時也錄製了一部分

簡單基於FFmpeg的移動端例子Android 視訊轉碼

=====================================================最簡單的基於FFmpeg的移動端例子系列文章列表:=====================================================本文記錄一個安

簡單基於FFmpeg的移動端例子IOS HelloWorld

=====================================================最簡單的基於FFmpeg的移動端例子系列文章列表:=====================================================本文記錄IOS

基於FFMPEG音訊編碼

編碼模組 編碼模組是編碼存放在FIFO中的資料,然後udp輸出,具體的資料流向圖如下: 編碼模組資料流向圖 編碼前,為32位雙聲道48KHz的PCM資料,因為ffmpeg MP2編碼器所支援的PCM資料為16位,所以需要PCM重取樣。  

4.基於FFMPEG音訊解碼為PCM

繼續FFMPEG學習之路,前面瞭解了將PCM編碼為AAC檔案,接下來則需要了解一下解碼方面,將MP3/AAC等音訊格式解碼為PCM資料,記錄一下過程。。。 1)解碼流程 整個解碼流程採用虛擬碼大致如下: 初始化複用器和解複用器—>獲取輸入檔案的一些資訊—->查詢解碼器

簡單基於Flash的流媒體示例RTMP推送接收(ActionScript)

=====================================================Flash流媒體文章列表:=====================================================本文記錄一些基於Flash的流媒體處理

史上簡單的SpringCloud教程 | 第二篇 服務消費者(rest+ribbon)

image tree 開啟 then rom cat learn 替代 官網 最新Finchley版本:https://www.fangzhipeng.com/springcloud/2018/08/30/sc-f2-ribbon/或者http://blog.csdn.n

史上簡單的 SpringCloud 教程 | 第一篇 服務的註冊與發現(Eureka)

add 過程 sdn 需要 2.3 boot one ini tail 最新Finchley版本請訪問:https://www.fangzhipeng.com/springcloud/2018/08/30/sc-f1-eureka/或者http://blog.csdn.n

簡單解決Python安裝拓展包問題unable to find vcvarsall.bat

一、問題描述  (1)、 電腦配置資訊:  win7系統 TMD GCC Python3.5.x  (2)使用pip命令安裝mysqlclient時報錯    error:unable to find vcvarsall.bat 我的電腦上安裝了TMD GCC但