|
|
|
@ -21,6 +21,7 @@
|
|
|
|
|
#include <binder/MemoryBase.h>
|
|
|
|
|
#include <binder/MemoryHeapBase.h>
|
|
|
|
|
#include <gui/Surface.h>
|
|
|
|
|
#include <gui/SurfaceComposerClient.h>
|
|
|
|
|
#include <inttypes.h>
|
|
|
|
|
#include <media/ICrypto.h>
|
|
|
|
|
#include <media/IMediaSource.h>
|
|
|
|
@ -28,6 +29,7 @@
|
|
|
|
|
#include <media/stagefright/foundation/avc_utils.h>
|
|
|
|
|
#include <media/stagefright/foundation/ADebug.h>
|
|
|
|
|
#include <media/stagefright/foundation/AMessage.h>
|
|
|
|
|
#include <media/stagefright/foundation/ColorUtils.h>
|
|
|
|
|
#include <media/stagefright/ColorConverter.h>
|
|
|
|
|
#include <media/stagefright/MediaBuffer.h>
|
|
|
|
|
#include <media/stagefright/MediaCodec.h>
|
|
|
|
@ -44,7 +46,7 @@ static const size_t kRetryCount = 50; // must be >0
|
|
|
|
|
|
|
|
|
|
sp<IMemory> allocVideoFrame(const sp<MetaData>& trackMeta,
|
|
|
|
|
int32_t width, int32_t height, int32_t tileWidth, int32_t tileHeight,
|
|
|
|
|
int32_t dstBpp, bool metaOnly = false) {
|
|
|
|
|
int32_t dstBpp, bool allocRotated, bool metaOnly) {
|
|
|
|
|
int32_t rotationAngle;
|
|
|
|
|
if (!trackMeta->findInt32(kKeyRotation, &rotationAngle)) {
|
|
|
|
|
rotationAngle = 0; // By default, no rotation
|
|
|
|
@ -74,6 +76,14 @@ sp<IMemory> allocVideoFrame(const sp<MetaData>& trackMeta,
|
|
|
|
|
displayHeight = height;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (allocRotated && (rotationAngle == 90 || rotationAngle == 270)) {
|
|
|
|
|
int32_t tmp;
|
|
|
|
|
tmp = width; width = height; height = tmp;
|
|
|
|
|
tmp = displayWidth; displayWidth = displayHeight; displayHeight = tmp;
|
|
|
|
|
tmp = tileWidth; tileWidth = tileHeight; tileHeight = tmp;
|
|
|
|
|
rotationAngle = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VideoFrame frame(width, height, displayWidth, displayHeight,
|
|
|
|
|
tileWidth, tileHeight, rotationAngle, dstBpp, !metaOnly, iccSize);
|
|
|
|
|
|
|
|
|
@ -94,6 +104,20 @@ sp<IMemory> allocVideoFrame(const sp<MetaData>& trackMeta,
|
|
|
|
|
return frameMem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sp<IMemory> allocVideoFrame(const sp<MetaData>& trackMeta,
|
|
|
|
|
int32_t width, int32_t height, int32_t tileWidth, int32_t tileHeight,
|
|
|
|
|
int32_t dstBpp, bool allocRotated = false) {
|
|
|
|
|
return allocVideoFrame(trackMeta, width, height, tileWidth, tileHeight, dstBpp,
|
|
|
|
|
allocRotated, false /*metaOnly*/);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sp<IMemory> allocMetaFrame(const sp<MetaData>& trackMeta,
|
|
|
|
|
int32_t width, int32_t height, int32_t tileWidth, int32_t tileHeight,
|
|
|
|
|
int32_t dstBpp) {
|
|
|
|
|
return allocVideoFrame(trackMeta, width, height, tileWidth, tileHeight, dstBpp,
|
|
|
|
|
false /*allocRotated*/, true /*metaOnly*/);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool findThumbnailInfo(
|
|
|
|
|
const sp<MetaData> &trackMeta, int32_t *width, int32_t *height,
|
|
|
|
|
uint32_t *type = NULL, const void **data = NULL, size_t *size = NULL) {
|
|
|
|
@ -117,23 +141,27 @@ bool findGridInfo(const sp<MetaData> &trackMeta,
|
|
|
|
|
bool getDstColorFormat(
|
|
|
|
|
android_pixel_format_t colorFormat,
|
|
|
|
|
OMX_COLOR_FORMATTYPE *dstFormat,
|
|
|
|
|
ui::PixelFormat *captureFormat,
|
|
|
|
|
int32_t *dstBpp) {
|
|
|
|
|
switch (colorFormat) {
|
|
|
|
|
case HAL_PIXEL_FORMAT_RGB_565:
|
|
|
|
|
{
|
|
|
|
|
*dstFormat = OMX_COLOR_Format16bitRGB565;
|
|
|
|
|
*captureFormat = ui::PixelFormat::RGB_565;
|
|
|
|
|
*dstBpp = 2;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
case HAL_PIXEL_FORMAT_RGBA_8888:
|
|
|
|
|
{
|
|
|
|
|
*dstFormat = OMX_COLOR_Format32BitRGBA8888;
|
|
|
|
|
*captureFormat = ui::PixelFormat::RGBA_8888;
|
|
|
|
|
*dstBpp = 4;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
case HAL_PIXEL_FORMAT_BGRA_8888:
|
|
|
|
|
{
|
|
|
|
|
*dstFormat = OMX_COLOR_Format32bitBGRA8888;
|
|
|
|
|
*captureFormat = ui::PixelFormat::BGRA_8888;
|
|
|
|
|
*dstBpp = 4;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
@ -150,9 +178,10 @@ bool getDstColorFormat(
|
|
|
|
|
sp<IMemory> FrameDecoder::getMetadataOnly(
|
|
|
|
|
const sp<MetaData> &trackMeta, int colorFormat, bool thumbnail) {
|
|
|
|
|
OMX_COLOR_FORMATTYPE dstFormat;
|
|
|
|
|
ui::PixelFormat captureFormat;
|
|
|
|
|
int32_t dstBpp;
|
|
|
|
|
if (!getDstColorFormat(
|
|
|
|
|
(android_pixel_format_t)colorFormat, &dstFormat, &dstBpp)) {
|
|
|
|
|
if (!getDstColorFormat((android_pixel_format_t)colorFormat,
|
|
|
|
|
&dstFormat, &captureFormat, &dstBpp)) {
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -170,8 +199,7 @@ sp<IMemory> FrameDecoder::getMetadataOnly(
|
|
|
|
|
tileWidth = tileHeight = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return allocVideoFrame(trackMeta,
|
|
|
|
|
width, height, tileWidth, tileHeight, dstBpp, true /*metaOnly*/);
|
|
|
|
|
return allocMetaFrame(trackMeta, width, height, tileWidth, tileHeight, dstBpp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FrameDecoder::FrameDecoder(
|
|
|
|
@ -194,15 +222,30 @@ FrameDecoder::~FrameDecoder() {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool isHDR(const sp<AMessage> &format) {
|
|
|
|
|
uint32_t standard, range, transfer;
|
|
|
|
|
if (!format->findInt32("color-standard", (int32_t*)&standard)) {
|
|
|
|
|
standard = 0;
|
|
|
|
|
}
|
|
|
|
|
if (!format->findInt32("color-range", (int32_t*)&range)) {
|
|
|
|
|
range = 0;
|
|
|
|
|
}
|
|
|
|
|
if (!format->findInt32("color-transfer", (int32_t*)&transfer)) {
|
|
|
|
|
transfer = 0;
|
|
|
|
|
}
|
|
|
|
|
return standard == ColorUtils::kColorStandardBT2020 &&
|
|
|
|
|
transfer == ColorUtils::kColorTransferST2084;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
status_t FrameDecoder::init(
|
|
|
|
|
int64_t frameTimeUs, int option, int colorFormat) {
|
|
|
|
|
if (!getDstColorFormat(
|
|
|
|
|
(android_pixel_format_t)colorFormat, &mDstFormat, &mDstBpp)) {
|
|
|
|
|
if (!getDstColorFormat((android_pixel_format_t)colorFormat,
|
|
|
|
|
&mDstFormat, &mCaptureFormat, &mDstBpp)) {
|
|
|
|
|
return ERROR_UNSUPPORTED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sp<AMessage> videoFormat = onGetFormatAndSeekOptions(
|
|
|
|
|
frameTimeUs, option, &mReadOptions);
|
|
|
|
|
frameTimeUs, option, &mReadOptions, &mSurface);
|
|
|
|
|
if (videoFormat == NULL) {
|
|
|
|
|
ALOGE("video format or seek mode not supported");
|
|
|
|
|
return ERROR_UNSUPPORTED;
|
|
|
|
@ -219,7 +262,7 @@ status_t FrameDecoder::init(
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = decoder->configure(
|
|
|
|
|
videoFormat, NULL /* surface */, NULL /* crypto */, 0 /* flags */);
|
|
|
|
|
videoFormat, mSurface, NULL /* crypto */, 0 /* flags */);
|
|
|
|
|
if (err != OK) {
|
|
|
|
|
ALOGW("configure returned error %d (%s)", err, asString(err));
|
|
|
|
|
decoder->release();
|
|
|
|
@ -367,8 +410,13 @@ status_t FrameDecoder::extractInternal() {
|
|
|
|
|
ALOGE("failed to get output buffer %zu", index);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
err = onOutputReceived(videoFrameBuffer, mOutputFormat, ptsUs, &done);
|
|
|
|
|
mDecoder->releaseOutputBuffer(index);
|
|
|
|
|
if (mSurface != nullptr) {
|
|
|
|
|
mDecoder->renderOutputBufferAndRelease(index);
|
|
|
|
|
err = onOutputReceived(videoFrameBuffer, mOutputFormat, ptsUs, &done);
|
|
|
|
|
} else {
|
|
|
|
|
err = onOutputReceived(videoFrameBuffer, mOutputFormat, ptsUs, &done);
|
|
|
|
|
mDecoder->releaseOutputBuffer(index);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
ALOGW("Received error %d (%s) instead of output", err, asString(err));
|
|
|
|
|
done = true;
|
|
|
|
@ -399,7 +447,9 @@ VideoFrameDecoder::VideoFrameDecoder(
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sp<AMessage> VideoFrameDecoder::onGetFormatAndSeekOptions(
|
|
|
|
|
int64_t frameTimeUs, int seekMode, MediaSource::ReadOptions *options) {
|
|
|
|
|
int64_t frameTimeUs, int seekMode,
|
|
|
|
|
MediaSource::ReadOptions *options,
|
|
|
|
|
sp<Surface> *window) {
|
|
|
|
|
mSeekMode = static_cast<MediaSource::ReadOptions::SeekMode>(seekMode);
|
|
|
|
|
if (mSeekMode < MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC ||
|
|
|
|
|
mSeekMode > MediaSource::ReadOptions::SEEK_FRAME_INDEX) {
|
|
|
|
@ -446,6 +496,16 @@ sp<AMessage> VideoFrameDecoder::onGetFormatAndSeekOptions(
|
|
|
|
|
videoFormat->setInt32("android._num-input-buffers", 1);
|
|
|
|
|
videoFormat->setInt32("android._num-output-buffers", 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (isHDR(videoFormat)) {
|
|
|
|
|
*window = initSurfaceControl();
|
|
|
|
|
if (*window == NULL) {
|
|
|
|
|
ALOGE("Failed to init surface control for HDR, fallback to non-hdr");
|
|
|
|
|
} else {
|
|
|
|
|
videoFormat->setInt32("color-format", OMX_COLOR_FormatAndroidOpaque);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return videoFormat;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -490,13 +550,22 @@ status_t VideoFrameDecoder::onOutputReceived(
|
|
|
|
|
int32_t width, height, stride, srcFormat;
|
|
|
|
|
if (!outputFormat->findInt32("width", &width) ||
|
|
|
|
|
!outputFormat->findInt32("height", &height) ||
|
|
|
|
|
!outputFormat->findInt32("stride", &stride) ||
|
|
|
|
|
!outputFormat->findInt32("color-format", &srcFormat)) {
|
|
|
|
|
ALOGE("format missing dimension or color: %s",
|
|
|
|
|
outputFormat->debugString().c_str());
|
|
|
|
|
return ERROR_MALFORMED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!outputFormat->findInt32("stride", &stride)) {
|
|
|
|
|
if (mSurfaceControl == NULL) {
|
|
|
|
|
ALOGE("format must have stride for byte buffer mode: %s",
|
|
|
|
|
outputFormat->debugString().c_str());
|
|
|
|
|
return ERROR_MALFORMED;
|
|
|
|
|
}
|
|
|
|
|
// for surface output, set stride to width, we don't actually need it.
|
|
|
|
|
stride = width;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int32_t crop_left, crop_top, crop_right, crop_bottom;
|
|
|
|
|
if (!outputFormat->findRect("crop", &crop_left, &crop_top, &crop_right, &crop_bottom)) {
|
|
|
|
|
crop_left = crop_top = 0;
|
|
|
|
@ -511,12 +580,17 @@ status_t VideoFrameDecoder::onOutputReceived(
|
|
|
|
|
(crop_bottom - crop_top + 1),
|
|
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
dstBpp());
|
|
|
|
|
dstBpp(),
|
|
|
|
|
mSurfaceControl != nullptr /*allocRotated*/);
|
|
|
|
|
mFrame = static_cast<VideoFrame*>(frameMem->pointer());
|
|
|
|
|
|
|
|
|
|
setFrame(frameMem);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (mSurfaceControl != nullptr) {
|
|
|
|
|
return captureSurfaceControl();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ColorConverter converter((OMX_COLOR_FORMATTYPE)srcFormat, dstFormat());
|
|
|
|
|
|
|
|
|
|
uint32_t standard, range, transfer;
|
|
|
|
@ -547,6 +621,101 @@ status_t VideoFrameDecoder::onOutputReceived(
|
|
|
|
|
return ERROR_UNSUPPORTED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sp<Surface> VideoFrameDecoder::initSurfaceControl() {
|
|
|
|
|
sp<SurfaceComposerClient> client = new SurfaceComposerClient();
|
|
|
|
|
if (client->initCheck() != NO_ERROR) {
|
|
|
|
|
ALOGE("failed to get SurfaceComposerClient");
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// create a container layer to hold the capture layer, so that we can
|
|
|
|
|
// use full frame drop. If without the container, the crop will be set
|
|
|
|
|
// to display size.
|
|
|
|
|
sp<SurfaceControl> parent = client->createSurface(
|
|
|
|
|
String8("parent"),
|
|
|
|
|
0 /* width */, 0 /* height */,
|
|
|
|
|
PIXEL_FORMAT_RGBA_8888,
|
|
|
|
|
ISurfaceComposerClient::eFXSurfaceContainer );
|
|
|
|
|
|
|
|
|
|
if (!parent) {
|
|
|
|
|
ALOGE("failed to get surface control parent");
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// create the surface with unknown size 1x1 for now, real size will
|
|
|
|
|
// be set before the capture when we have output format info.
|
|
|
|
|
sp<SurfaceControl> surfaceControl = client->createSurface(
|
|
|
|
|
String8("thumbnail"),
|
|
|
|
|
1 /* width */, 1 /* height */,
|
|
|
|
|
PIXEL_FORMAT_RGBA_8888,
|
|
|
|
|
ISurfaceComposerClient::eFXSurfaceBufferQueue,
|
|
|
|
|
parent.get());
|
|
|
|
|
|
|
|
|
|
if (!surfaceControl) {
|
|
|
|
|
ALOGE("failed to get surface control");
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SurfaceComposerClient::Transaction t;
|
|
|
|
|
t.hide(parent)
|
|
|
|
|
.show(surfaceControl)
|
|
|
|
|
.apply(true);
|
|
|
|
|
|
|
|
|
|
mSurfaceControl = surfaceControl;
|
|
|
|
|
mParent = parent;
|
|
|
|
|
|
|
|
|
|
return surfaceControl->getSurface();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
status_t VideoFrameDecoder::captureSurfaceControl() {
|
|
|
|
|
// set the layer size to the output size before the capture
|
|
|
|
|
SurfaceComposerClient::Transaction()
|
|
|
|
|
.setSize(mSurfaceControl, mFrame->mWidth, mFrame->mHeight)
|
|
|
|
|
.apply(true);
|
|
|
|
|
|
|
|
|
|
sp<GraphicBuffer> outBuffer;
|
|
|
|
|
status_t err = ScreenshotClient::captureChildLayers(
|
|
|
|
|
mParent->getHandle(),
|
|
|
|
|
ui::Dataspace::V0_SRGB,
|
|
|
|
|
captureFormat(),
|
|
|
|
|
Rect(0, 0, mFrame->mWidth, mFrame->mHeight),
|
|
|
|
|
{},
|
|
|
|
|
1.0f /*frameScale*/,
|
|
|
|
|
&outBuffer);
|
|
|
|
|
|
|
|
|
|
if (err != OK) {
|
|
|
|
|
ALOGE("failed to captureLayers: err %d", err);
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ALOGV("capture: %dx%d, format %d, stride %d",
|
|
|
|
|
outBuffer->getWidth(),
|
|
|
|
|
outBuffer->getHeight(),
|
|
|
|
|
outBuffer->getPixelFormat(),
|
|
|
|
|
outBuffer->getStride());
|
|
|
|
|
|
|
|
|
|
uint8_t *base;
|
|
|
|
|
int32_t outBytesPerPixel, outBytesPerStride;
|
|
|
|
|
err = outBuffer->lock(
|
|
|
|
|
GraphicBuffer::USAGE_SW_READ_OFTEN,
|
|
|
|
|
reinterpret_cast<void**>(&base),
|
|
|
|
|
&outBytesPerPixel,
|
|
|
|
|
&outBytesPerStride);
|
|
|
|
|
if (err != OK) {
|
|
|
|
|
ALOGE("failed to lock graphic buffer: err %d", err);
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint8_t *dst = mFrame->getFlattenedData();
|
|
|
|
|
for (size_t y = 0 ; y < fmin(mFrame->mHeight, outBuffer->getHeight()) ; y++) {
|
|
|
|
|
memcpy(dst, base, fmin(mFrame->mWidth, outBuffer->getWidth()) * mFrame->mBytesPerPixel);
|
|
|
|
|
dst += mFrame->mRowBytes;
|
|
|
|
|
base += outBuffer->getStride() * mFrame->mBytesPerPixel;
|
|
|
|
|
}
|
|
|
|
|
outBuffer->unlock();
|
|
|
|
|
return OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
ImageDecoder::ImageDecoder(
|
|
|
|
@ -566,7 +735,8 @@ ImageDecoder::ImageDecoder(
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sp<AMessage> ImageDecoder::onGetFormatAndSeekOptions(
|
|
|
|
|
int64_t frameTimeUs, int /*seekMode*/, MediaSource::ReadOptions *options) {
|
|
|
|
|
int64_t frameTimeUs, int /*seekMode*/,
|
|
|
|
|
MediaSource::ReadOptions *options, sp<Surface> * /*window*/) {
|
|
|
|
|
sp<MetaData> overrideMeta;
|
|
|
|
|
if (frameTimeUs < 0) {
|
|
|
|
|
uint32_t type;
|
|
|
|
|