目录

前言

提取前提----了解提取需要用到的工具类:MediaExtractor、MediaCodec

1.MediaExtractor

2.MediaCodec

3.释放

具体提取转码代码

调用

Demo下载

CSDN下载地址

github地址


前言

1.该文章系列是视频配音,涉及到把背景音频从视频中提取出来、背景音频根据台词时间戳进行截断、截断的背景和录音混合、混合音频进行拼接合并、合并后的音频转码为m4a格式、将m4a格式音频和视频合并生成mp4视频文件

2.使用的视频格式:MP4     音频格式以及编码:m4a(aac)    采样率:44100   声道:双声道  采样位数:16

3.文章使用的测试音频为http网络视频,如果不是在视频丢失的情况下无法读取,请阅读一下该文章:Http网络请求无响应解决办法

4.Android 音视频配音之音频提取、截断、混音、合并、合成(二)——将提取的PCM根据时间戳截断

提取前提----了解提取需要用到的工具类:MediaExtractor、MediaCodec

1.MediaExtractor

  • 初始化
mMediaExtractor = new MediaExtractor();
  • 设置数据源
//可以是本地文件或者网络文件
mMediaExtractor.setDataSource("xxx.mp4");
  • 获取视频总轨道以及音频所在的轨道索引
int count = mMediaExtractor.getTrackCount();
for (int i = 0; i < count; i++) {MediaFormat format = mMediaExtractor.getTrackFormat(i);//获取 mime 类型String mime = format.getString(MediaFormat.KEY_MIME);// 视频轨if (mime.startsWith("video")) {mVideoTrackId = i;mVideoFormat = format;} else if (mime.startsWith("audio")) {//音频轨mAudioTrackId = i;mAudioFormat = format;}
}
  • 读取音频的数据

int bufferSize = mMediaExtractor.readSampleData(inputBuffer, 0);
  • 读取下一帧数据

//指向下一帧
mMediaExtractor.advance();

2.MediaCodec

  • 初始化
MediaCodec mMediaDecode = MediaCodec.createDecoderByType(mime);
mMediaDecode.configure(mediaFormat, null, null, 0);//当解压的时候最后一个参数为0
mMediaDecode.start();//开始,进入runnable状态
  • 输入数据到解码器
//将输入缓存放入MediaCodec中
mMediaDecode.queueInputBuffer(inputBufferIndex, 0, bufferSize, time, 0);
  • 输出解码之后的数据
 //获取输出缓存,需要传入MediaCodec.BufferInfo 用于存储ByteBuffer信息
int outputBufferIndex = mMediaDecode.dequeueOutputBuffer(bufferInfo, kTimes);

3.释放

//释放资源
mMediaDecode.stop();
mMediaDecode.release();
mMediaExtractor.release();

具体提取转码代码

