2013-04-11 17 views
18

Uygulamamda, kullanıcının galeriden seçtiği bazı videoları yüklemeye çalışıyorum. Sorun şu ki, genellikle android video dosyaları yüklemek için çok büyük ve bu yüzden ilk önce daha düşük bit hızı/çözünürlüğü tarafından sıkıştırmak istiyoruz.Yeni MediaCodec Kitaplığı'nı kullanarak android üzerinde video sıkıştırma

API 16 ile tanıtılan yeni MediaCodec API'yı (ffmpeg ile bunu yapmaya çalıştım) yeni duydum.

Şu anda şu anda yapıyorum: İlk olarak video kod çözücü kullanarak giriş videosunu çözün ve giriş dosyasından okunan biçimle yapılandırın. Ardından, önceden tanımlanmış bazı parametrelere sahip standart bir video kodlayıcı oluşturun ve dekoder çıktı arabelleğini kodlamak için kullanın. Sonra kodlayıcı çıkış tamponunu bir dosyaya kaydediyorum.

Her şey iyi görünüyor - her bir giriş ve çıkış arabelleğinden aynı sayıda paket yazılıyor ve okunıyor, ancak son dosya bir video dosyası gibi görünmüyor ve herhangi bir video oynatıcı tarafından açılamıyor.

Kod çözme işlemi tamam gibi görünüyor, çünkü bunu Yüzeyde görüntüleyerek test ediyorum. İlk olarak kod çözücüyü bir Yüzey ile çalışacak şekilde yapılandıracağım ve releaseOutputBuffer dediğimizde render işaretini kullanıyoruz ve videoyu ekranda görebiliyoruz.

