具体的demo代码放在码云:https://gitee.com/Lemniscate/audio_and_video_demo
解析的过程类似于上一节 分离MP4:https://www.jianshu.com/p/1ceef353ede0
步骤主要是 拿到音轨, 拿到format 信息 , 用MediaCodec 解析 并将数据喂到相对应的地方
以下是解析音频代码:
private class AudioThread extends Thread {
private AudioTrack audioTrack;
private int audioInputBufferSize;
private int audioTrackIndex;
@Override
public void run() {
MediaExtractor mediaExtractor = new MediaExtractor();
MediaCodec mediaCodec = null;
try {
mediaExtractor.setDataSource(inputFilePath);
audioTrackIndex = 0;
for (int i = 0; i < mediaExtractor.getTrackCount(); i++) {
MediaFormat trackFormat = mediaExtractor.getTrackFormat(i);
String string = trackFormat.getString(MediaFormat.KEY_MIME);
if (string.startsWith("audio/")) {
audioTrackIndex = i;
break;
}
}
MediaFormat trackFormat = mediaExtractor.getTrackFormat(audioTrackIndex);
mediaExtractor.selectTrack(audioTrackIndex);
int audioChannelCount = trackFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT);
int audioSampleRate = trackFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE);
int minBufferSize = AudioTrack.getMinBufferSize(audioSampleRate,
(audioChannelCount == 1 ? AudioFormat.CHANNEL_OUT_MONO : AudioFormat.CHANNEL_OUT_STEREO),
AudioFormat.ENCODING_PCM_16BIT);
int maxInputSize = trackFormat.getInteger(MediaFormat.KEY_MAX_INPUT_SIZE);
audioInputBufferSize = minBufferSize > 0 ? minBufferSize * 4 : maxInputSize;
int frameSizeInBytes = audioChannelCount * 2;
audioInputBufferSize = (audioInputBufferSize / frameSizeInBytes) * frameSizeInBytes;
audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, audioSampleRate,
(audioChannelCount == 1 ? AudioFormat.CHANNEL_OUT_MONO : AudioFormat.CHANNEL_OUT_STEREO),
AudioFormat.ENCODING_PCM_16BIT, minBufferSize * 4, AudioTrack.MODE_STREAM);
audioTrack.play();
mediaCodec = MediaCodec.createDecoderByType(trackFormat.getString(MediaFormat.KEY_MIME));
mediaCodec.configure(trackFormat, null, null, 0);
} catch (IOException e) {
e.printStackTrace();
}
if (mediaCodec == null) {
return;
}
mediaCodec.start();
ByteBuffer[] buffers = mediaCodec.getOutputBuffers();
int capacity = buffers[0].capacity();
if (capacity <= 0) {
capacity = audioInputBufferSize;
}
byte[] mAudioOutTempBuf = new byte[capacity];
MediaCodec.BufferInfo audioBufferInfo = new MediaCodec.BufferInfo();
ByteBuffer[] inputBuffers = mediaCodec.getInputBuffers();
ByteBuffer[] outputBuffers = mediaCodec.getOutputBuffers();
boolean isAudioEos = false;
long startMs = System.currentTimeMillis();
while (!Thread.interrupted()) {
if (!isPlaying) {
continue;
}
if (!isAudioEos) {
isAudioEos = putBufferToCoder(mediaExtractor, mediaCodec, inputBuffers);
}
int outputBufferIndex = mediaCodec.dequeueOutputBuffer(audioBufferInfo, TIMEOUT_US);
switch (outputBufferIndex) {
case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED:
MediaFormat format = mediaCodec.getOutputFormat();
audioTrack.setPlaybackRate(format.getInteger(MediaFormat.KEY_SAMPLE_RATE));
break;
case MediaCodec.INFO_TRY_AGAIN_LATER:
break;
case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED:
outputBuffers = mediaCodec.getOutputBuffers();
break;
default:
ByteBuffer outputBuffer = outputBuffers[outputBufferIndex];
sleepRender(audioBufferInfo, startMs);
if (audioBufferInfo.size > 0) {
if (mAudioOutTempBuf.length < audioBufferInfo.size) {
mAudioOutTempBuf = new byte[audioBufferInfo.size];
}
outputBuffer.position(0);
outputBuffer.get(mAudioOutTempBuf, 0, audioBufferInfo.size);
outputBuffer.clear();
if (audioTrack != null) {
// audioTrack.write(mAudioOutTempBuf, 0, audioBufferInfo.size);//用这个写法会导致少帧?
audioTrack.write(mAudioOutTempBuf, audioBufferInfo.offset, audioBufferInfo.offset + audioBufferInfo.size);
}
}
mediaCodec.releaseOutputBuffer(outputBufferIndex, false);
break;
}
if ((audioBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
//stream end
break;
}
}
mediaCodec.stop();
mediaCodec.release();
mediaExtractor.release();
audioTrack.stop();
audioTrack.release();
}
}
解析视频的代码:
private class VideoThread extends Thread {
@Override
public void run() {
MediaExtractor mediaExtractor = new MediaExtractor();
MediaCodec videoCodec = null;
try {
mediaExtractor.setDataSource(inputFilePath);
int videoTrackIndex = 0;
int trackCount = mediaExtractor.getTrackCount();
for (int i = 0; i < trackCount; i++) {
MediaFormat trackFormat = mediaExtractor.getTrackFormat(i);
if (trackFormat.getString(MediaFormat.KEY_MIME).startsWith("video/")) {
videoTrackIndex = i;
break;
}
}
//infomation
MediaFormat trackFormat = mediaExtractor.getTrackFormat(videoTrackIndex);
int width = trackFormat.getInteger(MediaFormat.KEY_WIDTH);
int height = trackFormat.getInteger(MediaFormat.KEY_HEIGHT);
float time = trackFormat.getLong(MediaFormat.KEY_DURATION) / 1000000;
mediaExtractor.selectTrack(videoTrackIndex);
videoCodec = MediaCodec.createDecoderByType(trackFormat.getString(MediaFormat.KEY_MIME));
videoCodec.configure(trackFormat, surfaceView.getHolder().getSurface(), null, 0);
} catch (IOException e) {
e.printStackTrace();
}
if (videoCodec == null) {
return;
}
videoCodec.start();
MediaCodec.BufferInfo videoBufferInfo = new MediaCodec.BufferInfo();
ByteBuffer[] inputBuffers = videoCodec.getInputBuffers();
boolean isVideoEos = false;
long startMs = System.currentTimeMillis();
while (!Thread.interrupted()) {
if (!isPlaying) {
continue;
}
//将资源传递到解码器
if (!isVideoEos) {
isVideoEos = putBufferToCoder(mediaExtractor, videoCodec, inputBuffers);
}
int outputBufferIndex = videoCodec.dequeueOutputBuffer(videoBufferInfo, TIMEOUT_US);
switch (outputBufferIndex) {
case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED:
break;
case MediaCodec.INFO_TRY_AGAIN_LATER:
break;
case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED:
//outputBuffers = videoCodec.getOutputBuffers();
break;
default:
//直接渲染到Surface时使用不到outputBuffer
//ByteBuffer outputBuffer = outputBuffers[outputBufferIndex];
//延时操作
//如果缓冲区里的可展示时间>当前视频播放的进度,就休眠一下
sleepRender(videoBufferInfo, startMs);
//渲染
videoCodec.releaseOutputBuffer(outputBufferIndex, true);
break;
}
if ((videoBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
//buffer stram end
break;
}
}
videoCodec.stop();
videoCodec.release();
mediaExtractor.release();
}
}