使用 MediaCodec 进行转码会产生绿色且混乱的视频
Transcoding with MediaCodec results in green and messed up video
我正在尝试通过使用 MediaCodec 对视频进行解码和重新编码来减小视频文件的大小。但是,muxer生成的新视频文件都是messed up。你能告诉我我做错了什么吗?
我的代码是从这个 CTS 修改而来的,有两个主要区别:
- 我在带有回调的异步模式下使用编解码器。
- 我没有使用助手 类,而是将 encoder.createInputSurface() 生成的表面直接传递给 decoder.configure()。
我试图通过将 surfaceView.getHolder().getSurface() 传递给 decoder.configure() 将视频解码为 SurfaceView,并且视频播放正确。我认为 encoder/muxer 部分可能有问题。
这是我的代码:
解码器回调:
private class VideoDecodeCallBack extends MediaCodec.Callback {
@Override
public void onInputBufferAvailable(MediaCodec codec, int index) {
ByteBuffer buffer = codec.getInputBuffer(index);
if (extractorDone) {
return;
}
int size = videoExtractor.readSampleData(buffer, 0);
long pts = videoExtractor.getSampleTime();
if (size >= 0) {
codec.queueInputBuffer(index, 0, size, pts, videoExtractor.getSampleFlags());
}
extractorDone = !videoExtractor.advance();
if (extractorDone) {
codec.queueInputBuffer(index, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
}
}
@Override
public void onOutputBufferAvailable(
MediaCodec codec, int index, MediaCodec.BufferInfo info) {
if ((info.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {
codec.releaseOutputBuffer(index, false);
return;
}
boolean render = info.size != 0;
codec.releaseOutputBuffer(index, render);
if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
videoEncoder.signalEndOfInputStream();
}
}
@Override
public void onError(
MediaCodec codec, MediaCodec.CodecException e) {
}
@Override
public void onOutputFormatChanged(
MediaCodec codec, MediaFormat format) {
}
编码器回调:
private class VideoEncodeCallBack extends MediaCodec.Callback {
@Override
public void onInputBufferAvailable(MediaCodec codec, int index) {
}
@Override
public void onOutputBufferAvailable(
MediaCodec codec, int index, MediaCodec.BufferInfo info) {
ByteBuffer buffer = codec.getOutputBuffer(index);
if ((info.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {
codec.releaseOutputBuffer(index, false);
return;
}
if (info.size != 0) {
muxer.writeSampleData(outputVideoTrack, buffer, info);
}
if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
muxer.stop();
muxer.release();
}
codec.releaseOutputBuffer(index, false);
}
@Override
public void onError(
MediaCodec codec, MediaCodec.CodecException e) {
}
@Override
public void onOutputFormatChanged(
MediaCodec codec, MediaFormat format) {
outputVideoTrack = muxer.addTrack(format);
muxer.start();
}
}
我如何配置编解码器:
public void configure(String input, String output) throws IOException {
// Extractors
videoExtractor = new MediaExtractor();
videoExtractor.setDataSource(input);
int videoTrack = getAndSelectVideoTrackIndex(videoExtractor);
MediaFormat videoFormat = videoExtractor.getTrackFormat(videoTrack);
String mime = videoFormat.getString(MediaFormat.KEY_MIME);
// Codecs
videoDecoder = MediaCodec.createDecoderByType(mime);
videoEncoder = MediaCodec.createEncoderByType(mime);
videoDecoder.setCallback(new VideoDecodeCallBack(), videoDecodeThreadHandler);
videoEncoder.setCallback(new VideoEncodeCallBack(), videoEncodeThreadHandler);
videoEncoder.configure(createOutputFormat(), null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
Surface surface = videoEncoder.createInputSurface();
videoDecoder.configure(videoFormat, surface, null, 0);
// Muxer
muxer = new MediaMuxer(output, OutputFormat.MUXER_OUTPUT_MPEG_4);
}
需要的输出格式:
private static MediaFormat createOutputFormat() {
MediaFormat result = MediaFormat.createVideoFormat("video/avc", 540, 960);
result.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
result.setInteger(MediaFormat.KEY_BIT_RATE, 1300000);
result.setInteger(MediaFormat.KEY_FRAME_RATE, 30);
result.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1);
return result;
}
完整代码为here
我也有这个问题。我的猜测是,当解码器直接渲染到编码器的输入表面时,Android 无法重新缩放帧。
解决方法是使用 openGL 进行缩放。
(我没有 运行 这个项目,但我已经实现了类似的东西,所以它应该可以工作)
我正在尝试通过使用 MediaCodec 对视频进行解码和重新编码来减小视频文件的大小。但是,muxer生成的新视频文件都是messed up。你能告诉我我做错了什么吗?
我的代码是从这个 CTS 修改而来的,有两个主要区别:
- 我在带有回调的异步模式下使用编解码器。
- 我没有使用助手 类,而是将 encoder.createInputSurface() 生成的表面直接传递给 decoder.configure()。
我试图通过将 surfaceView.getHolder().getSurface() 传递给 decoder.configure() 将视频解码为 SurfaceView,并且视频播放正确。我认为 encoder/muxer 部分可能有问题。
这是我的代码:
解码器回调:
private class VideoDecodeCallBack extends MediaCodec.Callback {
@Override
public void onInputBufferAvailable(MediaCodec codec, int index) {
ByteBuffer buffer = codec.getInputBuffer(index);
if (extractorDone) {
return;
}
int size = videoExtractor.readSampleData(buffer, 0);
long pts = videoExtractor.getSampleTime();
if (size >= 0) {
codec.queueInputBuffer(index, 0, size, pts, videoExtractor.getSampleFlags());
}
extractorDone = !videoExtractor.advance();
if (extractorDone) {
codec.queueInputBuffer(index, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
}
}
@Override
public void onOutputBufferAvailable(
MediaCodec codec, int index, MediaCodec.BufferInfo info) {
if ((info.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {
codec.releaseOutputBuffer(index, false);
return;
}
boolean render = info.size != 0;
codec.releaseOutputBuffer(index, render);
if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
videoEncoder.signalEndOfInputStream();
}
}
@Override
public void onError(
MediaCodec codec, MediaCodec.CodecException e) {
}
@Override
public void onOutputFormatChanged(
MediaCodec codec, MediaFormat format) {
}
编码器回调:
private class VideoEncodeCallBack extends MediaCodec.Callback {
@Override
public void onInputBufferAvailable(MediaCodec codec, int index) {
}
@Override
public void onOutputBufferAvailable(
MediaCodec codec, int index, MediaCodec.BufferInfo info) {
ByteBuffer buffer = codec.getOutputBuffer(index);
if ((info.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {
codec.releaseOutputBuffer(index, false);
return;
}
if (info.size != 0) {
muxer.writeSampleData(outputVideoTrack, buffer, info);
}
if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
muxer.stop();
muxer.release();
}
codec.releaseOutputBuffer(index, false);
}
@Override
public void onError(
MediaCodec codec, MediaCodec.CodecException e) {
}
@Override
public void onOutputFormatChanged(
MediaCodec codec, MediaFormat format) {
outputVideoTrack = muxer.addTrack(format);
muxer.start();
}
}
我如何配置编解码器:
public void configure(String input, String output) throws IOException {
// Extractors
videoExtractor = new MediaExtractor();
videoExtractor.setDataSource(input);
int videoTrack = getAndSelectVideoTrackIndex(videoExtractor);
MediaFormat videoFormat = videoExtractor.getTrackFormat(videoTrack);
String mime = videoFormat.getString(MediaFormat.KEY_MIME);
// Codecs
videoDecoder = MediaCodec.createDecoderByType(mime);
videoEncoder = MediaCodec.createEncoderByType(mime);
videoDecoder.setCallback(new VideoDecodeCallBack(), videoDecodeThreadHandler);
videoEncoder.setCallback(new VideoEncodeCallBack(), videoEncodeThreadHandler);
videoEncoder.configure(createOutputFormat(), null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
Surface surface = videoEncoder.createInputSurface();
videoDecoder.configure(videoFormat, surface, null, 0);
// Muxer
muxer = new MediaMuxer(output, OutputFormat.MUXER_OUTPUT_MPEG_4);
}
需要的输出格式:
private static MediaFormat createOutputFormat() {
MediaFormat result = MediaFormat.createVideoFormat("video/avc", 540, 960);
result.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
result.setInteger(MediaFormat.KEY_BIT_RATE, 1300000);
result.setInteger(MediaFormat.KEY_FRAME_RATE, 30);
result.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1);
return result;
}
完整代码为here
我也有这个问题。我的猜测是,当解码器直接渲染到编码器的输入表面时,Android 无法重新缩放帧。
解决方法是使用 openGL 进行缩放。
(我没有 运行 这个项目,但我已经实现了类似的东西,所以它应该可以工作)