私のアプリでは、ユーザーがギャラリーから選んだビデオをアップロードしようとしています。問題は、通常、Android のビデオ ファイルはアップロードするには大きすぎるため、最初に低いビットレート/解像度で圧縮する必要があることです。
API 16 で導入された新しいMediaCodec apiについて聞いたことがあります(以前は ffmpeg で試しました)。
私が現在行っていることは次のとおりです。まず、ビデオ デコーダーを使用して入力ビデオをデコードし、入力ファイルから読み取った形式で構成します。次に、定義済みのパラメーターを使用して標準のビデオ エンコーダーを作成し、それを使用してデコーダーの出力バッファーをエンコードします。次に、エンコーダーの出力バッファーをファイルに保存します。
すべてがうまくいっているように見えます - 同じ数のパケットが各入力バッファーと出力バッファーから読み書きされますが、最終的なファイルはビデオ ファイルのようには見えず、どのビデオ プレーヤーでも開くことができません。
Surfaceに表示してテストしたので、デコードは問題ないようです。まず、Surface で動作するようにデコーダーを構成します。releaseOutputBuffer を呼び出すと、render フラグが使用され、画面にビデオが表示されます。
私が使用しているコードは次のとおりです。
//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...
そして、それらは私がデコード/エンコードに使用する方法です:
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;
}
私が間違っていることについて何か提案はありますか?
MediaCodec を使用したエンコードの例はあまり見つかりませんでした。デコード用のサンプル コードがいくつかあります...助けてくれてありがとう