Files
android/proprietary/wvm/WVMMediaSource.cpp
Jeffrey Tinker f5fa8f2017 Changes for calling the level 1 decrypt callout
Change-Id: Iccf76b59a64491952ee11ee2ed1a0e707a529f88
2011-10-07 19:25:20 -07:00

468 lines
13 KiB
C++

/*
* Copyright (C) 2011 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.
*/
#define LOG_TAG "WVMMediaSource"
#include <utils/Log.h>
#include "WVMMediaSource.h"
#include "WVMFileSource.h"
#include "WVMExtractorImpl.h"
#include "WVMErrorCodes.h"
#include "media/stagefright/MediaErrors.h"
#include "media/stagefright/MediaDefs.h"
#include "media/stagefright/MediaDebug.h"
#include "AndroidHooks.h"
namespace android {
static void _cb(int code)
{
WVMMediaSource::sLastError = (status_t)code;
}
status_t WVMMediaSource::sLastError = NO_ERROR;
WVMMediaSource::WVMMediaSource(WVSession *session, WVEsSelector esSelector,
const sp<MetaData> &metaData, bool isLive)
: mSession(session),
mESSelector(esSelector),
mTrackMetaData(metaData),
mStarted(false),
mIsLiveStream(isLive),
mNewSegment(false),
mGroup(NULL),
mDts(0),
mPts(0)
{
_ah010(_cb);
#ifdef REQUIRE_SECURE_BUFFERS
if (esSelector == WV_EsSelector_Video) {
OEMCrypto_Initialize();
}
#endif
}
// Since the WVMExtractor lifetime is short, we delegate ownership of some resources
// to the media source, which cleans them up after when the media source is destroyed
void WVMMediaSource::delegateFileSource(sp<WVMFileSource> fileSource)
{
mFileSource = fileSource;
}
void WVMMediaSource::delegateDataSource(sp<DataSource> dataSource)
{
mDataSource = dataSource;
}
void WVMMediaSource::allocBufferGroup()
{
if (mGroup)
delete mGroup;
mGroup = new MediaBufferGroup;
size_t size;
if (mESSelector == WV_EsSelector_Video)
size = 256 * 1024;
else
size = 64 * 1024;
mGroup->add_buffer(new MediaBuffer(size));
}
status_t WVMMediaSource::setBuffers(const Vector<MediaBuffer *> &buffers) {
#ifdef REQUIRE_SECURE_BUFFERS
if (!mIsLiveStream) {
LOGI("Using codec-supplied buffers");
delete mGroup;
mGroup = new MediaBufferGroup;
for (size_t i = 0; i < buffers.size(); ++i) {
mGroup->add_buffer(buffers.itemAt(i));
}
return OK;
} else {
return ERROR_UNSUPPORTED;
}
#else
return ERROR_UNSUPPORTED;
#endif
}
status_t WVMMediaSource::start(MetaData *)
{
//LOGD("WVMMediaSource::start()");
Mutex::Autolock autoLock(mLock);
CHECK(!mStarted);
mNewSegment = true;
mStarted = true;
mLogOnce = true;
// Let video stream control play/pause
if (mESSelector == WV_EsSelector_Video) {
float speed;
WVStatus result = WV_Play(mSession, 1.0, &speed, "now-");
if (result != WV_Status_OK) {
LOGE("WV_Play returned status %d in WVMMediaSource::start\n", result);
return ERROR_IO;
}
#ifndef REQUIRE_SECURE_BUFFERS
allocBufferGroup();
#else
if (mIsLiveStream) {
allocBufferGroup();
}
#endif
} else {
// audio
allocBufferGroup();
}
return OK;
}
status_t WVMMediaSource::stop()
{
//LOGD("WVMMediaSource::stop()");
Mutex::Autolock autoLock(mLock);
CHECK(mStarted);
status_t status = OK;
// Let video stream control play/pause
if (mESSelector == WV_EsSelector_Video) {
WVStatus result = WV_Pause(mSession, "now");
if (result != WV_Status_OK) {
LOGE("WV_Pause returned status %d in WVMMediaSource::stop\n", result);
status = ERROR_IO;
}
}
delete mGroup;
mGroup = NULL;
mStarted = false;
return status;
}
sp<MetaData> WVMMediaSource::getFormat()
{
Mutex::Autolock autoLock(mLock);
#ifdef REQUIRE_SECURE_BUFFERS
if (!mIsLiveStream && (mESSelector == WV_EsSelector_Video)) {
mTrackMetaData->setInt32(kKeyRequiresSecureBuffers, true);
}
#endif
return mTrackMetaData;
}
std::string usecToNPT(int64_t time)
{
unsigned hours = (unsigned)(time / (60LL * 60 * 1000000));
time -= (int64_t)hours * 60 * 60 * 1000000;
unsigned mins = (unsigned)(time / (60 * 1000000));
time -= (int64_t)mins * 60 * 1000000;
float secs = (float)time / 1000000;
char buf[32];
sprintf(buf, "%d:%d:%f", hours, mins, secs);
return std::string(buf);
}
status_t WVMMediaSource::read(MediaBuffer **buffer, const ReadOptions *options)
{
Mutex::Autolock autoLock(mLock);
CHECK(mStarted);
*buffer = NULL;
bool seekNextSync = false;
#if 0
// The sync bits aren't working right yet on live streams, so need to disable this
// for now.
if (mIsLiveStream && mNewSegment && (mESSelector == WV_EsSelector_Video)) {
seekNextSync = true;
mNewSegment = false;
}
#endif
int64_t seekTimeUs;
int retryLimit = 500; // Limit on number of retries before timeout, 10ms per retry
ReadOptions::SeekMode mode;
if (options && options->getSeekTo(&seekTimeUs, &mode)) {
// When doing a seek, use a longer timeout since we need to set up a new connection
retryLimit = 1500;
//LOGD("%s seek mode=%d, seek time=%lld lateby=%lld",
// (mESSelector == WV_EsSelector_Video) ? "video" : "audio",
// mode, seekTimeUs, options->getLateBy());
if (mode == ReadOptions::SEEK_NEXT_SYNC) {
// Handle seek next sync by dropping frames on this track that are
// prior to the specified time.
seekNextSync = true;
} else {
// Let video stream control seek
if (mESSelector == WV_EsSelector_Video) {
float scaleUsed;
std::string when = usecToNPT(seekTimeUs) + std::string("-");
WVStatus result = WV_Play(mSession, 1.0, &scaleUsed, when );
if (result != WV_Status_OK) {
LOGE("WV_Play returned status %d in WVMMediaSource::read\n", result);
return ERROR_IO;
}
}
}
}
MediaBuffer *mediaBuf;
status_t err = mGroup->acquire_buffer(&mediaBuf);
if (err != OK) {
CHECK_EQ(mediaBuf, NULL);
return err;
}
#ifdef REQUIRE_SECURE_BUFFERS
sDecryptContext[mESSelector].Initialize(mediaBuf);
#endif
size_t bytesRead;
bool auStart;
size_t offset = 0;
int64_t keyTime;
bool syncFrame;
int retryCount = 0;
// Pull full access units. Since we aren't sure how big they might be,
// start with initial buffer size, then allocate a larger buffer if we
// get a number of bytes equal to the full buffer size and go back
// for the rest. Only loop in this case, usually it's one pass through.
while (true) {
size_t size = mediaBuf->size() - offset;
WVStatus result = WV_GetEsData(mSession, mESSelector, (uint8_t *)mediaBuf->data() + offset,
size, bytesRead, auStart, mDts, mPts, syncFrame);
if (result != WV_Status_OK &&
result != WV_Status_Warning_Need_Key &&
result != WV_Status_Warning_Download_Stalled) {
status_t status;
switch(result) {
case WV_Status_End_Of_Media:
status = ERROR_END_OF_STREAM;
break;
case WV_Status_Terminate_Requested:
status = ERROR_HEARTBEAT_TERMINATE_REQUESTED;
break;
case YT_HEARTBEAT_NO_ACTIVE_PURCHASE_AGREEMENT:
status = ERROR_HEARTBEAT_NO_ACTIVE_PURCHASE_AGREEMENT;
break;
case YT_HEARTBEAT_CONCURRENT_PLAYBACK:
status = ERROR_HEARTBEAT_CONCURRENT_PLAYBACK;
break;
case YT_HEARTBEAT_UNUSUAL_ACTIVITY:
status = ERROR_HEARTBEAT_UNUSUAL_ACTIVITY;
break;
case YT_HEARTBEAT_STREAMING_UNAVAILABLE:
status = ERROR_HEARTBEAT_STREAMING_UNAVAILABLE;
break;
case YT_HEARTBEAT_CANNOT_ACTIVATE_RENTAL:
status = ERROR_HEARTBEAT_CANNOT_ACTIVATE_RENTAL;
break;
default:
if (mLogOnce) {
LOGE("WV_GetEsData returned ERROR %d in WVMMediaSource::read\n", result);
mLogOnce = false;
}
status = ERROR_IO;
break;
}
mediaBuf->release();
return status;
}
if (sLastError != NO_ERROR) {
mediaBuf->release();
status_t status = sLastError;
sLastError = NO_ERROR;
return status;
}
if (bytesRead == 0) {
if (retryCount++ >= retryLimit) {
// If no data received within the retry limit, return ERROR_IO
// This prevents the player from becoming unresponsive
mediaBuf->release();
return ERROR_IO;
} else {
// Didn't get anything, sleep a bit so we don't hog the CPU then try again
usleep(10000);
continue;
}
}
#define PCR_HZ 90000
keyTime = (int64_t)mPts * 1000000 / PCR_HZ;
if (seekNextSync && ((keyTime < seekTimeUs) || !syncFrame)) {
// drop frames up to next sync if requested
usleep(10000);
#ifdef REQUIRE_SECURE_BUFFERS
sDecryptContext[mESSelector].Initialize(mediaBuf);
#endif
continue;
}
if (offset + bytesRead < mediaBuf->size())
break;
#ifdef REQUIRE_SECURE_BUFFERS
LOGD("buffer overflow");
mediaBuf->release();
return ERROR_IO;
#endif
//LOGD("Resizing...");
// This buffer is too small, allocate a larger buffer twice the size
// and copy the data from the current buffer into the first part of
// the new buffer, then set offset to where the next read should go.
MediaBuffer *newBuffer = new MediaBuffer(mediaBuf->size() * 2);
newBuffer->add_ref();
memcpy(newBuffer->data(), mediaBuf->data(), mediaBuf->size());
offset = mediaBuf->size();
mGroup->add_buffer(newBuffer);
mediaBuf->release();
mediaBuf = newBuffer;
}
mediaBuf->meta_data()->clear();
mediaBuf->meta_data()->setInt64(kKeyTime, keyTime);
mediaBuf->set_range(0, bytesRead + offset);
#if 0
// debug code - log packets to files
char filename[32];
static int acounter = 0, vcounter = 0;
if (mESSelector == WV_EsSelector_Video)
sprintf(filename, "/data/wvm/v%d", vcounter++);
else
sprintf(filename, "/data/wvm/a%d", acounter++);
FILE *f = fopen(filename, "w");
if (!f)
LOGE("WVMFileSource: can't open %s", filename);
else {
fwrite(mediaBuf->data(), bytesRead + offset, 1, f);
fclose(f);
}
LOGD("WVMMediaSource::read writing (%d bytes to %s)", bytesRead + offset, filename);
#endif
#if 0
LOGD("[%p] %s packet length=%d kKeyTime=%lld %s\n", mediaBuf,
(mESSelector == WV_EsSelector_Video ? "video" : "audio"),
bytesRead + offset, keyTime, syncFrame ? "sync" : "");
#endif
*buffer = mediaBuf;
return OK;
}
#ifdef REQUIRE_SECURE_BUFFERS
WVMMediaSource::DecryptContext WVMMediaSource::sDecryptContext[2] = {};
void WVMMediaSource::DecryptCallback(WVEsSelector esType, void* input, void* output,
size_t length, int key)
{
//LOGD("DecryptCallback(type=%d, in=%p, out=%p, len=%d, key=%d\n",
// (int)esType, input, output, length, key);
DecryptContext &context = sDecryptContext[esType];
OEMCrypto_UINT32 copied = length;
OEMCryptoResult result;
unsigned char *iv = NULL;
if (key)
iv = context.mIV;
if (esType == WV_EsSelector_Video) {
result = OEMCrypto_DecryptVideo(iv, (OEMCrypto_UINT8 *)input, length,
(OEMCrypto_UINT32)(char *)context.mMediaBuf->data(),
context.mOffset, &copied);
} else {
result = OEMCrypto_DecryptAudio(iv, (OEMCrypto_UINT8 *)input, length,
(OEMCrypto_UINT8 *)context.mMediaBuf->data() + context.mOffset,
&copied);
}
if (result != OEMCrypto_SUCCESS) {
LOGD("OEMCrypto decrypt failure: %d", result);
}
context.mOffset += copied;
}
#endif
WVMMediaSource::~WVMMediaSource()
{
//LOGD("WVMMediaSource::~WVMMediaSource()");
if (mStarted) {
stop();
}
if (mESSelector == WV_EsSelector_Video) {
if (mSession != NULL) {
WV_Teardown(mSession);
#ifdef REQUIRE_SECURE_BUFFERS
OEMCrypto_Terminate();
#endif
}
WVMExtractorImpl::cleanup();
}
}
} // namespace android