Кодирование и мультиплексирование видео с использованием MediaCodec и MediaMuxer

Я разрабатываю приложение, где я декодирую видео и заменяю определенные кадры и перекодирую с помощью MediaMuxer и MediaCodec . Приложение работает, если я не заменяю какие-либо фреймы (за исключением 1080p видео, как я объясняю ниже), но когда я это делаю, кадры после замененных пикселов и видео нестабильны.

Кроме того, когда я пытаюсь выполнить свое приложение с 1920×1080 видео, я получаю странный вывод, где видео ничего не отображается, пока я не перейду к началу видео, а затем начнет показываться видео (но с той же проблемой, о которой говорилось выше Pixalation после редактирования.

Вот как я настраиваю свой кодер:

 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 – Поврежденные видео 1920×1080, если я не перейду к началу.

редактировать

Вот пример видео 1080p unedited, который дает зеленый экран, когда я играю на 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(); } } } } 

Solutions Collecting From Web of "Кодирование и мультиплексирование видео с использованием MediaCodec и MediaMuxer"

По-видимому, пикселизация связана с неправильными временными метками фрейма, поэтому убедитесь, что временные метки ваших кадров монотонно увеличиваются и становятся одинаковыми, когда вы передаете их MediaCodec и MediaMuxer. В этом конкретном случае вам нужно только заменить данные для фрейма, который вы собираетесь заменить, оставив его метку времени, как это было в исходном потоке.

Убедитесь, что вы преобразуете растровое изображение в цветовое пространство YUV, и вы используете правильный формат пикселей. Android хранит растровые изображения в RGBA с 4 байтами на пиксель, вам нужно преобразовать это в YUV с значением Y для каждого пикселя и значениями U и V для блока 2×2, а затем выложить их в отдельных плоскостях в массиве байтов, который переходит в кодек.

Кроме того, некоторое время назад я сделал пример приложения, которое изменяет размеры видео с помощью MediaCodec, это также может помочь вам: https://github.com/grishka/android-video-transcoder