501 lines
15 KiB
C++
501 lines
15 KiB
C++
/*
|
|
**
|
|
** Copyright 2012, 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 "MediaPlayerFactory"
|
|
#include <utils/Log.h>
|
|
|
|
#include <cutils/properties.h>
|
|
#include <media/IMediaPlayer.h>
|
|
#include <media/stagefright/DataSource.h>
|
|
#include <media/stagefright/FileSource.h>
|
|
#include <media/stagefright/foundation/ADebug.h>
|
|
#include <media/stagefright/Utils.h>
|
|
|
|
#include <utils/Errors.h>
|
|
#include <utils/misc.h>
|
|
|
|
#include "MediaPlayerFactory.h"
|
|
|
|
#include "TestPlayerStub.h"
|
|
#include "nuplayer/NuPlayerDriver.h"
|
|
|
|
#include "awplayer.h"
|
|
namespace android {
|
|
|
|
Mutex MediaPlayerFactory::sLock;
|
|
MediaPlayerFactory::tFactoryMap MediaPlayerFactory::sFactoryMap;
|
|
bool MediaPlayerFactory::sInitComplete = false;
|
|
// TODO: Temp hack until we can register players
|
|
typedef struct
|
|
{
|
|
const char *extension;
|
|
const player_type playertype;
|
|
} extmap;
|
|
|
|
extmap FILE_EXTS [] =
|
|
{
|
|
{".ogg", AW_PLAYER},
|
|
{".mp3", AW_PLAYER},
|
|
{".wav", AW_PLAYER},
|
|
{".amr", AW_PLAYER},
|
|
{".flac", AW_PLAYER},
|
|
{".m4a", AW_PLAYER},
|
|
{".m4r", AW_PLAYER},
|
|
{".3gpp", AW_PLAYER},
|
|
{".out", AW_PLAYER},
|
|
{".3gp", AW_PLAYER},
|
|
{".aac", AW_PLAYER},
|
|
|
|
{".mid", NU_PLAYER},
|
|
{".midi", NU_PLAYER},
|
|
{".smf", NU_PLAYER},
|
|
{".xmf", NU_PLAYER},
|
|
{".mxmf", NU_PLAYER},
|
|
{".imy", NU_PLAYER},
|
|
{".rtttl",NU_PLAYER},
|
|
{".rtx", NU_PLAYER},
|
|
{".ota", NU_PLAYER},
|
|
{".wvm", NU_PLAYER},
|
|
|
|
{".ape", AW_PLAYER},
|
|
{".ac3", AW_PLAYER},
|
|
{".dts", AW_PLAYER},
|
|
{".wma", AW_PLAYER},
|
|
{".aac", AW_PLAYER},
|
|
{".mp2", AW_PLAYER},
|
|
{".mp1", AW_PLAYER},
|
|
};
|
|
|
|
void getCallingProcessName(char *name)
|
|
{
|
|
char value[1024];
|
|
if(name == 0)
|
|
{
|
|
ALOGE("params error");
|
|
return;
|
|
}
|
|
int getpersist;
|
|
getpersist = property_get("persist.sys.cts", value, NULL);
|
|
ALOGD("Initialize getpersist = %d value=%s", getpersist, value);
|
|
strcpy(name, value);
|
|
}
|
|
player_type getPlayerType_l(int fd, int64_t offset, int64_t /*length*/)
|
|
{
|
|
int r_size;
|
|
char buf[4096];
|
|
lseek(fd, offset, SEEK_SET);
|
|
r_size = read(fd, buf, sizeof(buf));
|
|
lseek(fd, offset, SEEK_SET);
|
|
long ident = *((long*)buf);
|
|
|
|
// Ogg vorbis?
|
|
if (ident == 0x5367674f) {
|
|
// 'OggS'
|
|
return NU_PLAYER;
|
|
} else if(ident == 0x6d756874){
|
|
//return THUMBNAIL_PLAYER;
|
|
} else if (ident == 0x6468544d) {
|
|
//'MThd'
|
|
return NU_PLAYER;
|
|
}
|
|
|
|
char value[1024];
|
|
property_get("media.use_nuplayer", value, NULL);
|
|
if(strcmp(value, "true") == 0)
|
|
{
|
|
return NU_PLAYER;
|
|
}
|
|
|
|
// Modify for WVM
|
|
const char* my_uri;
|
|
my_uri = strdup((char*)nameForFd(fd).c_str());
|
|
if (strrchr(my_uri, '.') != NULL &&
|
|
(!strncasecmp(".wvm", strrchr(my_uri, '.'), 4)
|
|
|| !strncasecmp(".mid", strrchr(my_uri, '.'), 4)
|
|
|| !strncasecmp(".midi", strrchr(my_uri, '.'), 5)))
|
|
{
|
|
ALOGD("fd NU_PLAYER.");
|
|
free((void*)my_uri);
|
|
return NU_PLAYER;
|
|
}
|
|
else
|
|
{
|
|
free((void*)my_uri);
|
|
}
|
|
char mCallingProcess[256]={0};
|
|
getCallingProcessName(mCallingProcess);
|
|
if((strcmp(mCallingProcess, "android.drm.cts") == 0) ||
|
|
(strcmp(mCallingProcess, "com.google.android.media.gts") == 0) ||
|
|
(strcmp(mCallingProcess, "android.video.cts") == 0) ||
|
|
(strcmp(mCallingProcess, "android.media.cts") == 0) ||
|
|
(strcmp(mCallingProcess, "android.view.cts") == 0) ||
|
|
(strcmp(mCallingProcess, "android.security.cts") == 0) ||
|
|
(strcmp(mCallingProcess, "com.android.cts.media") == 0)){
|
|
return NU_PLAYER;
|
|
}
|
|
|
|
return AW_PLAYER;
|
|
}
|
|
|
|
player_type getPlayerType_l(const char* url)
|
|
{
|
|
if ((strrchr(url, '.') != NULL) && (!strncasecmp(".html", strrchr(url, '.'), 5)))
|
|
{
|
|
ALOGD("html NU_PLAYER.");
|
|
return NU_PLAYER;
|
|
}
|
|
|
|
if (TestPlayerStub::canBeUsed(url))
|
|
{
|
|
return TEST_PLAYER;
|
|
}
|
|
|
|
if(!strncasecmp("https://", url, 8))
|
|
{
|
|
return NU_PLAYER;
|
|
}
|
|
|
|
char value[1024];
|
|
property_get("media.use_nuplayer", value, NULL);
|
|
if(strcmp(value, "true") == 0)
|
|
{
|
|
return NU_PLAYER;
|
|
}
|
|
|
|
char mCallingProcess[256]={0};
|
|
getCallingProcessName(mCallingProcess);
|
|
if((strcmp(mCallingProcess, "android.drm.cts") == 0) ||
|
|
(strcmp(mCallingProcess, "com.google.android.media.gts") == 0) ||
|
|
(strcmp(mCallingProcess, "android.video.cts") == 0) ||
|
|
(strcmp(mCallingProcess, "android.media.cts") == 0) ||
|
|
(strcmp(mCallingProcess, "android.view.cts") == 0) ||
|
|
(strcmp(mCallingProcess, "android.security.cts") == 0) ||
|
|
(strcmp(mCallingProcess, "com.android.cts.media") == 0)){
|
|
return NU_PLAYER;
|
|
}
|
|
|
|
if (!strncasecmp("widevine://", url, 11))
|
|
{
|
|
ALOGD("widevine stream NU_PLAYER");
|
|
return NU_PLAYER;
|
|
}
|
|
|
|
if (!strncmp("data:;base64", url, strlen("data:;base64")))
|
|
{
|
|
return NU_PLAYER;
|
|
}
|
|
|
|
// use MidiFile for MIDI extensions
|
|
int lenURL = strlen(url);
|
|
int len;
|
|
int start;
|
|
for (int i = 0; i < NELEM(FILE_EXTS); ++i)
|
|
{
|
|
len = strlen(FILE_EXTS[i].extension);
|
|
start = lenURL - len;
|
|
if (start > 0)
|
|
{
|
|
if (!strncasecmp(url + start, FILE_EXTS[i].extension, len))
|
|
{
|
|
return FILE_EXTS[i].playertype;
|
|
}
|
|
}
|
|
}
|
|
|
|
return AW_PLAYER;
|
|
}
|
|
|
|
status_t MediaPlayerFactory::registerFactory_l(IFactory* factory,
|
|
player_type type) {
|
|
if (NULL == factory) {
|
|
ALOGE("Failed to register MediaPlayerFactory of type %d, factory is"
|
|
" NULL.", type);
|
|
return BAD_VALUE;
|
|
}
|
|
|
|
if (sFactoryMap.indexOfKey(type) >= 0) {
|
|
ALOGE("Failed to register MediaPlayerFactory of type %d, type is"
|
|
" already registered.", type);
|
|
return ALREADY_EXISTS;
|
|
}
|
|
|
|
if (sFactoryMap.add(type, factory) < 0) {
|
|
ALOGE("Failed to register MediaPlayerFactory of type %d, failed to add"
|
|
" to map.", type);
|
|
return UNKNOWN_ERROR;
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
static player_type getDefaultPlayerType() {
|
|
#if 0
|
|
return NU_PLAYER;
|
|
#else
|
|
return AW_PLAYER;
|
|
#endif
|
|
}
|
|
|
|
status_t MediaPlayerFactory::registerFactory(IFactory* factory,
|
|
player_type type) {
|
|
Mutex::Autolock lock_(&sLock);
|
|
return registerFactory_l(factory, type);
|
|
}
|
|
|
|
void MediaPlayerFactory::unregisterFactory(player_type type) {
|
|
Mutex::Autolock lock_(&sLock);
|
|
sFactoryMap.removeItem(type);
|
|
}
|
|
|
|
#define GET_PLAYER_TYPE_IMPL_ORIGINAL(a...) \
|
|
Mutex::Autolock lock_(&sLock); \
|
|
\
|
|
player_type ret = STAGEFRIGHT_PLAYER; \
|
|
float bestScore = 0.0; \
|
|
\
|
|
for (size_t i = 0; i < sFactoryMap.size(); ++i) { \
|
|
\
|
|
IFactory* v = sFactoryMap.valueAt(i); \
|
|
float thisScore; \
|
|
CHECK(v != NULL); \
|
|
thisScore = v->scoreFactory(a, bestScore); \
|
|
if (thisScore > bestScore) { \
|
|
ret = sFactoryMap.keyAt(i); \
|
|
bestScore = thisScore; \
|
|
} \
|
|
} \
|
|
\
|
|
if (0.0 == bestScore) { \
|
|
bestScore = getDefaultPlayerType(); \
|
|
} \
|
|
\
|
|
return ret;
|
|
|
|
#define GET_PLAYER_TYPE_IMPL(a...) \
|
|
Mutex::Autolock lock_(&sLock); \
|
|
\
|
|
player_type ret = AW_PLAYER; \
|
|
float bestScore = 0.0; \
|
|
\
|
|
for (size_t i = 0; i < sFactoryMap.size(); ++i) { \
|
|
\
|
|
IFactory* v = sFactoryMap.valueAt(i); \
|
|
float thisScore; \
|
|
CHECK(v != NULL); \
|
|
thisScore = v->scoreFactory(a, bestScore); \
|
|
if (thisScore > bestScore) { \
|
|
ret = sFactoryMap.keyAt(i); \
|
|
bestScore = thisScore; \
|
|
} \
|
|
} \
|
|
\
|
|
if (0.0 == bestScore) { \
|
|
ret = getDefaultPlayerType(); \
|
|
} \
|
|
\
|
|
return ret;
|
|
|
|
player_type MediaPlayerFactory::getPlayerType(const sp<IMediaPlayer>& /*client*/,
|
|
const char* url) {
|
|
ALOGV("MediaPlayerFactory::getPlayerType: url = %s", url);
|
|
|
|
return android::getPlayerType_l(url);
|
|
|
|
#if 0
|
|
GET_PLAYER_TYPE_IMPL(client, url);
|
|
#endif
|
|
|
|
}
|
|
|
|
player_type MediaPlayerFactory::getPlayerType(const sp<IMediaPlayer>& /*client*/,
|
|
int fd,
|
|
int64_t offset,
|
|
int64_t length) {
|
|
|
|
#if 0
|
|
GET_PLAYER_TYPE_IMPL(client, fd, offset, length);
|
|
#else
|
|
return android::getPlayerType_l(fd, offset, length);
|
|
#endif
|
|
}
|
|
|
|
player_type MediaPlayerFactory::getPlayerType(const sp<IMediaPlayer>& client,
|
|
const sp<IStreamSource> &source) {
|
|
GET_PLAYER_TYPE_IMPL(client, source);
|
|
}
|
|
|
|
player_type MediaPlayerFactory::getPlayerType(const sp<IMediaPlayer>& /*client*/,
|
|
const sp<DataSource> & /*source*/) {
|
|
#if 0
|
|
GET_PLAYER_TYPE_IMPL(client, source);
|
|
#endif
|
|
return NU_PLAYER;
|
|
}
|
|
|
|
#undef GET_PLAYER_TYPE_IMPL
|
|
|
|
sp<MediaPlayerBase> MediaPlayerFactory::createPlayer(
|
|
player_type playerType,
|
|
const wp<IMediaPlayer> &client,
|
|
notify_callback_f notifyFunc,
|
|
pid_t pid) {
|
|
sp<MediaPlayerBase> p;
|
|
IFactory* factory;
|
|
status_t init_result;
|
|
Mutex::Autolock lock_(&sLock);
|
|
|
|
if (sFactoryMap.indexOfKey(playerType) < 0) {
|
|
ALOGE("Failed to create player object of type %d, no registered"
|
|
" factory", playerType);
|
|
return p;
|
|
}
|
|
|
|
factory = sFactoryMap.valueFor(playerType);
|
|
CHECK(NULL != factory);
|
|
p = factory->createPlayer(pid);
|
|
|
|
if (p == NULL) {
|
|
ALOGE("Failed to create player object of type %d, create failed",
|
|
playerType);
|
|
return p;
|
|
}
|
|
|
|
init_result = p->initCheck();
|
|
if (init_result == NO_ERROR) {
|
|
p->setNotifyCallback(client, notifyFunc);
|
|
} else {
|
|
ALOGE("Failed to create player object of type %d, initCheck failed"
|
|
" (res = %d)", playerType, init_result);
|
|
p.clear();
|
|
}
|
|
|
|
return p;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* *
|
|
* Built-In Factory Implementations *
|
|
* *
|
|
*****************************************************************************/
|
|
|
|
class NuPlayerFactory : public MediaPlayerFactory::IFactory {
|
|
public:
|
|
virtual float scoreFactory(const sp<IMediaPlayer>& /*client*/,
|
|
const char* url,
|
|
float curScore) {
|
|
static const float kOurScore = 0.8;
|
|
|
|
if (kOurScore <= curScore)
|
|
return 0.0;
|
|
|
|
if (!strncasecmp("http://", url, 7)
|
|
|| !strncasecmp("https://", url, 8)
|
|
|| !strncasecmp("file://", url, 7)) {
|
|
size_t len = strlen(url);
|
|
if (len >= 5 && !strcasecmp(".m3u8", &url[len - 5])) {
|
|
return kOurScore;
|
|
}
|
|
|
|
if (strstr(url,"m3u8")) {
|
|
return kOurScore;
|
|
}
|
|
|
|
if ((len >= 4 && !strcasecmp(".sdp", &url[len - 4])) || strstr(url, ".sdp?")) {
|
|
return kOurScore;
|
|
}
|
|
}
|
|
|
|
if (!strncasecmp("rtsp://", url, 7)) {
|
|
return kOurScore;
|
|
}
|
|
|
|
return 0.0;
|
|
}
|
|
|
|
virtual float scoreFactory(const sp<IMediaPlayer>& /*client*/,
|
|
const sp<IStreamSource>& /*source*/,
|
|
float /*curScore*/) {
|
|
return 1.0;
|
|
}
|
|
|
|
virtual float scoreFactory(const sp<IMediaPlayer>& /*client*/,
|
|
const sp<DataSource>& /*source*/,
|
|
float /*curScore*/) {
|
|
// Only NuPlayer supports setting a DataSource source directly.
|
|
return 1.0;
|
|
}
|
|
|
|
virtual sp<MediaPlayerBase> createPlayer(pid_t pid) {
|
|
ALOGV(" create NuPlayer");
|
|
return new NuPlayerDriver(pid);
|
|
}
|
|
};
|
|
|
|
class TestPlayerFactory : public MediaPlayerFactory::IFactory {
|
|
public:
|
|
virtual float scoreFactory(const sp<IMediaPlayer>& /*client*/,
|
|
const char* url,
|
|
float /*curScore*/) {
|
|
if (TestPlayerStub::canBeUsed(url)) {
|
|
return 1.0;
|
|
}
|
|
|
|
return 0.0;
|
|
}
|
|
|
|
virtual sp<MediaPlayerBase> createPlayer(pid_t /* pid */) {
|
|
ALOGV("Create Test Player stub");
|
|
return new TestPlayerStub();
|
|
}
|
|
};
|
|
|
|
class AwPlayerFactory : public MediaPlayerFactory::IFactory {
|
|
public:
|
|
virtual float scoreFactory(const sp<IMediaPlayer>& /*client*/,
|
|
int /*fd*/,
|
|
int64_t /*offset*/,
|
|
int64_t /*length*/,
|
|
float /*curScore*/) {
|
|
|
|
return 0.0;
|
|
}
|
|
|
|
virtual sp<MediaPlayerBase> createPlayer(pid_t /* pid */) {
|
|
ALOGV(" create CmccPlayer");
|
|
return new AwPlayer();
|
|
}
|
|
};
|
|
|
|
void MediaPlayerFactory::registerBuiltinFactories() {
|
|
Mutex::Autolock lock_(&sLock);
|
|
|
|
if (sInitComplete)
|
|
return;
|
|
registerFactory_l(new AwPlayerFactory(), AW_PLAYER);
|
|
|
|
IFactory* factory = new NuPlayerFactory();
|
|
if (registerFactory_l(factory, NU_PLAYER) != OK)
|
|
delete factory;
|
|
factory = new TestPlayerFactory();
|
|
if (registerFactory_l(factory, TEST_PLAYER) != OK)
|
|
delete factory;
|
|
|
|
sInitComplete = true;
|
|
}
|
|
|
|
} // namespace android
|