package com.pgc.videodubdemo.utils;import android.media.AudioFormat;
import android.media.MediaCodec;
import android.media.MediaExtractor;
import android.media.MediaFormat;
import android.os.Handler;
import android.text.TextUtils;import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;/*** @author Created by PengGuiChu on 2021/1/6 11:57.* @explain  视频提取音频帮助类*/
public class VideoHelper {private static final String WAV=".wav";private static final String PCM=".pcm";//类型private static final String mime = "audio/mp4a-latm";private Handler handler;public VideoHelper(Handler handler) {this.handler = handler;}/**** @param dirPath 保存解码后的文件路径* @param decodePath 需要解码的MP4文件路径* @param audioName  音频名字* @param isWav 是否是wav格式   wav可以播放  pcm无法直接播放(裁剪格式)*/public void initDecodeVideoToAudio(String dirPath,String decodePath, String audioName,boolean isWav) throws IOException {if (handler!=null){handler.sendEmptyMessage(3);}String suffixName;if (isWav){suffixName=WAV;}else{suffixName=PCM;}MediaExtractor mediaExtractor=new MediaExtractor();//设置资源mediaExtractor.setDataSource(decodePath);//获取含有音频的MediaFormatMediaFormat mediaFormat = createMediaFormat(mediaExtractor);if (mediaFormat==null){if (handler!=null){handler.sendEmptyMessage(0);}return;}int KEY_SAMPLE_RATE=mediaFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE);int KEY_CHANNEL_COUNT=mediaFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT);int KEY_PCM_ENCODING= AudioFormat.ENCODING_PCM_16BIT;if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N&&mediaFormat.containsKey(MediaFormat.KEY_PCM_ENCODING)) {KEY_PCM_ENCODING=mediaFormat.getInteger(MediaFormat.KEY_PCM_ENCODING);}int bitNumber;switch (KEY_PCM_ENCODING) {case AudioFormat.ENCODING_PCM_FLOAT:bitNumber = 32;break;case AudioFormat.ENCODING_PCM_8BIT:bitNumber = 8;break;case AudioFormat.ENCODING_PCM_16BIT:default:bitNumber = 16;break;}if (handler!=null){handler.sendEmptyMessage(1);}MediaCodec mMediaDecode = MediaCodec.createDecoderByType(mime);mMediaDecode.configure(mediaFormat, null, null, 0);//当解压的时候最后一个参数为0mMediaDecode.start();//开始,进入runnable状态ByteBuffer[] inputBuffers = mMediaDecode.getInputBuffers();ByteBuffer[] outputBuffers = mMediaDecode.getOutputBuffers();MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();ByteArrayOutputStream byteArrayOutputStream=new ByteArrayOutputStream();decode(mMediaDecode,inputBuffers,mediaExtractor,outputBuffers,bufferInfo,byteArrayOutputStream);byte[] bytes=byteArrayOutputStream.toByteArray();FileOutputStream fileOutputStream=new FileOutputStream(new File(dirPath,audioName+suffixName));if (isWav){convertPcmToWav(fileOutputStream,bytes,KEY_SAMPLE_RATE,KEY_CHANNEL_COUNT,bitNumber);}else{fileOutputStream.write(bytes);}fileOutputStream.close();byteArrayOutputStream.close();if (handler!=null){handler.sendEmptyMessage(2);}}private void decode(MediaCodec mMediaDecode, ByteBuffer[] inputBuffers, MediaExtractor mMediaExtractor, ByteBuffer[] outputBuffers, MediaCodec.BufferInfo bufferInfo, ByteArrayOutputStream byteArrayOutputStream) {boolean inputSawEos = false;boolean outputSawEos = false;long kTimes = 5000;//循环时间while (!outputSawEos) {if (!inputSawEos) {//每5000毫秒查询一次int inputBufferIndex = mMediaDecode.dequeueInputBuffer(kTimes);//输入缓存index可用if (inputBufferIndex >= 0) {//获取可用的输入缓存ByteBuffer inputBuffer = inputBuffers[inputBufferIndex];//从MediaExtractor读取数据到输入缓存中,返回读取长度int bufferSize = mMediaExtractor.readSampleData(inputBuffer, 0);if (bufferSize <= 0) {//已经读取完//标志输入完毕inputSawEos = true;//做标识mMediaDecode.queueInputBuffer(inputBufferIndex, 0, 0, kTimes, MediaCodec.BUFFER_FLAG_END_OF_STREAM);} else {long time = mMediaExtractor.getSampleTime();//将输入缓存放入MediaCodec中mMediaDecode.queueInputBuffer(inputBufferIndex, 0, bufferSize, time, 0);//指向下一帧mMediaExtractor.advance();}}}//获取输出缓存,需要传入MediaCodec.BufferInfo 用于存储ByteBuffer信息int outputBufferIndex = mMediaDecode.dequeueOutputBuffer(bufferInfo, kTimes);if (outputBufferIndex >= 0) {if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {mMediaDecode.releaseOutputBuffer(outputBufferIndex, false);continue;}//有输出数据if (bufferInfo.size > 0) {//获取输出缓存ByteBuffer outputBuffer = outputBuffers[outputBufferIndex];//设置ByteBuffer的position位置outputBuffer.position(bufferInfo.offset);//设置ByteBuffer访问的结点outputBuffer.limit(bufferInfo.offset + bufferInfo.size);byte[] targetData = new byte[bufferInfo.size];//将数据填充到数组中outputBuffer.get(targetData);try {byteArrayOutputStream.write(targetData);} catch (IOException e) {e.printStackTrace();}}//释放输出缓存mMediaDecode.releaseOutputBuffer(outputBufferIndex, false);//判断缓存是否完结if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {outputSawEos = true;}} else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {outputBuffers = mMediaDecode.getOutputBuffers();}}//释放资源mMediaDecode.stop();mMediaDecode.release();mMediaExtractor.release();}private MediaFormat createMediaFormat(MediaExtractor mediaExtractor) {//获取文件的轨道数,做循环得到含有音频的mediaFormatfor (int i = 0; i < mediaExtractor.getTrackCount(); i++) {MediaFormat mediaFormat = mediaExtractor.getTrackFormat(i);//MediaFormat键值对应String mime = mediaFormat.getString(MediaFormat.KEY_MIME);if (!TextUtils.isEmpty(mime) && mime.contains(VideoHelper.mime)) {mediaExtractor.selectTrack(i);return mediaFormat;}}return null;}/*** PCM文件转WAV文件** @param out            输出WAV文件路径* @param sampleRate     采样率,例如15000* @param channels       声道数 单声道:1或双声道:2* @param bitNum         采样位数,8或16* @param totalAudio  字节数组*/private   void convertPcmToWav(FileOutputStream out,byte[] totalAudio,int sampleRate,int channels, int bitNum) {try {//采样字节byte率long byteRate = sampleRate * channels * bitNum / 8;//PCM文件大小long totalAudioLen = totalAudio.length;//总大小,由于不包括RIFF和WAV,所以是44 - 8 = 36,在加上PCM文件大小long totalDataLen = totalAudioLen + 36;writeWaveFileHeader(out, totalAudioLen, totalDataLen, sampleRate, channels, byteRate);out.write(totalAudio, 0, totalAudio.length);} catch (Exception e) {e.printStackTrace();if (handler!=null){handler.sendEmptyMessage(0);}} finally {if (out != null) {try {out.close();} catch (IOException e) {e.printStackTrace();}}}}/*** 输出WAV文件** @param out           WAV输出文件流* @param totalAudioLen 整个音频PCM数据大小* @param totalDataLen  整个数据大小* @param sampleRate    采样率* @param channels      声道数* @param byteRate      采样字节byte率* @throws IOException*/private void writeWaveFileHeader(FileOutputStream out, long totalAudioLen,long totalDataLen, int sampleRate, int channels, long byteRate) throws IOException {byte[] header = new byte[44];header[0] = 'R'; // RIFFheader[1] = 'I';header[2] = 'F';header[3] = 'F';header[4] = (byte) (totalDataLen & 0xff);//数据大小header[5] = (byte) ((totalDataLen >> 8) & 0xff);header[6] = (byte) ((totalDataLen >> 16) & 0xff);header[7] = (byte) ((totalDataLen >> 24) & 0xff);header[8] = 'W';//WAVEheader[9] = 'A';header[10] = 'V';header[11] = 'E';//FMT Chunkheader[12] = 'f'; // 'fmt 'header[13] = 'm';header[14] = 't';header[15] = ' ';//过渡字节//数据大小header[16] = 16; // 4 bytes: size of 'fmt ' chunkheader[17] = 0;header[18] = 0;header[19] = 0;//编码方式 10H为PCM编码格式header[20] = 1; // format = 1header[21] = 0;//通道数header[22] = (byte) channels;header[23] = 0;//采样率,每个通道的播放速度header[24] = (byte) (sampleRate & 0xff);header[25] = (byte) ((sampleRate >> 8) & 0xff);header[26] = (byte) ((sampleRate >> 16) & 0xff);header[27] = (byte) ((sampleRate >> 24) & 0xff);//音频数据传送速率,采样率*通道数*采样深度/8header[28] = (byte) (byteRate & 0xff);header[29] = (byte) ((byteRate >> 8) & 0xff);header[30] = (byte) ((byteRate >> 16) & 0xff);header[31] = (byte) ((byteRate >> 24) & 0xff);// 确定系统一次要处理多少个这样字节的数据,确定缓冲区,通道数*采样位数header[32] = (byte) (channels * 16 / 8);header[33] = 0;//每个样本的数据位数header[34] = 16;header[35] = 0;//Data chunkheader[36] = 'd';//dataheader[37] = 'a';header[38] = 't';header[39] = 'a';header[40] = (byte) (totalAudioLen & 0xff);header[41] = (byte) ((totalAudioLen >> 8) & 0xff);header[42] = (byte) ((totalAudioLen >> 16) & 0xff);header[43] = (byte) ((totalAudioLen >> 24) & 0xff);out.write(header, 0, 44);}
}

