我正在开发一个应用程序,在其中我解码视频,替换某些帧,并使用MediaMuxer
和MediaCodec
重新编码。如果我不替换任何帧(我在下面解释的1080p视频除外),该应用程序就可以工作,但当我替换帧时,被替换帧之后的帧会被像素化,并且视频会有起伏。
此外,当我使用1920x1080个视频尝试我的应用程序时,我会得到一个奇怪的输出,其中视频没有显示任何内容,直到我滚动到视频的开头,然后视频开始显示(但与之前提到的相同问题编辑后的像素化。
这是我如何配置我的编码器:
Video_format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, interval);
Video_format.setInteger(MediaFormat.KEY_BIT_RATE, bitRate);
Video_format.setInteger(MediaFormat.KEY_FRAME_RATE, frameRate);
Video_format.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, 0);
int color_format=MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar;
Video_format.setInteger(MediaFormat.KEY_COLOR_FORMAT, color_format);
encoder.configure(Video_format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
所以总结一下,我有两个问题:
1-修改帧后的像素化帧和断断续续的视频。
2-损坏的1920x1080视频,除非我滚动到开头。
编辑
这是一个未经编辑的1080p视频示例,当我在VLC上播放时会出现绿屏,并且在手机上播放不正确,除非我滚动到开始,现在奇怪地在YouTube上正常工作,除了开始时的绿色框架
这是一个720p的视频示例,开始时还有一个绿色框架,编辑后有清晰的像素化和滞后
以下是我用来解码重新编码的代码:
do{
Bitmap b1;
if(edited_frames.containsKey(extractor.getSampleTime()))
b1=BitmapFactory.decodeFile(edited_frames.get(extractor.getSampleTime()));
else
b1=decode(extractor.getSampleTime(),Preview_width,Preview_Height);
if(b1==null) continue;
Bitmap b_scal=Bitmap.createScaledBitmap(b1, Preview_width, Preview_Height, false);
if(b_scal==null) continue;
encode(b_scal, encoder, muxer, videoTrackIndex);
lastTime=extractor.getSampleTime();
}while(extractor.advance());
解码方法:
private Bitmap decode(final long time,final int width,final int height){
MediaFormat newFormat = codec.getOutputFormat();
Bitmap b = null;
final int TIMEOUT_USEC = 10000;
ByteBuffer[] decoderInputBuffers = codec.getInputBuffers();
MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
boolean outputDone = false;
boolean inputDone = false;
while (!outputDone) {
if (!inputDone) {
int inputBufIndex = codec.dequeueInputBuffer(TIMEOUT_USEC);
if (inputBufIndex >= 0) {
ByteBuffer inputBuf = decoderInputBuffers[inputBufIndex];
int chunkSize = extractor.readSampleData(inputBuf, 0);
if (chunkSize < 0) {
codec.queueInputBuffer(inputBufIndex, 0, 0, 0L, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
inputDone = true;
} else {
long presentationTimeUs = extractor.getSampleTime();
codec.queueInputBuffer(inputBufIndex, 0, chunkSize, presentationTimeUs, 0 );
}
inputBuf.clear();
decoderInputBuffers[inputBufIndex].clear();
} else {
}
}
ByteBuffer[] outputBuffers;
if (!outputDone) {
int decoderStatus = codec.dequeueOutputBuffer(info, TIMEOUT_USEC);
if (decoderStatus == MediaCodec.INFO_TRY_AGAIN_LATER) {
} else if (decoderStatus == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
outputBuffers = codec.getOutputBuffers();
} else if (decoderStatus == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
newFormat = codec.getOutputFormat();
} else if (decoderStatus < 0) {
} else {
if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
outputDone = true;
}
boolean doRender = (info.size != 0);
codec.releaseOutputBuffer(decoderStatus, false);
if (doRender) {
outputBuffers = codec.getOutputBuffers();
ByteBuffer buffer = outputBuffers[decoderStatus];
buffer = outputBuffers[decoderStatus];
outputDone = true;
byte[] outData = new byte[info.size];
buffer.get(outData);
buffer.clear();
outputBuffers[decoderStatus].clear();
try {
int colr_format=-1;
if(newFormat!=null && newFormat.getInteger(MediaFormat.KEY_COLOR_FORMAT)==21){
colr_format=ImageFormat.NV21;
}else if(newFormat!=null && newFormat.getInteger(MediaFormat.KEY_COLOR_FORMAT)!=21){
Toast.makeText(getApplicationContext(), "Unknown color format "+format.getInteger(MediaFormat.KEY_COLOR_FORMAT), Toast.LENGTH_LONG).show();
finish();
return null;
}
int[] arrrr=new int[format.getInteger(MediaFormat.KEY_WIDTH)* format.getInteger(MediaFormat.KEY_HEIGHT)];
YUV_NV21_TO_RGB(arrrr, outData, format.getInteger(MediaFormat.KEY_WIDTH), format.getInteger(MediaFormat.KEY_HEIGHT));
lastPresentationTimeUs = info.presentationTimeUs;
b = Bitmap.createBitmap(arrrr, format.getInteger(MediaFormat.KEY_WIDTH), format.getInteger(MediaFormat.KEY_HEIGHT), Bitmap.Config.ARGB_8888);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
return b;
}
下面是编码方法:
private void encode(Bitmap b, MediaCodec encoder, MediaMuxer muxer, int track_indx){
MediaCodec.BufferInfo enc_info = new MediaCodec.BufferInfo();
boolean enc_outputDone = false;
boolean enc_inputDone = false;
final int TIMEOUT_USEC = 10000;
ByteBuffer[] encoderInputBuffers = encoder.getInputBuffers();
ByteBuffer[] enc_outputBuffers = encoder.getOutputBuffers();
while (!enc_outputDone) {
if (!enc_inputDone) {
int inputBufIndex = encoder.dequeueInputBuffer(TIMEOUT_USEC);
if (inputBufIndex >= 0) {
ByteBuffer inputBuf = encoderInputBuffers[inputBufIndex];
int chunkSize = 0;
if(b==null){
}else{
int mWidth = b.getWidth();
int mHeight = b.getHeight();
byte [] yuv = new byte[mWidth*mHeight*3/2];
int [] argb = new int[mWidth * mHeight];
b.getPixels(argb, 0, mWidth, 0, 0, mWidth, mHeight);
encodeYUV420SP(yuv, argb, mWidth, mHeight);
b.recycle();
b=null;
inputBuf.put(yuv);
chunkSize = yuv.length;
}
if (chunkSize < 0) {
encoder.queueInputBuffer(inputBufIndex, 0, 0, 0L,
MediaCodec.BUFFER_FLAG_END_OF_STREAM);
} else {
long presentationTimeUs = extractor.getSampleTime();
Log.i("Encode","Encode Time: "+presentationTimeUs);
encoder.queueInputBuffer(inputBufIndex, 0, chunkSize, presentationTimeUs, 0);
inputBuf.clear();
encoderInputBuffers[inputBufIndex].clear();
enc_inputDone=true;
}
}
}
if (!enc_outputDone) {
int enc_decoderStatus = encoder.dequeueOutputBuffer(enc_info, TIMEOUT_USEC);
if (enc_decoderStatus == MediaCodec.INFO_TRY_AGAIN_LATER) {
} else if (enc_decoderStatus == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
enc_outputBuffers = encoder.getOutputBuffers();
} else if (enc_decoderStatus == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
MediaFormat newFormat = encoder.getOutputFormat();
} else if (enc_decoderStatus < 0) {
} else {
if ((enc_info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
enc_outputDone = true;
}
boolean enc_doRender = (enc_info.size != 0);
encoder.releaseOutputBuffer(enc_decoderStatus, false);
if (enc_doRender) {
enc_outputDone = true;
ByteBuffer enc_buffer = enc_outputBuffers[enc_decoderStatus];
try {
muxer.writeSampleData(track_indx, enc_buffer, enc_info);
} catch (Exception e) {
e.printStackTrace();
}
enc_buffer.clear();
enc_outputBuffers[enc_decoderStatus].clear();
}
}
}
}
像素化最有可能是由于错误的帧时间戳造成的,因此请确保帧的时间戳单调增加,并且在将它们传递给MediaCodec和MediaMuxer时是相同的。在这种特定情况下,只需替换要替换的帧的数据,将其时间戳保留在原始流中。
确保您正在将位图转换为YUV颜色空间并且您使用了正确的像素格式。Android以RGBA存储位图,每个像素4个字节,您需要将其转换为YUV,每个像素Y值,2x2块的U和V值,然后将它们布局在进入编解码器的字节数组中的单独平面中。
此外,不久前,我制作了一个示例应用程序,可以使用MediaCodec调整视频大小,它也可以帮助您:https://github.com/grishka/android-video-transcoder
我正在尝试使用MediaCodec和MediaMuxer对来自相机的视频和来自麦克风的音频进行编码。我在录制时使用OpenGL在图像上覆盖文本。 我以这些课程为例: http://bigflake.com/mediacodec/CameraToMpegTest.java.txt https://github.com/OnlyInAmerica/HWEncoderExperiments/blob/m
我正在修改一个Android框架示例,将MediaCodec生成的基本AAC流打包成一个独立的。mp4文件。我使用的是一个实例,其中包含一个由实例生成的AAC曲目。 然而,我总是最终得到一个错误消息调用:
我能够在MediaCodec和MediaMuxer的帮助下录制(编码)视频。接下来,我需要在MediaCodec和MediaMuxer的帮助下处理音频部分和带视频的mux音频。 我面临两个问题: > 如何将音频和视频数据传递给MediaMuxer(因为writeSampleData()方法一次只接受一种类型的数据)? 我提到了MediaMuxerTest,但它使用的是MediaExtractor。
我正在使用MediaCodec将PCM数据转换为AAC,并使用MediaMuxer将此aac数据存储到m4a文件中。没有视频。 该文件会生成,甚至会播放。但是没有声音。如果我将aac文件导入Audacity,它不会显示任何数据。音频的长度甚至是预期的时间。我知道数据正在被编码,尽管我不确定这些数据是否被正确编码。 对pcm数据进行编码: 我已经浏览了大量示例,我所做的一切似乎都是正确的。如果我在o
输出视频创建成功,没有任何问题,但mediacodec已经更改了更改的测试值,我无法检索它。 这是我的问题,这是一个正确的方法做视频隐写在Android?如果这不是正确的方法,你能提出一个建议吗?
我正在尝试使用android AudioRecord和MediaCodec对aac音频进行编码。我创建了一个非常类似于(使用Android MediaCodec从相机编码H.264)的编码器类。使用此类,我创建了一个AudioRecord实例,并告诉它将其byte[]数据读出到AudioEncoder(audioEncoder.offerEncoder(Data))。 这是我的音频记录设置 我成功