From 288fb7e5bd7060cbe9b737500975754c9312e2db Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Thu, 6 Jan 2011 11:12:17 -0800 Subject: Add support for the "compilation" tag in mp3, mp4 and ogg, and also add support for two common ways of specifying album artist in ogg files. b/3311831 Change-Id: Iba1152013b7577168af71f947d7249560419fa05 --- include/media/mediametadataretriever.h | 1 + include/media/stagefright/MetaData.h | 1 + media/libstagefright/MP3Extractor.cpp | 1 + media/libstagefright/MPEG4Extractor.cpp | 11 +++++++++++ media/libstagefright/OggExtractor.cpp | 3 +++ media/libstagefright/StagefrightMediaScanner.cpp | 1 + media/libstagefright/StagefrightMetadataRetriever.cpp | 1 + 7 files changed, 19 insertions(+) diff --git a/include/media/mediametadataretriever.h b/include/media/mediametadataretriever.h index dbbcc49..ddc07f6 100644 --- a/include/media/mediametadataretriever.h +++ b/include/media/mediametadataretriever.h @@ -56,6 +56,7 @@ enum { METADATA_KEY_MIMETYPE = 22, METADATA_KEY_DISC_NUMBER = 23, METADATA_KEY_ALBUMARTIST = 24, + METADATA_KEY_COMPILATION = 25, // Add more here... }; diff --git a/include/media/stagefright/MetaData.h b/include/media/stagefright/MetaData.h index 29bfc4a..bffb9e0 100644 --- a/include/media/stagefright/MetaData.h +++ b/include/media/stagefright/MetaData.h @@ -73,6 +73,7 @@ enum { kKeyDiscNumber = 'dnum', // cstring kKeyDate = 'date', // cstring kKeyWriter = 'writ', // cstring + kKeyCompilation = 'cpil', // cstring kKeyTimeScale = 'tmsl', // int32_t // video profile and level diff --git a/media/libstagefright/MP3Extractor.cpp b/media/libstagefright/MP3Extractor.cpp index 82c0426..b15c720 100644 --- a/media/libstagefright/MP3Extractor.cpp +++ b/media/libstagefright/MP3Extractor.cpp @@ -743,6 +743,7 @@ sp MP3Extractor::getMetaData() { { kKeyAuthor, "TXT", "TEXT" }, { kKeyCDTrackNumber, "TRK", "TRCK" }, { kKeyDiscNumber, "TPA", "TPOS" }, + { kKeyCompilation, "TCP", "TCMP" }, }; static const size_t kNumMapEntries = sizeof(kMap) / sizeof(kMap[0]); diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp index 2154f2f..34064c8 100644 --- a/media/libstagefright/MPEG4Extractor.cpp +++ b/media/libstagefright/MPEG4Extractor.cpp @@ -1176,6 +1176,17 @@ status_t MPEG4Extractor::parseMetaData(off_t offset, size_t size) { metadataKey = kKeyGenre; break; } + case FOURCC('c', 'p', 'i', 'l'): + { + if (size == 9 && flags == 21) { + char tmp[16]; + sprintf(tmp, "%d", + (int)buffer[size - 1]); + + mFileMetaData->setCString(kKeyCompilation, tmp); + } + break; + } case FOURCC('t', 'r', 'k', 'n'): { if (size == 16 && flags == 0) { diff --git a/media/libstagefright/OggExtractor.cpp b/media/libstagefright/OggExtractor.cpp index 43938b2..0368fb7 100644 --- a/media/libstagefright/OggExtractor.cpp +++ b/media/libstagefright/OggExtractor.cpp @@ -660,6 +660,9 @@ void MyVorbisExtractor::parseFileMetaData() { } kMap[] = { { "TITLE", kKeyTitle }, { "ARTIST", kKeyArtist }, + { "ALBUMARTIST", kKeyAlbumArtist }, + { "ALBUM ARTIST", kKeyAlbumArtist }, + { "COMPILATION", kKeyCompilation }, { "ALBUM", kKeyAlbum }, { "COMPOSER", kKeyComposer }, { "GENRE", kKeyGenre }, diff --git a/media/libstagefright/StagefrightMediaScanner.cpp b/media/libstagefright/StagefrightMediaScanner.cpp index 1629e9f..d3aa2f6 100644 --- a/media/libstagefright/StagefrightMediaScanner.cpp +++ b/media/libstagefright/StagefrightMediaScanner.cpp @@ -155,6 +155,7 @@ status_t StagefrightMediaScanner::processFile( { "year", METADATA_KEY_YEAR }, { "duration", METADATA_KEY_DURATION }, { "writer", METADATA_KEY_WRITER }, + { "compilation", METADATA_KEY_COMPILATION }, }; static const size_t kNumEntries = sizeof(kKeyMap) / sizeof(kKeyMap[0]); diff --git a/media/libstagefright/StagefrightMetadataRetriever.cpp b/media/libstagefright/StagefrightMetadataRetriever.cpp index 9b2dec9..ac208cd 100644 --- a/media/libstagefright/StagefrightMetadataRetriever.cpp +++ b/media/libstagefright/StagefrightMetadataRetriever.cpp @@ -355,6 +355,7 @@ void StagefrightMetadataRetriever::parseMetaData() { { kKeyTitle, METADATA_KEY_TITLE }, { kKeyYear, METADATA_KEY_YEAR }, { kKeyWriter, METADATA_KEY_WRITER }, + { kKeyCompilation, METADATA_KEY_COMPILATION }, }; static const size_t kNumMapEntries = sizeof(kMap) / sizeof(kMap[0]); -- cgit v1.1 From 32c8335c6b6eb87a88e9193db1a5aab29175d043 Mon Sep 17 00:00:00 2001 From: James Dong Date: Mon, 10 Jan 2011 08:55:02 -0800 Subject: Avoid deadlock in OMX::freeNode by making sure OMXCodecObserver does not hold the last reference of OMXCodec object - do not merge cherry-picked from: I4c79b66a900c527e3ae6a833f76d5da1b75c5a89 bug - 3336424 Change-Id: I2d8ecb79a5422342988c195c012c9e6327ac457a --- include/media/stagefright/OMXCodec.h | 9 +++++++-- media/libstagefright/OMXCodec.cpp | 4 ++-- media/libstagefright/omx/OMX.cpp | 12 +++++++++--- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/include/media/stagefright/OMXCodec.h b/include/media/stagefright/OMXCodec.h index fed6761..8274dfb 100644 --- a/include/media/stagefright/OMXCodec.h +++ b/include/media/stagefright/OMXCodec.h @@ -60,8 +60,6 @@ struct OMXCodec : public MediaSource, virtual status_t pause(); - void on_message(const omx_message &msg); - // from MediaBufferObserver virtual void signalBufferReturned(MediaBuffer *buffer); @@ -69,6 +67,13 @@ protected: virtual ~OMXCodec(); private: + + // Make sure mLock is accessible to OMXCodecObserver + friend class OMXCodecObserver; + + // Call this with mLock hold + void on_message(const omx_message &msg); + enum State { DEAD, LOADED, diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp index 980da77..b5d00bf 100644 --- a/media/libstagefright/OMXCodec.cpp +++ b/media/libstagefright/OMXCodec.cpp @@ -234,7 +234,9 @@ struct OMXCodecObserver : public BnOMXObserver { sp codec = mTarget.promote(); if (codec.get() != NULL) { + Mutex::Autolock autoLock(codec->mLock); codec->on_message(msg); + codec.clear(); } } @@ -1672,8 +1674,6 @@ status_t OMXCodec::allocateBuffersOnPort(OMX_U32 portIndex) { } void OMXCodec::on_message(const omx_message &msg) { - Mutex::Autolock autoLock(mLock); - switch (msg.type) { case omx_message::EVENT: { diff --git a/media/libstagefright/omx/OMX.cpp b/media/libstagefright/omx/OMX.cpp index f19c16a..2ba63f7 100644 --- a/media/libstagefright/omx/OMX.cpp +++ b/media/libstagefright/omx/OMX.cpp @@ -89,6 +89,9 @@ OMX::CallbackDispatcher::~CallbackDispatcher() { mQueueChanged.signal(); } + // Don't call join on myself + CHECK(mThread != pthread_self()); + void *dummy; pthread_join(mThread, &dummy); } @@ -249,9 +252,12 @@ status_t OMX::freeNode(node_id node) { status_t err = instance->freeNode(mMaster); - index = mDispatchers.indexOfKey(node); - CHECK(index >= 0); - mDispatchers.removeItemsAt(index); + { + Mutex::Autolock autoLock(mLock); + index = mDispatchers.indexOfKey(node); + CHECK(index >= 0); + mDispatchers.removeItemsAt(index); + } return err; } -- cgit v1.1 From 0d85990f20106513b6da5a446702aa62ab61397f Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Tue, 11 Jan 2011 09:51:34 -0800 Subject: DO NOT MERGE: Fix parsing of ntp= PLAY response. related-to-bug: 3340186 Squashed commit of the following: commit b61c36b7228aec9f5360883b1e1c1e0530488974 Author: Andreas Huber Date: Wed Oct 27 13:59:59 2010 -0700 Better support for MP4A-LATM RTP disassembly. This used to fail if mNumSubFrames > 1 and the sub frames did not align with RTP packet boundaries. commit b10f322c07e5bebcaf032e8624cb4a5d733dfc15 Author: Andreas Huber Date: Mon Oct 25 09:40:52 2010 -0700 We don't have access to the md5 implementation on the simulator, let's disable digest authentication in rtsp for simulator targets. commit 0aa83cf9e4637adf9501708fcdf7d0d6d4dc4fe1 Author: Andreas Huber Date: Wed Oct 20 15:00:34 2010 -0700 Support for BASIC and DIGEST authentication schemes in RTSP. Support for malformed packet descriptions that end lines in LF only, instead of CRLF. related-to-bug: 3084183 Change-Id: I6e512cb73cc8d5624a83f7154aa5699f7fef7534 --- media/libstagefright/rtsp/AMPEG4AudioAssembler.cpp | 384 ++++++++++++++++++++- media/libstagefright/rtsp/AMPEG4AudioAssembler.h | 13 +- media/libstagefright/rtsp/ARTPSource.cpp | 2 +- media/libstagefright/rtsp/ARTSPConnection.cpp | 265 +++++++++++++- media/libstagefright/rtsp/ARTSPConnection.h | 20 +- media/libstagefright/rtsp/ASessionDescription.cpp | 19 +- media/libstagefright/rtsp/Android.mk | 1 + media/libstagefright/rtsp/MyHandler.h | 25 +- 8 files changed, 690 insertions(+), 39 deletions(-) diff --git a/media/libstagefright/rtsp/AMPEG4AudioAssembler.cpp b/media/libstagefright/rtsp/AMPEG4AudioAssembler.cpp index b0d2c64..bbde516 100644 --- a/media/libstagefright/rtsp/AMPEG4AudioAssembler.cpp +++ b/media/libstagefright/rtsp/AMPEG4AudioAssembler.cpp @@ -18,18 +18,381 @@ #include "ARTPSource.h" +#include +#include #include #include #include +#include + +#include namespace android { -AMPEG4AudioAssembler::AMPEG4AudioAssembler(const sp ¬ify) +static bool GetAttribute(const char *s, const char *key, AString *value) { + value->clear(); + + size_t keyLen = strlen(key); + + for (;;) { + while (isspace(*s)) { + ++s; + } + + const char *colonPos = strchr(s, ';'); + + size_t len = + (colonPos == NULL) ? strlen(s) : colonPos - s; + + if (len >= keyLen + 1 && s[keyLen] == '=' && !strncmp(s, key, keyLen)) { + value->setTo(&s[keyLen + 1], len - keyLen - 1); + return true; + } + + if (colonPos == NULL) { + return false; + } + + s = colonPos + 1; + } +} + +static sp decodeHex(const AString &s) { + if ((s.size() % 2) != 0) { + return NULL; + } + + size_t outLen = s.size() / 2; + sp buffer = new ABuffer(outLen); + uint8_t *out = buffer->data(); + + uint8_t accum = 0; + for (size_t i = 0; i < s.size(); ++i) { + char c = s.c_str()[i]; + unsigned value; + if (c >= '0' && c <= '9') { + value = c - '0'; + } else if (c >= 'a' && c <= 'f') { + value = c - 'a' + 10; + } else if (c >= 'A' && c <= 'F') { + value = c - 'A' + 10; + } else { + return NULL; + } + + accum = (accum << 4) | value; + + if (i & 1) { + *out++ = accum; + + accum = 0; + } + } + + return buffer; +} + +static status_t parseAudioObjectType( + ABitReader *bits, unsigned *audioObjectType) { + *audioObjectType = bits->getBits(5); + if ((*audioObjectType) == 31) { + *audioObjectType = 32 + bits->getBits(6); + } + + return OK; +} + +static status_t parseGASpecificConfig( + ABitReader *bits, + unsigned audioObjectType, unsigned channelConfiguration) { + unsigned frameLengthFlag = bits->getBits(1); + unsigned dependsOnCoreCoder = bits->getBits(1); + if (dependsOnCoreCoder) { + /* unsigned coreCoderDelay = */bits->getBits(1); + } + unsigned extensionFlag = bits->getBits(1); + + if (!channelConfiguration) { + // program_config_element + return ERROR_UNSUPPORTED; // XXX to be implemented + } + + if (audioObjectType == 6 || audioObjectType == 20) { + /* unsigned layerNr = */bits->getBits(3); + } + + if (extensionFlag) { + if (audioObjectType == 22) { + /* unsigned numOfSubFrame = */bits->getBits(5); + /* unsigned layerLength = */bits->getBits(11); + } else if (audioObjectType == 17 || audioObjectType == 19 + || audioObjectType == 20 || audioObjectType == 23) { + /* unsigned aacSectionDataResilienceFlag = */bits->getBits(1); + /* unsigned aacScalefactorDataResilienceFlag = */bits->getBits(1); + /* unsigned aacSpectralDataResilienceFlag = */bits->getBits(1); + } + + unsigned extensionFlag3 = bits->getBits(1); + CHECK_EQ(extensionFlag3, 0u); // TBD in version 3 + } + + return OK; +} + +static status_t parseAudioSpecificConfig(ABitReader *bits) { + unsigned audioObjectType; + CHECK_EQ(parseAudioObjectType(bits, &audioObjectType), (status_t)OK); + + unsigned samplingFreqIndex = bits->getBits(4); + if (samplingFreqIndex == 0x0f) { + /* unsigned samplingFrequency = */bits->getBits(24); + } + + unsigned channelConfiguration = bits->getBits(4); + + unsigned extensionAudioObjectType = 0; + unsigned sbrPresent = 0; + + if (audioObjectType == 5) { + extensionAudioObjectType = audioObjectType; + sbrPresent = 1; + unsigned extensionSamplingFreqIndex = bits->getBits(4); + if (extensionSamplingFreqIndex == 0x0f) { + /* unsigned extensionSamplingFrequency = */bits->getBits(24); + } + CHECK_EQ(parseAudioObjectType(bits, &audioObjectType), (status_t)OK); + } + + CHECK((audioObjectType >= 1 && audioObjectType <= 4) + || (audioObjectType >= 6 && audioObjectType <= 7) + || audioObjectType == 17 + || (audioObjectType >= 19 && audioObjectType <= 23)); + + CHECK_EQ(parseGASpecificConfig( + bits, audioObjectType, channelConfiguration), (status_t)OK); + + if (audioObjectType == 17 + || (audioObjectType >= 19 && audioObjectType <= 27)) { + unsigned epConfig = bits->getBits(2); + if (epConfig == 2 || epConfig == 3) { + // ErrorProtectionSpecificConfig + return ERROR_UNSUPPORTED; // XXX to be implemented + + if (epConfig == 3) { + unsigned directMapping = bits->getBits(1); + CHECK_EQ(directMapping, 1u); + } + } + } + +#if 0 + // This is not supported here as the upper layers did not explicitly + // signal the length of AudioSpecificConfig. + + if (extensionAudioObjectType != 5 && bits->numBitsLeft() >= 16) { + unsigned syncExtensionType = bits->getBits(11); + if (syncExtensionType == 0x2b7) { + CHECK_EQ(parseAudioObjectType(bits, &extensionAudioObjectType), + (status_t)OK); + + sbrPresent = bits->getBits(1); + + if (sbrPresent == 1) { + unsigned extensionSamplingFreqIndex = bits->getBits(4); + if (extensionSamplingFreqIndex == 0x0f) { + /* unsigned extensionSamplingFrequency = */bits->getBits(24); + } + } + } + } +#endif + + return OK; +} + +static status_t parseStreamMuxConfig( + ABitReader *bits, + unsigned *numSubFrames, + unsigned *frameLengthType, + bool *otherDataPresent, + unsigned *otherDataLenBits) { + unsigned audioMuxVersion = bits->getBits(1); + + unsigned audioMuxVersionA = 0; + if (audioMuxVersion == 1) { + audioMuxVersionA = bits->getBits(1); + } + + CHECK_EQ(audioMuxVersionA, 0u); // otherwise future spec + + if (audioMuxVersion != 0) { + return ERROR_UNSUPPORTED; // XXX to be implemented; + } + CHECK_EQ(audioMuxVersion, 0u); // XXX to be implemented + + unsigned allStreamsSameTimeFraming = bits->getBits(1); + CHECK_EQ(allStreamsSameTimeFraming, 1u); // There's only one stream. + + *numSubFrames = bits->getBits(6); + unsigned numProgram = bits->getBits(4); + CHECK_EQ(numProgram, 0u); // disabled in RTP LATM + + unsigned numLayer = bits->getBits(3); + CHECK_EQ(numLayer, 0u); // disabled in RTP LATM + + if (audioMuxVersion == 0) { + // AudioSpecificConfig + CHECK_EQ(parseAudioSpecificConfig(bits), (status_t)OK); + } else { + TRESPASS(); // XXX to be implemented + } + + *frameLengthType = bits->getBits(3); + switch (*frameLengthType) { + case 0: + { + /* unsigned bufferFullness = */bits->getBits(8); + + // The "coreFrameOffset" does not apply since there's only + // a single layer. + break; + } + + case 1: + { + /* unsigned frameLength = */bits->getBits(9); + break; + } + + case 3: + case 4: + case 5: + { + /* unsigned CELPframeLengthTableIndex = */bits->getBits(6); + break; + } + + case 6: + case 7: + { + /* unsigned HVXCframeLengthTableIndex = */bits->getBits(1); + break; + } + + default: + break; + } + + *otherDataPresent = bits->getBits(1); + *otherDataLenBits = 0; + if (*otherDataPresent) { + if (audioMuxVersion == 1) { + TRESPASS(); // XXX to be implemented + } else { + *otherDataLenBits = 0; + + unsigned otherDataLenEsc; + do { + (*otherDataLenBits) <<= 8; + otherDataLenEsc = bits->getBits(1); + unsigned otherDataLenTmp = bits->getBits(8); + (*otherDataLenBits) += otherDataLenTmp; + } while (otherDataLenEsc); + } + } + + unsigned crcCheckPresent = bits->getBits(1); + if (crcCheckPresent) { + /* unsigned crcCheckSum = */bits->getBits(8); + } + + return OK; +} + +sp AMPEG4AudioAssembler::removeLATMFraming(const sp &buffer) { + CHECK(!mMuxConfigPresent); // XXX to be implemented + + sp out = new ABuffer(buffer->size()); + out->setRange(0, 0); + + size_t offset = 0; + uint8_t *ptr = buffer->data(); + + for (size_t i = 0; i <= mNumSubFrames; ++i) { + // parse PayloadLengthInfo + + unsigned payloadLength = 0; + + switch (mFrameLengthType) { + case 0: + { + unsigned muxSlotLengthBytes = 0; + unsigned tmp; + do { + CHECK_LT(offset, buffer->size()); + tmp = ptr[offset++]; + muxSlotLengthBytes += tmp; + } while (tmp == 0xff); + + payloadLength = muxSlotLengthBytes; + break; + } + + default: + TRESPASS(); // XXX to be implemented + break; + } + + CHECK_LE(offset + payloadLength, buffer->size()); + + memcpy(out->data() + out->size(), &ptr[offset], payloadLength); + out->setRange(0, out->size() + payloadLength); + + offset += payloadLength; + + if (mOtherDataPresent) { + // We want to stay byte-aligned. + + CHECK((mOtherDataLenBits % 8) == 0); + CHECK_LE(offset + (mOtherDataLenBits / 8), buffer->size()); + offset += mOtherDataLenBits / 8; + } + } + + CHECK_EQ(offset, buffer->size()); + + return out; +} + +AMPEG4AudioAssembler::AMPEG4AudioAssembler( + const sp ¬ify, const AString ¶ms) : mNotifyMsg(notify), + mMuxConfigPresent(false), mAccessUnitRTPTime(0), mNextExpectedSeqNoValid(false), mNextExpectedSeqNo(0), mAccessUnitDamaged(false) { + AString val; + if (!GetAttribute(params.c_str(), "cpresent", &val)) { + mMuxConfigPresent = true; + } else if (val == "0") { + mMuxConfigPresent = false; + } else { + CHECK(val == "1"); + mMuxConfigPresent = true; + } + + CHECK(GetAttribute(params.c_str(), "config", &val)); + + sp config = decodeHex(val); + CHECK(config != NULL); + + ABitReader bits(config->data(), config->size()); + status_t err = parseStreamMuxConfig( + &bits, &mNumSubFrames, &mFrameLengthType, + &mOtherDataPresent, &mOtherDataLenBits); + + CHECK_EQ(err, (status_t)NO_ERROR); } AMPEG4AudioAssembler::~AMPEG4AudioAssembler() { @@ -108,13 +471,7 @@ void AMPEG4AudioAssembler::submitAccessUnit() { while (it != mPackets.end()) { const sp &unit = *it; - size_t n = 0; - while (unit->data()[n] == 0xff) { - ++n; - } - ++n; - - totalSize += unit->size() - n; + totalSize += unit->size(); ++it; } @@ -124,20 +481,13 @@ void AMPEG4AudioAssembler::submitAccessUnit() { while (it != mPackets.end()) { const sp &unit = *it; - size_t n = 0; - while (unit->data()[n] == 0xff) { - ++n; - } - ++n; - memcpy((uint8_t *)accessUnit->data() + offset, - unit->data() + n, unit->size() - n); - - offset += unit->size() - n; + unit->data(), unit->size()); ++it; } + accessUnit = removeLATMFraming(accessUnit); CopyTimes(accessUnit, *mPackets.begin()); #if 0 diff --git a/media/libstagefright/rtsp/AMPEG4AudioAssembler.h b/media/libstagefright/rtsp/AMPEG4AudioAssembler.h index bf9f204..9cef94c 100644 --- a/media/libstagefright/rtsp/AMPEG4AudioAssembler.h +++ b/media/libstagefright/rtsp/AMPEG4AudioAssembler.h @@ -27,9 +27,11 @@ namespace android { struct AMessage; +struct AString; struct AMPEG4AudioAssembler : public ARTPAssembler { - AMPEG4AudioAssembler(const sp ¬ify); + AMPEG4AudioAssembler( + const sp ¬ify, const AString ¶ms); protected: virtual ~AMPEG4AudioAssembler(); @@ -40,6 +42,13 @@ protected: private: sp mNotifyMsg; + + bool mMuxConfigPresent; + unsigned mNumSubFrames; + unsigned mFrameLengthType; + bool mOtherDataPresent; + unsigned mOtherDataLenBits; + uint32_t mAccessUnitRTPTime; bool mNextExpectedSeqNoValid; uint32_t mNextExpectedSeqNo; @@ -49,6 +58,8 @@ private: AssemblyStatus addPacket(const sp &source); void submitAccessUnit(); + sp removeLATMFraming(const sp &buffer); + DISALLOW_EVIL_CONSTRUCTORS(AMPEG4AudioAssembler); }; diff --git a/media/libstagefright/rtsp/ARTPSource.cpp b/media/libstagefright/rtsp/ARTPSource.cpp index 2518264..5aae4e7 100644 --- a/media/libstagefright/rtsp/ARTPSource.cpp +++ b/media/libstagefright/rtsp/ARTPSource.cpp @@ -57,7 +57,7 @@ ARTPSource::ARTPSource( mAssembler = new AAVCAssembler(notify); mIssueFIRRequests = true; } else if (!strncmp(desc.c_str(), "MP4A-LATM/", 10)) { - mAssembler = new AMPEG4AudioAssembler(notify); + mAssembler = new AMPEG4AudioAssembler(notify, params); } else if (!strncmp(desc.c_str(), "H263-1998/", 10) || !strncmp(desc.c_str(), "H263-2000/", 10)) { mAssembler = new AH263Assembler(notify); diff --git a/media/libstagefright/rtsp/ARTSPConnection.cpp b/media/libstagefright/rtsp/ARTSPConnection.cpp index 5ec03b2..e936923 100644 --- a/media/libstagefright/rtsp/ARTSPConnection.cpp +++ b/media/libstagefright/rtsp/ARTSPConnection.cpp @@ -23,11 +23,13 @@ #include #include #include +#include #include #include #include #include +#include #include namespace android { @@ -37,6 +39,7 @@ const int64_t ARTSPConnection::kSelectTimeoutUs = 1000ll; ARTSPConnection::ARTSPConnection() : mState(DISCONNECTED), + mAuthType(NONE), mSocket(-1), mConnectionID(0), mNextCSeq(0), @@ -114,10 +117,13 @@ void ARTSPConnection::onMessageReceived(const sp &msg) { // static bool ARTSPConnection::ParseURL( - const char *url, AString *host, unsigned *port, AString *path) { + const char *url, AString *host, unsigned *port, AString *path, + AString *user, AString *pass) { host->clear(); *port = 0; path->clear(); + user->clear(); + pass->clear(); if (strncasecmp("rtsp://", url, 7)) { return false; @@ -133,7 +139,25 @@ bool ARTSPConnection::ParseURL( path->setTo(slashPos); } - char *colonPos = strchr(host->c_str(), ':'); + ssize_t atPos = host->find("@"); + + if (atPos >= 0) { + // Split of user:pass@ from hostname. + + AString userPass(*host, 0, atPos); + host->erase(0, atPos + 1); + + ssize_t colonPos = userPass.find(":"); + + if (colonPos < 0) { + *user = userPass; + } else { + user->setTo(userPass, 0, colonPos); + pass->setTo(userPass, colonPos + 1, userPass.size() - colonPos - 1); + } + } + + const char *colonPos = strchr(host->c_str(), ':'); if (colonPos != NULL) { unsigned long x; @@ -187,7 +211,12 @@ void ARTSPConnection::onConnect(const sp &msg) { AString host, path; unsigned port; - if (!ParseURL(url.c_str(), &host, &port, &path)) { + if (!ParseURL(url.c_str(), &host, &port, &path, &mUser, &mPass) + || (mUser.size() > 0 && mPass.size() == 0)) { + // If we have a user name but no password we have to give up + // right here, since we currently have no way of asking the user + // for this information. + LOGE("Malformed rtsp url %s", url.c_str()); reply->setInt32("result", ERROR_MALFORMED); @@ -197,6 +226,10 @@ void ARTSPConnection::onConnect(const sp &msg) { return; } + if (mUser.size() > 0) { + LOGV("user = '%s', pass = '%s'", mUser.c_str(), mPass.c_str()); + } + struct hostent *ent = gethostbyname(host.c_str()); if (ent == NULL) { LOGE("Unknown host %s", host.c_str()); @@ -262,6 +295,11 @@ void ARTSPConnection::onDisconnect(const sp &msg) { reply->setInt32("result", OK); mState = DISCONNECTED; + mUser.clear(); + mPass.clear(); + mAuthType = NONE; + mNonce.clear(); + reply->post(); } @@ -335,6 +373,12 @@ void ARTSPConnection::onSendRequest(const sp &msg) { AString request; CHECK(msg->findString("request", &request)); + // Just in case we need to re-issue the request with proper authentication + // later, stash it away. + reply->setString("original-request", request.c_str(), request.size()); + + addAuthentication(&request); + // Find the boundary between headers and the body. ssize_t i = request.find("\r\n\r\n"); CHECK_GE(i, 0); @@ -347,7 +391,7 @@ void ARTSPConnection::onSendRequest(const sp &msg) { request.insert(cseqHeader, i + 2); - LOGV("%s", request.c_str()); + LOGV("request: '%s'", request.c_str()); size_t numBytesSent = 0; while (numBytesSent < request.size()) { @@ -612,6 +656,30 @@ bool ARTSPConnection::receiveRTSPReponse() { } } + if (response->mStatusCode == 401) { + if (mAuthType == NONE && mUser.size() > 0 + && parseAuthMethod(response)) { + ssize_t i; + CHECK_EQ((status_t)OK, findPendingRequest(response, &i)); + CHECK_GE(i, 0); + + sp reply = mPendingRequests.valueAt(i); + mPendingRequests.removeItemsAt(i); + + AString request; + CHECK(reply->findString("original-request", &request)); + + sp msg = new AMessage(kWhatSendRequest, id()); + msg->setMessage("reply", reply); + msg->setString("request", request.c_str(), request.size()); + + LOGI("re-sending request with authentication headers..."); + onSendRequest(msg); + + return true; + } + } + return notifyResponseListener(response); } @@ -628,26 +696,47 @@ bool ARTSPConnection::ParseSingleUnsignedLong( return true; } -bool ARTSPConnection::notifyResponseListener( - const sp &response) { +status_t ARTSPConnection::findPendingRequest( + const sp &response, ssize_t *index) const { + *index = 0; + ssize_t i = response->mHeaders.indexOfKey("cseq"); if (i < 0) { - return true; + // This is an unsolicited server->client message. + return OK; } AString value = response->mHeaders.valueAt(i); unsigned long cseq; if (!ParseSingleUnsignedLong(value.c_str(), &cseq)) { - return false; + return ERROR_MALFORMED; } i = mPendingRequests.indexOfKey(cseq); if (i < 0) { - // Unsolicited response? - TRESPASS(); + return -ENOENT; + } + + *index = i; + + return OK; +} + +bool ARTSPConnection::notifyResponseListener( + const sp &response) { + ssize_t i; + status_t err = findPendingRequest(response, &i); + + if (err == OK && i < 0) { + // An unsolicited server response is not a problem. + return true; + } + + if (err != OK) { + return false; } sp reply = mPendingRequests.valueAt(i); @@ -660,4 +749,160 @@ bool ARTSPConnection::notifyResponseListener( return true; } +bool ARTSPConnection::parseAuthMethod(const sp &response) { + ssize_t i = response->mHeaders.indexOfKey("www-authenticate"); + + if (i < 0) { + return false; + } + + AString value = response->mHeaders.valueAt(i); + + if (!strncmp(value.c_str(), "Basic", 5)) { + mAuthType = BASIC; + } else { +#if !defined(HAVE_ANDROID_OS) + // We don't have access to the MD5 implementation on the simulator, + // so we won't support digest authentication. + return false; +#endif + + CHECK(!strncmp(value.c_str(), "Digest", 6)); + mAuthType = DIGEST; + + i = value.find("nonce="); + CHECK_GE(i, 0); + CHECK_EQ(value.c_str()[i + 6], '\"'); + ssize_t j = value.find("\"", i + 7); + CHECK_GE(j, 0); + + mNonce.setTo(value, i + 7, j - i - 7); + } + + return true; +} + +#if defined(HAVE_ANDROID_OS) +static void H(const AString &s, AString *out) { + out->clear(); + + MD5_CTX m; + MD5_Init(&m); + MD5_Update(&m, s.c_str(), s.size()); + + uint8_t key[16]; + MD5_Final(key, &m); + + for (size_t i = 0; i < 16; ++i) { + char nibble = key[i] >> 4; + if (nibble <= 9) { + nibble += '0'; + } else { + nibble += 'a' - 10; + } + out->append(&nibble, 1); + + nibble = key[i] & 0x0f; + if (nibble <= 9) { + nibble += '0'; + } else { + nibble += 'a' - 10; + } + out->append(&nibble, 1); + } +} +#endif + +static void GetMethodAndURL( + const AString &request, AString *method, AString *url) { + ssize_t space1 = request.find(" "); + CHECK_GE(space1, 0); + + ssize_t space2 = request.find(" ", space1 + 1); + CHECK_GE(space2, 0); + + method->setTo(request, 0, space1); + url->setTo(request, space1 + 1, space2 - space1); +} + +void ARTSPConnection::addAuthentication(AString *request) { + if (mAuthType == NONE) { + return; + } + + // Find the boundary between headers and the body. + ssize_t i = request->find("\r\n\r\n"); + CHECK_GE(i, 0); + + if (mAuthType == BASIC) { + AString tmp; + tmp.append(mUser); + tmp.append(":"); + tmp.append(mPass); + + AString out; + encodeBase64(tmp.c_str(), tmp.size(), &out); + + AString fragment; + fragment.append("Authorization: Basic "); + fragment.append(out); + fragment.append("\r\n"); + + request->insert(fragment, i + 2); + + return; + } + +#if defined(HAVE_ANDROID_OS) + CHECK_EQ((int)mAuthType, (int)DIGEST); + + AString method, url; + GetMethodAndURL(*request, &method, &url); + + AString A1; + A1.append(mUser); + A1.append(":"); + A1.append("Streaming Server"); + A1.append(":"); + A1.append(mPass); + + AString A2; + A2.append(method); + A2.append(":"); + A2.append(url); + + AString HA1, HA2; + H(A1, &HA1); + H(A2, &HA2); + + AString tmp; + tmp.append(HA1); + tmp.append(":"); + tmp.append(mNonce); + tmp.append(":"); + tmp.append(HA2); + + AString digest; + H(tmp, &digest); + + AString fragment; + fragment.append("Authorization: Digest "); + fragment.append("nonce=\""); + fragment.append(mNonce); + fragment.append("\", "); + fragment.append("username=\""); + fragment.append(mUser); + fragment.append("\", "); + fragment.append("uri=\""); + fragment.append(url); + fragment.append("\", "); + fragment.append("response=\""); + fragment.append(digest); + fragment.append("\""); + fragment.append("\r\n"); + + request->insert(fragment, i + 2); +#endif +} + } // namespace android diff --git a/media/libstagefright/rtsp/ARTSPConnection.h b/media/libstagefright/rtsp/ARTSPConnection.h index 96e0d5b..19be2a6 100644 --- a/media/libstagefright/rtsp/ARTSPConnection.h +++ b/media/libstagefright/rtsp/ARTSPConnection.h @@ -42,6 +42,10 @@ struct ARTSPConnection : public AHandler { void observeBinaryData(const sp &reply); + static bool ParseURL( + const char *url, AString *host, unsigned *port, AString *path, + AString *user, AString *pass); + protected: virtual ~ARTSPConnection(); virtual void onMessageReceived(const sp &msg); @@ -62,9 +66,18 @@ private: kWhatObserveBinaryData = 'obin', }; + enum AuthType { + NONE, + BASIC, + DIGEST + }; + static const int64_t kSelectTimeoutUs; State mState; + AString mUser, mPass; + AuthType mAuthType; + AString mNonce; int mSocket; int32_t mConnectionID; int32_t mNextCSeq; @@ -90,8 +103,11 @@ private: sp receiveBinaryData(); bool notifyResponseListener(const sp &response); - static bool ParseURL( - const char *url, AString *host, unsigned *port, AString *path); + bool parseAuthMethod(const sp &response); + void addAuthentication(AString *request); + + status_t findPendingRequest( + const sp &response, ssize_t *index) const; static bool ParseSingleUnsignedLong( const char *from, unsigned long *x); diff --git a/media/libstagefright/rtsp/ASessionDescription.cpp b/media/libstagefright/rtsp/ASessionDescription.cpp index 0db3595..5472bff 100644 --- a/media/libstagefright/rtsp/ASessionDescription.cpp +++ b/media/libstagefright/rtsp/ASessionDescription.cpp @@ -53,21 +53,30 @@ bool ASessionDescription::parse(const void *data, size_t size) { mFormats.push(AString("[root]")); AString desc((const char *)data, size); - LOGI("%s", desc.c_str()); size_t i = 0; for (;;) { - ssize_t eolPos = desc.find("\r\n", i); + ssize_t eolPos = desc.find("\n", i); + if (eolPos < 0) { break; } - AString line(desc, i, eolPos - i); + AString line; + if ((size_t)eolPos > i && desc.c_str()[eolPos - 1] == '\r') { + // We accept both '\n' and '\r\n' line endings, if it's + // the latter, strip the '\r' as well. + line.setTo(desc, i, eolPos - i - 1); + } else { + line.setTo(desc, i, eolPos - i); + } if (line.size() < 2 || line.c_str()[1] != '=') { return false; } + LOGI("%s", line.c_str()); + switch (line.c_str()[0]) { case 'v': { @@ -141,7 +150,7 @@ bool ASessionDescription::parse(const void *data, size_t size) { } } - i = eolPos + 2; + i = eolPos + 1; } return true; @@ -245,7 +254,7 @@ bool ASessionDescription::getDurationUs(int64_t *durationUs) const { return false; } - if (value == "npt=now-") { + if (value == "npt=now-" || value == "npt=0-") { return false; } diff --git a/media/libstagefright/rtsp/Android.mk b/media/libstagefright/rtsp/Android.mk index 081ae32..0bbadc1 100644 --- a/media/libstagefright/rtsp/Android.mk +++ b/media/libstagefright/rtsp/Android.mk @@ -23,6 +23,7 @@ LOCAL_C_INCLUDES:= \ $(JNI_H_INCLUDE) \ $(TOP)/frameworks/base/include/media/stagefright/openmax \ $(TOP)/frameworks/base/media/libstagefright/include \ + $(TOP)/external/openssl/include LOCAL_MODULE:= libstagefright_rtsp diff --git a/media/libstagefright/rtsp/MyHandler.h b/media/libstagefright/rtsp/MyHandler.h index 1bc9925..8f8e10a 100644 --- a/media/libstagefright/rtsp/MyHandler.h +++ b/media/libstagefright/rtsp/MyHandler.h @@ -96,6 +96,7 @@ struct MyHandler : public AHandler { mNetLooper(new ALooper), mConn(new ARTSPConnection), mRTPConn(new ARTPConnection), + mOriginalSessionURL(url), mSessionURL(url), mSetupTracksSuccessful(false), mSeekPending(false), @@ -113,6 +114,23 @@ struct MyHandler : public AHandler { mNetLooper->start(false /* runOnCallingThread */, false /* canCallJava */, PRIORITY_HIGHEST); + + // Strip any authentication info from the session url, we don't + // want to transmit user/pass in cleartext. + AString host, path, user, pass; + unsigned port; + if (ARTSPConnection::ParseURL( + mSessionURL.c_str(), &host, &port, &path, &user, &pass) + && user.size() > 0) { + mSessionURL.clear(); + mSessionURL.append("rtsp://"); + mSessionURL.append(host); + mSessionURL.append(":"); + mSessionURL.append(StringPrintf("%u", port)); + mSessionURL.append(path); + + LOGI("rewritten session url: '%s'", mSessionURL.c_str()); + } } void connect(const sp &doneMsg) { @@ -126,7 +144,7 @@ struct MyHandler : public AHandler { mConn->observeBinaryData(notify); sp reply = new AMessage('conn', id()); - mConn->connect(mSessionURL.c_str(), reply); + mConn->connect(mOriginalSessionURL.c_str(), reply); } void disconnect(const sp &doneMsg) { @@ -312,7 +330,7 @@ struct MyHandler : public AHandler { int32_t reconnect; if (msg->findInt32("reconnect", &reconnect) && reconnect) { sp reply = new AMessage('conn', id()); - mConn->connect(mSessionURL.c_str(), reply); + mConn->connect(mOriginalSessionURL.c_str(), reply); } else { (new AMessage('quit', id()))->post(); } @@ -922,7 +940,7 @@ struct MyHandler : public AHandler { CHECK(GetAttribute(range.c_str(), "npt", &val)); float npt1, npt2; - if (val == "now-") { + if (val == "now-" || val == "0-") { // This is a live stream and therefore not seekable. return; } else { @@ -992,6 +1010,7 @@ private: sp mConn; sp mRTPConn; sp mSessionDesc; + AString mOriginalSessionURL; // This one still has user:pass@ AString mSessionURL; AString mBaseURL; AString mSessionID; -- cgit v1.1 From 9a0cf4fb7d18e186e06341bde915de234992d969 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Tue, 11 Jan 2011 16:01:33 -0800 Subject: DO NOT MERGE: Fix Matroska issues related-to-bug: 3331623 Squashed commit of the following: commit 8484811f3784564ce962ec1b6311bac532d73ffc Author: Andreas Huber Date: Tue Jan 11 11:56:06 2011 -0800 Properly parse Matroska lacing flags and extract all frames contained in a block. related-to-bug: 3331623 commit 38f1f39381a01659577461b3d35bd22db45ba317 Author: Andreas Huber Date: Mon Nov 15 15:10:34 2010 -0800 Proper support for variable NALsize lengths of AVC content in .mkv files. related-to-bug: 3197442 Change-Id: I4b404f3c3d0a2a2fa336b9edff75eb25ffc66a2f --- .../libstagefright/matroska/MatroskaExtractor.cpp | 387 +++++++++++++++++---- media/libstagefright/matroska/mkvparser.cpp | 3 + media/libstagefright/matroska/mkvparser.hpp | 2 + 3 files changed, 317 insertions(+), 75 deletions(-) diff --git a/media/libstagefright/matroska/MatroskaExtractor.cpp b/media/libstagefright/matroska/MatroskaExtractor.cpp index d16476d..1661130 100644 --- a/media/libstagefright/matroska/MatroskaExtractor.cpp +++ b/media/libstagefright/matroska/MatroskaExtractor.cpp @@ -22,13 +22,15 @@ #include "mkvparser.hpp" +#include +#include #include #include -#include #include #include #include #include +#include #include namespace android { @@ -81,46 +83,6 @@ private: //////////////////////////////////////////////////////////////////////////////// -#include -static void hexdump(const void *_data, size_t size) { - const uint8_t *data = (const uint8_t *)_data; - size_t offset = 0; - while (offset < size) { - printf("0x%04x ", offset); - - size_t n = size - offset; - if (n > 16) { - n = 16; - } - - for (size_t i = 0; i < 16; ++i) { - if (i == 8) { - printf(" "); - } - - if (offset + i < size) { - printf("%02x ", data[offset + i]); - } else { - printf(" "); - } - } - - printf(" "); - - for (size_t i = 0; i < n; ++i) { - if (isprint(data[offset + i])) { - printf("%c", data[offset + i]); - } else { - printf("."); - } - } - - printf("\n"); - - offset += 16; - } -} - struct BlockIterator { BlockIterator(mkvparser::Segment *segment, unsigned long trackNum); @@ -156,6 +118,9 @@ struct MatroskaSource : public MediaSource { virtual status_t read( MediaBuffer **buffer, const ReadOptions *options); +protected: + virtual ~MatroskaSource(); + private: enum Type { AVC, @@ -167,9 +132,15 @@ private: size_t mTrackIndex; Type mType; BlockIterator mBlockIter; + size_t mNALSizeLen; // for type AVC + + List mPendingFrames; status_t advance(); + status_t readBlock(); + void clearPendingFrames(); + MatroskaSource(const MatroskaSource &); MatroskaSource &operator=(const MatroskaSource &); }; @@ -180,18 +151,35 @@ MatroskaSource::MatroskaSource( mTrackIndex(index), mType(OTHER), mBlockIter(mExtractor->mSegment, - mExtractor->mTracks.itemAt(index).mTrackNum) { + mExtractor->mTracks.itemAt(index).mTrackNum), + mNALSizeLen(0) { + sp meta = mExtractor->mTracks.itemAt(index).mMeta; + const char *mime; - CHECK(mExtractor->mTracks.itemAt(index).mMeta-> - findCString(kKeyMIMEType, &mime)); + CHECK(meta->findCString(kKeyMIMEType, &mime)); if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) { mType = AVC; + + 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); + LOGV("mNALSizeLen = %d", mNALSizeLen); } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC)) { mType = AAC; } } +MatroskaSource::~MatroskaSource() { + clearPendingFrames(); +} + status_t MatroskaSource::start(MetaData *params) { mBlockIter.reset(); @@ -199,6 +187,8 @@ status_t MatroskaSource::start(MetaData *params) { } status_t MatroskaSource::stop() { + clearPendingFrames(); + return OK; } @@ -276,66 +266,313 @@ int64_t BlockIterator::blockTimeUs() const { //////////////////////////////////////////////////////////////////////////////// -status_t MatroskaSource::read( - MediaBuffer **out, const ReadOptions *options) { - *out = NULL; +static unsigned U24_AT(const uint8_t *ptr) { + return ptr[0] << 16 | ptr[1] << 8 | ptr[2]; +} - int64_t seekTimeUs; - ReadOptions::SeekMode mode; - if (options && options->getSeekTo(&seekTimeUs, &mode)) { - mBlockIter.seek(seekTimeUs); +static size_t clz(uint8_t x) { + size_t numLeadingZeroes = 0; + + while (!(x & 0x80)) { + ++numLeadingZeroes; + x = x << 1; } + return numLeadingZeroes; +} + +void MatroskaSource::clearPendingFrames() { + while (!mPendingFrames.empty()) { + MediaBuffer *frame = *mPendingFrames.begin(); + mPendingFrames.erase(mPendingFrames.begin()); + + frame->release(); + frame = NULL; + } +} + +#define BAIL(err) \ + do { \ + if (bigbuf) { \ + bigbuf->release(); \ + bigbuf = NULL; \ + } \ + \ + return err; \ + } while (0) + +status_t MatroskaSource::readBlock() { + CHECK(mPendingFrames.empty()); + if (mBlockIter.eos()) { return ERROR_END_OF_STREAM; } const mkvparser::Block *block = mBlockIter.block(); + size_t size = block->GetSize(); int64_t timeUs = mBlockIter.blockTimeUs(); + int32_t isSync = block->IsKey(); - MediaBuffer *buffer = new MediaBuffer(size + 2); - buffer->meta_data()->setInt64(kKeyTime, timeUs); - buffer->meta_data()->setInt32(kKeyIsSyncFrame, block->IsKey()); + MediaBuffer *bigbuf = new MediaBuffer(size); long res = block->Read( - mExtractor->mReader, (unsigned char *)buffer->data() + 2); + mExtractor->mReader, (unsigned char *)bigbuf->data()); if (res != 0) { + bigbuf->release(); + bigbuf = NULL; + return ERROR_END_OF_STREAM; } - buffer->set_range(2, size); + mBlockIter.advance(); + + bigbuf->meta_data()->setInt64(kKeyTime, timeUs); + bigbuf->meta_data()->setInt32(kKeyIsSyncFrame, isSync); - if (mType == AVC) { - CHECK(size >= 2); + unsigned lacing = (block->Flags() >> 1) & 3; - uint8_t *data = (uint8_t *)buffer->data(); + if (lacing == 0) { + mPendingFrames.push_back(bigbuf); + return OK; + } - unsigned NALsize = data[2] << 8 | data[3]; - CHECK_EQ(size, NALsize + 2); + LOGV("lacing = %u, size = %d", lacing, size); - memcpy(data, "\x00\x00\x00\x01", 4); - buffer->set_range(0, size + 2); - } else if (mType == AAC) { - // There's strange junk at the beginning... + const uint8_t *data = (const uint8_t *)bigbuf->data(); + // hexdump(data, size); - const uint8_t *data = (const uint8_t *)buffer->data() + 2; - size_t offset = 0; - while (offset < size && data[offset] != 0x21) { - ++offset; - } - buffer->set_range(2 + offset, size - offset); + if (size == 0) { + BAIL(ERROR_MALFORMED); } - *out = buffer; + unsigned numFrames = (unsigned)data[0] + 1; + ++data; + --size; + + Vector frameSizes; + + switch (lacing) { + case 1: // Xiph + { + for (size_t i = 0; i < numFrames - 1; ++i) { + size_t frameSize = 0; + uint8_t byte; + do { + if (size == 0) { + BAIL(ERROR_MALFORMED); + } + byte = data[0]; + ++data; + --size; + + frameSize += byte; + } while (byte == 0xff); + + frameSizes.push(frameSize); + } + + break; + } + + case 2: // fixed-size + { + if ((size % numFrames) != 0) { + BAIL(ERROR_MALFORMED); + } + + size_t frameSize = size / numFrames; + for (size_t i = 0; i < numFrames - 1; ++i) { + frameSizes.push(frameSize); + } + + break; + } + + case 3: // EBML + { + uint64_t lastFrameSize = 0; + for (size_t i = 0; i < numFrames - 1; ++i) { + uint8_t byte; + + if (size == 0) { + BAIL(ERROR_MALFORMED); + } + byte = data[0]; + ++data; + --size; + + size_t numLeadingZeroes = clz(byte); + + uint64_t frameSize = byte & ~(0x80 >> numLeadingZeroes); + for (size_t j = 0; j < numLeadingZeroes; ++j) { + if (size == 0) { + BAIL(ERROR_MALFORMED); + } + + frameSize = frameSize << 8; + frameSize |= data[0]; + ++data; + --size; + } + + if (i == 0) { + frameSizes.push(frameSize); + } else { + size_t shift = + 7 - numLeadingZeroes + 8 * numLeadingZeroes; + + int64_t delta = + (int64_t)frameSize - (1ll << (shift - 1)) + 1; + + frameSize = lastFrameSize + delta; + + frameSizes.push(frameSize); + } + + lastFrameSize = frameSize; + } + break; + } + + default: + TRESPASS(); + } #if 0 - hexdump((const uint8_t *)buffer->data() + buffer->range_offset(), - buffer->range_length()); + AString out; + for (size_t i = 0; i < frameSizes.size(); ++i) { + if (i > 0) { + out.append(", "); + } + out.append(StringPrintf("%llu", frameSizes.itemAt(i))); + } + LOGV("sizes = [%s]", out.c_str()); #endif - mBlockIter.advance(); + for (size_t i = 0; i < frameSizes.size(); ++i) { + uint64_t frameSize = frameSizes.itemAt(i); + + if (size < frameSize) { + BAIL(ERROR_MALFORMED); + } + + MediaBuffer *mbuf = new MediaBuffer(frameSize); + mbuf->meta_data()->setInt64(kKeyTime, timeUs); + mbuf->meta_data()->setInt32(kKeyIsSyncFrame, isSync); + memcpy(mbuf->data(), data, frameSize); + mPendingFrames.push_back(mbuf); + + data += frameSize; + size -= frameSize; + } + + size_t offset = bigbuf->range_length() - size; + bigbuf->set_range(offset, size); + + mPendingFrames.push_back(bigbuf); + + return OK; +} + +#undef BAIL + +status_t MatroskaSource::read( + MediaBuffer **out, const ReadOptions *options) { + *out = NULL; + + int64_t seekTimeUs; + ReadOptions::SeekMode mode; + if (options && options->getSeekTo(&seekTimeUs, &mode)) { + clearPendingFrames(); + mBlockIter.seek(seekTimeUs); + } + +again: + while (mPendingFrames.empty()) { + status_t err = readBlock(); + + if (err != OK) { + clearPendingFrames(); + + return err; + } + } + + MediaBuffer *frame = *mPendingFrames.begin(); + mPendingFrames.erase(mPendingFrames.begin()); + + size_t size = frame->range_length(); + + if (mType != AVC) { + *out = frame; + + return OK; + } + + if (size < mNALSizeLen) { + frame->release(); + frame = NULL; + + return ERROR_MALFORMED; + } + + // In the case of AVC content, each NAL unit is prefixed by + // mNALSizeLen bytes of length. We want to prefix the data with + // a four-byte 0x00000001 startcode instead of the length prefix. + // mNALSizeLen ranges from 1 through 4 bytes, so add an extra + // 3 bytes of padding to the buffer start. + static const size_t kPadding = 3; + + MediaBuffer *buffer = new MediaBuffer(size + kPadding); + + int64_t timeUs; + CHECK(frame->meta_data()->findInt64(kKeyTime, &timeUs)); + int32_t isSync; + CHECK(frame->meta_data()->findInt32(kKeyIsSyncFrame, &isSync)); + + buffer->meta_data()->setInt64(kKeyTime, timeUs); + buffer->meta_data()->setInt32(kKeyIsSyncFrame, isSync); + + memcpy((uint8_t *)buffer->data() + kPadding, + (const uint8_t *)frame->data() + frame->range_offset(), + size); + + buffer->set_range(kPadding, size); + + frame->release(); + frame = NULL; + + uint8_t *data = (uint8_t *)buffer->data(); + + size_t NALsize; + switch (mNALSizeLen) { + case 1: NALsize = data[kPadding]; break; + case 2: NALsize = U16_AT(&data[kPadding]); break; + case 3: NALsize = U24_AT(&data[kPadding]); break; + case 4: NALsize = U32_AT(&data[kPadding]); break; + default: + TRESPASS(); + } + + if (size < NALsize + mNALSizeLen) { + buffer->release(); + buffer = NULL; + + return ERROR_MALFORMED; + } + + if (size > NALsize + mNALSizeLen) { + LOGW("discarding %d bytes of data.", size - NALsize - mNALSizeLen); + } + + // actual data starts at &data[kPadding + mNALSizeLen] + + memcpy(&data[mNALSizeLen - 1], "\x00\x00\x00\x01", 4); + buffer->set_range(mNALSizeLen - 1, NALsize + 4); + + *out = buffer; return OK; } diff --git a/media/libstagefright/matroska/mkvparser.cpp b/media/libstagefright/matroska/mkvparser.cpp index 455b1d6..7448d96 100644 --- a/media/libstagefright/matroska/mkvparser.cpp +++ b/media/libstagefright/matroska/mkvparser.cpp @@ -4474,6 +4474,9 @@ bool Block::IsKey() const return ((m_flags & static_cast(1 << 7)) != 0); } +unsigned char Block::Flags() const { + return m_flags; +} void Block::SetKey(bool bKey) { diff --git a/media/libstagefright/matroska/mkvparser.hpp b/media/libstagefright/matroska/mkvparser.hpp index c46d349..f7d8948 100644 --- a/media/libstagefright/matroska/mkvparser.hpp +++ b/media/libstagefright/matroska/mkvparser.hpp @@ -80,6 +80,8 @@ public: bool IsKey() const; void SetKey(bool); + unsigned char Flags() const; + long long GetOffset() const; long GetSize() const; long Read(IMkvReader*, unsigned char*) const; -- cgit v1.1 From e49051406baa71cb63d5754d33908ce3df201af1 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Wed, 12 Jan 2011 09:57:23 -0800 Subject: Fail to parse duration instead of asserting, if the server response cannot be parsed. Change-Id: I95c61ed83800db82e99c0023b942fb8ae05ed3cf related-to-bug: 3338518 --- media/libstagefright/rtsp/ASessionDescription.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/media/libstagefright/rtsp/ASessionDescription.cpp b/media/libstagefright/rtsp/ASessionDescription.cpp index 5472bff..0d0234b 100644 --- a/media/libstagefright/rtsp/ASessionDescription.cpp +++ b/media/libstagefright/rtsp/ASessionDescription.cpp @@ -265,15 +265,17 @@ bool ASessionDescription::getDurationUs(int64_t *durationUs) const { const char *s = value.c_str() + 4; char *end; double from = strtod(s, &end); - CHECK_GT(end, s); - CHECK_EQ(*end, '-'); + + if (end == s || *end != '-') { + return false; + } s = end + 1; double to = strtod(s, &end); - CHECK_GT(end, s); - CHECK_EQ(*end, '\0'); - CHECK_GE(to, from); + if (end == s || *end != '\0' || to < from) { + return false; + } *durationUs = (int64_t)((to - from) * 1E6); -- cgit v1.1 From 95304d5488ba465f19cc788f1c7394218c2ea2d1 Mon Sep 17 00:00:00 2001 From: James Dong Date: Thu, 2 Dec 2010 17:42:08 -0800 Subject: Squash commits of the following patches, cherry-picked from other branch - do not merge. o Prepare for publishing MediaMetadataRetriever as public API step one: o replaced captureFrame with getFrameAtTime o removed getMode o Replace MediaMetadataRetriever.captureFrame() with MediaMetadataRetriever.getFrameAtTime() as part of the preparation for publishing MediaMetadataRetriever as public Java API o Remove captureFrame from MediaMetadataRetriever.java class It has been replaced by getFrameAtTime() method o Replace extractAlbumArt() with getEmbeddedPicture() in MediaMetadataRetriever.java o Publish MediaMetadataRetriever.java as public API o Removed setMode() methods and related mode constants o Removed some of the unused the metadata keys o Updated the javadoc o part of a multi-project change. bug - 3309041 Change-Id: I2efb6e8b8d52897186b016cb4efda6862f5584c4 --- cmds/stagefright/stagefright.cpp | 12 +++--- include/media/IMediaMetadataRetriever.h | 4 +- include/media/MediaMetadataRetrieverInterface.h | 31 ++------------ include/media/mediametadataretriever.h | 37 +++------------- media/libmedia/IMediaMetadataRetriever.cpp | 50 +++++----------------- media/libmedia/mediametadataretriever.cpp | 28 ++---------- .../MetadataRetrieverClient.cpp | 50 +++------------------- .../MetadataRetrieverClient.h | 5 +-- media/libstagefright/StagefrightMediaScanner.cpp | 8 +--- .../StagefrightMetadataRetriever.cpp | 48 ++++++++++----------- .../include/StagefrightMetadataRetriever.h | 2 +- 11 files changed, 63 insertions(+), 212 deletions(-) diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp index f55b746..ff92431 100644 --- a/cmds/stagefright/stagefright.cpp +++ b/cmds/stagefright/stagefright.cpp @@ -596,21 +596,19 @@ int main(int argc, char **argv) { const char *filename = argv[k]; CHECK_EQ(retriever->setDataSource(filename), (status_t)OK); - CHECK_EQ(retriever->setMode( - METADATA_MODE_FRAME_CAPTURE_AND_METADATA_RETRIEVAL), - (status_t)OK); - - sp mem = retriever->captureFrame(); + sp mem = + retriever->getFrameAtTime(-1, + MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC); if (mem != NULL) { - printf("captureFrame(%s) => OK\n", filename); + printf("getFrameAtTime(%s) => OK\n", filename); } else { mem = retriever->extractAlbumArt(); if (mem != NULL) { printf("extractAlbumArt(%s) => OK\n", filename); } else { - printf("both captureFrame and extractAlbumArt " + printf("both getFrameAtTime and extractAlbumArt " "failed on file '%s'.\n", filename); } } diff --git a/include/media/IMediaMetadataRetriever.h b/include/media/IMediaMetadataRetriever.h index 9baba8e..8e3cdbb 100644 --- a/include/media/IMediaMetadataRetriever.h +++ b/include/media/IMediaMetadataRetriever.h @@ -32,9 +32,7 @@ public: virtual void disconnect() = 0; virtual status_t setDataSource(const char* srcUrl) = 0; virtual status_t setDataSource(int fd, int64_t offset, int64_t length) = 0; - virtual status_t setMode(int mode) = 0; - virtual status_t getMode(int* mode) const = 0; - virtual sp captureFrame() = 0; + virtual sp getFrameAtTime(int64_t timeUs, int option) = 0; virtual sp extractAlbumArt() = 0; virtual const char* extractMetadata(int keyCode) = 0; }; diff --git a/include/media/MediaMetadataRetrieverInterface.h b/include/media/MediaMetadataRetrieverInterface.h index ff57774..0449122 100644 --- a/include/media/MediaMetadataRetrieverInterface.h +++ b/include/media/MediaMetadataRetrieverInterface.h @@ -32,9 +32,7 @@ public: virtual ~MediaMetadataRetrieverBase() {} virtual status_t setDataSource(const char *url) = 0; virtual status_t setDataSource(int fd, int64_t offset, int64_t length) = 0; - virtual status_t setMode(int mode) = 0; - virtual status_t getMode(int* mode) const = 0; - virtual VideoFrame* captureFrame() = 0; + virtual VideoFrame* getFrameAtTime(int64_t timeUs, int option) = 0; virtual MediaAlbumArt* extractAlbumArt() = 0; virtual const char* extractMetadata(int keyCode) = 0; }; @@ -43,35 +41,12 @@ public: class MediaMetadataRetrieverInterface : public MediaMetadataRetrieverBase { public: - MediaMetadataRetrieverInterface() - : mMode(0) { - } + MediaMetadataRetrieverInterface() {} virtual ~MediaMetadataRetrieverInterface() {} - - // @param mode The intended mode of operations: - // can be any of the following: - // METADATA_MODE_NOOP: Experimental - just add and remove data source. - // METADATA_MODE_FRAME_CAPTURE_ONLY: For capture frame/thumbnail only. - // METADATA_MODE_METADATA_RETRIEVAL_ONLY: For meta data retrieval only. - // METADATA_MODE_FRAME_CAPTURE_AND_METADATA_RETRIEVAL: For both frame - // capture and meta data retrieval. - virtual status_t setMode(int mode) { - if (mode < METADATA_MODE_NOOP || - mode > METADATA_MODE_FRAME_CAPTURE_AND_METADATA_RETRIEVAL) { - return BAD_VALUE; - } - - mMode = mode; - return NO_ERROR; - } - - virtual status_t getMode(int* mode) const { *mode = mMode; return NO_ERROR; } - virtual VideoFrame* captureFrame() { return NULL; } + virtual VideoFrame* getFrameAtTime(int64_t timeUs, int option) { return NULL; } virtual MediaAlbumArt* extractAlbumArt() { return NULL; } virtual const char* extractMetadata(int keyCode) { return NULL; } - - uint32_t mMode; }; }; // namespace android diff --git a/include/media/mediametadataretriever.h b/include/media/mediametadataretriever.h index ddc07f6..e905006 100644 --- a/include/media/mediametadataretriever.h +++ b/include/media/mediametadataretriever.h @@ -42,37 +42,14 @@ enum { METADATA_KEY_YEAR = 8, METADATA_KEY_DURATION = 9, METADATA_KEY_NUM_TRACKS = 10, - METADATA_KEY_IS_DRM_CRIPPLED = 11, - METADATA_KEY_CODEC = 12, - METADATA_KEY_RATING = 13, - METADATA_KEY_COMMENT = 14, - METADATA_KEY_COPYRIGHT = 15, - METADATA_KEY_BIT_RATE = 16, - METADATA_KEY_FRAME_RATE = 17, - METADATA_KEY_VIDEO_FORMAT = 18, - METADATA_KEY_VIDEO_HEIGHT = 19, - METADATA_KEY_VIDEO_WIDTH = 20, - METADATA_KEY_WRITER = 21, - METADATA_KEY_MIMETYPE = 22, - METADATA_KEY_DISC_NUMBER = 23, - METADATA_KEY_ALBUMARTIST = 24, - METADATA_KEY_COMPILATION = 25, + METADATA_KEY_WRITER = 11, + METADATA_KEY_MIMETYPE = 12, + METADATA_KEY_ALBUMARTIST = 13, + METADATA_KEY_DISC_NUMBER = 14, + METADATA_KEY_COMPILATION = 15, // Add more here... }; -// The intended mode of operations:$ -// METADATA_MODE_NOOP: Experimental - just add and remove data source.$ -// METADATA_MODE_FRAME_CAPTURE_ONLY: For capture frame/thumbnail only.$ -// METADATA_MODE_METADATA_RETRIEVAL_ONLY: For meta data retrieval only.$ -// METADATA_MODE_FRAME_CAPTURE_AND_METADATA_RETRIEVAL: For both frame capture -// and meta data retrieval.$ -enum { - METADATA_MODE_NOOP = 0x00, - METADATA_MODE_METADATA_RETRIEVAL_ONLY = 0x01, - METADATA_MODE_FRAME_CAPTURE_ONLY = 0x02, - METADATA_MODE_FRAME_CAPTURE_AND_METADATA_RETRIEVAL = 0x03 -}; - class MediaMetadataRetriever: public RefBase { public: @@ -81,9 +58,7 @@ public: void disconnect(); status_t setDataSource(const char* dataSourceUrl); status_t setDataSource(int fd, int64_t offset, int64_t length); - status_t setMode(int mode); - status_t getMode(int* mode); - sp captureFrame(); + sp getFrameAtTime(int64_t timeUs, int option); sp extractAlbumArt(); const char* extractMetadata(int keyCode); diff --git a/media/libmedia/IMediaMetadataRetriever.cpp b/media/libmedia/IMediaMetadataRetriever.cpp index e529d25..d5298c9 100644 --- a/media/libmedia/IMediaMetadataRetriever.cpp +++ b/media/libmedia/IMediaMetadataRetriever.cpp @@ -81,9 +81,7 @@ enum { DISCONNECT = IBinder::FIRST_CALL_TRANSACTION, SET_DATA_SOURCE_URL, SET_DATA_SOURCE_FD, - SET_MODE, - GET_MODE, - CAPTURE_FRAME, + GET_FRAME_AT_TIME, EXTRACT_ALBUM_ART, EXTRACT_METADATA, }; @@ -124,32 +122,17 @@ public: return reply.readInt32(); } - status_t setMode(int mode) - { - Parcel data, reply; - data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor()); - data.writeInt32(mode); - remote()->transact(SET_MODE, data, &reply); - return reply.readInt32(); - } - - status_t getMode(int* mode) const - { - Parcel data, reply; - data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor()); - remote()->transact(GET_MODE, data, &reply); - *mode = reply.readInt32(); - return reply.readInt32(); - } - - sp captureFrame() + sp getFrameAtTime(int64_t timeUs, int option) { + LOGV("getTimeAtTime: time(%lld us) and option(%d)", timeUs, option); Parcel data, reply; data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor()); + data.writeInt64(timeUs); + data.writeInt32(option); #ifndef DISABLE_GROUP_SCHEDULE_HACK sendSchedPolicy(data); #endif - remote()->transact(CAPTURE_FRAME, data, &reply); + remote()->transact(GET_FRAME_AT_TIME, data, &reply); status_t ret = reply.readInt32(); if (ret != NO_ERROR) { return NULL; @@ -216,26 +199,15 @@ status_t BnMediaMetadataRetriever::onTransact( reply->writeInt32(setDataSource(fd, offset, length)); return NO_ERROR; } break; - case SET_MODE: { - CHECK_INTERFACE(IMediaMetadataRetriever, data, reply); - int mode = data.readInt32(); - reply->writeInt32(setMode(mode)); - return NO_ERROR; - } break; - case GET_MODE: { - CHECK_INTERFACE(IMediaMetadataRetriever, data, reply); - int mode; - status_t status = getMode(&mode); - reply->writeInt32(mode); - reply->writeInt32(status); - return NO_ERROR; - } break; - case CAPTURE_FRAME: { + case GET_FRAME_AT_TIME: { CHECK_INTERFACE(IMediaMetadataRetriever, data, reply); + int64_t timeUs = data.readInt64(); + int option = data.readInt32(); + LOGV("getTimeAtTime: time(%lld us) and option(%d)", timeUs, option); #ifndef DISABLE_GROUP_SCHEDULE_HACK setSchedPolicy(data); #endif - sp bitmap = captureFrame(); + sp bitmap = getFrameAtTime(timeUs, option); if (bitmap != 0) { // Don't send NULL across the binder interface reply->writeInt32(NO_ERROR); reply->writeStrongBinder(bitmap->asBinder()); diff --git a/media/libmedia/mediametadataretriever.cpp b/media/libmedia/mediametadataretriever.cpp index e2712ba..8dfcb3b 100644 --- a/media/libmedia/mediametadataretriever.cpp +++ b/media/libmedia/mediametadataretriever.cpp @@ -123,37 +123,15 @@ status_t MediaMetadataRetriever::setDataSource(int fd, int64_t offset, int64_t l return mRetriever->setDataSource(fd, offset, length); } -status_t MediaMetadataRetriever::setMode(int mode) +sp MediaMetadataRetriever::getFrameAtTime(int64_t timeUs, int option) { - LOGV("setMode(%d)", mode); - Mutex::Autolock _l(mLock); - if (mRetriever == 0) { - LOGE("retriever is not initialized"); - return INVALID_OPERATION; - } - return mRetriever->setMode(mode); -} - -status_t MediaMetadataRetriever::getMode(int* mode) -{ - LOGV("getMode"); - Mutex::Autolock _l(mLock); - if (mRetriever == 0) { - LOGE("retriever is not initialized"); - return INVALID_OPERATION; - } - return mRetriever->getMode(mode); -} - -sp MediaMetadataRetriever::captureFrame() -{ - LOGV("captureFrame"); + LOGV("getFrameAtTime: time(%lld us) option(%d)", timeUs, option); Mutex::Autolock _l(mLock); if (mRetriever == 0) { LOGE("retriever is not initialized"); return NULL; } - return mRetriever->captureFrame(); + return mRetriever->getFrameAtTime(timeUs, option); } const char* MediaMetadataRetriever::extractMetadata(int keyCode) diff --git a/media/libmediaplayerservice/MetadataRetrieverClient.cpp b/media/libmediaplayerservice/MetadataRetrieverClient.cpp index 39fce81..713e441 100644 --- a/media/libmediaplayerservice/MetadataRetrieverClient.cpp +++ b/media/libmediaplayerservice/MetadataRetrieverClient.cpp @@ -65,7 +65,6 @@ MetadataRetrieverClient::MetadataRetrieverClient(pid_t pid) mThumbnail = NULL; mAlbumArt = NULL; mRetriever = NULL; - mMode = METADATA_MODE_FRAME_CAPTURE_AND_METADATA_RETRIEVAL; } MetadataRetrieverClient::~MetadataRetrieverClient() @@ -80,7 +79,7 @@ status_t MetadataRetrieverClient::dump(int fd, const Vector& args) con char buffer[SIZE]; String8 result; result.append(" MetadataRetrieverClient\n"); - snprintf(buffer, 255, " pid(%d) mode(%d)\n", mPid, mMode); + snprintf(buffer, 255, " pid(%d)\n", mPid); result.append(buffer); write(fd, result.string(), result.size()); write(fd, "\n", 1); @@ -94,7 +93,6 @@ void MetadataRetrieverClient::disconnect() mRetriever.clear(); mThumbnail.clear(); mAlbumArt.clear(); - mMode = METADATA_MODE_FRAME_CAPTURE_AND_METADATA_RETRIEVAL; IPCThreadState::self()->flushCommands(); } @@ -140,10 +138,7 @@ status_t MetadataRetrieverClient::setDataSource(const char *url) LOGV("player type = %d", playerType); sp p = createRetriever(playerType); if (p == NULL) return NO_INIT; - status_t ret = p->setMode(mMode); - if (ret == NO_ERROR) { - ret = p->setDataSource(url); - } + status_t ret = p->setDataSource(url); if (ret == NO_ERROR) mRetriever = p; return ret; } @@ -181,55 +176,22 @@ status_t MetadataRetrieverClient::setDataSource(int fd, int64_t offset, int64_t ::close(fd); return NO_INIT; } - status_t status = p->setMode(mMode); - if (status == NO_ERROR) { - p->setDataSource(fd, offset, length); - } + status_t status = p->setDataSource(fd, offset, length); if (status == NO_ERROR) mRetriever = p; ::close(fd); return status; } -status_t MetadataRetrieverClient::setMode(int mode) -{ - LOGV("setMode"); - Mutex::Autolock lock(mLock); - if (mode < METADATA_MODE_NOOP || - mode > METADATA_MODE_FRAME_CAPTURE_AND_METADATA_RETRIEVAL) { - LOGE("invalid mode %d", mode); - return BAD_VALUE; - } - mMode = mode; - return NO_ERROR; -} - -status_t MetadataRetrieverClient::getMode(int* mode) const -{ - LOGV("getMode"); - Mutex::Autolock lock(mLock); - - // TODO: - // This may not be necessary. - // If setDataSource() has not been called, return the cached value - // otherwise, return the value retrieved from the retriever - if (mRetriever == NULL) { - *mode = mMode; - } else { - mRetriever->getMode(mode); - } - return NO_ERROR; -} - -sp MetadataRetrieverClient::captureFrame() +sp MetadataRetrieverClient::getFrameAtTime(int64_t timeUs, int option) { - LOGV("captureFrame"); + LOGV("getFrameAtTime: time(%lld us) option(%d)", timeUs, option); Mutex::Autolock lock(mLock); mThumbnail.clear(); if (mRetriever == NULL) { LOGE("retriever is not initialized"); return NULL; } - VideoFrame *frame = mRetriever->captureFrame(); + VideoFrame *frame = mRetriever->getFrameAtTime(timeUs, option); if (frame == NULL) { LOGE("failed to capture a video frame"); return NULL; diff --git a/media/libmediaplayerservice/MetadataRetrieverClient.h b/media/libmediaplayerservice/MetadataRetrieverClient.h index 4aab94f..b834715 100644 --- a/media/libmediaplayerservice/MetadataRetrieverClient.h +++ b/media/libmediaplayerservice/MetadataRetrieverClient.h @@ -43,9 +43,7 @@ public: virtual void disconnect(); virtual status_t setDataSource(const char *url); virtual status_t setDataSource(int fd, int64_t offset, int64_t length); - virtual status_t setMode(int mode); - virtual status_t getMode(int* mode) const; - virtual sp captureFrame(); + virtual sp getFrameAtTime(int64_t timeUs, int option); virtual sp extractAlbumArt(); virtual const char* extractMetadata(int keyCode); @@ -60,7 +58,6 @@ private: mutable Mutex mLock; sp mRetriever; pid_t mPid; - int mMode; // Keep the shared memory copy of album art and capture frame (for thumbnail) sp mAlbumArt; diff --git a/media/libstagefright/StagefrightMediaScanner.cpp b/media/libstagefright/StagefrightMediaScanner.cpp index d3aa2f6..10c6e41 100644 --- a/media/libstagefright/StagefrightMediaScanner.cpp +++ b/media/libstagefright/StagefrightMediaScanner.cpp @@ -130,9 +130,7 @@ status_t StagefrightMediaScanner::processFile( return HandleMIDI(path, &client); } - if (mRetriever->setDataSource(path) == OK - && mRetriever->setMode( - METADATA_MODE_METADATA_RETRIEVAL_ONLY) == OK) { + if (mRetriever->setDataSource(path) == OK) { const char *value; if ((value = mRetriever->extractMetadata( METADATA_KEY_MIMETYPE)) != NULL) { @@ -181,9 +179,7 @@ char *StagefrightMediaScanner::extractAlbumArt(int fd) { } lseek(fd, 0, SEEK_SET); - if (mRetriever->setDataSource(fd, 0, size) == OK - && mRetriever->setMode( - METADATA_MODE_FRAME_CAPTURE_ONLY) == OK) { + if (mRetriever->setDataSource(fd, 0, size) == OK) { sp mem = mRetriever->extractAlbumArt(); if (mem != NULL) { diff --git a/media/libstagefright/StagefrightMetadataRetriever.cpp b/media/libstagefright/StagefrightMetadataRetriever.cpp index ac208cd..e8f4839 100644 --- a/media/libstagefright/StagefrightMetadataRetriever.cpp +++ b/media/libstagefright/StagefrightMetadataRetriever.cpp @@ -108,7 +108,10 @@ status_t StagefrightMetadataRetriever::setDataSource( static VideoFrame *extractVideoFrameWithCodecFlags( OMXClient *client, const sp &trackMeta, - const sp &source, uint32_t flags) { + const sp &source, + uint32_t flags, + int64_t frameTimeUs, + int seekMode) { sp decoder = OMXCodec::Create( client->interface(), source->getFormat(), false, source, @@ -130,11 +133,22 @@ static VideoFrame *extractVideoFrameWithCodecFlags( // and spurious empty buffers. MediaSource::ReadOptions options; + if (seekMode < MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC || + seekMode > MediaSource::ReadOptions::SEEK_CLOSEST) { + + LOGE("Unknown seek mode: %d", seekMode); + return NULL; + } + + MediaSource::ReadOptions::SeekMode mode = + static_cast(seekMode); + int64_t thumbNailTime; - if (trackMeta->findInt64(kKeyThumbnailTime, &thumbNailTime)) { - options.setSeekTo(thumbNailTime); + if (frameTimeUs < 0 && trackMeta->findInt64(kKeyThumbnailTime, &thumbNailTime)) { + options.setSeekTo(thumbNailTime, mode); } else { thumbNailTime = -1; + options.setSeekTo(frameTimeUs, mode); } MediaBuffer *buffer = NULL; @@ -226,14 +240,10 @@ static VideoFrame *extractVideoFrameWithCodecFlags( return frame; } -VideoFrame *StagefrightMetadataRetriever::captureFrame() { - LOGV("captureFrame"); - - if (0 == (mMode & METADATA_MODE_FRAME_CAPTURE_ONLY)) { - LOGV("captureFrame disabled by mode (0x%08x)", mMode); +VideoFrame *StagefrightMetadataRetriever::getFrameAtTime( + int64_t timeUs, int option) { - return NULL; - } + LOGV("getFrameAtTime: %lld us option: %d", timeUs, option); if (mExtractor.get() == NULL) { LOGV("no extractor."); @@ -270,13 +280,15 @@ VideoFrame *StagefrightMetadataRetriever::captureFrame() { VideoFrame *frame = extractVideoFrameWithCodecFlags( - &mClient, trackMeta, source, OMXCodec::kPreferSoftwareCodecs); + &mClient, trackMeta, source, OMXCodec::kPreferSoftwareCodecs, + timeUs, option); if (frame == NULL) { LOGV("Software decoder failed to extract thumbnail, " "trying hardware decoder."); - frame = extractVideoFrameWithCodecFlags(&mClient, trackMeta, source, 0); + frame = extractVideoFrameWithCodecFlags(&mClient, trackMeta, source, 0, + timeUs, option); } return frame; @@ -285,12 +297,6 @@ VideoFrame *StagefrightMetadataRetriever::captureFrame() { MediaAlbumArt *StagefrightMetadataRetriever::extractAlbumArt() { LOGV("extractAlbumArt (extractor: %s)", mExtractor.get() != NULL ? "YES" : "NO"); - if (0 == (mMode & METADATA_MODE_METADATA_RETRIEVAL_ONLY)) { - LOGV("extractAlbumArt/metadata retrieval disabled by mode"); - - return NULL; - } - if (mExtractor == NULL) { return NULL; } @@ -309,12 +315,6 @@ MediaAlbumArt *StagefrightMetadataRetriever::extractAlbumArt() { } const char *StagefrightMetadataRetriever::extractMetadata(int keyCode) { - if (0 == (mMode & METADATA_MODE_METADATA_RETRIEVAL_ONLY)) { - LOGV("extractAlbumArt/metadata retrieval disabled by mode"); - - return NULL; - } - if (mExtractor == NULL) { return NULL; } diff --git a/media/libstagefright/include/StagefrightMetadataRetriever.h b/media/libstagefright/include/StagefrightMetadataRetriever.h index b80387f..07b1ec8 100644 --- a/media/libstagefright/include/StagefrightMetadataRetriever.h +++ b/media/libstagefright/include/StagefrightMetadataRetriever.h @@ -35,7 +35,7 @@ struct StagefrightMetadataRetriever : public MediaMetadataRetrieverInterface { virtual status_t setDataSource(const char *url); virtual status_t setDataSource(int fd, int64_t offset, int64_t length); - virtual VideoFrame *captureFrame(); + virtual VideoFrame *getFrameAtTime(int64_t timeUs, int option); virtual MediaAlbumArt *extractAlbumArt(); virtual const char *extractMetadata(int keyCode); -- cgit v1.1 From f4c056aeacad2dac60a83ccd7928bfeaa9d6ddf6 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Tue, 18 Jan 2011 15:26:40 -0800 Subject: DO NOT MERGE: Apparently our native TCP sockets do not return an error from blocking "connect" if the network interface is shutdown while connecting. Change-Id: I168c6026de24812efa9b7e607a9eb83efded8c1f related-to-bug: 3362836 --- media/libstagefright/HTTPStream.cpp | 85 +++++++++++++++++++++++++++++++++++-- 1 file changed, 81 insertions(+), 4 deletions(-) diff --git a/media/libstagefright/HTTPStream.cpp b/media/libstagefright/HTTPStream.cpp index ccc6a34..7194614 100644 --- a/media/libstagefright/HTTPStream.cpp +++ b/media/libstagefright/HTTPStream.cpp @@ -25,13 +25,14 @@ #include #include #include +#include #include #include #include #include #include -#include +#include namespace android { @@ -47,6 +48,82 @@ HTTPStream::~HTTPStream() { disconnect(); } +static bool MakeSocketBlocking(int s, bool blocking) { + // Make socket non-blocking. + int flags = fcntl(s, F_GETFL, 0); + if (flags == -1) { + return false; + } + + if (blocking) { + flags &= ~O_NONBLOCK; + } else { + flags |= O_NONBLOCK; + } + + return fcntl(s, F_SETFL, flags) != -1; +} + +static status_t MyConnect( + int s, const struct sockaddr *addr, socklen_t addrlen) { + status_t result = UNKNOWN_ERROR; + + MakeSocketBlocking(s, false); + + if (connect(s, addr, addrlen) == 0) { + result = OK; + } else if (errno != EINPROGRESS) { + result = -errno; + } else { + for (;;) { + fd_set rs, ws; + FD_ZERO(&rs); + FD_ZERO(&ws); + FD_SET(s, &rs); + FD_SET(s, &ws); + + struct timeval tv; + tv.tv_sec = 0; + tv.tv_usec = 100000ll; + + int nfds = ::select(s + 1, &rs, &ws, NULL, &tv); + + if (nfds < 0) { + if (errno == EINTR) { + continue; + } + + result = -errno; + break; + } + + if (FD_ISSET(s, &ws) && !FD_ISSET(s, &rs)) { + result = OK; + break; + } + + if (FD_ISSET(s, &rs) || FD_ISSET(s, &ws)) { + // Get the pending error. + int error = 0; + socklen_t errorLen = sizeof(error); + if (getsockopt(s, SOL_SOCKET, SO_ERROR, &error, &errorLen) == -1) { + // Couldn't get the real error, so report why not. + result = -errno; + } else { + result = -error; + } + break; + } + + // Timeout expired. Try again. + } + } + + MakeSocketBlocking(s, true); + + return result; +} + status_t HTTPStream::connect(const char *server, int port) { Mutex::Autolock autoLock(mLock); @@ -82,7 +159,7 @@ status_t HTTPStream::connect(const char *server, int port) { addr.sin_addr.s_addr = *(in_addr_t *)ent->h_addr; memset(addr.sin_zero, 0, sizeof(addr.sin_zero)); - int res = ::connect(s, (const struct sockaddr *)&addr, sizeof(addr)); + status_t res = MyConnect(s, (const struct sockaddr *)&addr, sizeof(addr)); mLock.lock(); @@ -90,12 +167,12 @@ status_t HTTPStream::connect(const char *server, int port) { return UNKNOWN_ERROR; } - if (res < 0) { + if (res != OK) { close(mSocket); mSocket = -1; mState = READY; - return UNKNOWN_ERROR; + return res; } mState = CONNECTED; -- cgit v1.1 From 6e3edc2dffb0d424b02ac57b55114b0d13163f56 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Wed, 19 Jan 2011 15:07:19 -0800 Subject: DO NOT MERGE: Prefill the cache before trying to instantiate the media extractor. The latter is an operation that otherwise could block on the datasource for a significant amount of time. During that time we'd be unable to abort the preparation phase without this prefill. Change-Id: I3bc889b264f599bfd5c2bbdf48b88ccb55d86172 related-to-bug: 3362836 --- media/libstagefright/AwesomePlayer.cpp | 33 +++++++++++++++++++++++++++++--- media/libstagefright/NuCachedSource2.cpp | 1 + 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp index 7dbb145..d99990b 100644 --- a/media/libstagefright/AwesomePlayer.cpp +++ b/media/libstagefright/AwesomePlayer.cpp @@ -53,6 +53,8 @@ namespace android { static int64_t kLowWaterMarkUs = 2000000ll; // 2secs static int64_t kHighWaterMarkUs = 10000000ll; // 10secs +static const size_t kLowWaterMarkBytes = 40000; +static const size_t kHighWaterMarkBytes = 200000; struct AwesomeEvent : public TimedEventQueue::Event { AwesomeEvent( @@ -589,9 +591,6 @@ void AwesomePlayer::onBufferingUpdate() { // We don't know the bitrate of the stream, use absolute size // limits to maintain the cache. - const size_t kLowWaterMarkBytes = 40000; - const size_t kHighWaterMarkBytes = 200000; - if ((mFlags & PLAYING) && !eos && (cachedDataRemaining < kLowWaterMarkBytes)) { LOGI("cache is running low (< %d) , pausing.", @@ -1478,6 +1477,34 @@ status_t AwesomePlayer::finishSetDataSource_l() { mConnectingDataSource.clear(); dataSource = mCachedSource; + + // We're going to prefill the cache before trying to instantiate + // the extractor below, as the latter is an operation that otherwise + // could block on the datasource for a significant amount of time. + // During that time we'd be unable to abort the preparation phase + // without this prefill. + + mLock.unlock(); + + for (;;) { + bool eos; + size_t cachedDataRemaining = + mCachedSource->approxDataRemaining(&eos); + + if (eos || cachedDataRemaining >= kHighWaterMarkBytes + || (mFlags & PREPARE_CANCELLED)) { + break; + } + + usleep(200000); + } + + mLock.lock(); + + if (mFlags & PREPARE_CANCELLED) { + LOGI("Prepare cancelled while waiting for initial cache fill."); + return UNKNOWN_ERROR; + } } else if (!strncasecmp(mUri.string(), "httplive://", 11)) { String8 uri("http://"); uri.append(mUri.string() + 11); diff --git a/media/libstagefright/NuCachedSource2.cpp b/media/libstagefright/NuCachedSource2.cpp index b67002d..5b0168b 100644 --- a/media/libstagefright/NuCachedSource2.cpp +++ b/media/libstagefright/NuCachedSource2.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +//#define LOG_NDEBUG 0 #define LOG_TAG "NuCachedSource2" #include -- cgit v1.1 From b517e006b51c95db9c44bf5f104f562d431ae597 Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Wed, 19 Jan 2011 18:36:13 -0800 Subject: do not merge - Fix issue 3371096. EffectModule::process() was copying effect chain input buffer to output buffer if no effect was active instead of accumulating it. Change-Id: If4ca75601ea69a088d0f71d88aec53e90a1dec89 --- services/audioflinger/AudioFlinger.cpp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index cd9b07e..5071555 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -5433,19 +5433,21 @@ void AudioFlinger::EffectModule::process() // clear auxiliary effect input buffer for next accumulation if ((mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) { - memset(mConfig.inputCfg.buffer.raw, 0, mConfig.inputCfg.buffer.frameCount*sizeof(int32_t)); + memset(mConfig.inputCfg.buffer.raw, 0, + mConfig.inputCfg.buffer.frameCount*sizeof(int32_t)); } } else if ((mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_INSERT && - mConfig.inputCfg.buffer.raw != mConfig.outputCfg.buffer.raw){ - // If an insert effect is idle and input buffer is different from output buffer, copy input to - // output + mConfig.inputCfg.buffer.raw != mConfig.outputCfg.buffer.raw) { + // If an insert effect is idle and input buffer is different from output buffer, + // accumulate input onto output sp chain = mChain.promote(); if (chain != 0 && chain->activeTracks() != 0) { - size_t size = mConfig.inputCfg.buffer.frameCount * sizeof(int16_t); - if (mConfig.inputCfg.channels == CHANNEL_STEREO) { - size *= 2; + size_t frameCnt = mConfig.inputCfg.buffer.frameCount * 2; //always stereo here + int16_t *in = mConfig.inputCfg.buffer.s16; + int16_t *out = mConfig.outputCfg.buffer.s16; + for (size_t i = 0; i < frameCnt; i++) { + out[i] = clamp16((int32_t)out[i] + (int32_t)in[i]); } - memcpy(mConfig.outputCfg.buffer.raw, mConfig.inputCfg.buffer.raw, size); } } } -- cgit v1.1 From 209821c7b7ead3ac58743d0a6d21dd05a2e77708 Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Tue, 18 Jan 2011 16:44:28 -0800 Subject: Increase the number of useful bits in the spectrum. Change-Id: I3efc2ac8db689285566df35cb6f594aab41141db --- media/libmedia/Visualizer.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/media/libmedia/Visualizer.cpp b/media/libmedia/Visualizer.cpp index a660429..43571cf 100644 --- a/media/libmedia/Visualizer.cpp +++ b/media/libmedia/Visualizer.cpp @@ -219,8 +219,13 @@ status_t Visualizer::doFft(uint8_t *fft, uint8_t *waveform) } for (uint32_t i = 0; i < mCaptureSize; i += 2) { - fft[i] = workspace[i >> 1] >> 24; - fft[i + 1] = workspace[i >> 1] >> 8; + short tmp = workspace[i >> 1] >> 21; + while (tmp > 127 || tmp < -128) tmp >>= 1; + fft[i] = tmp; + tmp = workspace[i >> 1]; + tmp >>= 5; + while (tmp > 127 || tmp < -128) tmp >>= 1; + fft[i + 1] = tmp; } return NO_ERROR; -- cgit v1.1 From 05b22175a0d186d964694a5162892b6b3476c562 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Mon, 24 Jan 2011 13:36:30 -0800 Subject: DO NOT MERGE: Support non-multiple-of-16 dimensions in MPEG4/H.263 software decoder Change-Id: I6c27c00a6e13cb3635e61089c0db0989f5810c37 related-to-bug: 3384367 --- .../codecs/m4v_h263/dec/M4vH263Decoder.cpp | 25 ++++++++++++++-------- .../codecs/m4v_h263/dec/include/mp4dec_api.h | 1 + .../codecs/m4v_h263/dec/src/pvdec_api.cpp | 6 ++++++ 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/media/libstagefright/codecs/m4v_h263/dec/M4vH263Decoder.cpp b/media/libstagefright/codecs/m4v_h263/dec/M4vH263Decoder.cpp index 0f08f6e..dcf129e 100644 --- a/media/libstagefright/codecs/m4v_h263/dec/M4vH263Decoder.cpp +++ b/media/libstagefright/codecs/m4v_h263/dec/M4vH263Decoder.cpp @@ -23,8 +23,8 @@ #include "mp4dec_api.h" #include +#include #include -#include #include #include #include @@ -106,7 +106,7 @@ status_t M4vH263Decoder::start(MetaData *) { int32_t vol_size = 0; if (meta->findData(kKeyESDS, &type, &data, &size)) { ESDS esds((const uint8_t *)data, size); - CHECK_EQ(esds.InitCheck(), OK); + CHECK_EQ(esds.InitCheck(), (status_t)OK); const void *codec_specific_data; size_t codec_specific_data_size; @@ -132,7 +132,7 @@ status_t M4vH263Decoder::start(MetaData *) { } MP4DecodingMode actualMode = PVGetDecBitstreamMode(mHandle); - CHECK_EQ(mode, actualMode); + CHECK_EQ((int)mode, (int)actualMode); PVSetPostProcType((VideoDecControls *) mHandle, 0); @@ -182,7 +182,7 @@ status_t M4vH263Decoder::read( ReadOptions::SeekMode mode; if (options && options->getSeekTo(&seekTimeUs, &mode)) { seeking = true; - CHECK_EQ(PVResetVideoDecoder(mHandle), PV_TRUE); + CHECK_EQ((int)PVResetVideoDecoder(mHandle), PV_TRUE); } MediaBuffer *inputBuffer = NULL; @@ -220,19 +220,26 @@ status_t M4vH263Decoder::read( return UNKNOWN_ERROR; } - int32_t width, height; - PVGetVideoDimensions(mHandle, &width, &height); - if (width != mWidth || height != mHeight) { + int32_t disp_width, disp_height; + PVGetVideoDimensions(mHandle, &disp_width, &disp_height); + + int32_t buf_width, buf_height; + PVGetBufferDimensions(mHandle, &buf_width, &buf_height); + + if (buf_width != mWidth || buf_height != mHeight) { ++mNumSamplesOutput; // The client will never get to see this frame. inputBuffer->release(); inputBuffer = NULL; - mWidth = width; - mHeight = height; + mWidth = buf_width; + mHeight = buf_height; mFormat->setInt32(kKeyWidth, mWidth); mFormat->setInt32(kKeyHeight, mHeight); + CHECK_LE(disp_width, buf_width); + CHECK_LE(disp_height, buf_height); + return INFO_FORMAT_CHANGED; } diff --git a/media/libstagefright/codecs/m4v_h263/dec/include/mp4dec_api.h b/media/libstagefright/codecs/m4v_h263/dec/include/mp4dec_api.h index ef09900..24a50ce 100644 --- a/media/libstagefright/codecs/m4v_h263/dec/include/mp4dec_api.h +++ b/media/libstagefright/codecs/m4v_h263/dec/include/mp4dec_api.h @@ -159,6 +159,7 @@ extern "C" Bool PVDecodeVopBody(VideoDecControls *decCtrl, int32 buffer_size[]); void PVDecPostProcess(VideoDecControls *decCtrl, uint8 *outputYUV); OSCL_IMPORT_REF void PVGetVideoDimensions(VideoDecControls *decCtrl, int32 *display_width, int32 *display_height); + OSCL_IMPORT_REF void PVGetBufferDimensions(VideoDecControls *decCtrl, int32 *buf_width, int32 *buf_height); OSCL_IMPORT_REF void PVSetPostProcType(VideoDecControls *decCtrl, int mode); uint32 PVGetVideoTimeStamp(VideoDecControls *decoderControl); int PVGetDecBitrate(VideoDecControls *decCtrl); diff --git a/media/libstagefright/codecs/m4v_h263/dec/src/pvdec_api.cpp b/media/libstagefright/codecs/m4v_h263/dec/src/pvdec_api.cpp index 0c354d9..844bd14 100644 --- a/media/libstagefright/codecs/m4v_h263/dec/src/pvdec_api.cpp +++ b/media/libstagefright/codecs/m4v_h263/dec/src/pvdec_api.cpp @@ -722,6 +722,12 @@ OSCL_EXPORT_REF void PVGetVideoDimensions(VideoDecControls *decCtrl, int32 *disp *display_height = video->displayHeight; } +OSCL_EXPORT_REF void PVGetBufferDimensions(VideoDecControls *decCtrl, int32 *width, int32 *height) { + VideoDecData *video = (VideoDecData *)decCtrl->videoDecoderData; + *width = video->width; + *height = video->height; +} + /* ======================================================================== */ /* Function : PVGetVideoTimeStamp() */ /* Date : 04/27/2000, 08/29/2000 */ -- cgit v1.1 From b1d099197a9385f3afc9411da32a341546991d85 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Tue, 18 Jan 2011 15:51:30 -0800 Subject: fix [3361121] hang in glClear() - device unresponsive, OTA fails (DO NOT MERGE) Generally we never want to lock a buffer for write access if it is at the "head" on the surfaceflinger side. The only exception (1) is when the buffer is not currently in use AND there is at least one queued buffer -- in which case, SurfaceFlinger will never use said buffer anymore, because on the next composition around, it will be able to retire the first queued buffer. The logic above relies on SurfaceFlinger always retiring and locking a buffer before composition -- unfortunately this didn't happen during a screenshot. This could leave us in a situation where a buffer is locked by the application for write, and used by SurfaceFlinger for texturing, causing a hang. Here, we fix this issue by never assuming the exception (1), it was intended as an optimization allowing ANativeWindow::lockBuffer() to return sooner and was justified when most of SF composition was done in software. The actual buffer locking is now ensured by gralloc. We could have handled screenshots in a similar way to a regular composition, but it could have caused glitches on screen, essentially, taking a screenshot could cause to skip a frame. now that we removed the notion of a "inUse" buffer in surfaceflinger a lot of code can be simplified / removed. noteworthy, the whole concept of "unlockClient" wrt. "compositionComplete" is also gone. --- include/private/surfaceflinger/SharedBufferStack.h | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/include/private/surfaceflinger/SharedBufferStack.h b/include/private/surfaceflinger/SharedBufferStack.h index d6ae5e9..4ae3cdf 100644 --- a/include/private/surfaceflinger/SharedBufferStack.h +++ b/include/private/surfaceflinger/SharedBufferStack.h @@ -105,7 +105,7 @@ public: volatile int32_t head; // server's current front buffer volatile int32_t available; // number of dequeue-able buffers volatile int32_t queued; // number of buffers waiting for post - volatile int32_t inUse; // buffer currently in use by SF + volatile int32_t reserved1; volatile status_t status; // surface's status code // not part of the conditions @@ -275,7 +275,6 @@ public: int32_t identity); ssize_t retireAndLock(); - status_t unlock(int buffer); void setStatus(status_t status); status_t reallocateAll(); status_t reallocateAllExcept(int buffer); @@ -346,11 +345,6 @@ private: int mNumBuffers; BufferList mBufferList; - struct UnlockUpdate : public UpdateBase { - const int lockedBuffer; - inline UnlockUpdate(SharedBufferBase* sbb, int lockedBuffer); - inline ssize_t operator()(); - }; struct RetireUpdate : public UpdateBase { const int numBuffers; -- cgit v1.1 From bfa2f13fd3f463dbceea4d3a18c3124e70df0a05 Mon Sep 17 00:00:00 2001 From: Jean-Michel Trivi Date: Mon, 15 Nov 2010 12:11:32 -0800 Subject: do not merge bug 3370834 Cherrypick from master Cherripick from master CL 79833, 79417, 78864, 80332, 87500 Add new audio mode and recording source for audio communications other than telelphony. The audio mode MODE_IN_CALL signals the system the device a phone call is currently underway. There was no way for audio video chat or VoIP applications to signal a call is underway, but not using the telephony resources. This change introduces a new mode to address this. Changes in other parts of the system (java and native) are required to take this new mode into account. The generic AudioPolicyManager is updated to not use its phone state variable directly, but to use two new convenience methods, isInCall() and isStateInCall(int) instead. Add a recording source used to designate a recording stream for voice communications such as VoIP. Update the platform-independent audio policy manager to pass the nature of the audio recording source to the audio policy client interface through the AudioPolicyClientInterface::setParameters() method. SIP calls should set the audio mode to MODE_IN_COMMUNICATION, Audio mode MODE_IN_CALL is reserved for telephony. SIP: Enable built-in echo canceler if available. 1. Always initialize AudioRecord with VOICE_COMMUNICATION. 2. If echo canceler is available, disable our echo suppressor. Note that this CL is intentionally not correcting the getAudioSourceMax() return value in MediaRecorder.java as the new source is hidden here. Change-Id: Ie68cd03c50553101aa2ad838fe9459b2cf151bc8 --- include/media/AudioSystem.h | 6 ++- include/media/EffectApi.h | 6 +-- include/media/mediarecorder.h | 3 +- media/libmedia/AudioSystem.cpp | 1 + services/audioflinger/AudioFlinger.cpp | 3 +- services/audioflinger/AudioHardwareInterface.cpp | 5 ++- services/audioflinger/AudioPolicyManagerBase.cpp | 52 +++++++++++++++--------- 7 files changed, 49 insertions(+), 27 deletions(-) diff --git a/include/media/AudioSystem.h b/include/media/AudioSystem.h index 9fd905f..e881747 100644 --- a/include/media/AudioSystem.h +++ b/include/media/AudioSystem.h @@ -156,6 +156,7 @@ public: MODE_NORMAL = 0, MODE_RINGTONE, MODE_IN_CALL, + MODE_IN_COMMUNICATION, NUM_MODES // not a valid entry, denotes end-of-list }; @@ -466,7 +467,7 @@ public: AudioParameter(const String8& keyValuePairs); virtual ~AudioParameter(); - // reserved parameter keys for changeing standard parameters with setParameters() function. + // reserved parameter keys for changing standard parameters with setParameters() function. // Using these keys is mandatory for AudioFlinger to properly monitor audio output/input // configuration changes and act accordingly. // keyRouting: to change audio routing, value is an int in AudioSystem::audio_devices @@ -474,11 +475,14 @@ public: // keyFormat: to change audio format, value is an int in AudioSystem::audio_format // keyChannels: to change audio channel configuration, value is an int in AudioSystem::audio_channels // keyFrameCount: to change audio output frame count, value is an int + // keyInputSource: to change audio input source, value is an int in audio_source + // (defined in media/mediarecorder.h) static const char *keyRouting; static const char *keySamplingRate; static const char *keyFormat; static const char *keyChannels; static const char *keyFrameCount; + static const char *keyInputSource; String8 toString(); diff --git a/include/media/EffectApi.h b/include/media/EffectApi.h index 16fb43c..b97c22e 100644 --- a/include/media/EffectApi.h +++ b/include/media/EffectApi.h @@ -602,9 +602,9 @@ enum audio_device_e { // Audio mode enum audio_mode_e { - AUDIO_MODE_NORMAL, // phone idle - AUDIO_MODE_RINGTONE, // phone ringing - AUDIO_MODE_IN_CALL // phone call connected + AUDIO_MODE_NORMAL, // device idle + AUDIO_MODE_RINGTONE, // device ringing + AUDIO_MODE_IN_CALL // audio call connected (VoIP or telephony) }; // Values for "accessMode" field of buffer_config_t: diff --git a/include/media/mediarecorder.h b/include/media/mediarecorder.h index 5ab1640..9a76393 100644 --- a/include/media/mediarecorder.h +++ b/include/media/mediarecorder.h @@ -44,7 +44,8 @@ enum audio_source { AUDIO_SOURCE_VOICE_CALL = 4, AUDIO_SOURCE_CAMCORDER = 5, AUDIO_SOURCE_VOICE_RECOGNITION = 6, - AUDIO_SOURCE_MAX = AUDIO_SOURCE_VOICE_RECOGNITION, + AUDIO_SOURCE_VOICE_COMMUNICATION = 7, + AUDIO_SOURCE_MAX = AUDIO_SOURCE_VOICE_COMMUNICATION, AUDIO_SOURCE_LIST_END // must be last - used to validate audio source type }; diff --git a/media/libmedia/AudioSystem.cpp b/media/libmedia/AudioSystem.cpp index 9c2a8ba..1a3fcd6 100644 --- a/media/libmedia/AudioSystem.cpp +++ b/media/libmedia/AudioSystem.cpp @@ -835,6 +835,7 @@ const char *AudioParameter::keySamplingRate = "sampling_rate"; const char *AudioParameter::keyFormat = "format"; const char *AudioParameter::keyChannels = "channels"; const char *AudioParameter::keyFrameCount = "frame_count"; +const char *AudioParameter::keyInputSource = "input_source"; AudioParameter::AudioParameter(const String8& keyValuePairs) { diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index 5071555..5935bf9 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -5810,7 +5810,8 @@ uint32_t AudioFlinger::EffectModule::deviceAudioSystemToEffectApi(uint32_t devic const uint32_t AudioFlinger::EffectModule::sModeConvTable[] = { AUDIO_MODE_NORMAL, // AudioSystem::MODE_NORMAL AUDIO_MODE_RINGTONE, // AudioSystem::MODE_RINGTONE - AUDIO_MODE_IN_CALL // AudioSystem::MODE_IN_CALL + AUDIO_MODE_IN_CALL, // AudioSystem::MODE_IN_CALL + AUDIO_MODE_IN_CALL // AudioSystem::MODE_IN_COMMUNICATION, same conversion as for MODE_IN_CALL }; int AudioFlinger::EffectModule::modeAudioSystemToEffectApi(uint32_t mode) diff --git a/services/audioflinger/AudioHardwareInterface.cpp b/services/audioflinger/AudioHardwareInterface.cpp index 9a4a7f9..f58e4c0 100644 --- a/services/audioflinger/AudioHardwareInterface.cpp +++ b/services/audioflinger/AudioHardwareInterface.cpp @@ -48,14 +48,15 @@ static const char* routingModeStrings[] = "CURRENT", "NORMAL", "RINGTONE", - "IN_CALL" + "IN_CALL", + "IN_COMMUNICATION" }; static const char* routeNone = "NONE"; static const char* displayMode(int mode) { - if ((mode < -2) || (mode > 2)) + if ((mode < AudioSystem::MODE_INVALID) || (mode >= AudioSystem::NUM_MODES)) return routingModeStrings[0]; return routingModeStrings[mode+3]; } diff --git a/services/audioflinger/AudioPolicyManagerBase.cpp b/services/audioflinger/AudioPolicyManagerBase.cpp index 8d16ab4..0da353a 100644 --- a/services/audioflinger/AudioPolicyManagerBase.cpp +++ b/services/audioflinger/AudioPolicyManagerBase.cpp @@ -235,7 +235,7 @@ void AudioPolicyManagerBase::setPhoneState(int state) // if leaving call state, handle special case of active streams // pertaining to sonification strategy see handleIncallSonification() - if (mPhoneState == AudioSystem::MODE_IN_CALL) { + if (isInCall()) { LOGV("setPhoneState() in call state management: new state is %d", state); for (int stream = 0; stream < AudioSystem::NUM_STREAM_TYPES; stream++) { handleIncallSonification(stream, false, true); @@ -248,16 +248,21 @@ void AudioPolicyManagerBase::setPhoneState(int state) bool force = false; // are we entering or starting a call - if ((oldState != AudioSystem::MODE_IN_CALL) && (state == AudioSystem::MODE_IN_CALL)) { + if (!isStateInCall(oldState) && isStateInCall(state)) { LOGV(" Entering call in setPhoneState()"); // force routing command to audio hardware when starting a call // even if no device change is needed force = true; - } else if ((oldState == AudioSystem::MODE_IN_CALL) && (state != AudioSystem::MODE_IN_CALL)) { + } else if (isStateInCall(oldState) && !isStateInCall(state)) { LOGV(" Exiting call in setPhoneState()"); // force routing command to audio hardware when exiting a call // even if no device change is needed force = true; + } else if (isStateInCall(state) && (state != oldState)) { + LOGV(" Switching between telephony and VoIP in setPhoneState()"); + // force routing command to audio hardware when switching between telephony and VoIP + // even if no device change is needed + force = true; } // check for device and output changes triggered by new phone state @@ -272,7 +277,7 @@ void AudioPolicyManagerBase::setPhoneState(int state) // force routing command to audio hardware when ending call // even if no device change is needed - if (oldState == AudioSystem::MODE_IN_CALL && newDevice == 0) { + if (isStateInCall(oldState) && newDevice == 0) { newDevice = hwOutputDesc->device(); } @@ -280,7 +285,7 @@ void AudioPolicyManagerBase::setPhoneState(int state) // immediately and delay the route change to avoid sending the ring tone // tail into the earpiece or headset. int delayMs = 0; - if (state == AudioSystem::MODE_IN_CALL && oldState == AudioSystem::MODE_RINGTONE) { + if (isStateInCall(state) && oldState == AudioSystem::MODE_RINGTONE) { // delay the device change command by twice the output latency to have some margin // and be sure that audio buffers not yet affected by the mute are out when // we actually apply the route change @@ -293,7 +298,7 @@ void AudioPolicyManagerBase::setPhoneState(int state) // if entering in call state, handle special case of active streams // pertaining to sonification strategy see handleIncallSonification() - if (state == AudioSystem::MODE_IN_CALL) { + if (isStateInCall(state)) { LOGV("setPhoneState() in call state management: new state is %d", state); // unmute the ringing tone after a sufficient delay if it was muted before // setting output device above @@ -564,7 +569,7 @@ status_t AudioPolicyManagerBase::startOutput(audio_io_handle_t output, setOutputDevice(output, getNewDevice(output)); // handle special case for sonification while in call - if (mPhoneState == AudioSystem::MODE_IN_CALL) { + if (isInCall()) { handleIncallSonification(stream, true, false); } @@ -589,7 +594,7 @@ status_t AudioPolicyManagerBase::stopOutput(audio_io_handle_t output, routing_strategy strategy = getStrategy((AudioSystem::stream_type)stream); // handle special case for sonification while in call - if (mPhoneState == AudioSystem::MODE_IN_CALL) { + if (isInCall()) { handleIncallSonification(stream, false, false); } @@ -738,10 +743,8 @@ status_t AudioPolicyManagerBase::startInput(audio_io_handle_t input) AudioParameter param = AudioParameter(); param.addInt(String8(AudioParameter::keyRouting), (int)inputDesc->mDevice); - // use Voice Recognition mode or not for this input based on input source - int vr_enabled = inputDesc->mInputSource == AUDIO_SOURCE_VOICE_RECOGNITION ? 1 : 0; - param.addInt(String8("vr_mode"), vr_enabled); - LOGV("AudioPolicyManager::startInput(%d), setting vr_mode to %d", inputDesc->mInputSource, vr_enabled); + param.addInt(String8(AudioParameter::keyInputSource), (int)inputDesc->mInputSource); + LOGV("AudioPolicyManager::startInput() input source = %d", inputDesc->mInputSource); mpClientInterface->setParameters(input, param.toString()); @@ -1494,7 +1497,7 @@ uint32_t AudioPolicyManagerBase::getNewDevice(audio_io_handle_t output, bool fro // use device for strategy media // 4: the strategy DTMF is active on the hardware output: // use device for strategy DTMF - if (mPhoneState == AudioSystem::MODE_IN_CALL || + if (isInCall() || outputDesc->isUsedByStrategy(STRATEGY_PHONE)) { device = getDeviceForStrategy(STRATEGY_PHONE, fromCache); } else if (outputDesc->isUsedByStrategy(STRATEGY_SONIFICATION)) { @@ -1549,7 +1552,7 @@ uint32_t AudioPolicyManagerBase::getDeviceForStrategy(routing_strategy strategy, switch (strategy) { case STRATEGY_DTMF: - if (mPhoneState != AudioSystem::MODE_IN_CALL) { + if (!isInCall()) { // when off call, DTMF strategy follows the same rules as MEDIA strategy device = getDeviceForStrategy(STRATEGY_MEDIA, false); break; @@ -1562,7 +1565,7 @@ uint32_t AudioPolicyManagerBase::getDeviceForStrategy(routing_strategy strategy, // of priority switch (mForceUse[AudioSystem::FOR_COMMUNICATION]) { case AudioSystem::FORCE_BT_SCO: - if (mPhoneState != AudioSystem::MODE_IN_CALL || strategy != STRATEGY_DTMF) { + if (!isInCall() || strategy != STRATEGY_DTMF) { device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_CARKIT; if (device) break; } @@ -1580,7 +1583,7 @@ uint32_t AudioPolicyManagerBase::getDeviceForStrategy(routing_strategy strategy, if (device) break; #ifdef WITH_A2DP // when not in a phone call, phone strategy should route STREAM_VOICE_CALL to A2DP - if (mPhoneState != AudioSystem::MODE_IN_CALL) { + if (!isInCall()) { device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP; if (device) break; device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES; @@ -1594,14 +1597,14 @@ uint32_t AudioPolicyManagerBase::getDeviceForStrategy(routing_strategy strategy, break; case AudioSystem::FORCE_SPEAKER: - if (mPhoneState != AudioSystem::MODE_IN_CALL || strategy != STRATEGY_DTMF) { + if (!isInCall() || strategy != STRATEGY_DTMF) { device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_CARKIT; if (device) break; } #ifdef WITH_A2DP // when not in a phone call, phone strategy should route STREAM_VOICE_CALL to // A2DP speaker when forcing to speaker output - if (mPhoneState != AudioSystem::MODE_IN_CALL) { + if (!isInCall()) { device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER; if (device) break; } @@ -1618,7 +1621,7 @@ uint32_t AudioPolicyManagerBase::getDeviceForStrategy(routing_strategy strategy, // If incall, just select the STRATEGY_PHONE device: The rest of the behavior is handled by // handleIncallSonification(). - if (mPhoneState == AudioSystem::MODE_IN_CALL) { + if (isInCall()) { device = getDeviceForStrategy(STRATEGY_PHONE, false); break; } @@ -1739,6 +1742,7 @@ uint32_t AudioPolicyManagerBase::getDeviceForInputSource(int inputSource) case AUDIO_SOURCE_DEFAULT: case AUDIO_SOURCE_MIC: case AUDIO_SOURCE_VOICE_RECOGNITION: + case AUDIO_SOURCE_VOICE_COMMUNICATION: if (mForceUse[AudioSystem::FOR_RECORD] == AudioSystem::FORCE_BT_SCO && mAvailableInputDevices & AudioSystem::DEVICE_IN_BLUETOOTH_SCO_HEADSET) { device = AudioSystem::DEVICE_IN_BLUETOOTH_SCO_HEADSET; @@ -1960,6 +1964,16 @@ void AudioPolicyManagerBase::handleIncallSonification(int stream, bool starting, } } +bool AudioPolicyManagerBase::isInCall() +{ + return isStateInCall(mPhoneState); +} + +bool AudioPolicyManagerBase::isStateInCall(int state) { + return ((state == AudioSystem::MODE_IN_CALL) || + (state == AudioSystem::MODE_IN_COMMUNICATION)); +} + bool AudioPolicyManagerBase::needsDirectOuput(AudioSystem::stream_type stream, uint32_t samplingRate, uint32_t format, -- cgit v1.1 From 97a38b4f1be201bbbfa23d298bb779fffd624a3d Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Wed, 26 Jan 2011 15:40:56 -0800 Subject: DO NOT MERGE: This particular RTSP server streams MPEG4-LATM audio with extra trailing bytes. And now we're just ignoring them. Yay standards. Change-Id: Ia8c0b9161e606152fb681f0dda3ba901954dc749 related-to-bug: 3353752 --- media/libstagefright/rtsp/AMPEG4AudioAssembler.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/media/libstagefright/rtsp/AMPEG4AudioAssembler.cpp b/media/libstagefright/rtsp/AMPEG4AudioAssembler.cpp index bbde516..8bfe285 100644 --- a/media/libstagefright/rtsp/AMPEG4AudioAssembler.cpp +++ b/media/libstagefright/rtsp/AMPEG4AudioAssembler.cpp @@ -359,7 +359,10 @@ sp AMPEG4AudioAssembler::removeLATMFraming(const sp &buffer) { } } - CHECK_EQ(offset, buffer->size()); + if (offset < buffer->size()) { + LOGI("ignoring %d bytes of trailing data", buffer->size() - offset); + } + CHECK_LE(offset, buffer->size()); return out; } -- cgit v1.1 From 5092d8c066a1a98343bbc16e33a6753e577b5e7f Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Thu, 27 Jan 2011 11:32:34 -0800 Subject: Fix issue 2988031. Limit SYSTEM stream volume when a headset is connected and music is playing. Change-Id: Ieb44ae5bb53ffa9cd5fe8e317798eed279b78df8 --- services/audioflinger/AudioPolicyManagerBase.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/services/audioflinger/AudioPolicyManagerBase.cpp b/services/audioflinger/AudioPolicyManagerBase.cpp index 0da353a..4612af1 100644 --- a/services/audioflinger/AudioPolicyManagerBase.cpp +++ b/services/audioflinger/AudioPolicyManagerBase.cpp @@ -1806,7 +1806,8 @@ float AudioPolicyManagerBase::computeVolume(int stream, int index, audio_io_hand AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES | AudioSystem::DEVICE_OUT_WIRED_HEADSET | AudioSystem::DEVICE_OUT_WIRED_HEADPHONE)) && - (getStrategy((AudioSystem::stream_type)stream) == STRATEGY_SONIFICATION) && + ((getStrategy((AudioSystem::stream_type)stream) == STRATEGY_SONIFICATION) || + (stream == AudioSystem::SYSTEM)) && streamDesc.mCanBeMuted) { volume *= SONIFICATION_HEADSET_VOLUME_FACTOR; // when the phone is ringing we must consider that music could have been paused just before -- cgit v1.1 From 4635eba3507750ed57817f244e84084d06df5b2c Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Fri, 28 Jan 2011 09:19:12 -0800 Subject: DO NOT MERGE: More robust parsing of NPT time ranges in RTSP. Change-Id: If5a00f1e29dbc12956e1fb000dac859706d19791 related-to-bug: 3217210 --- media/libstagefright/rtsp/ASessionDescription.cpp | 52 ++++++++++++++++------- media/libstagefright/rtsp/ASessionDescription.h | 8 ++++ media/libstagefright/rtsp/MyHandler.h | 6 +-- 3 files changed, 46 insertions(+), 20 deletions(-) diff --git a/media/libstagefright/rtsp/ASessionDescription.cpp b/media/libstagefright/rtsp/ASessionDescription.cpp index 0d0234b..2843ee6 100644 --- a/media/libstagefright/rtsp/ASessionDescription.cpp +++ b/media/libstagefright/rtsp/ASessionDescription.cpp @@ -254,26 +254,12 @@ bool ASessionDescription::getDurationUs(int64_t *durationUs) const { return false; } - if (value == "npt=now-" || value == "npt=0-") { - return false; - } - if (strncmp(value.c_str(), "npt=", 4)) { return false; } - const char *s = value.c_str() + 4; - char *end; - double from = strtod(s, &end); - - if (end == s || *end != '-') { - return false; - } - - s = end + 1; - double to = strtod(s, &end); - - if (end == s || *end != '\0' || to < from) { + float from, to; + if (!parseNTPRange(value.c_str() + 4, &from, &to)) { return false; } @@ -307,5 +293,39 @@ void ASessionDescription::ParseFormatDesc( } } +// static +bool ASessionDescription::parseNTPRange( + const char *s, float *npt1, float *npt2) { + if (s[0] == '-') { + return false; // no start time available. + } + + if (!strncmp("now", s, 3)) { + return false; // no absolute start time available + } + + char *end; + *npt1 = strtof(s, &end); + + if (end == s || *end != '-') { + // Failed to parse float or trailing "dash". + return false; + } + + s = end + 1; // skip the dash. + + if (!strncmp("now", s, 3)) { + return false; // no absolute end time available + } + + *npt2 = strtof(s, &end); + + if (end == s || *end != '\0') { + return false; + } + + return *npt2 > *npt1; +} + } // namespace android diff --git a/media/libstagefright/rtsp/ASessionDescription.h b/media/libstagefright/rtsp/ASessionDescription.h index a3fa79e..b462983 100644 --- a/media/libstagefright/rtsp/ASessionDescription.h +++ b/media/libstagefright/rtsp/ASessionDescription.h @@ -55,6 +55,14 @@ struct ASessionDescription : public RefBase { bool findAttribute(size_t index, const char *key, AString *value) const; + // parses strings of the form + // npt := npt-time "-" npt-time? | "-" npt-time + // npt-time := "now" | [0-9]+("." [0-9]*)? + // + // Returns true iff both "npt1" and "npt2" times were available, + // i.e. we have a fixed duration, otherwise this is live streaming. + static bool parseNTPRange(const char *s, float *npt1, float *npt2); + protected: virtual ~ASessionDescription(); diff --git a/media/libstagefright/rtsp/MyHandler.h b/media/libstagefright/rtsp/MyHandler.h index 8f8e10a..5c6ff82 100644 --- a/media/libstagefright/rtsp/MyHandler.h +++ b/media/libstagefright/rtsp/MyHandler.h @@ -938,13 +938,11 @@ struct MyHandler : public AHandler { AString val; CHECK(GetAttribute(range.c_str(), "npt", &val)); - float npt1, npt2; - if (val == "now-" || val == "0-") { + float npt1, npt2; + if (!ASessionDescription::parseNTPRange(val.c_str(), &npt1, &npt2)) { // This is a live stream and therefore not seekable. return; - } else { - CHECK_EQ(sscanf(val.c_str(), "%f-%f", &npt1, &npt2), 2); } i = response->mHeaders.indexOfKey("rtp-info"); -- cgit v1.1