Support post-decode video rotation.
Change-Id: I9ac90871269c5e132e98a9bf3ad8cae00f329f17
This commit is contained in:
@@ -84,11 +84,13 @@ struct AwesomeLocalRenderer : public AwesomeRenderer {
|
||||
OMX_COLOR_FORMATTYPE colorFormat,
|
||||
const sp<Surface> &surface,
|
||||
size_t displayWidth, size_t displayHeight,
|
||||
size_t decodedWidth, size_t decodedHeight)
|
||||
size_t decodedWidth, size_t decodedHeight,
|
||||
int32_t rotationDegrees)
|
||||
: mTarget(NULL) {
|
||||
init(colorFormat, surface,
|
||||
displayWidth, displayHeight,
|
||||
decodedWidth, decodedHeight);
|
||||
decodedWidth, decodedHeight,
|
||||
rotationDegrees);
|
||||
}
|
||||
|
||||
virtual void render(MediaBuffer *buffer) {
|
||||
@@ -113,7 +115,8 @@ private:
|
||||
OMX_COLOR_FORMATTYPE colorFormat,
|
||||
const sp<Surface> &surface,
|
||||
size_t displayWidth, size_t displayHeight,
|
||||
size_t decodedWidth, size_t decodedHeight);
|
||||
size_t decodedWidth, size_t decodedHeight,
|
||||
int32_t rotationDegrees);
|
||||
|
||||
AwesomeLocalRenderer(const AwesomeLocalRenderer &);
|
||||
AwesomeLocalRenderer &operator=(const AwesomeLocalRenderer &);;
|
||||
@@ -123,15 +126,19 @@ void AwesomeLocalRenderer::init(
|
||||
OMX_COLOR_FORMATTYPE colorFormat,
|
||||
const sp<Surface> &surface,
|
||||
size_t displayWidth, size_t displayHeight,
|
||||
size_t decodedWidth, size_t decodedHeight) {
|
||||
size_t decodedWidth, size_t decodedHeight,
|
||||
int32_t rotationDegrees) {
|
||||
mTarget = new SoftwareRenderer(
|
||||
colorFormat, surface, displayWidth, displayHeight,
|
||||
decodedWidth, decodedHeight);
|
||||
decodedWidth, decodedHeight, rotationDegrees);
|
||||
}
|
||||
|
||||
struct AwesomeNativeWindowRenderer : public AwesomeRenderer {
|
||||
AwesomeNativeWindowRenderer(const sp<ANativeWindow> &nativeWindow)
|
||||
AwesomeNativeWindowRenderer(
|
||||
const sp<ANativeWindow> &nativeWindow,
|
||||
int32_t rotationDegrees)
|
||||
: mNativeWindow(nativeWindow) {
|
||||
applyRotation(rotationDegrees);
|
||||
}
|
||||
|
||||
virtual void render(MediaBuffer *buffer) {
|
||||
@@ -153,6 +160,22 @@ protected:
|
||||
private:
|
||||
sp<ANativeWindow> mNativeWindow;
|
||||
|
||||
void applyRotation(int32_t rotationDegrees) {
|
||||
uint32_t transform;
|
||||
switch (rotationDegrees) {
|
||||
case 0: transform = 0; break;
|
||||
case 90: transform = HAL_TRANSFORM_ROT_90; break;
|
||||
case 180: transform = HAL_TRANSFORM_ROT_180; break;
|
||||
case 270: transform = HAL_TRANSFORM_ROT_270; break;
|
||||
default: transform = 0; break;
|
||||
}
|
||||
|
||||
if (transform) {
|
||||
CHECK_EQ(0, native_window_set_buffers_transform(
|
||||
mNativeWindow.get(), transform));
|
||||
}
|
||||
}
|
||||
|
||||
AwesomeNativeWindowRenderer(const AwesomeNativeWindowRenderer &);
|
||||
AwesomeNativeWindowRenderer &operator=(
|
||||
const AwesomeNativeWindowRenderer &);
|
||||
@@ -796,7 +819,19 @@ void AwesomePlayer::notifyVideoSize_l() {
|
||||
CHECK(meta->findInt32(kKeyWidth, &decodedWidth));
|
||||
CHECK(meta->findInt32(kKeyHeight, &decodedHeight));
|
||||
|
||||
notifyListener_l(MEDIA_SET_VIDEO_SIZE, decodedWidth, decodedHeight);
|
||||
int32_t rotationDegrees;
|
||||
if (!mVideoTrack->getFormat()->findInt32(
|
||||
kKeyRotation, &rotationDegrees)) {
|
||||
rotationDegrees = 0;
|
||||
}
|
||||
|
||||
if (rotationDegrees == 90 || rotationDegrees == 270) {
|
||||
notifyListener_l(
|
||||
MEDIA_SET_VIDEO_SIZE, decodedHeight, decodedWidth);
|
||||
} else {
|
||||
notifyListener_l(
|
||||
MEDIA_SET_VIDEO_SIZE, decodedWidth, decodedHeight);
|
||||
}
|
||||
}
|
||||
|
||||
void AwesomePlayer::initRenderer_l() {
|
||||
@@ -814,6 +849,12 @@ void AwesomePlayer::initRenderer_l() {
|
||||
CHECK(meta->findInt32(kKeyWidth, &decodedWidth));
|
||||
CHECK(meta->findInt32(kKeyHeight, &decodedHeight));
|
||||
|
||||
int32_t rotationDegrees;
|
||||
if (!mVideoTrack->getFormat()->findInt32(
|
||||
kKeyRotation, &rotationDegrees)) {
|
||||
rotationDegrees = 0;
|
||||
}
|
||||
|
||||
mVideoRenderer.clear();
|
||||
|
||||
// Must ensure that mVideoRenderer's destructor is actually executed
|
||||
@@ -824,7 +865,8 @@ void AwesomePlayer::initRenderer_l() {
|
||||
// Hardware decoders avoid the CPU color conversion by decoding
|
||||
// directly to ANativeBuffers, so we must use a renderer that
|
||||
// just pushes those buffers to the ANativeWindow.
|
||||
mVideoRenderer = new AwesomeNativeWindowRenderer(mSurface);
|
||||
mVideoRenderer =
|
||||
new AwesomeNativeWindowRenderer(mSurface, rotationDegrees);
|
||||
} else {
|
||||
// Other decoders are instantiated locally and as a consequence
|
||||
// allocate their buffers in local address space. This renderer
|
||||
@@ -834,7 +876,8 @@ void AwesomePlayer::initRenderer_l() {
|
||||
(OMX_COLOR_FORMATTYPE)format,
|
||||
mSurface,
|
||||
mVideoWidth, mVideoHeight,
|
||||
decodedWidth, decodedHeight);
|
||||
decodedWidth, decodedHeight,
|
||||
rotationDegrees);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1819,7 +1862,8 @@ status_t AwesomePlayer::resume() {
|
||||
state->mVideoWidth,
|
||||
state->mVideoHeight,
|
||||
state->mDecodedWidth,
|
||||
state->mDecodedHeight);
|
||||
state->mDecodedHeight,
|
||||
0);
|
||||
|
||||
mVideoRendererIsPreview = true;
|
||||
|
||||
|
||||
@@ -27,11 +27,11 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <media/stagefright/foundation/ADebug.h>
|
||||
#include <media/stagefright/DataSource.h>
|
||||
#include "include/ESDS.h"
|
||||
#include <media/stagefright/MediaBuffer.h>
|
||||
#include <media/stagefright/MediaBufferGroup.h>
|
||||
#include <media/stagefright/MediaDebug.h>
|
||||
#include <media/stagefright/MediaDefs.h>
|
||||
#include <media/stagefright/MediaSource.h>
|
||||
#include <media/stagefright/MetaData.h>
|
||||
@@ -766,55 +766,11 @@ status_t MPEG4Extractor::parseChunk(off_t *offset, int depth) {
|
||||
|
||||
case FOURCC('t', 'k', 'h', 'd'):
|
||||
{
|
||||
if (chunk_data_size < 4) {
|
||||
return ERROR_MALFORMED;
|
||||
status_t err;
|
||||
if ((err = parseTrackHeader(data_offset, chunk_data_size)) != OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
uint8_t version;
|
||||
if (mDataSource->readAt(data_offset, &version, 1) < 1) {
|
||||
return ERROR_IO;
|
||||
}
|
||||
|
||||
uint64_t ctime, mtime, duration;
|
||||
int32_t id;
|
||||
uint32_t width, height;
|
||||
|
||||
if (version == 1) {
|
||||
if (chunk_data_size != 36 + 60) {
|
||||
return ERROR_MALFORMED;
|
||||
}
|
||||
|
||||
uint8_t buffer[36 + 60];
|
||||
if (mDataSource->readAt(
|
||||
data_offset, buffer, sizeof(buffer)) < (ssize_t)sizeof(buffer)) {
|
||||
return ERROR_IO;
|
||||
}
|
||||
|
||||
ctime = U64_AT(&buffer[4]);
|
||||
mtime = U64_AT(&buffer[12]);
|
||||
id = U32_AT(&buffer[20]);
|
||||
duration = U64_AT(&buffer[28]);
|
||||
width = U32_AT(&buffer[88]);
|
||||
height = U32_AT(&buffer[92]);
|
||||
} else if (version == 0) {
|
||||
if (chunk_data_size != 24 + 60) {
|
||||
return ERROR_MALFORMED;
|
||||
}
|
||||
|
||||
uint8_t buffer[24 + 60];
|
||||
if (mDataSource->readAt(
|
||||
data_offset, buffer, sizeof(buffer)) < (ssize_t)sizeof(buffer)) {
|
||||
return ERROR_IO;
|
||||
}
|
||||
ctime = U32_AT(&buffer[4]);
|
||||
mtime = U32_AT(&buffer[8]);
|
||||
id = U32_AT(&buffer[12]);
|
||||
duration = U32_AT(&buffer[20]);
|
||||
width = U32_AT(&buffer[76]);
|
||||
height = U32_AT(&buffer[80]);
|
||||
}
|
||||
|
||||
mLastTrack->meta->setInt32(kKeyTrackID, id);
|
||||
*offset += chunk_size;
|
||||
break;
|
||||
}
|
||||
@@ -1275,6 +1231,93 @@ status_t MPEG4Extractor::parseChunk(off_t *offset, int depth) {
|
||||
return OK;
|
||||
}
|
||||
|
||||
status_t MPEG4Extractor::parseTrackHeader(
|
||||
off_t data_offset, off_t data_size) {
|
||||
if (data_size < 4) {
|
||||
return ERROR_MALFORMED;
|
||||
}
|
||||
|
||||
uint8_t version;
|
||||
if (mDataSource->readAt(data_offset, &version, 1) < 1) {
|
||||
return ERROR_IO;
|
||||
}
|
||||
|
||||
size_t dynSize = (version == 1) ? 36 : 24;
|
||||
|
||||
uint8_t buffer[36 + 60];
|
||||
|
||||
if (data_size != (off_t)dynSize + 60) {
|
||||
return ERROR_MALFORMED;
|
||||
}
|
||||
|
||||
if (mDataSource->readAt(
|
||||
data_offset, buffer, data_size) < (ssize_t)data_size) {
|
||||
return ERROR_IO;
|
||||
}
|
||||
|
||||
uint64_t ctime, mtime, duration;
|
||||
int32_t id;
|
||||
|
||||
if (version == 1) {
|
||||
ctime = U64_AT(&buffer[4]);
|
||||
mtime = U64_AT(&buffer[12]);
|
||||
id = U32_AT(&buffer[20]);
|
||||
duration = U64_AT(&buffer[28]);
|
||||
} else {
|
||||
CHECK_EQ((unsigned)version, 0u);
|
||||
|
||||
ctime = U32_AT(&buffer[4]);
|
||||
mtime = U32_AT(&buffer[8]);
|
||||
id = U32_AT(&buffer[12]);
|
||||
duration = U32_AT(&buffer[20]);
|
||||
}
|
||||
|
||||
mLastTrack->meta->setInt32(kKeyTrackID, id);
|
||||
|
||||
size_t matrixOffset = dynSize + 16;
|
||||
int32_t a00 = U32_AT(&buffer[matrixOffset]);
|
||||
int32_t a01 = U32_AT(&buffer[matrixOffset + 4]);
|
||||
int32_t dx = U32_AT(&buffer[matrixOffset + 8]);
|
||||
int32_t a10 = U32_AT(&buffer[matrixOffset + 12]);
|
||||
int32_t a11 = U32_AT(&buffer[matrixOffset + 16]);
|
||||
int32_t dy = U32_AT(&buffer[matrixOffset + 20]);
|
||||
|
||||
#if 0
|
||||
LOGI("x' = %.2f * x + %.2f * y + %.2f",
|
||||
a00 / 65536.0f, a01 / 65536.0f, dx / 65536.0f);
|
||||
LOGI("y' = %.2f * x + %.2f * y + %.2f",
|
||||
a10 / 65536.0f, a11 / 65536.0f, dy / 65536.0f);
|
||||
#endif
|
||||
|
||||
uint32_t rotationDegrees;
|
||||
|
||||
static const int32_t kFixedOne = 0x10000;
|
||||
if (a00 == kFixedOne && a01 == 0 && a10 == 0 && a11 == kFixedOne) {
|
||||
// Identity, no rotation
|
||||
rotationDegrees = 0;
|
||||
} else if (a00 == 0 && a01 == kFixedOne && a10 == -kFixedOne && a11 == 0) {
|
||||
rotationDegrees = 90;
|
||||
} else if (a00 == 0 && a01 == -kFixedOne && a10 == kFixedOne && a11 == 0) {
|
||||
rotationDegrees = 270;
|
||||
} else if (a00 == -kFixedOne && a01 == 0 && a10 == 0 && a11 == -kFixedOne) {
|
||||
rotationDegrees = 180;
|
||||
} else {
|
||||
LOGW("We only support 0,90,180,270 degree rotation matrices");
|
||||
rotationDegrees = 0;
|
||||
}
|
||||
|
||||
if (rotationDegrees != 0) {
|
||||
mLastTrack->meta->setInt32(kKeyRotation, rotationDegrees);
|
||||
}
|
||||
|
||||
#if 0
|
||||
uint32_t width = U32_AT(&buffer[dynSize + 52]);
|
||||
uint32_t height = U32_AT(&buffer[dynSize + 56]);
|
||||
#endif
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
status_t MPEG4Extractor::parseMetaData(off_t offset, size_t size) {
|
||||
if (size < 4) {
|
||||
return ERROR_MALFORMED;
|
||||
@@ -1588,7 +1631,7 @@ MPEG4Source::MPEG4Source(
|
||||
const uint8_t *ptr = (const uint8_t *)data;
|
||||
|
||||
CHECK(size >= 7);
|
||||
CHECK_EQ(ptr[0], 1); // configurationVersion == 1
|
||||
CHECK_EQ((unsigned)ptr[0], 1u); // configurationVersion == 1
|
||||
|
||||
// The number of bytes used to encode the length of a NAL unit.
|
||||
mNALLengthSize = 1 + (ptr[4] & 3);
|
||||
@@ -1736,7 +1779,7 @@ status_t MPEG4Source::read(
|
||||
}
|
||||
|
||||
uint32_t sampleTime;
|
||||
CHECK_EQ(OK, mSampleTable->getMetaDataForSample(
|
||||
CHECK_EQ((status_t)OK, mSampleTable->getMetaDataForSample(
|
||||
sampleIndex, NULL, NULL, &sampleTime));
|
||||
|
||||
if (mode == ReadOptions::SEEK_CLOSEST) {
|
||||
@@ -1783,7 +1826,7 @@ status_t MPEG4Source::read(
|
||||
err = mGroup->acquire_buffer(&mBuffer);
|
||||
|
||||
if (err != OK) {
|
||||
CHECK_EQ(mBuffer, NULL);
|
||||
CHECK(mBuffer == NULL);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,8 @@ SoftwareRenderer::SoftwareRenderer(
|
||||
OMX_COLOR_FORMATTYPE colorFormat,
|
||||
const sp<Surface> &surface,
|
||||
size_t displayWidth, size_t displayHeight,
|
||||
size_t decodedWidth, size_t decodedHeight)
|
||||
size_t decodedWidth, size_t decodedHeight,
|
||||
int32_t rotationDegrees)
|
||||
: mColorFormat(colorFormat),
|
||||
mConverter(NULL),
|
||||
mYUVMode(None),
|
||||
@@ -95,6 +96,20 @@ SoftwareRenderer::SoftwareRenderer(
|
||||
CHECK_EQ(0, native_window_set_buffers_geometry(
|
||||
mSurface.get(), mDecodedWidth, mDecodedHeight,
|
||||
halFormat));
|
||||
|
||||
uint32_t transform;
|
||||
switch (rotationDegrees) {
|
||||
case 0: transform = 0; break;
|
||||
case 90: transform = HAL_TRANSFORM_ROT_90; break;
|
||||
case 180: transform = HAL_TRANSFORM_ROT_180; break;
|
||||
case 270: transform = HAL_TRANSFORM_ROT_270; break;
|
||||
default: transform = 0; break;
|
||||
}
|
||||
|
||||
if (transform) {
|
||||
CHECK_EQ(0, native_window_set_buffers_transform(
|
||||
mSurface.get(), transform));
|
||||
}
|
||||
}
|
||||
|
||||
SoftwareRenderer::~SoftwareRenderer() {
|
||||
|
||||
@@ -88,6 +88,8 @@ private:
|
||||
bool mIsDrm;
|
||||
status_t parseDrmSINF(off_t *offset, off_t data_offset);
|
||||
|
||||
status_t parseTrackHeader(off_t data_offset, off_t data_size);
|
||||
|
||||
MPEG4Extractor(const MPEG4Extractor &);
|
||||
MPEG4Extractor &operator=(const MPEG4Extractor &);
|
||||
};
|
||||
|
||||
@@ -31,7 +31,8 @@ public:
|
||||
OMX_COLOR_FORMATTYPE colorFormat,
|
||||
const sp<Surface> &surface,
|
||||
size_t displayWidth, size_t displayHeight,
|
||||
size_t decodedWidth, size_t decodedHeight);
|
||||
size_t decodedWidth, size_t decodedHeight,
|
||||
int32_t rotationDegrees);
|
||||
|
||||
~SoftwareRenderer();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user