blob: 7e8d184ba0f4549e938cbf1c1208743d7d38b9ac [file] [log] [blame]
/*
**
** 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.
*/
#include "metadatadriver.h"
#ifdef LOG_TAG
#undef LOG_TAG
#define LOG_TAG "MediaMetadataDriver"
#endif
#include <media/thread_init.h>
using namespace android;
const char* MetadataDriver::ALBUM_ART_KEY = "graphic";
const char* MetadataDriver::METADATA_KEYS[] = {
"tracknumber",
"album",
"artist",
"author",
"composer",
"date",
"genre",
"title",
"year",
"duration",
"num-tracks",
"drm/is-protected",
"track-info/codec-name"
};
static void dumpkeystolog(PVPMetadataList list)
{
uint32 n = list.size();
for(uint32 i = 0; i < n; ++i) {
LOGI("@@@@@ wma key: %s", list[i].get_cstr());
}
}
MetadataDriver::MetadataDriver(uint32 mode): OsclTimerObject(OsclActiveObject::EPriorityNominal, "MetadataDriverTimerObject")
{
mMode = mode;
mUtil = NULL;
mBitmap = NULL;
mDataSource = NULL;
#if BEST_THUMBNAIL_MODE
mLocalDataSource = NULL;
#endif
mCmdId = 0;
mContextObjectRefValue = 0x5C7A; // Some random number
mContextObject = mContextObjectRefValue;
mMediaAlbumArt = new MediaAlbumArt();
LOGV("MetadataDriver: Mode (%d).", mMode);
InitializeForThread();
PV_MasterOMX_Init();
}
MetadataDriver::~MetadataDriver()
{
if (mBitmap) {
delete mBitmap;
mBitmap = NULL;
}
if (mMediaAlbumArt) {
delete mMediaAlbumArt;
mMediaAlbumArt = NULL;
}
// UninitializeForThread gets called automatically when the current thread exits, so don't call it from here.
PV_MasterOMX_Deinit();
}
const char* MetadataDriver::extractMetadata(int keyCode)
{
char *value = NULL;
if (mMode & GET_METADATA_ONLY) {
// Comparing int with unsigned int
if (keyCode < 0 || keyCode >= (int) NUM_METADATA_KEYS) {
LOGE("extractMetadata: Invalid keyCode: %d.", keyCode);
} else {
value = mMetadataValues[keyCode];
}
}
if (value == NULL || value[0] == '\0') {
return NULL;
}
return value;
}
MediaAlbumArt* MetadataDriver::extractAlbumArt()
{
if (mMode & GET_METADATA_ONLY) {
if (mMediaAlbumArt && mMediaAlbumArt->getLength() > 0) { // Copy out.
return new MediaAlbumArt(*mMediaAlbumArt);
}
}
return NULL;
}
// How to better manage these constant strings?
bool MetadataDriver::containsSupportedKey(const OSCL_HeapString<OsclMemAllocator>& str) const
{
const char* cStr = str.get_cstr();
for (uint32 i = 0; i < NUM_METADATA_KEYS; ++i) {
if (strcasestr(cStr, METADATA_KEYS[i])) {
return true;
}
}
// Key "graphic" is a special metadata key for retrieving album art image.
if (strcasestr(cStr, "graphic")) {
return true;
}
return false;
}
// Delete unnecessary keys before retrieving the metadata values to avoid
// retrieving all metadata values for all metadata keys
void MetadataDriver::trimKeys()
{
//dumpkeystolog(mMetadataKeyList);
mActualMetadataKeyList.clear();
uint32 n = mMetadataKeyList.size();
mActualMetadataKeyList.reserve(n);
for(uint32 i = 0; i < n; ++i) {
if (containsSupportedKey(mMetadataKeyList[i])) {
mActualMetadataKeyList.push_back(mMetadataKeyList[i]);
}
}
mMetadataKeyList.clear();
}
// Returns:
// 1. UNKNOWN_ERROR
// a. If the metadata value(s) is too long, and cannot be hold in valueLength bytes
// b. If nothing is found
// 2. OK
// a. If metadata value(s) is found
status_t MetadataDriver::extractMetadata(const char* key, char* value, uint32 valueLength)
{
bool found = false;
value[0] = '\0';
for (uint32 i = 0, n = mMetadataValueList.size(); i < n; ++i) {
if (strcasestr(mMetadataValueList[i].key, key)) {
found = true;
switch(GetValTypeFromKeyString(mMetadataValueList[i].key)) {
case PVMI_KVPVALTYPE_CHARPTR: {
uint32 length = oscl_strlen(mMetadataValueList[i].value.pChar_value) + 1;
if (length > valueLength) {
return UNKNOWN_ERROR;
}
oscl_snprintf(value, length, "%s", mMetadataValueList[i].value.pChar_value);
value[length] = '\0';
LOGV("value of char: %s.", mMetadataValueList[i].value.pChar_value);
break;
}
case PVMI_KVPVALTYPE_WCHARPTR: {
// Assume string is in UCS-2 encoding so convert to UTF-8.
uint32 length = oscl_strlen(mMetadataValueList[i].value.pWChar_value) + 1;
if (length > valueLength) {
return UNKNOWN_ERROR;
}
length = oscl_UnicodeToUTF8(mMetadataValueList[i].value.pWChar_value, length, value, valueLength);
value[length] = '\0';
LOGV("value of wchar: %ls.", mMetadataValueList[i].value.pWChar_value);
break;
}
case PVMI_KVPVALTYPE_UINT32:
oscl_snprintf(value, valueLength, "%d", mMetadataValueList[i].value.uint32_value);
value[valueLength] = '\0';
break;
case PVMI_KVPVALTYPE_INT32:
oscl_snprintf(value, valueLength, "%d", mMetadataValueList[i].value.int32_value);
value[valueLength] = '\0';
break;
case PVMI_KVPVALTYPE_UINT8:
oscl_snprintf(value, valueLength, "%d", mMetadataValueList[i].value.uint8_value);
value[valueLength] = '\0';
break;
case PVMI_KVPVALTYPE_FLOAT:
oscl_snprintf(value, valueLength, "%f", mMetadataValueList[i].value.float_value);
value[valueLength] = '\0';
break;
case PVMI_KVPVALTYPE_DOUBLE:
oscl_snprintf(value, valueLength, "%f", mMetadataValueList[i].value.double_value);
value[valueLength] = '\0';
break;
case PVMI_KVPVALTYPE_BOOL:
oscl_snprintf(value, valueLength, "%s", mMetadataValueList[i].value.bool_value? "true": "false");
value[valueLength] = '\0';
break;
default:
return UNKNOWN_ERROR;
}
break;
}
}
return found? OK: UNKNOWN_ERROR;
}
void MetadataDriver::cacheMetadataRetrievalResults()
{
#if 0
for (uint32 i = 0, n = mMetadataValueList.size(); i < n; ++i) {
LOGV("Value %d: Key string: %s.", (i+1), mMetadataValueList[i].key);
}
#endif
for(uint32 i = 0; i < NUM_METADATA_KEYS; ++i) {
LOGV("extract metadata key: %s", METADATA_KEYS[i]);
extractMetadata(METADATA_KEYS[i], mMetadataValues[i], MAX_METADATA_STRING_LENGTH - 1);
}
doExtractAlbumArt();
}
status_t MetadataDriver::extractEmbeddedAlbumArt(const PvmfApicStruct* apic)
{
char* buf = (char*) apic->iGraphicData;
uint32 size = apic->iGraphicDataLen;
LOGV("extractEmbeddedAlbumArt: Embedded graphic or album art (%d bytes) is found.", size);
if (size && buf) {
return mMediaAlbumArt->setData(size, buf);
}
return UNKNOWN_ERROR;
}
status_t MetadataDriver::extractExternalAlbumArt(const char* url)
{
LOGV("extractExternalAlbumArt: External graphic or album art is found: %s.", url);
if (mMediaAlbumArt) {
delete mMediaAlbumArt;
}
mMediaAlbumArt = new MediaAlbumArt(url);
return (mMediaAlbumArt && mMediaAlbumArt->getLength() > 0)? OK: UNKNOWN_ERROR;
}
// Finds the first album art and extract it.
status_t MetadataDriver::doExtractAlbumArt()
{
if (mMediaAlbumArt) {
mMediaAlbumArt->clearData(); // Clear data from previous retrieval.
status_t status = UNKNOWN_ERROR;
for (uint32 i = 0, n = mMetadataValueList.size(); i < n; ++i) {
if (strcasestr(mMetadataValueList[i].key, ALBUM_ART_KEY)) {
LOGV("doExtractAlbumArt: album art key: %s", mMetadataValueList[i].key);
if (PVMI_KVPVALTYPE_KSV == GetValTypeFromKeyString(mMetadataValueList[i].key)) {
mMediaAlbumArt->clearData(); // Here may be visited multiple times.
const char* embeddedKey = "graphic;format=APIC;valtype=ksv";
const char* externalKey = "graphic;valtype=char*";
if(strstr(mMetadataValueList[i].key, embeddedKey) && mMetadataValueList[i].value.key_specific_value) {
// Embedded album art.
status = extractEmbeddedAlbumArt(((PvmfApicStruct*)mMetadataValueList[i].value.key_specific_value));
} else if (strstr(mMetadataValueList[i].key, externalKey)) {
// Album art linked with an external url.
status = extractExternalAlbumArt(mMetadataValueList[i].value.pChar_value);
}
if (status != OK) {
continue;
}
return status; // Found the album art.
}
}
}
}
return UNKNOWN_ERROR;
}
void MetadataDriver::clearCache()
{
if (mBitmap) {
delete mBitmap;
mBitmap = NULL;
}
for(uint32 i = 0; i < NUM_METADATA_KEYS; ++i) {
mMetadataValues[i][0] = '\0';
}
}
status_t MetadataDriver::setDataSource(const char* srcUrl)
{
// Don't let somebody trick us in to reading some random block of memory.
if (strncmp("mem://", srcUrl, 6) == 0) {
LOGE("setDataSource: Invalid url (%s).", srcUrl);
return UNKNOWN_ERROR;
}
if (oscl_strlen(srcUrl) > MAX_STRING_LENGTH) {
LOGE("setDataSource: Data source url length (%d) is too long.", oscl_strlen(srcUrl));
return UNKNOWN_ERROR;
}
clearCache();
return doSetDataSource(srcUrl);
}
status_t MetadataDriver::doSetDataSource(const char* dataSrcUrl)
{
if (mMode & GET_FRAME_ONLY) {
#if BEST_THUMBNAIL_MODE
mFrameSelector.iSelectionMethod = PVFrameSelector::SPECIFIC_FRAME;
mFrameSelector.iFrameInfo.iTimeOffsetMilliSec = 0;
#else
mFrameSelector.iSelectionMethod=PVFrameSelector::SPECIFIC_FRAME;
mFrameSelector.iFrameInfo.iFrameIndex=0;
#endif
}
mIsSetDataSourceSuccessful = false;
oscl_wchar tmpWCharBuf[MAX_STRING_LENGTH];
oscl_UTF8ToUnicode(dataSrcUrl, oscl_strlen(dataSrcUrl), tmpWCharBuf, sizeof(tmpWCharBuf));
mDataSourceUrl.set(tmpWCharBuf, oscl_strlen(tmpWCharBuf));
OsclScheduler::Init("MetadataDriverScheduler", NULL, 3);
OsclExecScheduler *sched = OsclExecScheduler::Current();
if (!sched) {
LOGE("doSetDataSource: No scheduler is installed.");
return UNKNOWN_ERROR;
}
mState = STATE_CREATE;
AddToScheduler();
RunIfNotReady();
sched->StartScheduler(); // Block until StopScheduler is called.
OsclScheduler::Cleanup();
return mIsSetDataSourceSuccessful? OK: UNKNOWN_ERROR;
}
SkBitmap *MetadataDriver::captureFrame()
{
if (mMode & GET_FRAME_ONLY) {
if(mBitmap) { // Copy out
LOGV("captureFrame: Copy out");
return new SkBitmap(*mBitmap);
}
}
LOGV("captureFrame: return NULL");
return NULL;
}
void MetadataDriver::doColorConversion()
{
int width = mFrameBufferProp.iFrameWidth;
int height = mFrameBufferProp.iFrameHeight;
int displayWidth = mFrameBufferProp.iDisplayWidth;
int displayHeight = mFrameBufferProp.iDisplayHeight;
mBitmap = new SkBitmap();
if (mBitmap) {
mBitmap->setConfig(SkBitmap::kRGB_565_Config, displayWidth, displayHeight);
mBitmap->allocPixels();
// Do color conversion.
ColorConvertBase* colorConverter = ColorConvert16::NewL();
if (colorConverter) {
colorConverter->Init(displayWidth, displayHeight, width, displayWidth, displayHeight, displayWidth, CCROTATE_NONE);
colorConverter->SetMode(1);
colorConverter->Convert(mFrameBuffer, (uint8*)mBitmap->getPixels());
delete colorConverter;
} else {
LOGE("doColorConversion: Cannot instantiate a ColorConvertBase object.");
delete mBitmap;
mBitmap = NULL;
}
} else {
LOGE("doColorConversion: Cannot instantiate a SkBitmap object.");
}
}
// Instantiate a frame and metadata utility object.
void MetadataDriver::handleCreate()
{
int error = 0;
OSCL_HeapString<OsclMemAllocator> outputFrameTypeString;
GetFormatString(PVMF_YUV420, outputFrameTypeString);
OSCL_TRY(error, mUtil = PVFrameAndMetadataFactory::CreateFrameAndMetadataUtility(outputFrameTypeString.get_str(), this, this, this));
if (error || mUtil->SetMode(PV_FRAME_METADATA_INTERFACE_MODE_SOURCE_METADATA_AND_THUMBNAIL) != PVMFSuccess) {
handleCommandFailure();
} else {
mState = STATE_ADD_DATA_SOURCE;
RunIfNotReady();
}
}
// Create a data source and add it.
void MetadataDriver::handleAddDataSource()
{
int error = 0;
mDataSource = new PVPlayerDataSourceURL;
if (mDataSource) {
mDataSource->SetDataSourceURL(mDataSourceUrl);
mDataSource->SetDataSourceFormatType(PVMF_FORMAT_UNKNOWN);
if (mMode & GET_FRAME_ONLY) {
#if BEST_THUMBNAIL_MODE
// Set the intent to thumbnails.
mLocalDataSource = new PVMFLocalDataSource(false);
mLocalDataSource->iIntent = BITMASK_PVMF_SOURCE_INTENT_THUMBNAILS;
mDataSource->SetDataSourceContextData((OsclAny*)mLocalDataSource);
#endif
}
OSCL_TRY(error, mCmdId = mUtil->AddDataSource(*mDataSource, (OsclAny*)&mContextObject));
OSCL_FIRST_CATCH_ANY(error, handleCommandFailure());
}
}
void MetadataDriver::handleRemoveDataSource()
{
int error = 0;
OSCL_TRY(error, mCmdId = mUtil->RemoveDataSource(*mDataSource, (OsclAny*)&mContextObject));
OSCL_FIRST_CATCH_ANY(error, handleCommandFailure());
}
// Clean up, due to either failure or task completion.
void MetadataDriver::handleCleanUp()
{
if(mUtil)
{
PVFrameAndMetadataFactory::DeleteFrameAndMetadataUtility(mUtil);
mUtil = NULL;
}
#if BEST_THUMBNAIL_MODE
delete mLocalDataSource;
mLocalDataSource = NULL;
#endif
delete mDataSource;
mDataSource = NULL;
OsclExecScheduler *sched=OsclExecScheduler::Current();
if (sched) {
sched->StopScheduler();
}
}
// Retrieve all the available metadata keys.
void MetadataDriver::handleGetMetadataKeys()
{
int error = 0;
mMetadataKeyList.clear();
OSCL_TRY(error, mCmdId = mUtil->GetMetadataKeys(mMetadataKeyList, 0, -1, NULL, (OsclAny*)&mContextObject));
OSCL_FIRST_CATCH_ANY(error, handleCommandFailure());
}
// Retrieve a frame and store the contents into an internal buffer.
void MetadataDriver::handleGetFrame()
{
int error = 0;
mFrameBufferSize = MAX_VIDEO_FRAME_SIZE;
OSCL_TRY(error, mCmdId = mUtil->GetFrame(mFrameSelector, mFrameBuffer, mFrameBufferSize, mFrameBufferProp, (OsclAny*)&mContextObject));
OSCL_FIRST_CATCH_ANY(error, handleCommandFailure());
}
// Retrieve all the available metadata values associated with the given keys.
void MetadataDriver::handleGetMetadataValues()
{
int error = 0;
mNumMetadataValues = 0;
mMetadataValueList.clear();
trimKeys(); // Switch to use actual supported key list.
OSCL_TRY(error, mCmdId = mUtil->GetMetadataValues(mActualMetadataKeyList, 0, -1, mNumMetadataValues, mMetadataValueList, (OsclAny*)&mContextObject));
OSCL_FIRST_CATCH_ANY(error, handleCommandFailure());
}
void MetadataDriver::Run()
{
switch(mState) {
case STATE_CREATE:
handleCreate();
break;
case STATE_ADD_DATA_SOURCE:
handleAddDataSource();
break;
case STATE_GET_METADATA_KEYS:
handleGetMetadataKeys();
break;
case STATE_GET_METADATA_VALUES:
handleGetMetadataValues();
break;
case STATE_GET_FRAME:
handleGetFrame();
break;
case STATE_REMOVE_DATA_SOURCE:
handleRemoveDataSource();
break;
default:
handleCleanUp();
break;
}
}
bool MetadataDriver::isCommandSuccessful(const PVCmdResponse& aResponse) const
{
bool success = ((aResponse.GetCmdId() == mCmdId) &&
(aResponse.GetCmdStatus() == PVMFSuccess) &&
(aResponse.GetContext() == (OsclAny*)&mContextObject));
if (!success) {
LOGE("isCommandSuccessful: Command id(%d and expected %d) and status (%d and expected %d), data corruption (%s) at state (%d).",
aResponse.GetCmdId(), mCmdId, aResponse.GetCmdStatus(), PVMFSuccess, (aResponse.GetContext() == (OsclAny*)&mContextObject)? "false": "true", mState);
}
return success;
}
void MetadataDriver::handleCommandFailure()
{
if (mState == STATE_REMOVE_DATA_SOURCE) {
mState = STATE_CLEANUP_AND_COMPLETE;
}
else{
mState = STATE_REMOVE_DATA_SOURCE;
}
RunIfNotReady();
}
// Callback handler for a request completion by frameandmetadatautility.
void MetadataDriver::CommandCompleted(const PVCmdResponse& aResponse)
{
if (!isCommandSuccessful(aResponse)) {
handleCommandFailure();
return;
}
switch(mState) {
case STATE_ADD_DATA_SOURCE:
if (mMode & GET_METADATA_ONLY) {
mState = STATE_GET_METADATA_KEYS;
} else if (mMode & GET_FRAME_ONLY) {
mState = STATE_GET_FRAME;
} else {
LOGV("CommandCompleted: Neither retrieve metadata nor capture frame.");
mState = STATE_REMOVE_DATA_SOURCE;
}
mIsSetDataSourceSuccessful = true;
break;
case STATE_GET_METADATA_KEYS:
mState = STATE_GET_METADATA_VALUES;
break;
case STATE_GET_METADATA_VALUES:
if (mMode & GET_FRAME_ONLY) {
mState = STATE_GET_FRAME;
} else {
mState = STATE_REMOVE_DATA_SOURCE;
}
cacheMetadataRetrievalResults();
break;
case STATE_GET_FRAME:
doColorConversion();
mState = STATE_REMOVE_DATA_SOURCE;
break;
case STATE_REMOVE_DATA_SOURCE:
mState = STATE_CLEANUP_AND_COMPLETE;
break;
default:
mState = STATE_CLEANUP_AND_COMPLETE;
break;
}
RunIfNotReady();
}
void MetadataDriver::HandleErrorEvent(const PVAsyncErrorEvent& aEvent)
{
// Error occurs, clean up and terminate.
LOGE("HandleErrorEvent: Event [type(%d), response type(%d)] received.", aEvent.GetEventType(), aEvent.GetResponseType());
handleCommandFailure();
}
void MetadataDriver::HandleInformationalEvent(const PVAsyncInformationalEvent& aEvent)
{
LOGV("HandleInformationalEvent: Event [type(%d), response type(%d)] received.", aEvent.GetEventType(), aEvent.GetResponseType());
}
// Exported so that can be called outside as a global method through libraries.
// See file mediametadataretriever.cpp for details.
extern "C" {
void* createRetriever() {
return new MetadataDriver();
}
}