调用

 @OnClick(R.id.start_bt)public void onViewClicked() {new Thread(()->{try {videoHelper.initDecodeVideoToAudio(dirPath,videoUrl,"test",false);} catch (IOException e) {e.printStackTrace();handler.sendEmptyMessage(0);}}).start();}

Demo下载

CSDN下载地址

github地址

Android 音视频配音之音频提取、截断、混音、合并、合成(一)——从视频中提取音频文件相关推荐

  1. Android音频焦点及混音策略

    1.前言 1.1 音频焦点官方解读 两个或两个以上的 Android 应用可同时向同一输出流播放音频.系统会将所有音频流混合在一起.虽然这是一项出色的技术,但却会给用户带来很大的困扰.为了避免所有音乐 ...

  2. java sound 混音_iOS音频编程之混音

    title: iOS音频编程之混音 date: 2017-04-19 tags: Audio Unit,AUGraph, Mixer,混音 博客地址 iOS音频编程之混音 需求:多个音频源混合后输出, ...

  3. 【视频】国产电子核混音|Lost In Summer - Release The Soul |混音母带处理 by JeromeAlanChan

    大家好,我是Jerome,欢迎来到MZD Studios混音之道. 2018年末后期混音制作的一首国产电子核,效果怎么样?视频看起. [B站视频请戳: Jerome-Alan-Chan] 高音质请到网 ...

  4. 音视频5.4——两个MP3混音合成一个MP3

    音视频开发路线: Android 音视频开发入门指南_Jhuster的专栏的技术博客_51CTO博客_android 音视频开发入门 demo地址: GitHub - wygsqsj/videoPat ...

  5. Android上一种效果奇好的混音方法介绍

    本文将对几种音频混音的方法进行详细的介绍和比较,读完之后你应该可以对混音有个基本的认识,针对不同情形知道应该采用哪种具体的处理方法了. 如果对音频的一些基础知识还不是很了解的建议先去阅读一下上一篇文章 ...

  6. 混音机java通用版下载_Mixer混音台下载_Mixer混音台官方下载-太平洋下载中心

    Mixer混音台是一款功能十分强大的DJ混音软件,下载使用Mixer混音台中文软件可以让您对音乐的mix混响音质等效果进行强大的操作,马上下载Mixer混音台进行DJ mix混响吧. 软件截图1 软件 ...

  7. 软件混音 测试版,CBL E-Mix Pro Edition(DJ混音软件)

    CBL E-Mix Pro Edition内置多种混音工具,软件提供了两个操作界面,分别是混合器调整界面和均衡器界面,可以将不同的歌曲混合到一个音频文件上,混合的声音参数可以在数据库配置,也可以将视频 ...

  8. python从字符串中提取数字并转换为相应数据类型_python从PDF中提取数据的示例

    01 前言 数据是数据科学中任何分析的关键,大多数分析中最常用的数据集类型是存储在逗号分隔值(csv)表中的干净数据.然而,由于可移植文档格式(pdf)文件是最常用的文件格式之一,因此每个数据科学家都 ...

  9. 音频源代码_使用开放源代码从丢失的格式中恢复音频

    音频源代码 早在2000年代初,我们就做出了一项家庭决策,决定升级客厅音响. 当时的设备是基于我20年前刚获得稳定的大学后收入时购买的一系列齿轮的. 早期的收藏最好被描述为"工业时尚&quo ...

  10. php 提取文字,如何使用PHP从word文档中提取文本内容?

    我想用PHP从word文档中提取文本内容. 我在Microsoft Word for Mac 2011中创建了一个新的单词文档. 编辑:也通过在Windows 7中的Microsoft Word中创建 ...

