Я пытаюсь сжать видео с помощью медиа-кода c и сохранить исходный кадр в jpg. Я настраиваю медиакод c с поверхностью, мой код похож на коды ниже. Я хочу сохранить кадр в файл jpg после 'outputSurface.drawImage ();'. Я новичок в OpenGLES, поэтому я не знаю, как сохранить изображение из текстуры. Есть идеи?
private void doEncodeDecodeVideoFromSurfaceToSurface(MediaCodec encoder,
InputSurface inputSurface, int encoderColorFormat, MediaCodec decoder,
OutputSurface outputSurface) {
final int TIMEOUT_USEC = 10000;
ByteBuffer[] encoderOutputBuffers = encoder.getOutputBuffers();
ByteBuffer[] decoderInputBuffers = decoder.getInputBuffers();
MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
int generateIndex = 0;
int checkIndex = 0;
int badFrames = 0;
// Save a copy to disk. Useful for debugging the test. Note this is a raw elementary
// stream, not a .mp4 file, so not all players will know what to do with it.
FileOutputStream outputStream = null;
if (DEBUG_SAVE_FILE) {
String fileName = DEBUG_FILE_NAME_BASE + mWidth + "x" + mHeight + ".mp4";
try {
outputStream = new FileOutputStream(fileName);
Log.d(TAG, "encoded output will be saved as " + fileName);
} catch (IOException ioe) {
Log.w(TAG, "Unable to create debug output file " + fileName);
throw new RuntimeException(ioe);
}
}
// Loop until the output side is done.
boolean inputDone = false;
boolean encoderDone = false;
boolean outputDone = false;
while (!outputDone) {
if (VERBOSE) Log.d(TAG, "loop");
// If we're not done submitting frames, generate a new one and submit it. The
// eglSwapBuffers call will block if the input is full.
if (!inputDone) {
if (generateIndex == NUM_FRAMES) {
// Send an empty frame with the end-of-stream flag set.
if (VERBOSE) Log.d(TAG, "signaling input EOS");
encoder.signalEndOfInputStream();
inputDone = true;
} else {
inputSurface.makeCurrent();
generateSurfaceFrame(generateIndex);
inputSurface.setPresentationTime(computePresentationTime(generateIndex) * 1000);
if (VERBOSE) Log.d(TAG, "inputSurface swapBuffers");
inputSurface.swapBuffers();
}
generateIndex++;
}
// Assume output is available. Loop until both assumptions are false.
boolean decoderOutputAvailable = true;
boolean encoderOutputAvailable = !encoderDone;
while (decoderOutputAvailable || encoderOutputAvailable) {
// Start by draining any pending output from the decoder. It's important to
// do this before we try to stuff any more data in.
int decoderStatus = decoder.dequeueOutputBuffer(info, TIMEOUT_USEC);
if (decoderStatus == MediaCodec.INFO_TRY_AGAIN_LATER) {
// no output available yet
if (VERBOSE) Log.d(TAG, "no output from decoder available");
decoderOutputAvailable = false;
} else if (decoderStatus == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
if (VERBOSE) Log.d(TAG, "decoder output buffers changed (but we don't care)");
} else if (decoderStatus == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
// this happens before the first frame is returned
MediaFormat decoderOutputFormat = decoder.getOutputFormat();
if (VERBOSE) Log.d(TAG, "decoder output format changed: " +
decoderOutputFormat);
} else if (decoderStatus < 0) {
fail("unexpected result from deocder.dequeueOutputBuffer: " + decoderStatus);
} else { // decoderStatus >= 0
if (VERBOSE) Log.d(TAG, "surface decoder given buffer " + decoderStatus +
" (size=" + info.size + ")");
if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
if (VERBOSE) Log.d(TAG, "output EOS");
outputDone = true;
}
// The ByteBuffers are null references, but we still get a nonzero size for
// the decoded data.
boolean doRender = (info.size != 0);
// As soon as we call releaseOutputBuffer, the buffer will be forwarded
// to SurfaceTexture to convert to a texture. The API doesn't guarantee
// that the texture will be available before the call returns, so we
// need to wait for the onFrameAvailable callback to fire. If we don't
// wait, we risk dropping frames.
outputSurface.makeCurrent();
decoder.releaseOutputBuffer(decoderStatus, doRender);
if (doRender) {
assertEquals("Wrong time stamp", computePresentationTime(checkIndex),
info.presentationTimeUs);
if (VERBOSE) Log.d(TAG, "awaiting frame " + checkIndex);
outputSurface.awaitNewImage();
outputSurface.drawImage();
**// TODO: Save the decoded image here!**
if (!checkSurfaceFrame(checkIndex++)) {
badFrames++;
}
}
}
if (decoderStatus != MediaCodec.INFO_TRY_AGAIN_LATER) {
// Continue attempts to drain output.
continue;
}
// Decoder is drained, check to see if we've got a new buffer of output from
// the encoder.
if (!encoderDone) {
int encoderStatus = encoder.dequeueOutputBuffer(info, TIMEOUT_USEC);
if (encoderStatus == MediaCodec.INFO_TRY_AGAIN_LATER) {
// no output available yet
if (VERBOSE) Log.d(TAG, "no output from encoder available");
encoderOutputAvailable = false;
} else if (encoderStatus == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
// not expected for an encoder
encoderOutputBuffers = encoder.getOutputBuffers();
if (VERBOSE) Log.d(TAG, "encoder output buffers changed");
} else if (encoderStatus == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
// not expected for an encoder
MediaFormat newFormat = encoder.getOutputFormat();
if (VERBOSE) Log.d(TAG, "encoder output format changed: " + newFormat);
} else if (encoderStatus < 0) {
fail("unexpected result from encoder.dequeueOutputBuffer: " + encoderStatus);
} else { // encoderStatus >= 0
ByteBuffer encodedData = encoderOutputBuffers[encoderStatus];
if (encodedData == null) {
fail("encoderOutputBuffer " + encoderStatus + " was null");
}
// It's usually necessary to adjust the ByteBuffer values to match BufferInfo.
encodedData.position(info.offset);
encodedData.limit(info.offset + info.size);
if (outputStream != null) {
byte[] data = new byte[info.size];
encodedData.get(data);
encodedData.position(info.offset);
try {
outputStream.write(data);
} catch (IOException ioe) {
Log.w(TAG, "failed writing debug data to file");
throw new RuntimeException(ioe);
}
}
// Get a decoder input buffer, blocking until it's available. We just
// drained the decoder output, so we expect there to be a free input
// buffer now or in the near future (i.e. this should never deadlock
// if the codec is meeting requirements).
//
// The first buffer of data we get will have the BUFFER_FLAG_CODEC_CONFIG
// flag set; the decoder will see this and finish configuring itself.
int inputBufIndex = decoder.dequeueInputBuffer(-1);
ByteBuffer inputBuf = decoderInputBuffers[inputBufIndex];
inputBuf.clear();
inputBuf.put(encodedData);
decoder.queueInputBuffer(inputBufIndex, 0, info.size,
info.presentationTimeUs, info.flags);
// If everything from the encoder has been passed to the decoder, we
// can stop polling the encoder output. (This just an optimization.)
if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
encoderDone = true;
encoderOutputAvailable = false;
}
if (VERBOSE) Log.d(TAG, "passed " + info.size + " bytes to decoder"
+ (encoderDone ? " (EOS)" : ""));
encoder.releaseOutputBuffer(encoderStatus, false);
}
}
}
}
if (outputStream != null) {
try {
outputStream.close();
} catch (IOException ioe) {
Log.w(TAG, "failed closing debug file");
throw new RuntimeException(ioe);
}
}
if (checkIndex != NUM_FRAMES) {
fail("expected " + NUM_FRAMES + " frames, only decoded " + checkIndex);
}
if (badFrames != 0) {
fail("Found " + badFrames + " bad frames");
}
}