201 lines
5.1 KiB
C++
201 lines
5.1 KiB
C++
|
/*
|
||
|
* Copyright (C) 2009 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_NDEBUG 0
|
||
|
#define LOG_TAG "TestPlayerStub"
|
||
|
#include "utils/Log.h"
|
||
|
|
||
|
#include "TestPlayerStub.h"
|
||
|
|
||
|
#include <dlfcn.h> // for dlopen/dlclose
|
||
|
#include <stdlib.h>
|
||
|
#include <string.h>
|
||
|
#include <cutils/properties.h>
|
||
|
#include <utils/Errors.h> // for status_t
|
||
|
|
||
|
#include "media/MediaPlayerInterface.h"
|
||
|
|
||
|
|
||
|
namespace {
|
||
|
using android::status_t;
|
||
|
using android::MediaPlayerBase;
|
||
|
|
||
|
const char *kTestUrlScheme = "test:";
|
||
|
const char *kUrlParam = "url=";
|
||
|
|
||
|
const char *kBuildTypePropName = "ro.build.type";
|
||
|
const char *kEngBuild = "eng";
|
||
|
const char *kTestBuild = "test";
|
||
|
|
||
|
// @return true if the current build is 'eng' or 'test'.
|
||
|
bool isTestBuild()
|
||
|
{
|
||
|
char prop[PROPERTY_VALUE_MAX] = { '\0', };
|
||
|
|
||
|
property_get(kBuildTypePropName, prop, '\0');
|
||
|
return strcmp(prop, kEngBuild) == 0 || strcmp(prop, kTestBuild) == 0;
|
||
|
}
|
||
|
|
||
|
// @return true if the url scheme is 'test:'
|
||
|
bool isTestUrl(const char *url)
|
||
|
{
|
||
|
return url && strncmp(url, kTestUrlScheme, strlen(kTestUrlScheme)) == 0;
|
||
|
}
|
||
|
|
||
|
} // anonymous namespace
|
||
|
|
||
|
namespace android {
|
||
|
|
||
|
TestPlayerStub::TestPlayerStub()
|
||
|
:mUrl(NULL), mFilename(NULL), mContentUrl(NULL),
|
||
|
mHandle(NULL), mNewPlayer(NULL), mDeletePlayer(NULL),
|
||
|
mPlayer(NULL) { }
|
||
|
|
||
|
TestPlayerStub::~TestPlayerStub()
|
||
|
{
|
||
|
resetInternal();
|
||
|
}
|
||
|
|
||
|
status_t TestPlayerStub::initCheck()
|
||
|
{
|
||
|
return isTestBuild() ? OK : INVALID_OPERATION;
|
||
|
}
|
||
|
|
||
|
// Parse mUrl to get:
|
||
|
// * The library to be dlopened.
|
||
|
// * The url to be passed to the real setDataSource impl.
|
||
|
//
|
||
|
// mUrl is expected to be in following format:
|
||
|
//
|
||
|
// test:<name of the .so>?url=<url for setDataSource>
|
||
|
//
|
||
|
// The value of the url parameter is treated as a string (no
|
||
|
// unescaping of illegal charaters).
|
||
|
status_t TestPlayerStub::parseUrl()
|
||
|
{
|
||
|
if (strlen(mUrl) < strlen(kTestUrlScheme)) {
|
||
|
resetInternal();
|
||
|
return BAD_VALUE;
|
||
|
}
|
||
|
|
||
|
char *i = mUrl + strlen(kTestUrlScheme);
|
||
|
|
||
|
mFilename = i;
|
||
|
|
||
|
while (*i != '\0' && *i != '?') {
|
||
|
++i;
|
||
|
}
|
||
|
|
||
|
if (*i == '\0' || strncmp(i + 1, kUrlParam, strlen(kUrlParam)) != 0) {
|
||
|
resetInternal();
|
||
|
return BAD_VALUE;
|
||
|
}
|
||
|
*i = '\0'; // replace '?' to nul-terminate mFilename
|
||
|
|
||
|
mContentUrl = i + 1 + strlen(kUrlParam);
|
||
|
return OK;
|
||
|
}
|
||
|
|
||
|
// Load the dynamic library.
|
||
|
// Create the test player.
|
||
|
// Call setDataSource on the test player with the url in param.
|
||
|
status_t TestPlayerStub::setDataSource(
|
||
|
const char *url, const KeyedVector<String8, String8> *headers) {
|
||
|
if (!isTestUrl(url) || NULL != mHandle) {
|
||
|
return INVALID_OPERATION;
|
||
|
}
|
||
|
|
||
|
mUrl = strdup(url);
|
||
|
|
||
|
status_t status = parseUrl();
|
||
|
|
||
|
if (OK != status) {
|
||
|
resetInternal();
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
::dlerror(); // Clears any pending error.
|
||
|
|
||
|
// Load the test player from the url. dlopen will fail if the lib
|
||
|
// is not there. dls are under /system/lib
|
||
|
// None of the entry points should be NULL.
|
||
|
mHandle = ::dlopen(mFilename, RTLD_NOW | RTLD_GLOBAL);
|
||
|
if (!mHandle) {
|
||
|
LOGE("dlopen failed: %s", ::dlerror());
|
||
|
resetInternal();
|
||
|
return UNKNOWN_ERROR;
|
||
|
}
|
||
|
|
||
|
// Load the 2 entry points to create and delete instances.
|
||
|
const char *err;
|
||
|
mNewPlayer = reinterpret_cast<NEW_PLAYER>(dlsym(mHandle,
|
||
|
"newPlayer"));
|
||
|
err = ::dlerror();
|
||
|
if (err || mNewPlayer == NULL) {
|
||
|
// if err is NULL the string <null> is inserted in the logs =>
|
||
|
// mNewPlayer was NULL.
|
||
|
LOGE("dlsym for newPlayer failed %s", err);
|
||
|
resetInternal();
|
||
|
return UNKNOWN_ERROR;
|
||
|
}
|
||
|
|
||
|
mDeletePlayer = reinterpret_cast<DELETE_PLAYER>(dlsym(mHandle,
|
||
|
"deletePlayer"));
|
||
|
err = ::dlerror();
|
||
|
if (err || mDeletePlayer == NULL) {
|
||
|
LOGE("dlsym for deletePlayer failed %s", err);
|
||
|
resetInternal();
|
||
|
return UNKNOWN_ERROR;
|
||
|
}
|
||
|
|
||
|
mPlayer = (*mNewPlayer)();
|
||
|
return mPlayer->setDataSource(mContentUrl, headers);
|
||
|
}
|
||
|
|
||
|
// Internal cleanup.
|
||
|
status_t TestPlayerStub::resetInternal()
|
||
|
{
|
||
|
if(mUrl) {
|
||
|
free(mUrl);
|
||
|
mUrl = NULL;
|
||
|
}
|
||
|
mFilename = NULL;
|
||
|
mContentUrl = NULL;
|
||
|
|
||
|
if (mPlayer) {
|
||
|
LOG_ASSERT(mDeletePlayer != NULL, "mDeletePlayer is null");
|
||
|
(*mDeletePlayer)(mPlayer);
|
||
|
mPlayer = NULL;
|
||
|
}
|
||
|
|
||
|
if (mHandle) {
|
||
|
::dlclose(mHandle);
|
||
|
mHandle = NULL;
|
||
|
}
|
||
|
return OK;
|
||
|
}
|
||
|
|
||
|
/* static */ bool TestPlayerStub::canBeUsed(const char *url)
|
||
|
{
|
||
|
return isTestBuild() && isTestUrl(url);
|
||
|
}
|
||
|
|
||
|
status_t TestPlayerStub::setParameters(const String8& params) {
|
||
|
return NO_ERROR;
|
||
|
}
|
||
|
|
||
|
} // namespace android
|