• FFMPEG学习----使用SDL播放PCM数据


    参考雷神的代码:

    /**
    * 最简单的SDL2播放音频的例子(SDL2播放PCM)
    * Simplest Audio Play SDL2 (SDL2 play PCM)
    *
    * 本程序使用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.h"
    };
    
    Uint32  audio_len;//音频数据大小
    Uint8  *audio_pos;//指向音频数据的指针
    
    
    /**回调函数(由系统调用)
    *  函数声明:typedef void (SDLCALL * SDL_AudioCallback) 
    *            (void *userdata, Uint8 * stream, int len);
    *  This function is called when the audio device needs more data.
    *
    *  userdata: An application-specific parameter saved in the SDL_AudioSpec structure(SDL_AudioSpec结构中的用户自定义数据,一般情况下可以不用)
    *  stream:   A pointer to the audio data buffer.(该指针指向需要填充的音频缓冲区)
    *  len:      The length of that buffer in bytes.(音频缓冲区的大小,以字节为单位)
    *
    *  Once the callback returns, the buffer will no longer be valid.
    *  Stereo samples are stored in a LRLRLR ordering.
    *
    *  You can choose to avoid callbacks and use SDL_QueueAudio() instead, if
    *  you like. Just open your audio device with a NULL callback.
    */
    
    void  fill_audio(void *userdata, Uint8 *stream, int len)
    {
    	//SDL2中必须首先使用SDL_memset()将stream中的数据设置为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  */
    
    	/**
    	*  函数声明:extern DECLSPEC void SDLCALL 
    	*  SDL_MixAudio(Uint8 * dst, const Uint8 * src, Uint32 len, int volume);
    	*  This takes two audio buffers of the playing audio format and mixes
    	*  them, performing addition, volume adjustment, and overflow clipping.
    	*  The volume ranges from 0 - 128, and should be set to ::SDL_MIX_MAXVOLUME
    	*  for full audio volume.  Note this does not change hardware volume.
    	*  This is provided for convenience -- you can mix your own audio data.
    	*
    	*  #define SDL_MIX_MAXVOLUME 128
    	*/
    
    	SDL_MixAudio(stream, audio_pos, len, SDL_MIX_MAXVOLUME);
    	audio_pos += len;
    	audio_len -= len;
    }
    
    int main(int argc, char* argv[])
    {
    	//初始化SDL
    	if (SDL_Init(SDL_INIT_AUDIO))
    	{
    		printf("Could not initialize SDL - %s
    ", SDL_GetError());
    		return -1;
    	}
    
    	//SDL_AudioSpec初始化
    	SDL_AudioSpec wanted_spec;
    	wanted_spec.freq = 44100;			//音频数据的采样率(常用的有48000,44100等)
    	wanted_spec.format = AUDIO_S16SYS;//音频数据的格式
    	wanted_spec.channels = 2;			//声道数(例如单声道取值为1,立体声取值为2)
    	wanted_spec.silence = 0;			//设置静音的值
    	wanted_spec.samples = 1024;		//音频缓冲区中的采样个数(要求必须是2的n次方)
    	wanted_spec.callback = fill_audio;//填充音频缓冲区的回调函数
    
    	//打开音频
    	if (SDL_OpenAudio(&wanted_spec, NULL) < 0)
    	{
    		printf("can't open audio.
    ");
    		return -1;
    	}
    
    	FILE *fp_pcm = fopen("..\FFmpeg_PCM\output.pcm", "rb");
    	if (fp_pcm == NULL)
    	{
    		printf("cannot open this file
    ");
    		return -1;
    	}
    
    	int pcm_buffer_size = 4096;//每次读取4096字节的数据,同时也是音频帧大小
    	char *pcm_buffer = (char *)malloc(pcm_buffer_size);
    	int data_count = 0;
    
    	//播放音频数据
    	SDL_PauseAudio(0);
    
    	while (true)
    	{
    		int ret = fread(pcm_buffer, 1, pcm_buffer_size, fp_pcm);
    		if (ret != pcm_buffer_size)
    		{
    			//这里有可能是会有剩余音频数据的,不知道这样改对不对?
    			audio_pos = (Uint8 *)pcm_buffer;
    			audio_len = ret;
    			while (audio_len > 0)
    			{
    				SDL_Delay(1);
    			}
    
    			//退出
    			break;
    			
    			//循环播放
    			fseek(fp_pcm, 0, SEEK_SET);
    			fread(pcm_buffer, 1, pcm_buffer_size, fp_pcm);
    			data_count = 0;
    		}
    		printf("Now Playing %10d Bytes data.
    ", data_count);
    		data_count += pcm_buffer_size;
    		
    
    		audio_pos = (Uint8 *)pcm_buffer;
    		//Audio buffer length 
    		audio_len = pcm_buffer_size;
    		
    
    		while (audio_len > 0)//Wait until finish
    		{
    			SDL_Delay(1);
    		}
    	}
    	free(pcm_buffer);
    	fclose(fp_pcm);
    	SDL_Quit();
    	return 0;
    }
    
    

    雷神代码中if (fread(pcm_buffer, 1, pcm_buffer_size, fp) != pcm_buffer_size){}
    如果获取不到4096个字节的音频数据,就从头接着播放了,当然这意味着读到末尾了,但是剩下的音频数据没有做处理?

    疑问:

    wanted_spec.silence = 0; //设置静音的值
    wanted_spec.samples = 1024; //音频缓冲区中的采样个数(要求必须是2的n次方)

    int pcm_buffer_size = 4096;//每次读取4096字节的数据,同时也是音频帧大小

    Keep it simple!
    作者:N3verL4nd
    知识共享,欢迎转载。
  • 相关阅读:
    Hdoj 1203.I NEED A OFFER! 题解
    Hdoj 4508.湫湫系列故事——减肥记I 题解
    Hdoj 2191.悼念512汶川大地震遇难同胞——珍惜现在,感恩生活 题解
    Hdoj 1248.寒冰王座 题解
    idea配置(卡顿、开发环境等配置),code style template
    经典收藏链接(C#总结)
    C#设计模式:原型模式(Prototype)及深拷贝、浅拷贝
    值类型和引用类型深入理解
    Resharper的配置和使用
    批处理bat 命令
  • 原文地址:https://www.cnblogs.com/lgh1992314/p/5834636.html
Copyright © 2020-2023  润新知