Merge "NuPlayerDriver: fix misc. getCurrentPosition issue."
diff --git a/include/media/stagefright/MetaData.h b/include/media/stagefright/MetaData.h
index 6c14a94..91341b8 100644
--- a/include/media/stagefright/MetaData.h
+++ b/include/media/stagefright/MetaData.h
@@ -185,6 +185,9 @@
// MPEG user data offsets
kKeyMpegUserData = 'mpud', // size_t[]
+
+ // Size of NALU length in mkv/mp4
+ kKeyNalLengthSize = 'nals', // int32_t
};
enum {
diff --git a/include/media/stagefright/NuMediaExtractor.h b/include/media/stagefright/NuMediaExtractor.h
index 1ba9545..b8bb824 100644
--- a/include/media/stagefright/NuMediaExtractor.h
+++ b/include/media/stagefright/NuMediaExtractor.h
@@ -114,6 +114,7 @@
bool getTotalBitrate(int64_t *bitRate) const;
void updateDurationAndBitrate();
+ status_t appendVorbisNumPageSamples(TrackInfo *info, const sp<ABuffer> &buffer);
DISALLOW_EVIL_CONSTRUCTORS(NuMediaExtractor);
};
diff --git a/include/ndk/NdkMediaCodec.h b/include/ndk/NdkMediaCodec.h
index 034cb35..c6035bd 100644
--- a/include/ndk/NdkMediaCodec.h
+++ b/include/ndk/NdkMediaCodec.h
@@ -154,6 +154,18 @@
media_status_t AMediaCodec_releaseOutputBuffer(AMediaCodec*, size_t idx, bool render);
/**
+ * Dynamically sets the output surface of a codec.
+ *
+ * This can only be used if the codec was configured with an output surface. The
+ * new output surface should have a compatible usage type to the original output surface.
+ * E.g. codecs may not support switching from a SurfaceTexture (GPU readable) output
+ * to ImageReader (software readable) output.
+ *
+ * For more details, see the Java documentation for MediaCodec.setOutputSurface.
+ */
+media_status_t AMediaCodec_setOutputSurface(AMediaCodec*, ANativeWindow* surface);
+
+/**
* If you are done with a buffer, use this call to update its surface timestamp
* and return it to the codec to render it on the output surface. If you
* have not specified an output surface when configuring this video codec,
@@ -164,7 +176,6 @@
media_status_t AMediaCodec_releaseOutputBufferAtTime(
AMediaCodec *mData, size_t idx, int64_t timestampNs);
-
typedef enum {
AMEDIACODECRYPTOINFO_MODE_CLEAR = 0,
AMEDIACODECRYPTOINFO_MODE_AES_CTR = 1
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index 5c13633..cede584 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -2142,11 +2142,7 @@
mPausedForBuffering = true;
onPause();
}
- // fall-thru
- }
- case Source::kWhatBufferingStart:
- {
notifyListener(MEDIA_INFO, MEDIA_INFO_BUFFERING_START, 0);
break;
}
@@ -2164,11 +2160,7 @@
onResume();
}
}
- // fall-thru
- }
- case Source::kWhatBufferingEnd:
- {
notifyListener(MEDIA_INFO, MEDIA_INFO_BUFFERING_END, 0);
break;
}
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
index ce87f87..4678956 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
@@ -525,7 +525,10 @@
ALOGI("[%s] resubmitting CSD", mComponentName.c_str());
msg->setBuffer("buffer", buffer);
mCSDsToSubmit.removeAt(0);
- CHECK(onInputBufferFetched(msg));
+ if (!onInputBufferFetched(msg)) {
+ handleError(UNKNOWN_ERROR);
+ return false;
+ }
return true;
}
@@ -862,7 +865,11 @@
// copy into codec buffer
if (buffer != codecBuffer) {
- CHECK_LE(buffer->size(), codecBuffer->capacity());
+ if (buffer->size() > codecBuffer->capacity()) {
+ handleError(ERROR_BUFFER_TOO_SMALL);
+ mDequeuedInputBuffers.push_back(bufferIx);
+ return false;
+ }
codecBuffer->setRange(0, buffer->size());
memcpy(codecBuffer->data(), buffer->data(), buffer->size());
}
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerSource.h b/media/libmediaplayerservice/nuplayer/NuPlayerSource.h
index fba4540..0176eafa 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerSource.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerSource.h
@@ -46,8 +46,6 @@
kWhatFlagsChanged,
kWhatVideoSizeChanged,
kWhatBufferingUpdate,
- kWhatBufferingStart,
- kWhatBufferingEnd,
kWhatPauseOnBufferingStart,
kWhatResumeOnBufferingEnd,
kWhatCacheStats,
diff --git a/media/libmediaplayerservice/nuplayer/RTSPSource.cpp b/media/libmediaplayerservice/nuplayer/RTSPSource.cpp
index c861fd1..2ecab6d 100644
--- a/media/libmediaplayerservice/nuplayer/RTSPSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/RTSPSource.cpp
@@ -725,7 +725,7 @@
mBuffering = true;
sp<AMessage> notify = dupNotify();
- notify->setInt32("what", kWhatBufferingStart);
+ notify->setInt32("what", kWhatPauseOnBufferingStart);
notify->post();
}
}
@@ -741,7 +741,7 @@
mBuffering = false;
sp<AMessage> notify = dupNotify();
- notify->setInt32("what", kWhatBufferingEnd);
+ notify->setInt32("what", kWhatResumeOnBufferingEnd);
notify->post();
}
diff --git a/media/libstagefright/NuMediaExtractor.cpp b/media/libstagefright/NuMediaExtractor.cpp
index 67d9921..8809640 100644
--- a/media/libstagefright/NuMediaExtractor.cpp
+++ b/media/libstagefright/NuMediaExtractor.cpp
@@ -449,6 +449,59 @@
return OK;
}
+status_t NuMediaExtractor::appendVorbisNumPageSamples(TrackInfo *info, const sp<ABuffer> &buffer) {
+ int32_t numPageSamples;
+ if (!info->mSample->meta_data()->findInt32(
+ kKeyValidSamples, &numPageSamples)) {
+ numPageSamples = -1;
+ }
+
+ memcpy((uint8_t *)buffer->data() + info->mSample->range_length(),
+ &numPageSamples,
+ sizeof(numPageSamples));
+
+ uint32_t type;
+ const void *data;
+ size_t size, size2;
+ if (info->mSample->meta_data()->findData(kKeyEncryptedSizes, &type, &data, &size)) {
+ // Signal numPageSamples (a plain int32_t) is appended at the end,
+ // i.e. sizeof(numPageSamples) plain bytes + 0 encrypted bytes
+ if (SIZE_MAX - size < sizeof(int32_t)) {
+ return -ENOMEM;
+ }
+
+ size_t newSize = size + sizeof(int32_t);
+ sp<ABuffer> abuf = new ABuffer(newSize);
+ uint8_t *adata = static_cast<uint8_t *>(abuf->data());
+ if (adata == NULL) {
+ return -ENOMEM;
+ }
+
+ // append 0 to encrypted sizes
+ int32_t zero = 0;
+ memcpy(adata, data, size);
+ memcpy(adata + size, &zero, sizeof(zero));
+ info->mSample->meta_data()->setData(kKeyEncryptedSizes, type, adata, newSize);
+
+ if (info->mSample->meta_data()->findData(kKeyPlainSizes, &type, &data, &size2)) {
+ if (size2 != size) {
+ return ERROR_MALFORMED;
+ }
+ memcpy(adata, data, size);
+ } else {
+ // if sample meta data does not include plain size array, assume filled with zeros,
+ // i.e. entire buffer is encrypted
+ memset(adata, 0, size);
+ }
+ // append sizeof(numPageSamples) to plain sizes.
+ int32_t int32Size = sizeof(numPageSamples);
+ memcpy(adata + size, &int32Size, sizeof(int32Size));
+ info->mSample->meta_data()->setData(kKeyPlainSizes, type, adata, newSize);
+ }
+
+ return OK;
+}
+
status_t NuMediaExtractor::readSampleData(const sp<ABuffer> &buffer) {
Mutex::Autolock autoLock(mLock);
@@ -478,21 +531,16 @@
memcpy((uint8_t *)buffer->data(), src, info->mSample->range_length());
+ status_t err = OK;
if (info->mTrackFlags & kIsVorbis) {
- int32_t numPageSamples;
- if (!info->mSample->meta_data()->findInt32(
- kKeyValidSamples, &numPageSamples)) {
- numPageSamples = -1;
- }
-
- memcpy((uint8_t *)buffer->data() + info->mSample->range_length(),
- &numPageSamples,
- sizeof(numPageSamples));
+ err = appendVorbisNumPageSamples(info, buffer);
}
- buffer->setRange(0, sampleSize);
+ if (err == OK) {
+ buffer->setRange(0, sampleSize);
+ }
- return OK;
+ return err;
}
status_t NuMediaExtractor::getSampleTrackIndex(size_t *trackIndex) {
diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp
index 2a8b635..17fa239 100644
--- a/media/libstagefright/Utils.cpp
+++ b/media/libstagefright/Utils.cpp
@@ -461,6 +461,13 @@
msg->setBuffer("csd-2", buffer);
}
+ // TODO expose "crypto-key"/kKeyCryptoKey through public api
+ if (meta->findData(kKeyCryptoKey, &type, &data, &size)) {
+ sp<ABuffer> buffer = new (std::nothrow) ABuffer(size);
+ msg->setBuffer("crypto-key", buffer);
+ memcpy(buffer->data(), data, size);
+ }
+
*format = msg;
return OK;
diff --git a/media/libstagefright/matroska/Android.mk b/media/libstagefright/matroska/Android.mk
index 193408c..b0cbf08 100644
--- a/media/libstagefright/matroska/Android.mk
+++ b/media/libstagefright/matroska/Android.mk
@@ -7,6 +7,7 @@
LOCAL_C_INCLUDES:= \
$(TOP)/external/libvpx/libwebm \
$(TOP)/frameworks/native/include/media/openmax \
+ $(TOP)/frameworks/av/media/libstagefright/include \
LOCAL_CFLAGS += -Wno-multichar -Werror -Wall
LOCAL_CLANG := true
diff --git a/media/libstagefright/matroska/MatroskaExtractor.cpp b/media/libstagefright/matroska/MatroskaExtractor.cpp
index 82b471f..861bdc5 100644
--- a/media/libstagefright/matroska/MatroskaExtractor.cpp
+++ b/media/libstagefright/matroska/MatroskaExtractor.cpp
@@ -19,9 +19,11 @@
#include <utils/Log.h>
#include "MatroskaExtractor.h"
+#include "avc_utils.h"
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AUtils.h>
+#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/hexdump.h>
#include <media/stagefright/DataSource.h>
#include <media/stagefright/MediaBuffer.h>
@@ -144,12 +146,13 @@
Type mType;
bool mIsAudio;
BlockIterator mBlockIter;
- size_t mNALSizeLen; // for type AVC
+ ssize_t mNALSizeLen; // for type AVC
List<MediaBuffer *> mPendingFrames;
status_t advance();
+ status_t setWebmBlockCryptoInfo(MediaBuffer *mbuf);
status_t readBlock();
void clearPendingFrames();
@@ -213,7 +216,7 @@
mBlockIter(mExtractor.get(),
mExtractor->mTracks.itemAt(index).mTrackNum,
index),
- mNALSizeLen(0) {
+ mNALSizeLen(-1) {
sp<MetaData> meta = mExtractor->mTracks.itemAt(index).mMeta;
const char *mime;
@@ -227,13 +230,18 @@
uint32_t dummy;
const uint8_t *avcc;
size_t avccSize;
- CHECK(meta->findData(
- kKeyAVCC, &dummy, (const void **)&avcc, &avccSize));
-
- CHECK_GE(avccSize, 5u);
-
- mNALSizeLen = 1 + (avcc[4] & 3);
- ALOGV("mNALSizeLen = %zu", mNALSizeLen);
+ int32_t nalSizeLen = 0;
+ if (meta->findInt32(kKeyNalLengthSize, &nalSizeLen)) {
+ if (nalSizeLen >= 0 && nalSizeLen <= 4) {
+ mNALSizeLen = nalSizeLen;
+ }
+ } else if (meta->findData(kKeyAVCC, &dummy, (const void **)&avcc, &avccSize)
+ && avccSize >= 5u) {
+ mNALSizeLen = 1 + (avcc[4] & 3);
+ ALOGV("mNALSizeLen = %zd", mNALSizeLen);
+ } else {
+ ALOGE("No mNALSizeLen");
+ }
} else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC)) {
mType = AAC;
}
@@ -244,6 +252,10 @@
}
status_t MatroskaSource::start(MetaData * /* params */) {
+ if (mType == AVC && mNALSizeLen < 0) {
+ return ERROR_MALFORMED;
+ }
+
mBlockIter.reset();
return OK;
@@ -492,6 +504,9 @@
}
int64_t BlockIterator::blockTimeUs() const {
+ if (mCluster == NULL || mBlockEntry == NULL) {
+ return -1;
+ }
return (mBlockEntry->GetBlock()->GetTime(mCluster) + 500ll) / 1000ll;
}
@@ -511,6 +526,72 @@
}
}
+status_t MatroskaSource::setWebmBlockCryptoInfo(MediaBuffer *mbuf) {
+ if (mbuf->range_length() < 1 || mbuf->range_length() - 1 > INT32_MAX) {
+ // 1-byte signal
+ return ERROR_MALFORMED;
+ }
+
+ const uint8_t *data = (const uint8_t *)mbuf->data() + mbuf->range_offset();
+ bool blockEncrypted = data[0] & 0x1;
+ if (blockEncrypted && mbuf->range_length() < 9) {
+ // 1-byte signal + 8-byte IV
+ return ERROR_MALFORMED;
+ }
+
+ sp<MetaData> meta = mbuf->meta_data();
+ if (blockEncrypted) {
+ /*
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Signal Byte | |
+ * +-+-+-+-+-+-+-+-+ IV |
+ * | |
+ * | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | | |
+ * |-+-+-+-+-+-+-+-+ |
+ * : Bytes 1..N of encrypted frame :
+ * | |
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+ int32_t plainSizes[] = { 0 };
+ int32_t encryptedSizes[] = { static_cast<int32_t>(mbuf->range_length() - 9) };
+ uint8_t ctrCounter[16] = { 0 };
+ uint32_t type;
+ const uint8_t *keyId;
+ size_t keyIdSize;
+ sp<MetaData> trackMeta = mExtractor->mTracks.itemAt(mTrackIndex).mMeta;
+ CHECK(trackMeta->findData(kKeyCryptoKey, &type, (const void **)&keyId, &keyIdSize));
+ meta->setData(kKeyCryptoKey, 0, keyId, keyIdSize);
+ memcpy(ctrCounter, data + 1, 8);
+ meta->setData(kKeyCryptoIV, 0, ctrCounter, 16);
+ meta->setData(kKeyPlainSizes, 0, plainSizes, sizeof(plainSizes));
+ meta->setData(kKeyEncryptedSizes, 0, encryptedSizes, sizeof(encryptedSizes));
+ mbuf->set_range(9, mbuf->range_length() - 9);
+ } else {
+ /*
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Signal Byte | |
+ * +-+-+-+-+-+-+-+-+ |
+ * : Bytes 1..N of unencrypted frame :
+ * | |
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+ int32_t plainSizes[] = { static_cast<int32_t>(mbuf->range_length() - 1) };
+ int32_t encryptedSizes[] = { 0 };
+ meta->setData(kKeyPlainSizes, 0, plainSizes, sizeof(plainSizes));
+ meta->setData(kKeyEncryptedSizes, 0, encryptedSizes, sizeof(encryptedSizes));
+ mbuf->set_range(1, mbuf->range_length() - 1);
+ }
+
+ return OK;
+}
+
status_t MatroskaSource::readBlock() {
CHECK(mPendingFrames.empty());
@@ -529,13 +610,19 @@
mbuf->meta_data()->setInt64(kKeyTime, timeUs);
mbuf->meta_data()->setInt32(kKeyIsSyncFrame, block->IsKey());
- long n = frame.Read(mExtractor->mReader, (unsigned char *)mbuf->data());
- if (n != 0) {
+ status_t err = frame.Read(mExtractor->mReader, static_cast<uint8_t *>(mbuf->data()));
+ if (err == OK
+ && mExtractor->mIsWebm
+ && mExtractor->mTracks.itemAt(mTrackIndex).mEncrypted) {
+ err = setWebmBlockCryptoInfo(mbuf);
+ }
+
+ if (err != OK) {
mPendingFrames.clear();
mBlockIter.advance();
mbuf->release();
- return ERROR_IO;
+ return err;
}
mPendingFrames.push_back(mbuf);
@@ -582,7 +669,7 @@
MediaBuffer *frame = *mPendingFrames.begin();
mPendingFrames.erase(mPendingFrames.begin());
- if (mType != AVC) {
+ if (mType != AVC || mNALSizeLen == 0) {
if (targetSampleTimeUs >= 0ll) {
frame->meta_data()->setInt64(
kKeyTargetTime, targetSampleTimeUs);
@@ -598,6 +685,9 @@
// followed by a corresponding number of bytes containing the fragment.
// We output all these fragments into a single large buffer separated
// by startcodes (0x00 0x00 0x00 0x01).
+ //
+ // When mNALSizeLen is 0, we assume the data is already in the format
+ // desired.
const uint8_t *srcPtr =
(const uint8_t *)frame->data() + frame->range_offset();
@@ -939,6 +1029,35 @@
return OK;
}
+status_t MatroskaExtractor::synthesizeAVCC(TrackInfo *trackInfo, size_t index) {
+ BlockIterator iter(this, trackInfo->mTrackNum, index);
+ if (iter.eos()) {
+ return ERROR_MALFORMED;
+ }
+
+ const mkvparser::Block *block = iter.block();
+ if (block->GetFrameCount() <= 0) {
+ return ERROR_MALFORMED;
+ }
+
+ const mkvparser::Block::Frame &frame = block->GetFrame(0);
+ sp<ABuffer> abuf = new ABuffer(frame.len);
+ long n = frame.Read(mReader, abuf->data());
+ if (n != 0) {
+ return ERROR_MALFORMED;
+ }
+
+ sp<MetaData> avcMeta = MakeAVCCodecSpecificData(abuf);
+ if (avcMeta == NULL) {
+ return ERROR_MALFORMED;
+ }
+
+ // Override the synthesized nal length size, which is arbitrary
+ avcMeta->setInt32(kKeyNalLengthSize, 0);
+ trackInfo->mMeta = avcMeta;
+ return OK;
+}
+
void MatroskaExtractor::addTracks() {
const mkvparser::Tracks *tracks = mSegment->GetTracks();
@@ -1051,10 +1170,31 @@
meta->setInt64(kKeyDuration, (durationNs + 500) / 1000);
mTracks.push();
- TrackInfo *trackInfo = &mTracks.editItemAt(mTracks.size() - 1);
+ size_t n = mTracks.size() - 1;
+ TrackInfo *trackInfo = &mTracks.editItemAt(n);
trackInfo->mTrackNum = track->GetNumber();
trackInfo->mMeta = meta;
trackInfo->mExtractor = this;
+
+ trackInfo->mEncrypted = false;
+ for(size_t i = 0; i < track->GetContentEncodingCount() && !trackInfo->mEncrypted; i++) {
+ const mkvparser::ContentEncoding *encoding = track->GetContentEncodingByIndex(i);
+ for(size_t j = 0; j < encoding->GetEncryptionCount(); j++) {
+ const mkvparser::ContentEncoding::ContentEncryption *encryption;
+ encryption = encoding->GetEncryptionByIndex(j);
+ meta->setData(kKeyCryptoKey, 0, encryption->key_id, encryption->key_id_len);
+ trackInfo->mEncrypted = true;
+ break;
+ }
+ }
+
+ if (!strcmp("V_MPEG4/ISO/AVC", codecID) && codecPrivateSize == 0) {
+ // Attempt to recover from AVC track without codec private data
+ err = synthesizeAVCC(trackInfo, n);
+ if (err != OK) {
+ mTracks.pop();
+ }
+ }
}
}
diff --git a/media/libstagefright/matroska/MatroskaExtractor.h b/media/libstagefright/matroska/MatroskaExtractor.h
index 120ef82..a1d6b00 100644
--- a/media/libstagefright/matroska/MatroskaExtractor.h
+++ b/media/libstagefright/matroska/MatroskaExtractor.h
@@ -55,6 +55,7 @@
struct TrackInfo {
unsigned long mTrackNum;
+ bool mEncrypted;
sp<MetaData> mMeta;
const MatroskaExtractor *mExtractor;
Vector<const mkvparser::CuePoint*> mCuePoints;
@@ -74,6 +75,7 @@
bool mIsWebm;
int64_t mSeekPreRollNs;
+ status_t synthesizeAVCC(TrackInfo *trackInfo, size_t index);
void addTracks();
void findThumbnails();
diff --git a/media/ndk/NdkMediaCodec.cpp b/media/ndk/NdkMediaCodec.cpp
index 8e36065..5bb2dcd 100644
--- a/media/ndk/NdkMediaCodec.cpp
+++ b/media/ndk/NdkMediaCodec.cpp
@@ -359,6 +359,15 @@
return translate_error(mData->mCodec->renderOutputBufferAndRelease(idx, timestampNs));
}
+EXPORT
+media_status_t AMediaCodec_setOutputSurface(AMediaCodec *mData, ANativeWindow* window) {
+ sp<Surface> surface = NULL;
+ if (window != NULL) {
+ surface = (Surface*) window;
+ }
+ return translate_error(mData->mCodec->setSurface(surface));
+}
+
//EXPORT
media_status_t AMediaCodec_setNotificationCallback(AMediaCodec *mData, OnCodecEvent callback,
void *userdata) {