diff options
27 files changed, 521 insertions, 359 deletions
diff --git a/include/media/PVMediaRecorder.h b/include/media/PVMediaRecorder.h deleted file mode 100644 index 4b44ccc..0000000 --- a/include/media/PVMediaRecorder.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - ** - ** Copyright 2008, The Android Open Source Project - ** - ** Licensed under the Apache License, Version 2.0 (the "License"); - ** you may not use this file except in compliance with the License. - ** You may obtain a copy of the License at - ** - ** http://www.apache.org/licenses/LICENSE-2.0 - ** - ** Unless required by applicable law or agreed to in writing, software - ** distributed under the License is distributed on an "AS IS" BASIS, - ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ** See the License for the specific language governing permissions and - ** limitations under the License. - */ - -#ifndef ANDROID_PVMEDIARECORDER_H -#define ANDROID_PVMEDIARECORDER_H - -#include <media/IMediaRecorderClient.h> -#include <media/MediaRecorderBase.h> - -namespace android { - -class Surface; -class ICamera; -class AuthorDriverWrapper; - -class PVMediaRecorder : public MediaRecorderBase { -public: - PVMediaRecorder(); - virtual ~PVMediaRecorder(); - - virtual status_t init(); - virtual status_t setAudioSource(audio_source as); - virtual status_t setVideoSource(video_source vs); - virtual status_t setOutputFormat(output_format of); - virtual status_t setAudioEncoder(audio_encoder ae); - virtual status_t setVideoEncoder(video_encoder ve); - virtual status_t setVideoSize(int width, int height); - virtual status_t setVideoFrameRate(int frames_per_second); - virtual status_t setCamera(const sp<ICamera>& camera); - virtual status_t setPreviewSurface(const sp<Surface>& surface); - virtual status_t setOutputFile(const char *path); - virtual status_t setOutputFile(int fd, int64_t offset, int64_t length); - virtual status_t setParameters(const String8& params); - virtual status_t setListener(const sp<IMediaRecorderClient>& listener); - virtual status_t prepare(); - virtual status_t start(); - virtual status_t stop(); - virtual status_t close(); - virtual status_t reset(); - virtual status_t getMaxAmplitude(int *max); - virtual status_t dump(int fd, const Vector<String16>& args) const; - -private: - status_t doStop(); - - AuthorDriverWrapper* mAuthorDriverWrapper; - - PVMediaRecorder(const PVMediaRecorder &); - PVMediaRecorder &operator=(const PVMediaRecorder &); -}; - -}; // namespace android - -#endif // ANDROID_PVMEDIARECORDER_H diff --git a/include/media/PVMetadataRetriever.h b/include/media/PVMetadataRetriever.h deleted file mode 100644 index c202dfe..0000000 --- a/include/media/PVMetadataRetriever.h +++ /dev/null @@ -1,51 +0,0 @@ -/* -** -** Copyright (C) 2008 The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ - -#ifndef ANDROID_PVMETADATARETRIEVER_H -#define ANDROID_PVMETADATARETRIEVER_H - -#include <utils/Errors.h> -#include <media/MediaMetadataRetrieverInterface.h> -#include <private/media/VideoFrame.h> - -namespace android { - -class MetadataDriver; - -class PVMetadataRetriever : public MediaMetadataRetrieverInterface -{ -public: - PVMetadataRetriever(); - virtual ~PVMetadataRetriever(); - - 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 VideoFrame* captureFrame(); - virtual MediaAlbumArt* extractAlbumArt(); - virtual const char* extractMetadata(int keyCode); - -private: - mutable Mutex mLock; - MetadataDriver* mMetadataDriver; - char* mDataSourcePath; -}; - -}; // namespace android - -#endif // ANDROID_PVMETADATARETRIEVER_H diff --git a/include/media/PVPlayer.h b/include/media/PVPlayer.h deleted file mode 100644 index 657e7a6..0000000 --- a/include/media/PVPlayer.h +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ANDROID_PVPLAYER_H -#define ANDROID_PVPLAYER_H - -#include <utils/Errors.h> -#include <media/MediaPlayerInterface.h> -#include <media/Metadata.h> - -#define MAX_OPENCORE_INSTANCES 25 - -#ifdef MAX_OPENCORE_INSTANCES -#include <cutils/atomic.h> -#endif - -class PlayerDriver; - -namespace android { - -class PVPlayer : public MediaPlayerInterface -{ -public: - PVPlayer(); - virtual ~PVPlayer(); - - virtual status_t initCheck(); - - virtual status_t setDataSource( - const char *url, const KeyedVector<String8, String8> *headers); - - virtual status_t setDataSource(int fd, int64_t offset, int64_t length); - virtual status_t setVideoISurface(const sp<ISurface>& surface); - virtual status_t setVideoSurface(const sp<Surface>& surface); - virtual status_t prepare(); - virtual status_t prepareAsync(); - virtual status_t start(); - virtual status_t stop(); - virtual status_t pause(); - virtual bool isPlaying(); - virtual status_t seekTo(int msec); - virtual status_t getCurrentPosition(int *msec); - virtual status_t getDuration(int *msec); - virtual status_t reset(); - virtual status_t setLooping(int loop); - virtual player_type playerType() { return PV_PLAYER; } - virtual status_t invoke(const Parcel& request, Parcel *reply); - virtual status_t getMetadata( - const SortedVector<media::Metadata::Type>& ids, - Parcel *records); - - // make available to PlayerDriver - void sendEvent(int msg, int ext1=0, int ext2=0) { MediaPlayerBase::sendEvent(msg, ext1, ext2); } - -private: - static void do_nothing(status_t s, void *cookie, bool cancelled) { } - static void run_init(status_t s, void *cookie, bool cancelled); - static void run_set_video_surface(status_t s, void *cookie, bool cancelled); - static void run_set_audio_output(status_t s, void *cookie, bool cancelled); - static void run_prepare(status_t s, void *cookie, bool cancelled); - static void check_for_live_streaming(status_t s, void *cookie, bool cancelled); - - PlayerDriver* mPlayerDriver; - char * mDataSourcePath; - bool mIsDataSourceSet; - sp<ISurface> mSurface; - int mSharedFd; - status_t mInit; - int mDuration; - -#ifdef MAX_OPENCORE_INSTANCES - static volatile int32_t sNumInstances; -#endif -}; - -}; // namespace android - -#endif // ANDROID_PVPLAYER_H diff --git a/media/libmediaplayerservice/Android.mk b/media/libmediaplayerservice/Android.mk index 55846be..3341ff7 100644 --- a/media/libmediaplayerservice/Android.mk +++ b/media/libmediaplayerservice/Android.mk @@ -38,14 +38,6 @@ LOCAL_SHARED_LIBRARIES := \ LOCAL_STATIC_LIBRARIES := \ libstagefright_rtsp -ifneq ($(BUILD_WITHOUT_PV),true) -LOCAL_SHARED_LIBRARIES += \ - libopencore_player \ - libopencore_author -else -LOCAL_CFLAGS += -DNO_OPENCORE -endif - ifneq ($(TARGET_SIMULATOR),true) LOCAL_SHARED_LIBRARIES += libdl endif diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp index bb86e05..e84c2dc 100644 --- a/media/libmediaplayerservice/MediaPlayerService.cpp +++ b/media/libmediaplayerservice/MediaPlayerService.cpp @@ -56,7 +56,6 @@ #include "MetadataRetrieverClient.h" #include "MidiFile.h" -#include <media/PVPlayer.h> #include "TestPlayerStub.h" #include "StagefrightPlayer.h" @@ -196,11 +195,6 @@ extmap FILE_EXTS [] = { {".rtttl", SONIVOX_PLAYER}, {".rtx", SONIVOX_PLAYER}, {".ota", SONIVOX_PLAYER}, -#ifndef NO_OPENCORE - {".wma", PV_PLAYER}, - {".wmv", PV_PLAYER}, - {".asf", PV_PLAYER}, -#endif }; // TODO: Find real cause of Audio/Video delay in PV framework and remove this workaround @@ -691,14 +685,6 @@ player_type getPlayerType(int fd, int64_t offset, int64_t length) if (ident == 0x5367674f) // 'OggS' return STAGEFRIGHT_PLAYER; -#ifndef NO_OPENCORE - if (ident == 0x75b22630) { - // The magic number for .asf files, i.e. wmv and wma content. - // These are not currently supported through stagefright. - return PV_PLAYER; - } -#endif - // Some kind of MIDI? EAS_DATA_HANDLE easdata; if (EAS_Init(&easdata) == EAS_SUCCESS) { @@ -737,16 +723,6 @@ player_type getPlayerType(const char* url) } } - if (!strncasecmp(url, "rtsp://", 7)) { - char value[PROPERTY_VALUE_MAX]; - if (property_get("media.stagefright.enable-rtsp", value, NULL) - && (strcmp(value, "1") && strcasecmp(value, "true"))) { - // For now, we're going to use PV for rtsp-based playback - // by default until we can clear up a few more issues. - return PV_PLAYER; - } - } - return getDefaultPlayerType(); } @@ -755,12 +731,6 @@ static sp<MediaPlayerBase> createPlayer(player_type playerType, void* cookie, { sp<MediaPlayerBase> p; switch (playerType) { -#ifndef NO_OPENCORE - case PV_PLAYER: - LOGV(" create PVPlayer"); - p = new PVPlayer(); - break; -#endif case SONIVOX_PLAYER: LOGV(" create MidiFile"); p = new MidiFile(); @@ -773,6 +743,9 @@ static sp<MediaPlayerBase> createPlayer(player_type playerType, void* cookie, LOGV("Create Test Player stub"); p = new TestPlayerStub(); break; + default: + LOGE("Unknown player type: %d", playerType); + return NULL; } if (p != NULL) { if (p->initCheck() == NO_ERROR) { diff --git a/media/libmediaplayerservice/MediaRecorderClient.cpp b/media/libmediaplayerservice/MediaRecorderClient.cpp index be6a8be..1a1780c 100644 --- a/media/libmediaplayerservice/MediaRecorderClient.cpp +++ b/media/libmediaplayerservice/MediaRecorderClient.cpp @@ -31,10 +31,6 @@ #include <binder/MemoryHeapBase.h> #include <binder/MemoryBase.h> -#ifndef NO_OPENCORE -#include <media/PVMediaRecorder.h> -#endif - #include <utils/String16.h> #include <media/AudioTrack.h> @@ -304,22 +300,7 @@ MediaRecorderClient::MediaRecorderClient(const sp<MediaPlayerService>& service, { LOGV("Client constructor"); mPid = pid; - - char value[PROPERTY_VALUE_MAX]; - if (!property_get("media.stagefright.enable-record", value, NULL) - || !strcmp(value, "1") || !strcasecmp(value, "true")) { - mRecorder = new StagefrightRecorder; - } else -#ifndef NO_OPENCORE - { - mRecorder = new PVMediaRecorder(); - } -#else - { - mRecorder = NULL; - } -#endif - + mRecorder = new StagefrightRecorder; mMediaPlayerService = service; } diff --git a/media/libmediaplayerservice/MetadataRetrieverClient.cpp b/media/libmediaplayerservice/MetadataRetrieverClient.cpp index 39fce81..b069345 100644 --- a/media/libmediaplayerservice/MetadataRetrieverClient.cpp +++ b/media/libmediaplayerservice/MetadataRetrieverClient.cpp @@ -35,7 +35,6 @@ #include <binder/IServiceManager.h> #include <media/MediaMetadataRetrieverInterface.h> #include <media/MediaPlayerInterface.h> -#include <media/PVMetadataRetriever.h> #include <private/media/VideoFrame.h> #include "MidiMetadataRetriever.h" #include "MetadataRetrieverClient.h" @@ -107,12 +106,6 @@ static sp<MediaMetadataRetrieverBase> createRetriever(player_type playerType) p = new StagefrightMetadataRetriever; break; } -#ifndef NO_OPENCORE - case PV_PLAYER: - LOGV("create pv metadata retriever"); - p = new PVMetadataRetriever(); - break; -#endif case SONIVOX_PLAYER: LOGV("create midi metadata retriever"); p = new MidiMetadataRetriever(); diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk index 1df0bed..8fe1d4d 100644 --- a/media/libstagefright/Android.mk +++ b/media/libstagefright/Android.mk @@ -68,7 +68,8 @@ LOCAL_SHARED_LIBRARIES := \ libsurfaceflinger_client \ libstagefright_yuv \ libcamera_client \ - libdrmframework + libdrmframework \ + libcrypto LOCAL_STATIC_LIBRARIES := \ libstagefright_aacdec \ diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp index f084e28..41f5f30 100644 --- a/media/libstagefright/AwesomePlayer.cpp +++ b/media/libstagefright/AwesomePlayer.cpp @@ -49,6 +49,8 @@ #include <media/stagefright/foundation/ALooper.h> +#define USE_SURFACE_ALLOC 1 + namespace android { static int64_t kLowWaterMarkUs = 2000000ll; // 2secs @@ -294,6 +296,16 @@ status_t AwesomePlayer::setDataSource_l( mUri = uri; + if (!strncmp("http://", uri, 7)) { + // Hack to support http live. + + size_t len = strlen(uri); + if (!strcasecmp(&uri[len - 5], ".m3u8")) { + mUri = "httplive://"; + mUri.append(&uri[7]); + } + } + if (headers) { mUriHeaders = *headers; } @@ -873,7 +885,7 @@ void AwesomePlayer::initRenderer_l() { IPCThreadState::self()->flushCommands(); if (mSurface != NULL) { - if (strncmp(component, "OMX.", 4) == 0) { + if (USE_SURFACE_ALLOC && strncmp(component, "OMX.", 4) == 0) { // Hardware decoders avoid the CPU color conversion by decoding // directly to ANativeBuffers, so we must use a renderer that // just pushes those buffers to the ANativeWindow. @@ -1143,7 +1155,7 @@ status_t AwesomePlayer::initVideoDecoder(uint32_t flags) { mClient.interface(), mVideoTrack->getFormat(), false, // createEncoder mVideoTrack, - NULL, flags, mSurface); + NULL, flags, USE_SURFACE_ALLOC ? mSurface : NULL); if (mVideoSource != NULL) { int64_t durationUs; diff --git a/media/libstagefright/NuHTTPDataSource.cpp b/media/libstagefright/NuHTTPDataSource.cpp index 7aac447..133f225 100644 --- a/media/libstagefright/NuHTTPDataSource.cpp +++ b/media/libstagefright/NuHTTPDataSource.cpp @@ -5,6 +5,7 @@ #include "include/NuHTTPDataSource.h" #include <cutils/properties.h> +#include <media/stagefright/foundation/ALooper.h> #include <media/stagefright/MediaDebug.h> #include <media/stagefright/MediaErrors.h> @@ -68,6 +69,9 @@ NuHTTPDataSource::NuHTTPDataSource() mOffset(0), mContentLength(0), mContentLengthValid(false), + mNumBandwidthHistoryItems(0), + mTotalTransferTimeUs(0), + mTotalTransferBytes(0), mDecryptHandle(NULL), mDrmManagerClient(NULL) { } @@ -189,6 +193,20 @@ status_t NuHTTPDataSource::connect( return ERROR_IO; } + { + string value; + if (mHTTP.find_header_value("Transfer-Encoding", &value)) { + // We don't currently support any transfer encodings. + + mState = DISCONNECTED; + mHTTP.disconnect(); + + LOGE("We don't support '%s' transfer encoding.", value.c_str()); + + return ERROR_UNSUPPORTED; + } + } + applyTimeoutResponse(); if (offset == 0) { @@ -254,6 +272,8 @@ ssize_t NuHTTPDataSource::readAt(off_t offset, void *data, size_t size) { size_t numBytesRead = 0; while (numBytesRead < size) { + int64_t startTimeUs = ALooper::GetNowUs(); + ssize_t n = mHTTP.receive((uint8_t *)data + numBytesRead, size - numBytesRead); @@ -261,6 +281,9 @@ ssize_t NuHTTPDataSource::readAt(off_t offset, void *data, size_t size) { return n; } + int64_t delayUs = ALooper::GetNowUs() - startTimeUs; + addBandwidthMeasurement_l(n, delayUs); + numBytesRead += (size_t)n; if (n == 0) { @@ -345,6 +368,36 @@ void NuHTTPDataSource::applyTimeoutResponse() { } } +bool NuHTTPDataSource::estimateBandwidth(int32_t *bandwidth_bps) { + Mutex::Autolock autoLock(mLock); + + if (mNumBandwidthHistoryItems < 10) { + return false; + } + + *bandwidth_bps = ((double)mTotalTransferBytes * 8E6 / mTotalTransferTimeUs); + + return true; +} + +void NuHTTPDataSource::addBandwidthMeasurement_l( + size_t numBytes, int64_t delayUs) { + BandwidthEntry entry; + entry.mDelayUs = delayUs; + entry.mNumBytes = numBytes; + mTotalTransferTimeUs += delayUs; + mTotalTransferBytes += numBytes; + + mBandwidthHistory.push_back(entry); + if (++mNumBandwidthHistoryItems > 100) { + BandwidthEntry *entry = &*mBandwidthHistory.begin(); + mTotalTransferTimeUs -= entry->mDelayUs; + mTotalTransferBytes -= entry->mNumBytes; + mBandwidthHistory.erase(mBandwidthHistory.begin()); + --mNumBandwidthHistoryItems; + } +} + DecryptHandle* NuHTTPDataSource::DrmInitialization(DrmManagerClient* client) { if (client == NULL) { return NULL; diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp index 8edcd12..5ed4d84 100644 --- a/media/libstagefright/OMXCodec.cpp +++ b/media/libstagefright/OMXCodec.cpp @@ -54,6 +54,7 @@ #include <OMX_Component.h> #include "include/ThreadedSource.h" +#include "include/avc_utils.h" namespace android { @@ -264,39 +265,6 @@ static const char *GetCodec(const CodecInfo *info, size_t numInfos, return NULL; } -enum { - kAVCProfileBaseline = 0x42, - kAVCProfileMain = 0x4d, - kAVCProfileExtended = 0x58, - kAVCProfileHigh = 0x64, - kAVCProfileHigh10 = 0x6e, - kAVCProfileHigh422 = 0x7a, - kAVCProfileHigh444 = 0xf4, - kAVCProfileCAVLC444Intra = 0x2c -}; - -static const char *AVCProfileToString(uint8_t profile) { - switch (profile) { - case kAVCProfileBaseline: - return "Baseline"; - case kAVCProfileMain: - return "Main"; - case kAVCProfileExtended: - return "Extended"; - case kAVCProfileHigh: - return "High"; - case kAVCProfileHigh10: - return "High 10"; - case kAVCProfileHigh422: - return "High 422"; - case kAVCProfileHigh444: - return "High 444"; - case kAVCProfileCAVLC444Intra: - return "CAVLC 444 Intra"; - default: return "Unknown"; - } -} - template<class T> static void InitOMXParams(T *params) { params->nSize = sizeof(T); diff --git a/media/libstagefright/avc_utils.cpp b/media/libstagefright/avc_utils.cpp index 478e40c..2fe5e18 100644 --- a/media/libstagefright/avc_utils.cpp +++ b/media/libstagefright/avc_utils.cpp @@ -218,6 +218,28 @@ static sp<ABuffer> FindNAL( return NULL; } +const char *AVCProfileToString(uint8_t profile) { + switch (profile) { + case kAVCProfileBaseline: + return "Baseline"; + case kAVCProfileMain: + return "Main"; + case kAVCProfileExtended: + return "Extended"; + case kAVCProfileHigh: + return "High"; + case kAVCProfileHigh10: + return "High 10"; + case kAVCProfileHigh422: + return "High 422"; + case kAVCProfileHigh444: + return "High 444"; + case kAVCProfileCAVLC444Intra: + return "CAVLC 444 Intra"; + default: return "Unknown"; + } +} + sp<MetaData> MakeAVCCodecSpecificData(const sp<ABuffer> &accessUnit) { const uint8_t *data = accessUnit->data(); size_t size = accessUnit->size(); @@ -244,6 +266,10 @@ sp<MetaData> MakeAVCCodecSpecificData(const sp<ABuffer> &accessUnit) { *out++ = 0x01; // configurationVersion memcpy(out, seqParamSet->data() + 1, 3); // profile/level... + + uint8_t profile = out[0]; + uint8_t level = out[2]; + out += 3; *out++ = (0x3f << 2) | 1; // lengthSize == 2 bytes *out++ = 0xe0 | 1; @@ -271,7 +297,8 @@ sp<MetaData> MakeAVCCodecSpecificData(const sp<ABuffer> &accessUnit) { meta->setInt32(kKeyWidth, width); meta->setInt32(kKeyHeight, height); - LOGI("found AVC codec config (%d x %d)", width, height); + LOGI("found AVC codec config (%d x %d, %s-profile level %d.%d)", + width, height, AVCProfileToString(profile), level / 10, level % 10); return meta; } diff --git a/media/libstagefright/httplive/Android.mk b/media/libstagefright/httplive/Android.mk index cc7dd4f..3aabf5f 100644 --- a/media/libstagefright/httplive/Android.mk +++ b/media/libstagefright/httplive/Android.mk @@ -9,7 +9,8 @@ LOCAL_SRC_FILES:= \ LOCAL_C_INCLUDES:= \ $(JNI_H_INCLUDE) \ $(TOP)/frameworks/base/include/media/stagefright/openmax \ - $(TOP)/frameworks/base/media/libstagefright + $(TOP)/frameworks/base/media/libstagefright \ + $(TOP)/external/openssl/include LOCAL_MODULE:= libstagefright_httplive diff --git a/media/libstagefright/httplive/LiveSource.cpp b/media/libstagefright/httplive/LiveSource.cpp index 4124571..39e3e75 100644 --- a/media/libstagefright/httplive/LiveSource.cpp +++ b/media/libstagefright/httplive/LiveSource.cpp @@ -22,9 +22,14 @@ #include "include/M3UParser.h" #include "include/NuHTTPDataSource.h" +#include <cutils/properties.h> +#include <media/stagefright/foundation/hexdump.h> #include <media/stagefright/foundation/ABuffer.h> +#include <media/stagefright/foundation/ADebug.h> #include <media/stagefright/FileSource.h> -#include <media/stagefright/MediaDebug.h> + +#include <ctype.h> +#include <openssl/aes.h> namespace android { @@ -38,7 +43,9 @@ LiveSource::LiveSource(const char *url) mSourceSize(0), mOffsetBias(0), mSignalDiscontinuity(false), - mPrevBandwidthIndex(-1) { + mPrevBandwidthIndex(-1), + mAESKey((AES_KEY *)malloc(sizeof(AES_KEY))), + mStreamEncrypted(false) { if (switchToNext()) { mInitCheck = OK; @@ -47,6 +54,8 @@ LiveSource::LiveSource(const char *url) } LiveSource::~LiveSource() { + free(mAESKey); + mAESKey = NULL; } status_t LiveSource::initCheck() const { @@ -68,7 +77,77 @@ static double uniformRand() { return (double)rand() / RAND_MAX; } -bool LiveSource::loadPlaylist(bool fetchMaster) { +size_t LiveSource::getBandwidthIndex() { + if (mBandwidthItems.size() == 0) { + return 0; + } + +#if 1 + int32_t bandwidthBps; + if (mSource != NULL && mSource->estimateBandwidth(&bandwidthBps)) { + LOGI("bandwidth estimated at %.2f kbps", bandwidthBps / 1024.0f); + } else { + LOGI("no bandwidth estimate."); + return 0; // Pick the lowest bandwidth stream by default. + } + + char value[PROPERTY_VALUE_MAX]; + if (property_get("media.httplive.max-bw", value, NULL)) { + char *end; + long maxBw = strtoul(value, &end, 10); + if (end > value && *end == '\0') { + if (maxBw > 0 && bandwidthBps > maxBw) { + LOGV("bandwidth capped to %ld bps", maxBw); + bandwidthBps = maxBw; + } + } + } + + // Consider only 80% of the available bandwidth usable. + bandwidthBps = (bandwidthBps * 8) / 10; + + // Pick the highest bandwidth stream below or equal to estimated bandwidth. + + size_t index = mBandwidthItems.size() - 1; + while (index > 0 && mBandwidthItems.itemAt(index).mBandwidth + > (size_t)bandwidthBps) { + --index; + } +#elif 0 + // Change bandwidth at random() + size_t index = uniformRand() * mBandwidthItems.size(); +#elif 0 + // There's a 50% chance to stay on the current bandwidth and + // a 50% chance to switch to the next higher bandwidth (wrapping around + // to lowest) + const size_t kMinIndex = 0; + + size_t index; + if (mPrevBandwidthIndex < 0) { + index = kMinIndex; + } else if (uniformRand() < 0.5) { + index = (size_t)mPrevBandwidthIndex; + } else { + index = mPrevBandwidthIndex + 1; + if (index == mBandwidthItems.size()) { + index = kMinIndex; + } + } +#elif 0 + // Pick the highest bandwidth stream below or equal to 1.2 Mbit/sec + + size_t index = mBandwidthItems.size() - 1; + while (index > 0 && mBandwidthItems.itemAt(index).mBandwidth > 1200000) { + --index; + } +#else + size_t index = mBandwidthItems.size() - 1; // Highest bandwidth stream +#endif + + return index; +} + +bool LiveSource::loadPlaylist(bool fetchMaster, size_t bandwidthIndex) { mSignalDiscontinuity = false; mPlaylist.clear(); @@ -112,49 +191,35 @@ bool LiveSource::loadPlaylist(bool fetchMaster) { mBandwidthItems.sort(SortByBandwidth); +#if 1 // XXX + if (mBandwidthItems.size() > 1) { + // Remove the lowest bandwidth stream, this is sometimes + // an AAC program stream, which we don't support at this point. + mBandwidthItems.removeItemsAt(0); + } +#endif + for (size_t i = 0; i < mBandwidthItems.size(); ++i) { const BandwidthItem &item = mBandwidthItems.itemAt(i); LOGV("item #%d: %s", i, item.mURI.c_str()); } + + bandwidthIndex = getBandwidthIndex(); } } if (mBandwidthItems.size() > 0) { -#if 0 - // Change bandwidth at random() - size_t index = uniformRand() * mBandwidthItems.size(); -#elif 0 - // There's a 50% chance to stay on the current bandwidth and - // a 50% chance to switch to the next higher bandwidth (wrapping around - // to lowest) - size_t index; - if (uniformRand() < 0.5) { - index = mPrevBandwidthIndex < 0 ? 0 : (size_t)mPrevBandwidthIndex; - } else { - if (mPrevBandwidthIndex < 0) { - index = 0; - } else { - index = mPrevBandwidthIndex + 1; - if (index == mBandwidthItems.size()) { - index = 0; - } - } - } -#else - // Stay on the lowest bandwidth available. - size_t index = mBandwidthItems.size() - 1; // Highest bandwidth stream -#endif + mURL = mBandwidthItems.editItemAt(bandwidthIndex).mURI; - mURL = mBandwidthItems.editItemAt(index).mURI; - - if (mPrevBandwidthIndex >= 0 && (size_t)mPrevBandwidthIndex != index) { + if (mPrevBandwidthIndex >= 0 + && (size_t)mPrevBandwidthIndex != bandwidthIndex) { // If we switched streams because of bandwidth changes, // we'll signal this discontinuity by inserting a // special transport stream packet into the stream. mSignalDiscontinuity = true; } - mPrevBandwidthIndex = index; + mPrevBandwidthIndex = bandwidthIndex; } else { mURL = mMasterURL; } @@ -199,12 +264,15 @@ bool LiveSource::switchToNext() { mOffsetBias += mSourceSize; mSourceSize = 0; + size_t bandwidthIndex = getBandwidthIndex(); + if (mLastFetchTimeUs < 0 || getNowUs() >= mLastFetchTimeUs + 15000000ll - || mPlaylistIndex == mPlaylist->size()) { + || mPlaylistIndex == mPlaylist->size() + || (ssize_t)bandwidthIndex != mPrevBandwidthIndex) { int32_t nextSequenceNumber = mPlaylistIndex + mFirstItemSequenceNumber; - if (!loadPlaylist(mLastFetchTimeUs < 0)) { + if (!loadPlaylist(mLastFetchTimeUs < 0, bandwidthIndex)) { LOGE("failed to reload playlist"); return false; } @@ -227,6 +295,10 @@ bool LiveSource::switchToNext() { mLastFetchTimeUs = getNowUs(); } + if (!setupCipher()) { + return false; + } + AString uri; sp<AMessage> itemMeta; CHECK(mPlaylist->itemAt(mPlaylistIndex, &uri, &itemMeta)); @@ -243,6 +315,121 @@ bool LiveSource::switchToNext() { } mPlaylistIndex++; + + return true; +} + +bool LiveSource::setupCipher() { + sp<AMessage> itemMeta; + bool found = false; + AString method; + + for (ssize_t i = mPlaylistIndex; i >= 0; --i) { + AString uri; + CHECK(mPlaylist->itemAt(i, &uri, &itemMeta)); + + if (itemMeta->findString("cipher-method", &method)) { + found = true; + break; + } + } + + if (!found) { + method = "NONE"; + } + + mStreamEncrypted = false; + + if (method == "AES-128") { + AString keyURI; + if (!itemMeta->findString("cipher-uri", &keyURI)) { + LOGE("Missing key uri"); + return false; + } + + if (keyURI.size() >= 2 + && keyURI.c_str()[0] == '"' + && keyURI.c_str()[keyURI.size() - 1] == '"') { + // Remove surrounding quotes. + AString tmp(keyURI, 1, keyURI.size() - 2); + keyURI = tmp; + } + + ssize_t index = mAESKeyForURI.indexOfKey(keyURI); + + sp<ABuffer> key; + if (index >= 0) { + key = mAESKeyForURI.valueAt(index); + } else { + key = new ABuffer(16); + + sp<NuHTTPDataSource> keySource = new NuHTTPDataSource; + status_t err = keySource->connect(keyURI.c_str()); + + if (err == OK) { + size_t offset = 0; + while (offset < 16) { + ssize_t n = keySource->readAt( + offset, key->data() + offset, 16 - offset); + if (n <= 0) { + err = ERROR_IO; + break; + } + + offset += n; + } + } + + if (err != OK) { + LOGE("failed to fetch cipher key from '%s'.", keyURI.c_str()); + return false; + } + + mAESKeyForURI.add(keyURI, key); + } + + if (AES_set_decrypt_key(key->data(), 128, (AES_KEY *)mAESKey) != 0) { + LOGE("failed to set AES decryption key."); + return false; + } + + AString iv; + if (itemMeta->findString("cipher-iv", &iv)) { + if ((!iv.startsWith("0x") && !iv.startsWith("0X")) + || iv.size() != 16 * 2 + 2) { + LOGE("malformed cipher IV '%s'.", iv.c_str()); + return false; + } + + memset(mAESIVec, 0, sizeof(mAESIVec)); + for (size_t i = 0; i < 16; ++i) { + char c1 = tolower(iv.c_str()[2 + 2 * i]); + char c2 = tolower(iv.c_str()[3 + 2 * i]); + if (!isxdigit(c1) || !isxdigit(c2)) { + LOGE("malformed cipher IV '%s'.", iv.c_str()); + return false; + } + uint8_t nibble1 = isdigit(c1) ? c1 - '0' : c1 - 'a' + 10; + uint8_t nibble2 = isdigit(c2) ? c2 - '0' : c2 - 'a' + 10; + + mAESIVec[i] = nibble1 << 4 | nibble2; + } + } else { + size_t seqNum = mPlaylistIndex + mFirstItemSequenceNumber; + + memset(mAESIVec, 0, sizeof(mAESIVec)); + mAESIVec[15] = seqNum & 0xff; + mAESIVec[14] = (seqNum >> 8) & 0xff; + mAESIVec[13] = (seqNum >> 16) & 0xff; + mAESIVec[12] = (seqNum >> 24) & 0xff; + } + + mStreamEncrypted = true; + } else if (!(method == "NONE")) { + LOGE("Unsupported cipher method '%s'", method.c_str()); + return false; + } + return true; } @@ -279,6 +466,7 @@ ssize_t LiveSource::readAt(off_t offset, void *data, size_t size) { return avail; } + bool done = false; size_t numRead = 0; while (numRead < size) { ssize_t n = mSource->readAt( @@ -289,7 +477,44 @@ ssize_t LiveSource::readAt(off_t offset, void *data, size_t size) { break; } + if (mStreamEncrypted) { + size_t nmod = n % 16; + CHECK(nmod == 0); + + sp<ABuffer> tmp = new ABuffer(n); + + AES_cbc_encrypt((const unsigned char *)data + numRead, + tmp->data(), + n, + (const AES_KEY *)mAESKey, + mAESIVec, + AES_DECRYPT); + + if (mSourceSize == (off_t)(offset + numRead - delta + n)) { + // check for padding at the end of the file. + + size_t pad = tmp->data()[n - 1]; + CHECK_GT(pad, 0u); + CHECK_LE(pad, 16u); + CHECK_GE((size_t)n, pad); + for (size_t i = 0; i < pad; ++i) { + CHECK_EQ((unsigned)tmp->data()[n - 1 - i], pad); + } + + n -= pad; + mSourceSize -= pad; + + done = true; + } + + memcpy((uint8_t *)data + numRead, tmp->data(), n); + } + numRead += n; + + if (done) { + break; + } } return numRead; @@ -359,19 +584,17 @@ bool LiveSource::seekTo(int64_t seekTimeUs) { return false; } - size_t newPlaylistIndex = mFirstItemSequenceNumber + index; - - if (newPlaylistIndex == mPlaylistIndex) { + if (index == mPlaylistIndex) { return false; } - mPlaylistIndex = newPlaylistIndex; + mPlaylistIndex = index; + + LOGV("seeking to index %lld", index); switchToNext(); mOffsetBias = 0; - LOGV("seeking to index %lld", index); - return true; } diff --git a/media/libstagefright/httplive/M3UParser.cpp b/media/libstagefright/httplive/M3UParser.cpp index 90f3d6d..b166cc3 100644 --- a/media/libstagefright/httplive/M3UParser.cpp +++ b/media/libstagefright/httplive/M3UParser.cpp @@ -158,6 +158,11 @@ status_t M3UParser::parse(const void *_data, size_t size) { return ERROR_MALFORMED; } err = parseMetaData(line, &mMeta, "media-sequence"); + } else if (line.startsWith("#EXT-X-KEY")) { + if (mIsVariantPlaylist) { + return ERROR_MALFORMED; + } + err = parseCipherInfo(line, &itemMeta); } else if (line.startsWith("#EXT-X-ENDLIST")) { mIsComplete = true; } else if (line.startsWith("#EXTINF")) { @@ -292,6 +297,57 @@ status_t M3UParser::parseStreamInf( } // static +status_t M3UParser::parseCipherInfo( + const AString &line, sp<AMessage> *meta) { + ssize_t colonPos = line.find(":"); + + if (colonPos < 0) { + return ERROR_MALFORMED; + } + + size_t offset = colonPos + 1; + + while (offset < line.size()) { + ssize_t end = line.find(",", offset); + if (end < 0) { + end = line.size(); + } + + AString attr(line, offset, end - offset); + attr.trim(); + + offset = end + 1; + + ssize_t equalPos = attr.find("="); + if (equalPos < 0) { + continue; + } + + AString key(attr, 0, equalPos); + key.trim(); + + AString val(attr, equalPos + 1, attr.size() - equalPos - 1); + val.trim(); + + LOGV("key=%s value=%s", key.c_str(), val.c_str()); + + key.tolower(); + + if (key == "method" || key == "uri" || key == "iv") { + if (meta->get() == NULL) { + *meta = new AMessage; + } + + key.insert(AString("cipher-"), 0); + + (*meta)->setString(key.c_str(), val.c_str(), val.size()); + } + } + + return OK; +} + +// static status_t M3UParser::ParseInt32(const char *s, int32_t *x) { char *end; long lval = strtol(s, &end, 10); diff --git a/media/libstagefright/include/LiveSource.h b/media/libstagefright/include/LiveSource.h index 55dd45e..7ba1f44 100644 --- a/media/libstagefright/include/LiveSource.h +++ b/media/libstagefright/include/LiveSource.h @@ -21,6 +21,7 @@ #include <media/stagefright/foundation/ABase.h> #include <media/stagefright/foundation/AString.h> #include <media/stagefright/DataSource.h> +#include <utils/KeyedVector.h> #include <utils/Vector.h> namespace android { @@ -72,14 +73,23 @@ private: bool mSignalDiscontinuity; ssize_t mPrevBandwidthIndex; + void *mAESKey; + unsigned char mAESIVec[16]; + bool mStreamEncrypted; + + KeyedVector<AString, sp<ABuffer> > mAESKeyForURI; + status_t fetchM3U(const char *url, sp<ABuffer> *buffer); static int SortByBandwidth(const BandwidthItem *a, const BandwidthItem *b); bool switchToNext(); - bool loadPlaylist(bool fetchMaster); + bool loadPlaylist(bool fetchMaster, size_t bandwidthIndex); void determineSeekability(); + size_t getBandwidthIndex(); + bool setupCipher(); + DISALLOW_EVIL_CONSTRUCTORS(LiveSource); }; diff --git a/media/libstagefright/include/M3UParser.h b/media/libstagefright/include/M3UParser.h index bd9eebe..531d184 100644 --- a/media/libstagefright/include/M3UParser.h +++ b/media/libstagefright/include/M3UParser.h @@ -66,6 +66,9 @@ private: static status_t parseStreamInf( const AString &line, sp<AMessage> *meta); + static status_t parseCipherInfo( + const AString &line, sp<AMessage> *meta); + static status_t ParseInt32(const char *s, int32_t *x); DISALLOW_EVIL_CONSTRUCTORS(M3UParser); diff --git a/media/libstagefright/include/NuHTTPDataSource.h b/media/libstagefright/include/NuHTTPDataSource.h index 93b7a76..c707fdc 100644 --- a/media/libstagefright/include/NuHTTPDataSource.h +++ b/media/libstagefright/include/NuHTTPDataSource.h @@ -3,6 +3,7 @@ #define NU_HTTP_DATA_SOURCE_H_ #include <media/stagefright/DataSource.h> +#include <utils/List.h> #include <utils/String8.h> #include <utils/threads.h> @@ -26,6 +27,10 @@ struct NuHTTPDataSource : public DataSource { virtual status_t getSize(off_t *size); virtual uint32_t flags(); + // Returns true if bandwidth could successfully be estimated, + // false otherwise. + bool estimateBandwidth(int32_t *bandwidth_bps); + virtual DecryptHandle* DrmInitialization(DrmManagerClient *client); virtual void getDrmInfo(DecryptHandle **handle, DrmManagerClient **client); @@ -39,6 +44,11 @@ private: CONNECTED }; + struct BandwidthEntry { + int64_t mDelayUs; + size_t mNumBytes; + }; + Mutex mLock; State mState; @@ -54,6 +64,11 @@ private: off_t mContentLength; bool mContentLengthValid; + List<BandwidthEntry> mBandwidthHistory; + size_t mNumBandwidthHistoryItems; + int64_t mTotalTransferTimeUs; + size_t mTotalTransferBytes; + DecryptHandle *mDecryptHandle; DrmManagerClient *mDrmManagerClient; @@ -66,6 +81,7 @@ private: off_t offset); void applyTimeoutResponse(); + void addBandwidthMeasurement_l(size_t numBytes, int64_t delayUs); static void MakeFullHeaders( const KeyedVector<String8, String8> *overrides, diff --git a/media/libstagefright/include/avc_utils.h b/media/libstagefright/include/avc_utils.h index 62cfc36..3aeb07f 100644 --- a/media/libstagefright/include/avc_utils.h +++ b/media/libstagefright/include/avc_utils.h @@ -24,6 +24,17 @@ namespace android { struct ABitReader; +enum { + kAVCProfileBaseline = 0x42, + kAVCProfileMain = 0x4d, + kAVCProfileExtended = 0x58, + kAVCProfileHigh = 0x64, + kAVCProfileHigh10 = 0x6e, + kAVCProfileHigh422 = 0x7a, + kAVCProfileHigh444 = 0xf4, + kAVCProfileCAVLC444Intra = 0x2c +}; + void FindAVCDimensions( const sp<ABuffer> &seqParamSet, int32_t *width, int32_t *height); @@ -39,6 +50,8 @@ sp<MetaData> MakeAVCCodecSpecificData(const sp<ABuffer> &accessUnit); bool IsIDR(const sp<ABuffer> &accessUnit); +const char *AVCProfileToString(uint8_t profile); + } // namespace android #endif // AVC_UTILS_H_ diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp index c88c6c1..f06a1bb 100644 --- a/media/libstagefright/mpeg2ts/ATSParser.cpp +++ b/media/libstagefright/mpeg2ts/ATSParser.cpp @@ -274,6 +274,8 @@ ATSParser::Stream::Stream( mQueue(streamType == 0x1b ? ElementaryStreamQueue::H264 : ElementaryStreamQueue::AAC) { mBuffer->setRange(0, 0); + + LOGV("new stream PID 0x%02x, type 0x%02x", elementaryPID, streamType); } ATSParser::Stream::~Stream() { @@ -307,7 +309,8 @@ void ATSParser::Stream::parse( } void ATSParser::Stream::signalDiscontinuity(bool isASeek) { - LOGV("Stream discontinuity"); + isASeek = false; // Always signal a "real" discontinuity + mPayloadStarted = false; mBuffer->setRange(0, 0); @@ -317,7 +320,9 @@ void ATSParser::Stream::signalDiscontinuity(bool isASeek) { // This is only a "minor" discontinuity, we stay within the same // bitstream. - mSource->clear(); + if (mSource != NULL) { + mSource->clear(); + } return; } diff --git a/media/libstagefright/mpeg2ts/ESQueue.cpp b/media/libstagefright/mpeg2ts/ESQueue.cpp index b0b9e66..f11b3c3 100644 --- a/media/libstagefright/mpeg2ts/ESQueue.cpp +++ b/media/libstagefright/mpeg2ts/ESQueue.cpp @@ -41,7 +41,10 @@ sp<MetaData> ElementaryStreamQueue::getFormat() { } void ElementaryStreamQueue::clear() { - mBuffer->setRange(0, 0); + if (mBuffer != NULL) { + mBuffer->setRange(0, 0); + } + mTimestamps.clear(); mFormat.clear(); } diff --git a/media/mtp/MtpDatabase.h b/media/mtp/MtpDatabase.h index c8cb016..fafd221 100644 --- a/media/mtp/MtpDatabase.h +++ b/media/mtp/MtpDatabase.h @@ -75,6 +75,12 @@ public: virtual MtpResponseCode resetDeviceProperty(MtpDeviceProperty property) = 0; + virtual MtpResponseCode getObjectPropertyList(MtpObjectHandle handle, + MtpObjectFormat format, + MtpObjectProperty property, + int groupCode, int depth, + MtpDataPacket& packet) = 0; + virtual MtpResponseCode getObjectInfo(MtpObjectHandle handle, MtpDataPacket& packet) = 0; diff --git a/media/mtp/MtpDebug.cpp b/media/mtp/MtpDebug.cpp index 3416807..1668ecf 100644 --- a/media/mtp/MtpDebug.cpp +++ b/media/mtp/MtpDebug.cpp @@ -56,6 +56,10 @@ static const CodeEntry sOperationCodes[] = { { "MTP_OPERATION_GET_OBJECT_PROP_DESC", 0x9802 }, { "MTP_OPERATION_GET_OBJECT_PROP_VALUE", 0x9803 }, { "MTP_OPERATION_SET_OBJECT_PROP_VALUE", 0x9804 }, + { "MTP_OPERATION_GET_OBJECT_PROP_LIST", 0x9805 }, + { "MTP_OPERATION_SET_OBJECT_PROP_LIST", 0x9806 }, + { "MTP_OPERATION_GET_INTERDEPENDENT_PROP_DESC", 0x9807 }, + { "MTP_OPERATION_SEND_OBJECT_PROP_LIST", 0x9808 }, { "MTP_OPERATION_GET_OBJECT_REFERENCES", 0x9810 }, { "MTP_OPERATION_SET_OBJECT_REFERENCES", 0x9811 }, { "MTP_OPERATION_SKIP", 0x9820 }, @@ -371,15 +375,21 @@ const char* MtpDebug::getOperationCodeName(MtpOperationCode code) { return getCodeName(code, sOperationCodes); } -const char* MtpDebug::getFormatCodeName(MtpOperationCode code) { +const char* MtpDebug::getFormatCodeName(MtpObjectFormat code) { + if (code == 0) + return "NONE"; return getCodeName(code, sFormatCodes); } const char* MtpDebug::getObjectPropCodeName(MtpPropertyCode code) { + if (code == 0) + return "NONE"; return getCodeName(code, sObjectPropCodes); } const char* MtpDebug::getDevicePropCodeName(MtpPropertyCode code) { + if (code == 0) + return "NONE"; return getCodeName(code, sDevicePropCodes); } diff --git a/media/mtp/MtpProperty.cpp b/media/mtp/MtpProperty.cpp index f7c12d6..86889c3 100644 --- a/media/mtp/MtpProperty.cpp +++ b/media/mtp/MtpProperty.cpp @@ -53,7 +53,7 @@ MtpProperty::MtpProperty(MtpPropertyCode propCode, mDefaultArrayValues(NULL), mCurrentArrayLength(0), mCurrentArrayValues(NULL), - mGroupCode(0), + mGroupCode(-1), // disable multiple properties in GetObjectPropList for now mFormFlag(kFormNone), mEnumLength(0), mEnumValues(NULL) diff --git a/media/mtp/MtpServer.cpp b/media/mtp/MtpServer.cpp index 5ba6be9..6cf70ec 100644 --- a/media/mtp/MtpServer.cpp +++ b/media/mtp/MtpServer.cpp @@ -72,6 +72,10 @@ static const MtpOperationCode kSupportedOperationCodes[] = { MTP_OPERATION_GET_OBJECT_PROP_DESC, MTP_OPERATION_GET_OBJECT_PROP_VALUE, MTP_OPERATION_SET_OBJECT_PROP_VALUE, + MTP_OPERATION_GET_OBJECT_PROP_LIST, +// MTP_OPERATION_SET_OBJECT_PROP_LIST, +// MTP_OPERATION_GET_INTERDEPENDENT_PROP_DESC, +// MTP_OPERATION_SEND_OBJECT_PROP_LIST, MTP_OPERATION_GET_OBJECT_REFERENCES, MTP_OPERATION_SET_OBJECT_REFERENCES, // MTP_OPERATION_SKIP, @@ -276,6 +280,9 @@ bool MtpServer::handleRequest() { case MTP_OPERATION_RESET_DEVICE_PROP_VALUE: response = doResetDevicePropValue(); break; + case MTP_OPERATION_GET_OBJECT_PROP_LIST: + response = doGetObjectPropList(); + break; case MTP_OPERATION_GET_OBJECT_INFO: response = doGetObjectInfo(); break; @@ -523,6 +530,20 @@ MtpResponseCode MtpServer::doResetDevicePropValue() { return mDatabase->resetDeviceProperty(property); } +MtpResponseCode MtpServer::doGetObjectPropList() { + + MtpObjectHandle handle = mRequest.getParameter(1); + MtpObjectFormat format = mRequest.getParameter(2); + MtpDeviceProperty property = mRequest.getParameter(3); + int groupCode = mRequest.getParameter(4); + int depth = mRequest.getParameter(4); + LOGD("GetObjectPropList %d format: %s property: %s group: %d depth: %d\n", + handle, MtpDebug::getFormatCodeName(format), + MtpDebug::getObjectPropCodeName(property), groupCode, depth); + + return mDatabase->getObjectPropertyList(handle, format, property, groupCode, depth, mData); +} + MtpResponseCode MtpServer::doGetObjectInfo() { MtpObjectHandle handle = mRequest.getParameter(1); return mDatabase->getObjectInfo(handle, mData); diff --git a/media/mtp/MtpServer.h b/media/mtp/MtpServer.h index 68a6564..e65ddb0 100644 --- a/media/mtp/MtpServer.h +++ b/media/mtp/MtpServer.h @@ -93,6 +93,7 @@ private: MtpResponseCode doGetDevicePropValue(); MtpResponseCode doSetDevicePropValue(); MtpResponseCode doResetDevicePropValue(); + MtpResponseCode doGetObjectPropList(); MtpResponseCode doGetObjectInfo(); MtpResponseCode doGetObject(); MtpResponseCode doSendObjectInfo(); diff --git a/media/mtp/mtp.h b/media/mtp/mtp.h index b7afa66..8bc2e22 100644 --- a/media/mtp/mtp.h +++ b/media/mtp/mtp.h @@ -37,7 +37,7 @@ #define MTP_CONTAINER_PARAMETER_OFFSET 12 #define MTP_CONTAINER_HEADER_SIZE 12 -// MTP Types +// MTP Data Types #define MTP_TYPE_UNDEFINED 0x0000 // Undefined #define MTP_TYPE_INT8 0x0001 // Signed 8-bit integer #define MTP_TYPE_UINT8 0x0002 // Unsigned 8-bit integer @@ -383,6 +383,10 @@ #define MTP_OPERATION_GET_OBJECT_PROP_DESC 0x9802 #define MTP_OPERATION_GET_OBJECT_PROP_VALUE 0x9803 #define MTP_OPERATION_SET_OBJECT_PROP_VALUE 0x9804 +#define MTP_OPERATION_GET_OBJECT_PROP_LIST 0x9805 +#define MTP_OPERATION_SET_OBJECT_PROP_LIST 0x9806 +#define MTP_OPERATION_GET_INTERDEPENDENT_PROP_DESC 0x9807 +#define MTP_OPERATION_SEND_OBJECT_PROP_LIST 0x9808 #define MTP_OPERATION_GET_OBJECT_REFERENCES 0x9810 #define MTP_OPERATION_SET_OBJECT_REFERENCES 0x9811 #define MTP_OPERATION_SKIP 0x9820 |