diff --git a/proprietary/drmwvmplugin/lib/libwvdrm.so b/proprietary/drmwvmplugin/lib/libwvdrm.so index 1cbc4be8..b0d9938f 100644 Binary files a/proprietary/drmwvmplugin/lib/libwvdrm.so and b/proprietary/drmwvmplugin/lib/libwvdrm.so differ diff --git a/proprietary/drmwvmplugin/test/TestPlugin.cpp b/proprietary/drmwvmplugin/test/TestPlugin.cpp index 4cec87a0..3135eb4e 100644 --- a/proprietary/drmwvmplugin/test/TestPlugin.cpp +++ b/proprietary/drmwvmplugin/test/TestPlugin.cpp @@ -84,11 +84,17 @@ void WVMDrmPluginTest::Run() exit(-1); } - // Remote asset + String8 url; + + // Remote asset url = String8("http://seawwws001.cdn.shibboleth.tv/videos/qa/adventures_d_ch_444169.wvm"); TestAsset(plugin, url); + // Remote asset using widevine:// protocol + url = String8("widevine://seawwws001.cdn.shibboleth.tv/videos/qa/adventures_d_ch_444169.wvm"); + TestAsset(plugin, url); + // Local asset url = String8("file:///sdcard/Widevine/trailers_d_ch_444169.wvm"); TestAsset(plugin, url); diff --git a/proprietary/samplePlayer/src/com/widevine/demo/ConfigXMLParser.java b/proprietary/samplePlayer/src/com/widevine/demo/ConfigXMLParser.java index eeb1aeac..f60faf20 100644 --- a/proprietary/samplePlayer/src/com/widevine/demo/ConfigXMLParser.java +++ b/proprietary/samplePlayer/src/com/widevine/demo/ConfigXMLParser.java @@ -52,10 +52,9 @@ public class ConfigXMLParser { Element asset = assetlist.getChild("asset"); asset.setEndElementListener(new EndElementListener() { public void end() { - if (currentAssetDescriptor.getUri().indexOf("http") != -1 - && (currentAssetDescriptor.getUri().indexOf(".wvm") != -1 - || !currentAssetDescriptor.getUri().substring(currentAssetDescriptor - .getUri().lastIndexOf("/")).contains("."))) { + if (currentAssetDescriptor.getUri().indexOf(".wvm") != -1 + || !currentAssetDescriptor.getUri().substring(currentAssetDescriptor + .getUri().lastIndexOf("/")).contains(".")) { assetDescriptors.add(currentAssetDescriptor.copy()); } } diff --git a/proprietary/samplePlayer/src/com/widevine/demo/HttpParser.java b/proprietary/samplePlayer/src/com/widevine/demo/HttpParser.java index de5218ef..51c02339 100644 --- a/proprietary/samplePlayer/src/com/widevine/demo/HttpParser.java +++ b/proprietary/samplePlayer/src/com/widevine/demo/HttpParser.java @@ -83,12 +83,13 @@ public class HttpParser extends Thread { String assetPath = null; String title = null; String imagePath = null; - start = htmlText.indexOf(":", start); + start = htmlText.indexOf("href=\"", start); if (start == -1) { break; } else { + start += "href=\"".length(); end = htmlText.indexOf("\"", start); - assetPath = "http" + htmlText.substring(start, end); + assetPath = htmlText.substring(start, end); start = end + 1; start = htmlText.indexOf("\"", start) + 1; end = htmlText.indexOf("\"", start); diff --git a/proprietary/streamcontrol/include/WVStreamControlAPI.h b/proprietary/streamcontrol/include/WVStreamControlAPI.h index d26ee45d..8bec70b2 100644 --- a/proprietary/streamcontrol/include/WVStreamControlAPI.h +++ b/proprietary/streamcontrol/include/WVStreamControlAPI.h @@ -1020,6 +1020,27 @@ WV_Info_GetAudioBitrate(WVSession *session, unsigned long *bitRate); CLIENT_API WVStatus WV_Info_CurrentBandwidth(WVSession *session, unsigned long *bandwidth); +// +// METHOD: WV_Info_BytesBuffered +// +// This method retrieves information about the adaptive streaming buffer level +// for the media stream that was setup on the specified session. +// +// Parameters: +// [in] session - The session to query +// +// [out] bytesBuffered - The number of bytes currently queued in the adaptive +// streaming buffer +// +// Returns: +// WV_Status_OK on success, otherwise one of the WVStatus values +// indicating the specific error. +// + +CLIENT_API WVStatus +WV_Info_BytesBuffered(WVSession *session, unsigned long long *bytesBuffered); + + // // METHOD: WV_Info_GetAdaptiveBitrates // diff --git a/proprietary/streamcontrol/lib/libWVStreamControlAPI.so b/proprietary/streamcontrol/lib/libWVStreamControlAPI.so index 21b977a2..d5d0cc99 100644 Binary files a/proprietary/streamcontrol/lib/libWVStreamControlAPI.so and b/proprietary/streamcontrol/lib/libWVStreamControlAPI.so differ diff --git a/proprietary/wvm/Android.mk b/proprietary/wvm/Android.mk index 006fe59a..459ccad6 100644 --- a/proprietary/wvm/Android.mk +++ b/proprietary/wvm/Android.mk @@ -8,13 +8,14 @@ LOCAL_SRC_FILES:= \ WVMLogging.cpp \ WVMExtractorImpl.cpp \ WVMFileSource.cpp \ - WVMMediaSource.cpp \ + WVMMediaSource.cpp \ WVMInfoListener.cpp LOCAL_C_INCLUDES:= \ bionic \ bionic/libstdc++ \ external/stlport/stlport \ + frameworks/base/media/libstagefright/include \ vendor/widevine/proprietary/streamcontrol/include \ vendor/widevine/proprietary/wvm/include diff --git a/proprietary/wvm/WVMExtractorImpl.cpp b/proprietary/wvm/WVMExtractorImpl.cpp index f7456d00..9db38299 100644 --- a/proprietary/wvm/WVMExtractorImpl.cpp +++ b/proprietary/wvm/WVMExtractorImpl.cpp @@ -76,7 +76,7 @@ static int _cb2(char *in, char *out, int length, char *iv) namespace android { // DLL entry - construct an extractor and return it -MediaExtractor *GetInstance(sp dataSource) { +WVMLoadableExtractor *GetInstance(sp dataSource) { return new WVMExtractorImpl(dataSource); } @@ -84,6 +84,8 @@ WVMExtractorImpl::WVMExtractorImpl(sp dataSource) : mFileMetaData(new MetaData()), mDataSource(dataSource), mHaveMetaData(false), + mUseAdaptiveStreaming(false), + mIsLiveStream(false), mSession(NULL), mSetupStatus(OK) { @@ -102,12 +104,16 @@ WVMExtractorImpl::WVMExtractorImpl(sp dataSource) } else mSetupStatus = ERROR_DRM_NO_LICENSE; - WVCredentials credentials; - // Set an info listener to handle messages from the drm plugin mInfoListener = new WVMInfoListener(); sDrmManagerClient->setOnInfoListener(mInfoListener); +} + +void WVMExtractorImpl::Initialize() +{ + //LOGD("WVMExtractorImpl::Initialize(%d)\n", getAdaptiveStreamingMode()); + WVCredentials credentials; WVStatus result = WV_Initialize(NULL); if (result != WV_Status_OK) { @@ -117,10 +123,20 @@ WVMExtractorImpl::WVMExtractorImpl(sp dataSource) // Enable for debugging HTTP messages // WV_SetLogging(WV_Logging_HTTP); - mFileSource = new WVMFileSource(dataSource); - result = WV_Setup(mSession, mFileSource.get(), - "RAW/RAW/RAW;destination=getdata", credentials, - WV_OutputFormat_ES, kStreamCacheSize); + if (mDataSource->getUri().size() > 0 && getAdaptiveStreamingMode()) { + // Use the URI - streaming case, only for widevine:// protocol + result = WV_Setup(mSession, mDataSource->getUri().string(), + "RAW/RAW/RAW;destination=getdata", credentials, + WV_OutputFormat_ES, kStreamCacheSize); + + mIsLiveStream = (mDataSource->getUri().getPathExtension().find(".m3u8") == 0); + } else { + // No URI supplied or not adaptive, pull data from the stagefright data source. + mFileSource = new WVMFileSource(mDataSource); + result = WV_Setup(mSession, mFileSource.get(), + "RAW/RAW/RAW;destination=getdata", credentials, + WV_OutputFormat_ES, kStreamCacheSize); + } if (result != WV_Status_OK) { LOGE("WV_Setup returned status %d in WVMMediaSource::start\n", result); @@ -144,6 +160,8 @@ status_t WVMExtractorImpl::readMetaData() if (mHaveMetaData) return OK; + Initialize(); + if (mSetupStatus != OK) return mSetupStatus; @@ -154,11 +172,11 @@ status_t WVMExtractorImpl::readMetaData() unsigned short level; unsigned short width, height; float aspect, frameRate; - unsigned long bitRate; + unsigned long videoBitRate; WVStatus result = WV_Info_GetVideoConfiguration(mSession, &videoType, &videoStreamID, &videoProfile, &level, &width, &height, - &aspect, &frameRate, &bitRate); + &aspect, &frameRate, &videoBitRate); if (result != WV_Status_OK) return ERROR_MALFORMED; @@ -168,15 +186,21 @@ status_t WVMExtractorImpl::readMetaData() unsigned short audioProfile; unsigned short numChannels; unsigned long sampleRate; + unsigned long audioBitRate; result = WV_Info_GetAudioConfiguration(mSession, &audioType, &audioStreamID, &audioProfile, - &numChannels, &sampleRate, &bitRate); + &numChannels, &sampleRate, &audioBitRate); if (result != WV_Status_OK) return ERROR_MALFORMED; std::string durationString = WV_Info_GetDuration(mSession, "sec"); if (durationString == "") { - return ERROR_MALFORMED; + // We won't have a duration for live streams, and Stagefright doesn't seem to + // have a way to represent that. Give a default duration of 1 hour for now. + if (mIsLiveStream) + durationString = "3600"; + else + return ERROR_MALFORMED; } int64_t duration = (int64_t)(strtod(durationString.c_str(), NULL) * 1000000); @@ -187,6 +211,9 @@ status_t WVMExtractorImpl::readMetaData() audioMetaData->setInt64(kKeyDuration, duration); videoMetaData->setInt64(kKeyDuration, duration); + audioMetaData->setInt32(kKeyBitRate, audioBitRate); + videoMetaData->setInt32(kKeyBitRate, videoBitRate); + switch(videoType) { case WV_VideoType_H264: videoMetaData->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC); @@ -398,5 +425,61 @@ sp WVMExtractorImpl::getMetaData() { return mFileMetaData; } +int64_t WVMExtractorImpl::getCachedDurationUs(status_t *finalStatus) { + const size_t kMaxEncodedRates = 10; + unsigned long encodedRates[kMaxEncodedRates]; + size_t ratesReturned, currentTrack; + uint64_t durationUs = 0; + + WVStatus status = WV_Info_GetAdaptiveBitrates(mSession, encodedRates, kMaxEncodedRates, + &ratesReturned, ¤tTrack); + if (status == WV_Status_OK) { + if (currentTrack == kMaxEncodedRates || ratesReturned == 0) { + *finalStatus = ERROR_IO; + } else { + unsigned long long bytesBuffered; + status = WV_Info_BytesBuffered(mSession, &bytesBuffered); + if (status == WV_Status_OK) { + *finalStatus = OK; + durationUs = 8000000LL * bytesBuffered / encodedRates[currentTrack]; + } else if (status == WV_Status_End_Of_Media) { + *finalStatus = ERROR_END_OF_STREAM; + } + + // Log the current adaptive rate every 5 seconds + // FIXME: need to see if there is a way to send this info to the app + time_t now = time(0); + static time_t lastLogTime = now; + if (now > lastLogTime + 5) { + lastLogTime = now; + LOGI("using adaptive track #%d, rate=%ld\n", + currentTrack, encodedRates[currentTrack]); + } + } + } else { + *finalStatus = ERROR_IO; + } + + // Scale the duration to account for Stagefright's conservative buffering. + // This provides much more responsive operation and due to the ability to + // adapt, we don't need to prebuffer so much data. Based on testing, 2x + // is a reasonable compromise between faster startup time and additional + // pauses after playback starts. + + return durationUs * 2; +} + +void WVMExtractorImpl::setAdaptiveStreamingMode(bool adaptive) +{ + //LOGD("WVMExtractorImpl::setAdaptiveStreamingMode(%d)", adaptive); + mUseAdaptiveStreaming = adaptive; +} + +bool WVMExtractorImpl::getAdaptiveStreamingMode() const +{ + //LOGD("WVMExtractorImpl::getAdaptiveStreamingMode - %d", mUseAdaptiveStreaming); + return mUseAdaptiveStreaming; +} + } // namespace android diff --git a/proprietary/wvm/include/WVMExtractorImpl.h b/proprietary/wvm/include/WVMExtractorImpl.h index f97ee692..948d3ca9 100644 --- a/proprietary/wvm/include/WVMExtractorImpl.h +++ b/proprietary/wvm/include/WVMExtractorImpl.h @@ -21,7 +21,7 @@ #include "AndroidConfig.h" #include "WVStreamControlAPI.h" #include "WVMInfoListener.h" -#include +#include "WVMExtractor.h" #include #include @@ -30,13 +30,13 @@ namespace android { -MediaExtractor *GetInstance(sp dataSource); +WVMLoadableExtractor *GetInstance(sp dataSource); class WVMMediaSource; class WVMFileSource; -class WVMExtractorImpl : public MediaExtractor { +class WVMExtractorImpl : public WVMLoadableExtractor { public: WVMExtractorImpl(sp dataSource); @@ -45,9 +45,13 @@ public: virtual sp getTrackMetaData(size_t index, uint32_t flags); virtual sp getMetaData(); + virtual int64_t getCachedDurationUs(status_t *finalStatus); + virtual void setAdaptiveStreamingMode(bool adaptive); + bool getAdaptiveStreamingMode() const; protected: virtual ~WVMExtractorImpl(); + void Initialize(); private: status_t readAVCCMetaData(sp videoMetaData); @@ -61,6 +65,9 @@ private: sp mInfoListener; bool mHaveMetaData; + bool mUseAdaptiveStreaming; + bool mIsLiveStream; + bool mAdaptivePrefetching; WVSession *mSession;