Merge "Switch to use kKeyFrameRate for recordvideo utility"
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_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 @@
{".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 @@
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 @@
}
}
- 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 @@
{
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 @@
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 @@
{
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
mOffset(0),
mContentLength(0),
mContentLengthValid(false),
+ mNumBandwidthHistoryItems(0),
+ mTotalTransferTimeUs(0),
+ mTotalTransferBytes(0),
mDecryptHandle(NULL),
mDrmManagerClient(NULL) {
}
@@ -189,6 +193,20 @@
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 @@
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 @@
return n;
}
+ int64_t delayUs = ALooper::GetNowUs() - startTimeUs;
+ addBandwidthMeasurement_l(n, delayUs);
+
numBytesRead += (size_t)n;
if (n == 0) {
@@ -345,6 +368,36 @@
}
}
+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 @@
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 @@
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 @@
*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 @@
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_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 @@
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() {
+ free(mAESKey);
+ mAESKey = NULL;
}
status_t LiveSource::initCheck() const {
@@ -68,7 +77,77 @@
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 @@
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 @@
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 @@
mLastFetchTimeUs = getNowUs();
}
+ if (!setupCipher()) {
+ return false;
+ }
+
AString uri;
sp<AMessage> itemMeta;
CHECK(mPlaylist->itemAt(mPlaylistIndex, &uri, &itemMeta));
@@ -243,6 +315,121 @@
}
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 @@
return avail;
}
+ bool done = false;
size_t numRead = 0;
while (numRead < size) {
ssize_t n = mSource->readAt(
@@ -289,7 +477,44 @@
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 @@
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 @@
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 @@
}
// 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 @@
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 @@
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 @@
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 @@
CONNECTED
};
+ struct BandwidthEntry {
+ int64_t mDelayUs;
+ size_t mNumBytes;
+ };
+
Mutex mLock;
State mState;
@@ -54,6 +64,11 @@
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 @@
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 @@
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 @@
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 @@
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::signalDiscontinuity(bool isASeek) {
- LOGV("Stream discontinuity");
+ isASeek = false; // Always signal a "real" discontinuity
+
mPayloadStarted = false;
mBuffer->setRange(0, 0);
@@ -317,7 +320,9 @@
// 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 @@
}
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 @@
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 @@
{ "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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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