2013-05-28 10 views
7

mam pewne kłopoty z plikami pisanie mp4 na Androidzie używając MediaRecorder i Jcodec, tutaj jest mój kodTworzenie plików mp4 na Androidzie używając 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(); 
    } 
} 

Jak widzimy Android MediaCodec oczekiwać YUV420SemiPlanar jako obrazu wejściowego, tak ja Podaję mu właściwą. W rezultacie mam uszkodzony plik MP4 z nieprawidłowymi kolorami, kiedy otwieram ten plik MP4 z AVCon widzę, że format koloru w pliku wyjściowym to yuv420p, więc może to jest problem? Proszę zasugerować, jak to naprawić.

Mam również inne pytanie, jak dodać skompresowany strumień audio do Muxer, nie znalazłem przykłady.

Odpowiedz

0

YUV420SemiPlanar obrazu 4x4 ma format YYYYYYYYYYYYYYYUVUVUVUV, a nie YYYYYYYYYYYYYYYYUUUUVVVV. Mogę pobrać plik MP4 o odpowiednim kolorze za pomocą Jcodec i MediaCodec na Androida po tym, jak przekazałem obraz w formacie.

Nie mam odpowiedzi na temat dźwięku.

2

Android 4.3 (API 18) ma dwie nowe funkcje, które mogą być przydatne.

Po pierwsze, klasa MediaCodec akceptuje dane wejściowe z powierzchni, więc wszystko, co można odkodować na powierzchni lub renderować za pomocą OpenGL ES, można nagrać bez konieczności grania z płaszczyznami kolorów YUV.

Po drugie, nowa klasa MediaMuxer zapewnia sposób na połączenie audio i wideo w formacie H.264 do pliku .mp4.

Przykładowy kod źródłowy (głównie w aspekcie wideo) można znaleźć here.

Powiązane problemy