//将视频分离成无声视频和纯音频,再合成为原来的视频
* AudioRecord 录制音频bugger,可以直接将buffer保存为pcm的文件,手动加音频头变成wav的无损音频;也可以直接给muxer自动加头生成音频,也可以将bugger给mediacodec进行编码亚索,压缩后通过muxer生成体积小的音频
* Mediacodec 实现编解码,及实现buffer的压缩和解压
* Muxer 将buffer 进行混合封装,生成音频和视频,音频封装时自动加音频头;可以将音频buffer和视频buffer混合成视频,可以将音频buffer混合成混合成音频文件,可以将视频buffer混合成视频文件
* MediaExtractor 分离获取器,通过此类可以获取文件的音轨、视轨、字母轨等各种轨道和信息并获取buffer数据,目的就是buffer数据,有了buffer数据就可进行编码解码压缩混合成文件
* 使用AudioRecord录制的直接生成的是PCM(脉冲编码调制)文件(IO操作),该文件可以用AudioTrack进行播放但是普通播放器可能播放不来
* 可以添加文件头并修改为wav格式,就成了无损音频,大部分手机音乐播放器可以播放,可以使用MediaExtractor获取文件信息buffer
* 1、AudioRecord录制的pcm是无损的,Mediacodec使用的是H264编码解码,H264是MPEG-4标准所定义的最新编码格式,同时也是技术含量最高、代表最新技术水品的编码格式之一,标准格式是H.264,H264格式是经过有损压缩的,但在技术上尽可能做到了降低存储体积的情况下获得较好图像质量和低宽带图像快速传输。
* 文件格式、视频封装格式、视频编码方式区别:https://blog.csdn.net/electrombile/article/details/51067344
//不同文件保存方式情况下,各播放器播放测试
* muxer保存时不添加后缀名:muxer_video 手机选择视频打开可以播放;电脑使用windowmediaplayer打开,弹框:无法识别扩展名但仍然可以播放;muxer_audio 手机选择音频无法打开,用视频播放器可以打开;电脑使用windowmediaplayer打开,弹框:无法识别扩展名但仍然可以播放。
* file_audio 手机选择音频无法打开,用视频播放器可以打开;电脑使用windowmediaplayer打开,弹框:无法识别扩展名但仍然可以播放。
* 分离出音频时,如果给音频添加后缀名aac或者买a,则都可以用音频打开
* mediaFormat 的信息打印参见 https://www.jianshu.com/p/180f81390b58
private static finical String SDCARD_PATH = Environment.getExternalStorageDirectory().getPath() + "/Pictures/Screenshots";
//将视频分离为音频muxer_audio.m4a和无声视频muxer_vido.mp4
private void splitAudioAndVideo() {
MediaExtractor mediaExtractor = new MediaExtractor();
MediaFormat audioTrackFormat = null;
MediaFormat videoTrackFormat = null;
int audioTrackIndex = -1;
int vidoeTrackIndex = -1;
try {
mediaExtractor.setDataSource(SDCARD_PATH + "/input.mp4");
int trackCount = mediaExtractor.getTrackCount();
//分别获取音轨视轨格式和对应的轨道编号
for(int i = 0; i
String keyMime = trackFormat.getString(MediaFormat.KEY_MIME);
if(keyMime.startsWith("video")) {
videoTrackFormat = trackFormat;
vidoeTrackIndex = i;
} else if (keyMime.startsWith("audio")) {
audioTrackFormat = trackFormat;
audioTrackIndex = i;
}
}
//读取input.mp4中的的音频数据,并用mediacodec进行编码生成音频,生成的muxer_audio文件,不加后缀名的话,无法使用手机的音乐播放器打开,可以用视频播放器打开,电脑上用windowsmediaplayer也可以打开
//如果生成的音频是加后缀名的如muxer_audio.aac或者muxer_audio.m4a,可以用手机的音乐播放器软件打开
mediaExtractor.selectTrack(audioTrackIndex);
MediaMuxer audioMediaMuxer = new MediaMuxer(SDCARD_PATH + "/muxer_audio.m4a", MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
int trackIndex = audioMediaMuxer.addTrack(audioTrackFormat);
audioMediaMuxer.start();
ByteBuffer maxInputByteBuffer = ByteBuffer.allocate(videoTrackFormat.getlnteger(MediaFormat.KEY_MAX_INPUT_SIZE));
MediaCodec.Bufferlnfo audioBufferlnfo = new MediaCodec.BufferlnfoO;
while (true) {
int readSampleData = mediaExtractor.readSampleData(m axInputByteBuffer, 0);
if (readSampleData < 0) {
mediaExtractor.unselectTrack(audioTracklndex);
break;
}
audioBufferlnfo.size = readSampleData;
audioBufferlnfo.offset = 0;
audioBufferlnfo.presentationTimeUs = mediaExtractor.getSampleTime();
audioBufferlnfo.flags = mediaExtractor.getSampleFlags();
audioMediaMuxer.writeSampleData(tracklndex, maxInputByteBuffer, audioBufferlnfo);
mediaExtractor.advance();
}
if (audioMediaMuxer != null) {
audioMediaMuxer.stop();
audioMediaMuxer.release();
}
//读取input.mp4中的视频数据,并用mediacodec进行编码成不带声音的视频
mediaExtractor.selectTrack(videoTrackIndex);
MediaMuxer audioMediaMuxer = new MediaMuxer(SDCARD_PATH + "/muxer_video.mp4", MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
int i = videoMediaMuxer.addTrack(videoTrackFormat);
videoMediaMuxer.start();
ByteBuffer allocate = ByteBuffer.allocate(videoTrackFormat.getlnteger(MediaFormat.KEY_MAX_INPUT_SIZE));
MediaCodec.Bufferlnfo bufferlnfo = new MediaCodec.Bufferlnfo();
while (true) {
int readSampleData = mediaExtractor.readSampleData(allocate, 0);
if (readSampleData < 0) {
mediaExtractor.unselectTrack( videoTrackIndex);
break;
}
bufferlnfo.flags = mediaExtractor.getSampleFlags();
bufferlnfo.presentationTimeUs = mediaExtractor.getSampleTime();
bufferlnfo.offset = 0;
bufferlnfo.size = readSampleData;
videoMediaMuxer.writeSampleData( i, allocate, bufferlnfo);
mediaExtractor.advance();
}
videoMediaMuxer.stop();
videoMediaMuxer.release();
mediaExtractor.release();
Toast.makeText(this,"音频和视频分离成功", Toast_ENGTH_SHORT).show();
} catch (lOException e) {
e.printStackTraceO;
}
}
//将分离的音频音频muxer_audio.m4a和无声视频muxer_vido.mp4合成为原来的视频
private void combineAudioAndVideo() {
try {
//获取音频格式及音轨
MediaExtractor audioMediaExtractor = new MediaExtractor();
audioMediaExtractor.setDataSource(SDCARD_PATH + "/muxer_audio.m4a");
int audioTrackConut = audioMediaExtractor.getTrackCount();
MediaFormat audioTrackFormat = null;
int audioExtractorIndex = 0;
for (int i = 0; i
String keyMime = audioMediaExtractor.getString(MediaFormat.KEY_MIME);
if (keyMime.startsWith("audio")) {
audioExtractorIndex = i;
}
}
audioMediaExtractor.selectTrack(audioExtractorIndex);
//获取视频格式及视轨
MediaExtractor videoMediaExtractor = new MediaExtractor();
videoMediaExtractor.setDataSource(SDCARD_PATH + "/muxer_video.mp4");
int videoTrackConut = videoMediaExtractor.getTrackCount();
MediaFormat videoTrackFormat = null;
int videoExtractorIndex = 0;
for (int i = 0; i
String keyMime = videoMediaExtractor.getString(MediaFormat.KEY_MIME);
if (keyMime.startsWith("video")) {
videoExtractorIndex = i;
}
}
videoMediaExtractor.selectTrack(videoExtractorIndex);
//开始进行混合
MediaMuxer mediaMuxer = new MediaMuxer(SDCARD_PATH + "/muxer_comnine.mp4", MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
int audioTrackIndex = mediaMuxer.addTrack(audioTrackFormat);
int videoTrackIndex = mediaMuxer.addTrack(videoTrackFormat);
audioMediaMuxer.start();
//视频的音轨、视轨分辨处理,先将音轨混合到视频文件中,混合结束后再混合视轨
//也可以音轨一帧视轨一帧交错混合,MediaMuxer在writeSampleData时,传入的轨道编号和格式信息不同,就会将不同的数据混合到相应的轨道
//交错处理
ByteBuffer audioByteBuffer = ByteBuffer.allocate(audioTrackFormat.getlnteger(MediaFormat.KEY_MAX_INPUT_SIZE));
ByteBuffer videoByteBuffer = ByteBuffer.allocate(videoTrackFormat.getlnteger(MediaFormat.KEY_MAX_INPUT_SIZE));
MediaCodec.BufferInfo audioBufferInfo = MediaCodec.BufferInfo();
MediaCodec.BufferInfo videoBufferInfo = MediaCodec.BufferInfo();
while(true) {
int readSampleDataAudio = audioMediaExtractor.readSampleData(audioByteBuffer,0);
if(readSampleDataAudio > 0) {
audioBufferlnfo.size = readSampleDataAudio;
audioBufferlnfo.flags = audioMediaExtractor.getSampleFlags();
audioBufferlnfo.offset = 0;
audioBufferlnfo.presentationTimeUs = audioMediaExtractor.getSampleTime();
audioMediaMuxer.writeSampleData(audioTrackIndex, audioByteBuffer, audioBufferlnfo);
audioMediaExtractor.advance();
}
int readSampleDataVideo = videoMediaExtractor.readSampleData(videoByteBuffer,0);
if(readSampleDataVideo > 0) {
videoBufferInfo.size = readSampleDataVideo;
videoBufferInfo.flags = videoMediaExtractor.getSampleFlags();
videoBufferInfo.offset = 0;
videoBufferInfo.presentationTimeUs = videoMediaExtractor.getSampleTime();
videoBufferInfo.writeSampleData(videoTrackIndex, videoByteBuffer, videoBufferInfo);
videoMediaExtractor.advance();
}
if (readSampleDataAudio < 0 && readSampleDataVideo < 0) {
audioMediaExtractor.unselectTrack(videoExtractorIndex);
videoMediaExtractor.unselectTrack(videoExtractorIndex);
break;
}
}
mediaMuxer.stop();
mediaMuxer.release();
audioMediaExtractor.release();
videoMediaExtractor.release();
Toast.makeText(this,"分离的视频音频合成完成", Toast.LENGTH_SHOT).show();
} catch(IOException e) {
e.printStackTrace();
}
}
//同时分离出音频和视频file,音频添加头可以播放,视频不能播放
private void exactorMediaToFile() {
FileOutputStream vos = null;
FileOutputStream aos = null;
try {
File videoFile = new File(SDCARD_PATH, "file_video.mp4");
if(!videoFile.exists()) {
videoFile.createNewFile();
}
File audioFile = new File(SDCARD_PATH, "file_audio.acc");
vos = new FileOutputStream(videoFile);
aos = new FileOutputStream(audioFile);
mediaExtractor.setDataSource(SDCARD_PATH+"/input.mp4");
int trackCount = mediaExtractor.getTrackCount;
int audioTrackIndex = -1;
int vidoeTrackIndex = -1;
for(int i = 0; i
String mineType = trackFormat.getString(MediaFormat.KEY_MIME);
if(mineType.startsWith("video")) {
vidoeTrackIndex = i;
}
if (mineType.startsWith("audio")) {
audioTrackIndex = i;
}
ByteBuffer byteBuffer = ByteBuffer.allocate(500*1024);
mediaExtractor.selectTrack(videoTrackIndex);
while(true) {
int readSampleCount = mediaExtractor.readSampleData(byteBuffer,0);
if(readSampleCount <0 ) {
break;
}
byte[] buffer = new byte[readSampleCount];
byteBuffer.get(buffer);
vos.write(buffer);
byteBuffer.clear();
mediaExtractor.advance();
}
mediaExtractor.selectTrack(audioTrackIndex);
while(true) {
int readSampleCount = mediaExtractor.readSampleData(byteBuffer,0);
if(readSampleCount <0 ) {
break;
}
byte[] buffer = new byte[readSampleCount];
byteBuffer.get(buffer);
//此处添加文件头,音频才可以播放,直接write buffer的文件不能播放
byte[] newbuff = new byte[readSampleCount + 7];
addADTStoPacket(newbuff, readSampleCount+7);
System.arraycopy(buffer, 0, newbuff, 7, readSampleCount);
aos.write(newbuff);
byteBuffer.clear();
mediaExtractor.advance();
}
Toast.makeText(this,"分离视频通过IO保存完成", Toast.LENGTH_SHOT).show();
} catch(Exception e) {
e.printStackTrace();
} finally {
mediaExtractor.release();
try {
vos.close();
} catch(Exception e) {
e.printStackTrace();
}
}
}
}
private static void addADTStoPacket(byte[] packet, int packetLen) {
int profile = 2; // AAC LC
int freqIdx = 3; // 8表示16000,取待定
int chanCfg = 1; // 音频声道数
// fill in ADTS data
packet[0] = (byte) 0xFF;
packet[1] = (byte) 0xF1;
packet[2] = (byte) (((profile - 1) << 6) + (freqIdx << 2) + (chanCfg >> 2));
packet[3] = (byte) (((chanCfg & 3) << 6) + (packetLen >> 11));
packet[4] = (byte) ((packetLen & 0x7FF) >> 3);
packet[5] = (byte) (((packetLen & 7) << 5) + 0x1F);
}