//init decoder 
    MediaCodec decoder = MediaCodec.createDecoderByType(mime); 
    decoder.configure(format, null , null , 0); 
    decoder.start(); 
    ByteBuffer[] codecInputBuffers = decoder.getInputBuffers(); 
    ByteBuffer[] codecOutputBuffers = decoder.getOutputBuffers(); 

    //init encoder 
    MediaCodec encoder = MediaCodec.createEncoderByType(mime); 
    int width = format.getInteger(MediaFormat.KEY_WIDTH); 
    int height = format.getInteger(MediaFormat.KEY_HEIGHT); 
    MediaFormat mediaFormat = MediaFormat.createVideoFormat(mime, width, height); 
    mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, 400000); 
    mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, 25); 
    mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar); 
    mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 5); 
    encoder.configure(mediaFormat, null , null , MediaCodec.CONFIGURE_FLAG_ENCODE); 
    encoder.start(); 
    ByteBuffer[] encoderInputBuffers = encoder.getInputBuffers(); 
    ByteBuffer[] encoderOutputBuffers = encoder.getOutputBuffers(); 

    extractor.selectTrack(0); 

    boolean sawInputEOS = false; 
    boolean sawOutputEOS = false; 
    boolean sawOutputEOS2 = false; 
    MediaCodec.BufferInfo info = new MediaCodec.BufferInfo(); 
    BufferInfo encoderInfo = new MediaCodec.BufferInfo(); 

    while (!sawInputEOS || !sawOutputEOS || !sawOutputEOS2) { 
     if (!sawInputEOS) { 
      sawInputEOS = decodeInput(extractor, decoder, codecInputBuffers); 
     } 

     if (!sawOutputEOS) { 
      int outputBufIndex = decoder.dequeueOutputBuffer(info, 0); 
      if (outputBufIndex >= 0) { 
       sawOutputEOS = decodeEncode(extractor, decoder, encoder, codecOutputBuffers, encoderInputBuffers, info, outputBufIndex); 
      } else if (outputBufIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) { 
       Log.d(LOG_TAG, "decoding INFO_OUTPUT_BUFFERS_CHANGED"); 
       codecOutputBuffers = decoder.getOutputBuffers(); 
      } else if (outputBufIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { 
       final MediaFormat oformat = decoder.getOutputFormat(); 
       Log.d(LOG_TAG, "decoding Output format has changed to " + oformat); 
      } else if (outputBufIndex == MediaCodec.INFO_TRY_AGAIN_LATER) { 
       Log.d(LOG_TAG, "decoding dequeueOutputBuffer timed out!"); 
      } 
     } 

     if (!sawOutputEOS2) { 
      int encodingOutputBufferIndex = encoder.dequeueOutputBuffer(encoderInfo, 0); 
      if (encodingOutputBufferIndex >= 0) { 
       sawOutputEOS2 = encodeOuput(outputStream, encoder, encoderOutputBuffers, encoderInfo, encodingOutputBufferIndex); 
      } else if (encodingOutputBufferIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) { 
       Log.d(LOG_TAG, "encoding INFO_OUTPUT_BUFFERS_CHANGED"); 
       encoderOutputBuffers = encoder.getOutputBuffers(); 
      } else if (encodingOutputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { 
       final MediaFormat oformat = encoder.getOutputFormat(); 
       Log.d(LOG_TAG, "encoding Output format has changed to " + oformat); 
      } else if (encodingOutputBufferIndex == MediaCodec.INFO_TRY_AGAIN_LATER) { 
       Log.d(LOG_TAG, "encoding dequeueOutputBuffer timed out!"); 
      } 
     } 
    } 
      //clear some stuff here... 

ve bu ben kod çözme/kodlamak için kullanmak yöntemdir: Burada

ben kod kullanıyorum edilir

private boolean decodeInput(MediaExtractor extractor, MediaCodec decoder, ByteBuffer[] codecInputBuffers) { 
     boolean sawInputEOS = false; 
     int inputBufIndex = decoder.dequeueInputBuffer(0); 
     if (inputBufIndex >= 0) { 
      ByteBuffer dstBuf = codecInputBuffers[inputBufIndex]; 
      input1count++; 

      int sampleSize = extractor.readSampleData(dstBuf, 0); 
      long presentationTimeUs = 0; 
      if (sampleSize < 0) { 
       sawInputEOS = true; 
       sampleSize = 0; 
       Log.d(LOG_TAG, "done decoding input: #" + input1count); 
      } else { 
       presentationTimeUs = extractor.getSampleTime(); 
      } 

      decoder.queueInputBuffer(inputBufIndex, 0, sampleSize, presentationTimeUs, sawInputEOS ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0); 
      if (!sawInputEOS) { 
       extractor.advance(); 
      } 
     } 
     return sawInputEOS; 
    } 
    private boolean decodeOutputToFile(MediaExtractor extractor, MediaCodec decoder, ByteBuffer[] codecOutputBuffers, 
      MediaCodec.BufferInfo info, int outputBufIndex, OutputStream output) throws IOException { 
     boolean sawOutputEOS = false; 

     ByteBuffer buf = codecOutputBuffers[outputBufIndex]; 
     if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) { 
      sawOutputEOS = true; 
      Log.d(LOG_TAG, "done decoding output: #" + output1count); 
     } 

     if (info.size > 0) { 
      output1count++; 
      byte[] outData = new byte[info.size]; 
      buf.get(outData); 
      output.write(outData, 0, outData.length); 
     } else { 
      Log.d(LOG_TAG, "no data available " + info.size); 
     } 
     buf.clear(); 
     decoder.releaseOutputBuffer(outputBufIndex, false); 
     return sawOutputEOS; 
    } 

    private boolean encodeInputFromFile(MediaCodec encoder, ByteBuffer[] encoderInputBuffers, MediaCodec.BufferInfo info, FileChannel channel) throws IOException { 
      boolean sawInputEOS = false; 
      int inputBufIndex = encoder.dequeueInputBuffer(0); 
      if (inputBufIndex >= 0) { 
       ByteBuffer dstBuf = encoderInputBuffers[inputBufIndex]; 
       input1count++; 

       int sampleSize = channel.read(dstBuf); 
       if (sampleSize < 0) { 
        sawInputEOS = true; 
        sampleSize = 0; 
        Log.d(LOG_TAG, "done encoding input: #" + input1count); 
       } 

       encoder.queueInputBuffer(inputBufIndex, 0, sampleSize, channel.position(), sawInputEOS ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0); 
      } 
      return sawInputEOS; 
    } 

yanlış yapıyorum üzerinde herhangi bir öneri?

Ben MediaCodec ile kodlama için çok fazla örnekler çözme için sadece birkaç örnek kodu ... sayesinde yardım

+1

MediaCodec'e ihtiyacınız yoktur. Tek başına gitmek tehlikelidir, bunu yapın: http://stackoverflow.com/a/23815402 –

+1

Yorumlar bölümünde belirtildiği gibi - ffmpeg kullanarak bittim – shem

+0

@ shem: Bu sorunu çözdünüz mü? Aynı problem üzerinde başarı olmadan çalışıyorum. Like @fadden, çıktıyı bir MediaMuxer ile biçimlendirmemiz gerektiğine katılıyorum. Lütfen bir dosya okuyan (MediaExtractor) bir örneğe işaret edin ve farklı bir boyutta kodlanmış bir video yazdım (API 22 seviyesini kullanıyorum, bu yüzden asenkron bir örnek daha iyi olurdu!) –

cevap

5

MediaCodec çıkışı ham ilköğretim akışıdır için çok bulamadık. Birçok oyuncu bunu fark etmeden önce bir video dosyası biçiminde paketlemeniz gerekir (muhtemelen sesi tekrar dinlemek). FWIW, Linux için GStreamer tabanlı Totem Movie Player'ın "raw" video/avc dosyalarını oynayacağını keşfettim.

Güncelleme: yönlü Android Android 4.3 (API 18) tanıtıldı MediaMuxer class, ile ilgili .MP4 H.264 dönüştürmek. Kullanımını gösteren bir couple of examples (EncodeAndMuxTest, CameraToMpegTest) vardır. http://code.google.com/p/mp4parser/

+0

kullanarak bitti Ham akışı, kod çözdükten sonra aldığım şeydir, bu yüzden video akışını da kodluyorum (bunun ses akışı olmadan olduğunu biliyorum). kodlanmış dosya normal bir video olmalı, doğru muyum? – shem

+0

MediaCodec kodlayıcıdan çıkan kodlanmış veriler, yalnızca ham video temel akışıdır. Dosyanın bir hex dökümüne bakın ve MediaRecorder'dan çıkan .mp4 ile karşılaştırın ve hemen yapısal farkları göreceksiniz. – fadden

+0

Teşekkürler. Yani bir videoyu kod çözerek ve sonra başka bir bit hızı/boyutu ile kodlayarak sıkıştırmak istersem, bunu MediaCodec çerçevesiyle nasıl yapabilirim? Kod çözme, kodu çözülen akışı oynatılabilir biçime dönüştürmek için tasarlanmamış mı? Eğer değilse, videoları android üzerinde sıkıştırmamı nasıl öneriyorsunuz? – shem

3

Sadece video oynatıcılar bunu tanıyacağı Uygun kapsayıcı oluşturmak yardımcı olabilecek bu kütüphaneye tökezledi. Medyanın kalitesini ana hatlarıyla yaparken Video ve görüntülerin sıkıştırılması.

İlgili konular