diff options
author | Nicolas Catania <niko@google.com> | 2009-07-13 14:37:49 -0700 |
---|---|---|
committer | Nicolas Catania <niko@google.com> | 2009-07-16 11:22:31 -0700 |
commit | 8f5fcab05f1d6f644a9c30f012b8ff302f24a118 (patch) | |
tree | 55cd20372765a9f652b41eda82cef1f24063bbfb /media | |
parent | 358efe418eae266734a16e9ae8f26043e9c64f05 (diff) | |
download | frameworks_base-8f5fcab05f1d6f644a9c30f012b8ff302f24a118.zip frameworks_base-8f5fcab05f1d6f644a9c30f012b8ff302f24a118.tar.gz frameworks_base-8f5fcab05f1d6f644a9c30f012b8ff302f24a118.tar.bz2 |
New test player stub to load mock native players.
Added a new class TestPlayerStub that takes a magic url in the setDataSource call.
Based on the value of the url, the stub is going to load a DL and create the concrete
player used during the test.
After these initialization steps TestPlayerStub is just a wrapper.
Added a new functional test MediaPlayerInvokeTest to demonstrate how a new
mock player to test the invoke method can be loaded.
Added a new mock player for the invoke test: invoke_mock_media_player.cpp.
Diffstat (limited to 'media')
9 files changed, 566 insertions, 5 deletions
diff --git a/media/libmediaplayerservice/Android.mk b/media/libmediaplayerservice/Android.mk index 6978b3d..f74ef3a 100644 --- a/media/libmediaplayerservice/Android.mk +++ b/media/libmediaplayerservice/Android.mk @@ -10,6 +10,7 @@ LOCAL_SRC_FILES:= \ MediaRecorderClient.cpp \ MediaPlayerService.cpp \ MetadataRetrieverClient.cpp \ + TestPlayerStub.cpp \ VorbisPlayer.cpp \ MidiFile.cpp @@ -28,6 +29,10 @@ LOCAL_SHARED_LIBRARIES := \ libmedia \ libandroid_runtime +ifneq ($(TARGET_SIMULATOR),true) +LOCAL_SHARED_LIBRARIES += libdl +endif + LOCAL_C_INCLUDES := external/tremor/Tremor \ $(call include-path-for, graphics corecg) \ $(TOP)/external/opencore/extern_libs_v2/khronos/openmax/include diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp index c575f6c..02327d8 100644 --- a/media/libmediaplayerservice/MediaPlayerService.cpp +++ b/media/libmediaplayerservice/MediaPlayerService.cpp @@ -29,7 +29,7 @@ #include <string.h> #include <cutils/atomic.h> -#include <cutils/properties.h> +#include <cutils/properties.h> // for property_get #include <utils/misc.h> @@ -58,6 +58,8 @@ #include "MidiFile.h" #include "VorbisPlayer.h" #include <media/PVPlayer.h> +#include "TestPlayerStub.h" + #if USE_STAGEFRIGHT #include "StagefrightPlayer.h" #endif @@ -68,6 +70,8 @@ #include <media/IOMX.h> #endif + + /* desktop Linux needs a little help with gettid() */ #if defined(HAVE_GETTID) && !defined(HAVE_ANDROID_OS) #define __KERNEL__ @@ -656,6 +660,10 @@ static player_type getPlayerType(int fd, int64_t offset, int64_t length) static player_type getPlayerType(const char* url) { + if (TestPlayerStub::canBeUsed(url)) { + return TEST_PLAYER; + } + // use MidiFile for MIDI extensions int lenURL = strlen(url); for (int i = 0; i < NELEM(FILE_EXTS); ++i) { @@ -706,6 +714,10 @@ static sp<MediaPlayerBase> createPlayer(player_type playerType, void* cookie, "Should not be here, stagefright player not enabled."); break; #endif + case TEST_PLAYER: + LOGV("Create Test Player stub"); + p = new TestPlayerStub(); + break; } if (p != NULL) { if (p->initCheck() == NO_ERROR) { @@ -770,7 +782,11 @@ status_t MediaPlayerService::Client::setDataSource(const char *url) // now set data source LOGV(" setDataSource"); mStatus = p->setDataSource(url); - if (mStatus == NO_ERROR) mPlayer = p; + if (mStatus == NO_ERROR) { + mPlayer = p; + } else { + LOGE(" error: %d", mStatus); + } return mStatus; } } diff --git a/media/libmediaplayerservice/TestPlayerStub.cpp b/media/libmediaplayerservice/TestPlayerStub.cpp new file mode 100644 index 0000000..8627708 --- /dev/null +++ b/media/libmediaplayerservice/TestPlayerStub.cpp @@ -0,0 +1,196 @@ +/* + * 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) +{ + 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); +} + +// Internal cleanup. +status_t TestPlayerStub::resetInternal() +{ + if(mUrl) { + free(mUrl); + mUrl = NULL; + } + mFilename = NULL; + mContentUrl = NULL; + + if (mPlayer) { + LOG_ASSERT(mDeletePlayer != NULL); + (*mDeletePlayer)(mPlayer); + mPlayer = NULL; + } + + if (mHandle) { + ::dlclose(mHandle); + mHandle = NULL; + } + return OK; +} + +/* static */ bool TestPlayerStub::canBeUsed(const char *url) +{ + return isTestBuild() && isTestUrl(url); +} + +} // namespace android diff --git a/media/libmediaplayerservice/TestPlayerStub.h b/media/libmediaplayerservice/TestPlayerStub.h new file mode 100644 index 0000000..80d53a8 --- /dev/null +++ b/media/libmediaplayerservice/TestPlayerStub.h @@ -0,0 +1,119 @@ +/* + * 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. + */ + +#ifndef ANDROID_FRAMEWORKS_BASE_MEDIA_LIBMEDIAPLAYERSERVICE_TESTPLAYERSTUB_H__ +#define ANDROID_FRAMEWORKS_BASE_MEDIA_LIBMEDIAPLAYERSERVICE_TESTPLAYERSTUB_H__ + +#include <media/MediaPlayerInterface.h> +#include <utils/Errors.h> + +namespace android { +class MediaPlayerBase; // in media/MediaPlayerInterface.h + +// Wrapper around a test media player that gets dynamically loaded. +// +// The URL passed to setDataSource has this format: +// +// test:<name of the .so>?url=<url for the real setDataSource impl.> +// +// e.g: +// test:invoke_test_media_player.so?url=http://youtube.com/ +// test:invoke_test_media_player.so?url=speedtest +// +// TestPlayerStub::setDataSource loads the library in the test url. 2 +// entry points with C linkage are expected. One to create the test +// player and one to destroy it. +// +// extern "C" android::MediaPlayerBase* newPlayer(); +// extern "C" android::status_t deletePlayer(android::MediaPlayerBase *p); +// +// Once the test player has been loaded, its setDataSource +// implementation is called with the value of the 'url' parameter. +// +// typical usage in a java test: +// ============================ +// +// MediaPlayer p = new MediaPlayer(); +// p.setDataSource("test:invoke_mock_media_player.so?url=http://youtube.com"); +// p.prepare(); +// ... +// p.release(); + +class TestPlayerStub : public MediaPlayerInterface { + public: + typedef MediaPlayerBase* (*NEW_PLAYER)(); + typedef status_t (*DELETE_PLAYER)(MediaPlayerBase *); + + TestPlayerStub(); + virtual ~TestPlayerStub(); + + // Called right after the constructor. Check if the current build + // allows test players. + virtual status_t initCheck(); + + // @param url Should be a test url. See class comment. + virtual status_t setDataSource(const char* url); + + // Test player for a file descriptor source is not supported. + virtual status_t setDataSource(int, int64_t, int64_t) { + return INVALID_OPERATION; + } + + + // All the methods below wrap the mPlayer instance. + virtual status_t setVideoSurface(const android::sp<android::ISurface>& s) { + return mPlayer->setVideoSurface(s); + } + virtual status_t prepare() {return mPlayer->prepare();} + virtual status_t prepareAsync() {return mPlayer->prepareAsync();} + virtual status_t start() {return mPlayer->start();} + virtual status_t stop() {return mPlayer->stop();} + virtual status_t pause() {return mPlayer->pause();} + virtual bool isPlaying() {return mPlayer->isPlaying();} + virtual status_t seekTo(int msec) {return mPlayer->seekTo(msec);} + virtual status_t getCurrentPosition(int *p) { + return mPlayer->getCurrentPosition(p); + } + virtual status_t getDuration(int *d) {return mPlayer->getDuration(d);} + virtual status_t reset() {return mPlayer->reset();} + virtual status_t setLooping(int b) {return mPlayer->setLooping(b);} + virtual player_type playerType() {return mPlayer->playerType();} + virtual status_t invoke(const android::Parcel& in, android::Parcel *out) { + return mPlayer->invoke(in, out); + } + + + // @return true if the current build is 'eng' or 'test' and the + // url's scheme is 'test:' + static bool canBeUsed(const char *url); + + private: + // Release the player, dlclose the library. + status_t resetInternal(); + status_t parseUrl(); + + char *mUrl; // test:foo.so?url=http://bar + char *mFilename; // foo.so + char *mContentUrl; // http://bar + void *mHandle; // returned by dlopen + NEW_PLAYER mNewPlayer; + DELETE_PLAYER mDeletePlayer; + MediaPlayerBase *mPlayer; // wrapped player +}; + +} // namespace android + +#endif diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTestRunner.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTestRunner.java index 6edc2cc..2a4e9a0 100755 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTestRunner.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTestRunner.java @@ -23,6 +23,7 @@ import com.android.mediaframeworktest.functional.MediaMimeTest; import com.android.mediaframeworktest.functional.MediaPlayerApiTest; import com.android.mediaframeworktest.functional.MediaRecorderTest; import com.android.mediaframeworktest.functional.SimTonesTest; +import com.android.mediaframeworktest.functional.MediaPlayerInvokeTest; import junit.framework.TestSuite; @@ -32,7 +33,7 @@ import android.test.InstrumentationTestSuite; /** * Instrumentation Test Runner for all MediaPlayer tests. - * + * * Running all tests: * * adb shell am instrument \ @@ -52,6 +53,7 @@ public class MediaFrameworkTestRunner extends InstrumentationTestRunner { suite.addTestSuite(MediaRecorderTest.class); suite.addTestSuite(MediaAudioTrackTest.class); suite.addTestSuite(MediaMimeTest.class); + suite.addTestSuite(MediaPlayerInvokeTest.class); return suite; } @@ -60,5 +62,3 @@ public class MediaFrameworkTestRunner extends InstrumentationTestRunner { return MediaFrameworkTestRunner.class.getClassLoader(); } } - - diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerInvokeTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerInvokeTest.java new file mode 100644 index 0000000..ab8b311 --- /dev/null +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerInvokeTest.java @@ -0,0 +1,70 @@ +/* + * 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. + */ + +package com.android.mediaframeworktest.functional; + +import com.android.mediaframeworktest.MediaFrameworkTest; +import com.android.mediaframeworktest.MediaNames; + +import android.test.ActivityInstrumentationTestCase2; +import android.util.Log; +import android.test.suitebuilder.annotation.LargeTest; +import android.test.suitebuilder.annotation.MediumTest; +import android.test.suitebuilder.annotation.Suppress; + +import android.media.MediaPlayer; +import android.os.Parcel; + +import java.util.Calendar; +import java.util.Random; + +// Tests for the invoke method in the MediaPlayer. +public class MediaPlayerInvokeTest extends ActivityInstrumentationTestCase2<MediaFrameworkTest> { + private static final String TAG = "MediaPlayerInvokeTest"; + private MediaPlayer mPlayer; + private Random rnd; + + public MediaPlayerInvokeTest() { + super("com.android.mediaframeworktest", MediaFrameworkTest.class); + rnd = new Random(Calendar.getInstance().getTimeInMillis()); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + mPlayer = new MediaPlayer(); + } + + @Override + protected void tearDown() throws Exception { + mPlayer.release(); + super.tearDown(); + } + + // Generate a random number, sends it to the ping test player. + @MediumTest + public void testPing() throws Exception { + mPlayer.setDataSource("test:invoke_mock_media_player.so?url=ping"); + + Parcel request = mPlayer.newRequest(); + Parcel reply = Parcel.obtain(); + + int val = rnd.nextInt(); + request.writeInt(val); + assertEquals(0, mPlayer.invoke(request, reply)); + assertEquals(val, reply.readInt()); + } +} diff --git a/media/tests/players/Android.mk b/media/tests/players/Android.mk new file mode 100644 index 0000000..eb50a51 --- /dev/null +++ b/media/tests/players/Android.mk @@ -0,0 +1,29 @@ +# 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. + +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= invoke_mock_media_player.cpp + +LOCAL_SHARED_LIBRARIES:= \ + libbinder \ + libutils + +LOCAL_MODULE:= invoke_mock_media_player +LOCAL_MODULE_TAGS := test eng +LOCAL_PRELINK_MODULE:= false + +include $(BUILD_SHARED_LIBRARY) diff --git a/media/tests/players/README b/media/tests/players/README new file mode 100644 index 0000000..edf9bd6 --- /dev/null +++ b/media/tests/players/README @@ -0,0 +1,8 @@ +Native test players for system tests. + +For functional/system/performance tests, a native test player can be used. +This directory contains the sources of such players. +The class TestPlayerStub uses the dynamic loader to load any of them. + + + diff --git a/media/tests/players/invoke_mock_media_player.cpp b/media/tests/players/invoke_mock_media_player.cpp new file mode 100644 index 0000000..f02298d --- /dev/null +++ b/media/tests/players/invoke_mock_media_player.cpp @@ -0,0 +1,118 @@ +/* + * 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 <string.h> + +#include "binder/Parcel.h" +#include "media/MediaPlayerInterface.h" +#include "utils/Errors.h" + +using android::ISurface; +using android::MediaPlayerBase; +using android::OK; +using android::Parcel; +using android::TEST_PLAYER; +using android::UNKNOWN_ERROR; +using android::player_type; +using android::sp; +using android::status_t; + +// This file contains a test player that is loaded via the +// TestPlayerStub class. The player contains various implementation +// of the invoke method that java tests can use. + +namespace { +const char *kPing = "ping"; + +class Player: public MediaPlayerBase +{ + public: + enum TestType {TEST_UNKNOWN, PING}; + Player() {} + virtual ~Player() {} + + virtual status_t initCheck() {return OK;} + virtual bool hardwareOutput() {return true;} + + virtual status_t setDataSource(const char *url) { + LOGV("setDataSource %s", url); + mTest = TEST_UNKNOWN; + if (strncmp(url, kPing, strlen(kPing)) == 0) { + mTest = PING; + } + return OK; + } + + virtual status_t setDataSource(int fd, int64_t offset, int64_t length) {return OK;} + virtual status_t setVideoSurface(const sp<ISurface>& surface) {return OK;} + virtual status_t prepare() {return OK;} + virtual status_t prepareAsync() {return OK;} + virtual status_t start() {return OK;} + virtual status_t stop() {return OK;} + virtual status_t pause() {return OK;} + virtual bool isPlaying() {return true;} + virtual status_t seekTo(int msec) {return OK;} + virtual status_t getCurrentPosition(int *msec) {return OK;} + virtual status_t getDuration(int *msec) {return OK;} + virtual status_t reset() {return OK;} + virtual status_t setLooping(int loop) {return OK;} + virtual player_type playerType() {return TEST_PLAYER;} + virtual status_t invoke(const Parcel& request, Parcel *reply); + private: + // Take a request, copy it to the reply. + void ping(const Parcel& request, Parcel *reply); + + status_t mStatus; + TestType mTest; +}; + +status_t Player::invoke(const Parcel& request, Parcel *reply) +{ + switch (mTest) { + case PING: + ping(request, reply); + break; + default: mStatus = UNKNOWN_ERROR; + } + return mStatus; +} + +void Player::ping(const Parcel& request, Parcel *reply) +{ + const size_t len = request.dataAvail(); + + reply->setData(static_cast<const uint8_t*>(request.readInplace(len)), len); + mStatus = OK; +} + +} + +extern "C" android::MediaPlayerBase* newPlayer() +{ + LOGD("New invoke test player"); + return new Player(); +} + +extern "C" android::status_t deletePlayer(android::MediaPlayerBase *player) +{ + LOGD("Delete invoke test player"); + delete player; + return OK; +} |