最新文章

  1. Educational Codeforces Round 39 B Weird Subtraction Process
  2. 机器学习(10)随机森林(预测泰坦尼克号旅客存活率)
  3. 对象的浅克隆与深克隆
  4. (软件工程复习核心重点)第八章面向对象方法学-第三节:面向对象建模之对象模型
  5. volatile深入
  6. 解决AutoCAD acmgd.dll ARX命令中发现异常
  7. 前端开发者必备google插件
  8. Java突击学习 Day1
  9. Luogu1502 窗口的星星
  10. 如何使用CANape实现XCP/CCP“Measurement测量”和“Calibration标定”变量
  11. 捷信达酒店管理系统使用说明-前言
  12. 极路由 斐讯K2 Newifi 华硕固件 实现ipv6穿透方法
  13. 【论文投稿】电子信息类中文权威期刊(SCIEI)投稿攻略
  14. 中国工程咨询行业十四五投资机会与运营风险评估报告2022-2028年
  15. 主引导记录(MBR)
  16. 十分钟文档化你的C++代码——DoxyGen
  17. 泛型中extends和super的区别
  18. [HR规划]什么是人力资源规划?(zt)
  19. Promise is a promise
  20. 【日常记录】小米笔记本蓝屏拆机维修(错误信息:WHEA_UNCORRECTABLE_ERROR)

热门文章

  1. express实现图片上传
  2. 3288 android5.1 编译,【DLT-RK3288试用】8. RK3288 编译 Android 5.1 源码
  3. 【Python】检测下载不完整、半截灰色的JPG、JPEG、PNG图片脚本
  4. DLL load failed while importing _cvxcore解决办法
  5. 5G将又是一个失败的存在
  6. 2021年中国网上办理车辆和驾驶证业务情况:网上办理车辆和驾驶证相关业务6769万次其中,网上发放临时号牌2043万副[
  7. 贵州大学计算机研究生排名,贵大计算机研究生怎么样?
  8. oracle获取每月的第一天和最后一天
  9. 根据三角形三边长求面积 c++
  10. 计算机的运作流程的个人感想