PCM(Pulse Code Modulation),脉冲编码调制。人耳听到的是模拟信号,PCM是把声音从模拟信号转化为数字信号的技术。原理是用一个固定的频率对模拟信号进行采样,采样后的信号在波形上看就像一串连续的幅值不一的脉冲(脉搏似的短暂起伏的电冲击),把这些脉冲的幅值按一定精度进行量化,这些量化后的数值被连续的输出、传输、处理或记录到存储介质中,所有这些组成了数字音频的产生过程(抽样、量化、编码三个过程)。
播放音乐时,应用程序从存储介质中读取音频数据(MP3、WMA、AAC…),经过解码后,最终送到音频驱动程序中的就是PCM数据,反过来,在录音时,音频驱动不停地把采样所得的PCM数据送回给应用程序,由应用程序完成压缩、存储等任务。
下面我们通过ffmpeg音频转原生的PCM
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void play(View view) {
player();
}
private static String[] permissions = new String[]{
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_EXTERNAL_STORAGE
};
private void player(){
String[] permissions1 = checkPermission(this);
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.M){
if(permissions1.length<=0){
MyPlayer myPlayer = new MyPlayer();
String input = new File(Environment.getExternalStorageDirectory(),"input.mp3").getAbsolutePath();
String output = new File(Environment.getExternalStorageDirectory(),"output.pcm").getAbsolutePath();
myPlayer.sound(input,output);
}else{
ActivityCompat.requestPermissions(this, permissions, 100);
}
}else{//6.0以下不需要申请权限
MyPlayer myPlayer = new MyPlayer();
String input = new File(Environment.getExternalStorageDirectory(),"input.mp3").getAbsolutePath();
String output = new File(Environment.getExternalStorageDirectory(),"output.pcm").getAbsolutePath();
myPlayer.sound(input,output);
}
}
public static String[] checkPermission(Context context){
List<String> data = new ArrayList<>();//存储未申请的权限
for (String permission : permissions) {
int checkSelfPermission = ContextCompat.checkSelfPermission(context, permission);
if(checkSelfPermission == PackageManager.PERMISSION_DENIED){//未申请
data.add(permission);
}
}
return data.toArray(new String[data.size()]);
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if(requestCode == 100){
boolean flag = true;
for(int i : grantResults){
if(i != PackageManager.PERMISSION_GRANTED){
flag = false;
break;
}
}
if(flag){
player();
}else{
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}else{
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
}
public class MyPlayer {
static{
System.loadLibrary("avutil-54");
System.loadLibrary("swresample-1");
System.loadLibrary("avcodec-56");
System.loadLibrary("avformat-56");
System.loadLibrary("swscale-3");
System.loadLibrary("postproc-53");
System.loadLibrary("avfilter-5");
System.loadLibrary("avdevice-56");
System.loadLibrary("native-lib");
}
public native void sound(String input,String output);
}
核心代码
#include <jni.h>
#include <string>
#include <android/log.h>
#define LOGI(FORMAT,...) __android_log_print(ANDROID_LOG_INFO,"twy",FORMAT,##__VA_ARGS__);
#define LOGE(FORMAT,...) __android_log_print(ANDROID_LOG_ERROR,"twy",FORMAT,##__VA_ARGS__);
#define MAX_AUDIO_FRME_SIZE 48000 * 4
extern "C"{
//封装格式
#include "libavformat/avformat.h"
//解码
#include "libavcodec/avcodec.h"
//缩放
#include "libswscale/swscale.h"
//重采样
#include "libswresample/swresample.h"
};
extern "C"
{
JNIEXPORT void JNICALL
Java_com_dongnao_ffmpegmusicdemo_MyPlayer_sound(JNIEnv *env, jobject instance, jstring input_,
jstring output_) {
const char *input = env->GetStringUTFChars(input_, 0);
const char *output = env->GetStringUTFChars(output_, 0);
av_register_all();
AVFormatContext *pFormatCtx = avformat_alloc_context();
//打开音频文件
if(avformat_open_input(&pFormatCtx,input,NULL,NULL) != 0){
LOGI("%s","无法打开音频文件");
return;
}
//获取输入文件信息
if(avformat_find_stream_info(pFormatCtx,NULL) < 0){
LOGI("%s","无法获取输入文件信息");
return;
}
//获取音频流索引位置
int i = 0, audio_stream_idx = -1;
for(; i < pFormatCtx->nb_streams;i++){
if(pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO){
audio_stream_idx = i;
break;
}
}
//获取解码器
AVCodecContext *codecCtx = pFormatCtx->streams[audio_stream_idx]->codec;
AVCodec *codec = avcodec_find_decoder(codecCtx->codec_id);
//打开解码器
if(avcodec_open2(codecCtx,codec,NULL) < 0){
LOGI("%s","无法打开解码器");
return;
}
//压缩数据
AVPacket *packet = (AVPacket *)av_malloc(sizeof(AVPacket));
//解压缩数据
AVFrame *frame = av_frame_alloc();
//frame->16bit 44100 PCM 统一音频采样格式与采样率
SwrContext *swrContext = swr_alloc();
// 音频格式 重采样设置参数
AVSampleFormat in_sample = codecCtx->sample_fmt;
// 输出采样格式
AVSampleFormat out_sample=AV_SAMPLE_FMT_S16;
// 输入采样率
int in_sample_rate = codecCtx->sample_rate;
// 输出采样
int out_sample_rate=44100;
// 输入声道布局
uint64_t in_ch_layout=codecCtx->channel_layout;
// 输出声道布局
uint64_t out_ch_layout = AV_CH_LAYOUT_STEREO;
swr_alloc_set_opts(swrContext,out_ch_layout,out_sample,out_sample_rate
,in_ch_layout,in_sample,in_sample_rate,0,NULL);
swr_init(swrContext);
int got_frame=0;
int ret;
int out_channerl_nb = av_get_channel_layout_nb_channels(out_ch_layout);
LOGE("声道数量%d ",out_channerl_nb);
int count=0;
// 设置音频缓冲区间 16bit 44100 PCM数据
uint8_t *out_buffer = (uint8_t *) av_malloc(2 * 44100);
FILE *fp_pcm = fopen(output, "wb");
while (av_read_frame(pFormatCtx, packet)>=0) {
ret = avcodec_decode_audio4(codecCtx, frame, &got_frame, packet);
LOGE("正在解码%d",count++);
if (ret < 0) {
LOGE("解码完成");
}
// 解码一帧
if (got_frame > 0) {
swr_convert(swrContext, &out_buffer, 2 * 44100,
(const uint8_t **) frame->data, frame->nb_samples);
int out_buffer_size=av_samples_get_buffer_size(NULL, out_channerl_nb, frame->nb_samples, out_sample, 1);
fwrite(out_buffer, 1, out_buffer_size,fp_pcm);
}
av_free_packet(packet);
}
fclose(fp_pcm);
av_frame_free(&frame);
av_free(out_buffer);
swr_free(&swrContext);
avcodec_close(codecCtx);
avformat_close_input(&pFormatCtx);
}
}
Native 层回调播放
public class MyPlayer {
static{
System.loadLibrary("avutil-54");
System.loadLibrary("swresample-1");
System.loadLibrary("avcodec-56");
System.loadLibrary("avformat-56");
System.loadLibrary("swscale-3");
System.loadLibrary("postproc-53");
System.loadLibrary("avfilter-5");
System.loadLibrary("avdevice-56");
System.loadLibrary("native-lib");
}
private AudioTrack audioTrack;
public native void sound(String input,String output);
// 这个方法 是C进行调用 通道数
public void createAudio(int sampleRateInHz,int nb_channals) {
int channaleConfig;
if (nb_channals == 1) {
channaleConfig = AudioFormat.CHANNEL_OUT_MONO;
} else if (nb_channals == 2) {
channaleConfig = AudioFormat.CHANNEL_OUT_STEREO;
}else {
channaleConfig = AudioFormat.CHANNEL_OUT_MONO;
}
int buffersize= AudioTrack.getMinBufferSize(sampleRateInHz,
channaleConfig, AudioFormat.ENCODING_PCM_16BIT);
audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,sampleRateInHz,channaleConfig,
AudioFormat.ENCODING_PCM_16BIT,buffersize,AudioTrack.MODE_STREAM);
audioTrack.play();
}
//C传入音频数据
public synchronized void playTrack(byte[] buffer, int lenth) {
if (audioTrack != null && audioTrack.getPlayState() == AudioTrack.PLAYSTATE_PLAYING) {
audioTrack.write(buffer, 0, lenth);//写入进来喇叭就直接播放了
}
}
}
#include <jni.h>
#include <string>
#include <android/log.h>
#define LOGI(FORMAT,...) __android_log_print(ANDROID_LOG_INFO,"twy",FORMAT,##__VA_ARGS__);
#define LOGE(FORMAT,...) __android_log_print(ANDROID_LOG_ERROR,"twy",FORMAT,##__VA_ARGS__);
#define MAX_AUDIO_FRME_SIZE 48000 * 4
extern "C"{
//封装格式
#include "libavformat/avformat.h"
//解码
#include "libavcodec/avcodec.h"
//缩放
#include "libswscale/swscale.h"
//重采样
#include "libswresample/swresample.h"
};
extern "C"
{
JNIEXPORT void JNICALL
Java_com_dongnao_ffmpegmusicdemo_MyPlayer_sound(JNIEnv *env, jobject instance, jstring input_,
jstring output_) {
const char *input = env->GetStringUTFChars(input_, 0);
const char *output = env->GetStringUTFChars(output_, 0);
av_register_all();
AVFormatContext *pFormatCtx = avformat_alloc_context();
//打开音频文件
if(avformat_open_input(&pFormatCtx,input,NULL,NULL) != 0){
LOGI("%s","无法打开音频文件");
return;
}
//获取输入文件信息
if(avformat_find_stream_info(pFormatCtx,NULL) < 0){
LOGI("%s","无法获取输入文件信息");
return;
}
//获取音频流索引位置
int i = 0, audio_stream_idx = -1;
for(; i < pFormatCtx->nb_streams;i++){
if(pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO){
audio_stream_idx = i;
break;
}
}
//获取解码器
AVCodecContext *codecCtx = pFormatCtx->streams[audio_stream_idx]->codec;
AVCodec *codec = avcodec_find_decoder(codecCtx->codec_id);
//打开解码器
if(avcodec_open2(codecCtx,codec,NULL) < 0){
LOGI("%s","无法打开解码器");
return;
}
//压缩数据
AVPacket *packet = (AVPacket *)av_malloc(sizeof(AVPacket));
//解压缩数据
AVFrame *frame = av_frame_alloc();
//音频转换上下文 frame->16bit 44100 PCM 统一音频采样格式与采样率
SwrContext *swrContext = swr_alloc();
// 音频格式 重采样设置参数
AVSampleFormat in_sample = codecCtx->sample_fmt;
// 输出采样格式
AVSampleFormat out_sample=AV_SAMPLE_FMT_S16;
// 输入采样率
int in_sample_rate = codecCtx->sample_rate;
// 输出采样
int out_sample_rate=44100;
// 输入声道布局
uint64_t in_ch_layout=codecCtx->channel_layout;
// 输出声道布局
uint64_t out_ch_layout = AV_CH_LAYOUT_STEREO;
swr_alloc_set_opts(swrContext,out_ch_layout,out_sample,out_sample_rate
,in_ch_layout,in_sample,in_sample_rate,0,NULL);
swr_init(swrContext);
// 获取通道数 2
int out_channer_nb = av_get_channel_layout_nb_channels(AV_CH_LAYOUT_STEREO);
// 反射得到Class类型
jclass david_player = env->GetObjectClass(instance);
// 反射得到createAudio方法
jmethodID createAudio = env->GetMethodID(david_player, "createAudio", "(II)V");
// 反射调用createAudio
env->CallVoidMethod(instance, createAudio, 44100, out_channer_nb);
jmethodID audio_write = env->GetMethodID(david_player, "playTrack", "([BI)V");
int got_frame=0;
int ret;
int out_channerl_nb = av_get_channel_layout_nb_channels(out_ch_layout);
LOGE("声道数量%d ",out_channerl_nb);
int count=0;
// 设置音频缓冲区间 16bit 44100 PCM数据
uint8_t *out_buffer = (uint8_t *) av_malloc(2 * 44100);
FILE *fp_pcm = fopen(output, "wb");
while (av_read_frame(pFormatCtx, packet)>=0) {
ret = avcodec_decode_audio4(codecCtx, frame, &got_frame, packet);
LOGE("正在解码%d",count++);
if (ret < 0) {
LOGE("解码完成");
}
// 解码一帧
if (got_frame > 0) {
swr_convert(swrContext, &out_buffer, 2 * 44100,
(const uint8_t **) frame->data, frame->nb_samples);
int out_buffer_size=av_samples_get_buffer_size(NULL, out_channerl_nb, frame->nb_samples, out_sample, 1);
//缓冲区的大小
int size = av_samples_get_buffer_size(NULL, out_channer_nb, frame->nb_samples,AV_SAMPLE_FMT_S16, 1);
jbyteArray audio_sample_array = env->NewByteArray(size);
env->SetByteArrayRegion(audio_sample_array, 0, size, (const jbyte *) out_buffer);
env->CallVoidMethod(instance, audio_write, audio_sample_array, size);
env->DeleteLocalRef(audio_sample_array);
//fwrite(out_buffer, 1, out_buffer_size,fp_pcm);
}
av_free_packet(packet);
}
fclose(fp_pcm);
av_frame_free(&frame);
av_free(out_buffer);
swr_free(&swrContext);
avcodec_close(codecCtx);
avformat_close_input(&pFormatCtx);
}
}
这样手机就能播放出来