2013-05-28 5 views
7

у меня есть некоторые проблемы с написанием mp4 файлов на Android с помощью MediaRecorder и Jcodec, вот мой кодСоздать mp4 файлы на Android с помощью Jcodec

public class SequenceEncoder { 
    private final static String CLASSTAG = SequenceEncoder.class.getSimpleName(); 

    private SeekableByteChannel ch; 

    private byte[] yuv = null; 

    private ArrayList<ByteBuffer> spsList; 
    private ArrayList<ByteBuffer> ppsList; 

    private CompressedTrack outTrack; 

    private int frameNo; 
    private MP4Muxer muxer; 

    ArrayList<ByteBuffer> spsListTmp = new ArrayList<ByteBuffer>(); 
    ArrayList<ByteBuffer> ppsListTmp = new ArrayList<ByteBuffer>(); 

    // Encoder 
    private MediaCodec mediaCodec = null; 

    public SequenceEncoder(File out) throws IOException { 
     this.ch = NIOUtils.writableFileChannel(out); 

     // Muxer that will store the encoded frames 
     muxer = new MP4Muxer(ch, Brand.MP4); 

     // Add video track to muxer 
     outTrack = muxer.addTrackForCompressed(TrackType.VIDEO, 25); 

     // Encoder extra data (SPS, PPS) to be stored in a special place of 
     // MP4 
     spsList = new ArrayList<ByteBuffer>(); 
     ppsList = new ArrayList<ByteBuffer>(); 
    } 

    @SuppressWarnings("unchecked") 
    public void encodeImage(ByteBuffer buffer, int width, int height) throws IOException { 
     if (yuv == null) { 
      int bufferSize = width * height * 3/2; 

      yuv = new byte[bufferSize]; 

      int bitRate = bufferSize; 
      int frameRate = 25; 
      String mimeType = "video/avc"; 

      // "video/avc" 
      mediaCodec = MediaCodec.createEncoderByType(mimeType); 
      MediaFormat mediaFormat = MediaFormat.createVideoFormat(mimeType, width, height); 
      mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, bitRate); // 125000); 
      mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, frameRate); 
      mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar); 
      mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 5); 

      mediaCodec.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE); 
      mediaCodec.start(); 
     } 

     byte[] rgba = buffer.array(); 

     // Convert RGBA image to NV12 (YUV420SemiPlanar) 
     Rgba2Yuv420.convert(rgba, yuv, width, height); 

     synchronized (mediaCodec) { 
     try { 
      ByteBuffer[] inputBuffers = mediaCodec.getInputBuffers(); 
      ByteBuffer[] outputBuffers = mediaCodec.getOutputBuffers(); 

      int inputBufferIndex = mediaCodec.dequeueInputBuffer(-1); 
      if (inputBufferIndex >= 0) { 
       ByteBuffer inputBuffer = inputBuffers[inputBufferIndex]; 
       inputBuffer.clear(); 
       inputBuffer.put(yuv); 
       mediaCodec.queueInputBuffer(inputBufferIndex, 0, 
         yuv.length, 0, 0); 
      } 

      MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo(); 
      int outputBufferIndex = mediaCodec.dequeueOutputBuffer(
        bufferInfo, 0); 

      while (outputBufferIndex >= 0) { 
       ByteBuffer outputBuffer = outputBuffers[outputBufferIndex]; 
       byte[] outData = new byte[bufferInfo.size]; 
       outputBuffer.get(outData); 

       ByteBuffer frameBuffer = ByteBuffer.wrap(outData); 

       spsListTmp.clear(); 
       ppsListTmp.clear(); 

       H264Utils.encodeMOVPacket(frameBuffer, spsListTmp, ppsListTmp); 

       if (!spsListTmp.isEmpty()) 
        spsList = (ArrayList<ByteBuffer>) spsListTmp.clone(); 
       if (!ppsListTmp.isEmpty()) 
        ppsList = (ArrayList<ByteBuffer>) ppsListTmp.clone(); 

       outTrack.addFrame(new MP4Packet(frameBuffer, frameNo, 25, 1, 
         frameNo, true, null, frameNo, 0)); 

       frameNo++; 

       mediaCodec.releaseOutputBuffer(outputBufferIndex, false); 
       outputBufferIndex = mediaCodec.dequeueOutputBuffer(
         bufferInfo, 0); 
      } 

      if (outputBufferIndex < 0) 
       switch (outputBufferIndex) { 
       case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED: 
        outputBuffers = mediaCodec.getOutputBuffers(); 
        break; 
       case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED: 
        break; 
       case MediaCodec.INFO_TRY_AGAIN_LATER: 
        break; 
       default: 
        break; 
       } 
      } catch (Exception e) { 
      } 
     } 
    } 

    public void finish() throws IOException { 
     if (!ch.isOpen()) 
      return; 

     if (mediaCodec != null) { 
      mediaCodec.stop(); 
      mediaCodec.release(); 
     } 

     outTrack.addSampleEntry(H264Utils.createMOVSampleEntry(spsList, ppsList)); 

     // Write MP4 header and finalize recording 
     muxer.writeHeader(); 
     NIOUtils.closeQuietly(ch); 

     ch.close(); 
    } 
} 

Как мы можем видеть, Android MediaCodec ожидать YUV420SemiPlanar в качестве входного изображения, так что я Он дал ему правильный. В результате у меня есть поврежденный файл mp4 с недопустимыми цветами, когда я открываю этот файл mp4 из AVCon. Я вижу, что формат цвета в выходном файле - yuv420p, так что, может быть, проблема? Пожалуйста, предложите, как это исправить.

Также возникает вопрос, как добавить сжатый аудиопоток в мультиплексор, не нашли примеров.

ответ

0

YUV420SemiPlanar изображения 4x4 - это формат, как YYYYYYYYYYYYYYYYUVUVUVUV, а не YYYYYYYYYYYYYYYYYYUYVVV. Я могу получить файл mp4 с правильным цветом с помощью Jcodec и MediaCodec на Android после того, как я передал изображение с форматом.

У меня нет ответа об аудио.

2

Android 4.3 (API 18) имеет две новые функции, которые могут быть полезны.

Во-первых, класс MediaCodec принимает входные данные от поверхности, поэтому все, что вы можете декодировать на поверхность или визуализировать с помощью OpenGL ES, можно записать без необходимости рисовать цветными плоскостями YUV.

Во-вторых, новый класс MediaMuxer обеспечивает способ объединения аудио и видео H.264 в файл .mp4.

Исходный код примера (в основном, для аспектов видео) можно найти here.

 Смежные вопросы

  • Нет связанных вопросов^_^