Android 使用MediaCodec和MediaMuxer对视频进行编码和muxing

Android 使用MediaCodec和MediaMuxer对视频进行编码和muxing,android,video,android-mediacodec,mediamuxer,Android,Video,Android Mediacodec,Mediamuxer,我正在开发一个应用程序,在该应用程序中,我解码视频并替换某些帧,然后使用MediaMuxer和MediaCodec重新编码。如果我不替换任何帧(我在下面解释的1080p视频除外),该应用程序就可以工作,但当我替换帧时,被替换帧之后的帧会被像素化,并且视频会有起伏 另外,当我用1920x1080视频尝试我的应用程序时,我会得到一个奇怪的输出,视频没有显示任何内容,直到我滚动到视频的开头,然后视频开始显示(但编辑后出现了前面提到的像素化问题) 以下是我如何配置编码器: Video_format.se

我正在开发一个应用程序,在该应用程序中,我解码视频并替换某些帧,然后使用
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视频,除非我滚动到开头

编辑

这是一个未编辑的示例,当我在VLC上播放时,它会显示一个绿色屏幕,在手机上播放时会出现错误,除非我滚动到开始,现在在YouTube上正常工作,除了开始时的绿色边框

这是一个在开始时使用绿色边框编辑的示例,编辑后清除像素和延迟

以下是我用来解码重新编码的代码:

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();
      }
    }
  }
}
私有位图解码(最终长时间、最终整数宽度、最终整数高度){
MediaFormat newFormat=codec.getOutputFormat();
位图b=null;
最终整数超时时间=10000;
ByteBuffer[]decoderInputBuffers=codec.getInputBuffers();
MediaCodec.BufferInfo=新的MediaCodec.BufferInfo();
布尔输出完成=假;
布尔inputDone=false;
而(!outputDone){
如果(!inputDone){
int inputBufIndex=codec.dequeueInputBuffer(超时\u USEC);
如果(inputBufIndex>=0){
ByteBuffer inputBuf=decoderInputBuffers[inputBufIndex];
int chunkSize=extractor.readSampleData(inputBuf,0);
if(chunkSize<0){
codec.queueInputBuffer(inputBufIndex,0,0,0L,MediaCodec.BUFFER\u标志\u结束\u流);
输入完成=真;
}否则{
long presentationTimeUs=提取器.getSampleTime();
queueInputBuffer(inputBufIndex,0,chunkSize,presentationTimeUs,0);
}
inputBuf.clear();
decoderInputBuffers[inputBufIndex].clear();
}否则{
}
}
ByteBuffer[]输出缓冲区;
如果(!outputDone){
int decoderStatus=codec.dequeueOutputBuffer(信息,超时\u USEC);
如果(decoderStatus==MediaCodec.INFO\u请稍后再试){
}else if(decoderStatus==MediaCodec.INFO\u输出\u缓冲区\u更改){
outputBuffers=codec.getOutputBuffers();
}else if(decoderStatus==MediaCodec.INFO\u输出\u格式\u更改){
newFormat=codec.getOutputFormat();
}else if(decoderStatus<0){
}否则{
if((流的info.flags和MediaCodec.BUFFER\u FLAG\u END)!=0){
outputDone=true;
}
布尔值doRender=(info.size!=0);
codec.releaseOutputBuffer(decoderStatus,false);
如果(多伦德){
outputBuffers=codec.getOutputBuffers();
ByteBuffer buffer=输出缓冲区[decoderStatus];
缓冲区=输出缓冲区[decoderStatus];
outputDone=true;
字节[]输出数据=新字节[信息大小];
buffer.get(outData);
buffer.clear();
outputBuffers[decoderStatus].clear();
试一试{
int colr_格式=-1;
if(newFormat!=null&&newFormat.getInteger(MediaFormat.KEY\u COLOR\u FORMAT)==21){
colr_format=ImageFormat.NV21;
}如果(newFormat!=null&&newFormat.getInteger(MediaFormat.KEY\u COLOR\u FORMAT)!=21){
Toast.makeText(getApplicationContext(),“未知颜色格式”+format.getInteger(MediaFormat.KEY\u color\u format),Toast.LENGTH\u LONG.show();
完成();
返回null;
}
int[]arrrr=new int[format.getInteger(MediaFormat.KEY\u-WIDTH)*format.getInteger(MediaFormat.KEY\u-HEIGHT)];
YUV_NV21_TO_RGB(arrrr,outData,format.getInteger(MediaFormat.KEY_宽度),format.getInteger(MediaFormat.KEY_高度));
lastPresentationTimeUs=info.presentationTimeUs;
b=Bitmap.createBitmap(arrrr、format.getInteger(MediaFormat.KEY\u-WIDTH)、format.getInteger(MediaFormat.KEY\u-HEIGHT)、Bitmap.Config.ARGB\u 8888);
}捕获(例外e){
e、 printStackTrace();
}
}
}
}
}
返回b;
}
下面是编码方法:

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();
      }
    }
  }
}
private void encode(位图b、MediaCodec编码器、MediaMuxer-muxer、int-track\u-indx){
MediaCodec.BufferInfo enc_info=新的MediaCodec.BufferInfo();
布尔值enc_outputDone=false;
布尔enc_inputDone=false;
最终整数超时时间=10000;
ByteBuffer[]encoderInputBuffers=encoder.getInputBuffers();
ByteBuffer[]enc_outputBuffers=encoder.getOutputBuffers();
而(!enc_outputDone){
如果(!enc_inputDone){
int inputBufIndex=encoder.dequeueInputBuffer(超时\u USEC);
如果(inputBufIndex>=0){
ByteBuffer inputBuf=encoderInputBuffers[inputBufIndex];
int chunkSize=0;
如果(b==null){
}否则{
int mWidth=b.getWidth();
int mHeight=b.getHeight();
字节[]yuv=新字节[mWidth*mHeight*3/2];
int[]argb=新的int[mWidth*mHeight];
b、 获取像素(argb,0,mWidth,0,0,mWidth,mHeight);
编码YUV420SP(yuv、argb、mWidth、mHight);
b、 回收();
b=零;
输入输出(yuv);
chunkSize=yuv.length;
}
if(chunkSize<0){
编码器.queueInputBuffer(inputBufIndex,0,0,0L,
MediaCodec.BUFFER\u标志\u结束\u流);
}否则{
long presentationTimeUs=提取器.getSampleTime();
Log.i(“编码”,“编码时间:+presentationTimeUs”);