実現したいこと
androidのJetpackのライブラリ media3内のtransformerクラスを使い、media3が対応している動画を独自形式の動画に変換しようとしています。
独自形式ですのでエンコーダー用Codecと、そのCodecのインスタンスを生成するEncoderFactory自作の物です。
とりあえず実験として、エンコーダーに来た映像データをJPEG化しています。
発生している問題・分からないこと
どうやらTransformerのデコーダ内部で動いているMediaCodecが同期モードになっているようで、変換処理が重いとフレームドロップしますし、スレッドにわけまくると普通に胴切れを起こします。
Transformer内部で動いているデコーダーのMediaCodecを非同期モードで動かせれば良いはずなのですが、なにか手は無いでしょうか?
該当のソースコード
Java
1//Transformer起動部分 2//変換先Codecがmp4になっていますが、ここは無視してください。関係無くなります。 3 EditedMediaItem srcItem ; 4 MediaItem srcMediaItem = new MediaItem.Builder() 5 .setUri(uri) 6 .build() ; 7 8 srcItem = new EditedMediaItem.Builder(srcMediaItem) 9 .build() ; 10 11 Codec.EncoderFactory encoderFactory = new mjpegEncoderFactory(this) ; 12 13 Transformer transform = new Transformer.Builder(this) 14 .setVideoMimeType(MimeTypes.VIDEO_H264) 15 .setAudioMimeType(MimeTypes.AUDIO_AAC) 16 .setEncoderFactory(encoderFactory) 17 .addListener(new Transformer.Listener() { 18 @Override 19 public void onCompleted(Composition composition, ExportResult exportResult) { 20 Log.i("mjpegMakeTransMainActivity:Transformer.Builder", "onCompleted") ; 21 Transformer.Listener.super.onCompleted(composition, exportResult); 22 } 23 24 @Override 25 public void onError(Composition composition, ExportResult exportResult, ExportException exportException) { 26 Log.i("mjpegMakeTransMainActivity:Transformer.Builder", "onError") ; 27 Transformer.Listener.super.onError(composition, exportResult, exportException); 28 } 29 30 @Override 31 public void onFallbackApplied(Composition composition, TransformationRequest originalTransformationRequest, TransformationRequest fallbackTransformationRequest) { 32 Log.i("mjpegMakeTransMainActivity:Transformer.Builder", "onFallbackApplied") ; 33 Transformer.Listener.super.onFallbackApplied(composition, originalTransformationRequest, fallbackTransformationRequest); 34 } 35 }) 36 .build() ; 37 38 File file = getExternalFilesDir(null) ; 39 40 transform.start(srcItem, file.getPath() + "/tmpout.mp4"); 41
Java
1// getOutputBufferで返すoutputBuffer は未設定です。ここは無視してください 2 3package com.tmptest.jpegmaketest; 4 5import static androidx.media3.common.Format.NO_VALUE; 6 7import android.graphics.Bitmap; 8import android.graphics.Canvas; 9import android.graphics.ImageFormat; 10import android.graphics.Rect; 11import android.graphics.YuvImage; 12import android.media.Image; 13import android.media.ImageReader; 14import android.media.MediaCodec; 15import android.os.Environment; 16import android.os.Handler; 17import android.os.HandlerThread; 18import android.os.Looper; 19import android.util.Log; 20import android.view.PixelCopy; 21import android.view.Surface; 22import android.widget.FrameLayout; 23 24import androidx.annotation.Nullable; 25import androidx.media3.common.Format; 26import androidx.media3.common.MimeTypes; 27import androidx.media3.common.util.UnstableApi; 28import androidx.media3.decoder.DecoderInputBuffer; 29import androidx.media3.transformer.Codec; 30import androidx.media3.transformer.ExportException; 31 32import java.io.File; 33import java.io.FileNotFoundException; 34import java.io.FileOutputStream; 35import java.io.IOException; 36import java.nio.ByteBuffer; 37 38@UnstableApi 39public class jpegMakeCodec implements Codec { 40 MainActivity activity ; 41 Format configurationFormat ; 42 Format outputFormat ; 43 Surface inputSurface ; 44 ByteBuffer outputBuffer ; 45 MediaCodec.BufferInfo outputBufferInfo ; 46 ImageReader iReader ; 47 HandlerThread outputThread ; 48 HandlerThread fileSaveThread ; 49 Rect copyRect ; 50 Bitmap drawBase ; 51 int frameNo ; 52 int no ; 53 54 public jpegMakeCodec(MainActivity useAct, Format configurationBaseFormat) { 55 activity = useAct ; 56 57 configurationFormat = configurationBaseFormat ; 58 59 outputFormat = configurationFormat.buildUpon() 60 .build() ; 61 62 iReader = ImageReader.newInstance(configurationBaseFormat.width, configurationFormat.height, ImageFormat.YUY2, 1) ; 63 inputSurface = iReader.getSurface() ; 64 65 outputBuffer = ByteBuffer.allocate(outputFormat.width*outputFormat.height*4*2) ; 66 outputBufferInfo = new MediaCodec.BufferInfo() ; 67 outputBufferInfo.set(0, outputBuffer.remaining(), 0, 0); 68 69 outputThread = new HandlerThread("OutputThread") ; 70 outputThread.start() ; 71 72 fileSaveThread = new HandlerThread("FileSave") ; 73 fileSaveThread.start() ; 74 75 copyRect = new Rect(0, 0, configurationFormat.width, configurationFormat.height) ; 76 drawBase = Bitmap.createBitmap(configurationFormat.width, configurationFormat.height, Bitmap.Config.ARGB_8888) ; 77 78 no = 0 ; 79 frameNo = 0 ; 80 } 81 82 @Override 83 public Format getConfigurationFormat() { 84 return configurationFormat; 85 } 86 87 @Override 88 public String getName() { 89 return "testEncoder"; 90 } 91 92 @Override 93 public Surface getInputSurface() { 94 return inputSurface; 95 } 96 97 @Override 98 public boolean maybeDequeueInputBuffer(DecoderInputBuffer inputBuffer) throws ExportException { 99 return false; 100 } 101 102 @Override 103 public void queueInputBuffer(DecoderInputBuffer inputBuffer) throws ExportException { 104 105 } 106 107 @Override 108 public void signalEndOfInputStream() throws ExportException { 109 110 } 111 112 @Override 113 public Format getInputFormat() throws ExportException { 114 return configurationFormat; 115 } 116 117 @Nullable 118 @Override 119 public Format getOutputFormat() throws ExportException { 120 return outputFormat; 121 } 122 123 @Nullable 124 @Override 125 public ByteBuffer getOutputBuffer() throws ExportException { 126 { 127 PixelCopy.request(inputSurface, copyRect, drawBase, (int result) -> { 128 if (result != PixelCopy.SUCCESS) { 129 return; 130 } 131 Handler handle = new Handler(fileSaveThread.getLooper()) ; 132 handle.post(new Runnable() { 133 @Override 134 public void run() { 135 try { 136 File file = activity.getExternalFilesDir(null) ; 137 FileOutputStream tmpOut ; 138 try { 139 tmpOut = new FileOutputStream(file.getAbsolutePath()+String.format("/tmp%03d.jpg", no)) ; 140 drawBase.compress(Bitmap.CompressFormat.JPEG, 100, tmpOut) ; 141 tmpOut.close(); 142 no ++ ; 143 frameNo ++ ; 144 } catch(FileNotFoundException e) { 145 throw new RuntimeException(e); 146 } catch(IOException e) { 147 throw new RuntimeException(e); 148 } 149 } catch (Exception e) { 150 } 151 } 152 }) ; 153 }, new Handler(Looper.getMainLooper())); 154 155// int [] pixels = new int[1920*1080] ; 156// drawBase.getPixels(pixels, 0, 0, 0, 0, 1920, 1080); 157// if(pixels != null) { 158 159// } 160 } 161 162 ByteBuffer returnValue = outputBuffer ; 163 outputBuffer = null ; 164 return returnValue; 165 } 166 167 @Nullable 168 @Override 169 public MediaCodec.BufferInfo getOutputBufferInfo() throws ExportException { 170 MediaCodec.BufferInfo returnInfo = outputBufferInfo ; 171 long sampleTime = (long)((1000 / configurationFormat.frameRate) * frameNo) ; 172 173 outputBufferInfo.set(0, outputBufferInfo.size, sampleTime, 0); 174 return returnInfo; 175 } 176 177 @Override 178 public void releaseOutputBuffer(boolean render) throws ExportException { 179 outputBuffer = ByteBuffer.allocate(outputFormat.width*outputFormat.height*4*2) ; 180 } 181 182 @Override 183 public void releaseOutputBuffer(long renderPresentationTimeUs) throws ExportException { 184 outputBuffer = ByteBuffer.allocate(outputFormat.width*outputFormat.height*4*2) ; 185 } 186 187 @Override 188 public boolean isEnded() { 189 return false; 190 } 191 192 @Override 193 public void release() { 194 195 } 196} 197
試したこと・調べたこと
- teratailやGoogle等で検索した
- ソースコードを自分なりに変更した
- 知人に聞いた
- その他
上記の詳細・結果
PixelCopyの実行スレッドをoutputThreadにすると胴切れします。
メインスレッドでやると胴切れしませんがフレームドロップします。
補足
特になし

回答1件
あなたの回答
tips
プレビュー