There are 2 major tasks to plug our Media player into Android architecture, Mediaplayer service creates appropriate instance of a media player based on the data source, in our case the extension “avi”. Since avi is not supported, I choose to add this as myPlayer support format.
1. We need to extend MediaPlayerInterface, defined in media/MediaPlayerInterface.h.
2. Insert our player instance at the appropriate place in MediaPlayerService.cpp
Below is the interface myPlayer I derived from MediaPlayerInterface. I have used Vorbis implementation as base to get this up and running in a day ( well .. spent a week on thinking how to..). I have added my understanding as comments in the code below
class myPlayer : public MediaPlayerInterface {
public:
myPlayer();
virtual ~myPlayer();
virtual void onFirstRef();
virtual status_t initCheck(); //return player status
virtual status_t setAudioStreamType(int type);
virtual status_t setDataSource(const char *url);
virtual status_t setDataSource(int fd, int64_t offset, int64_t length);
virtual status_t setVideoSurface(const sp
virtual status_t prepare();
virtual status_t prepareAsync();
virtual status_t start();
virtual status_t stop();
virtual status_t pause();
virtual bool isPlaying();
virtual status_t getVideoWidth(int *w);
virtual status_t getVideoHeight(int *h);
virtual status_t seekTo(int msec);
virtual status_t getCurrentPosition(int *msec);
virtual status_t getDuration(int *msec);
virtual status_t reset();
virtual status_t setLooping(int loop);
virtual player_type playerType() { return MY_PLAYER; }
//myPlayer methods //---------------------------------------------------------------------------------------
static int renderThread(void*);
int render();
//-----------------------------------------------------------------------------------------------------------
private:
bool mIsDataSourceSet;
sp
status_t mStatus;
int mDuration;
int mStreamType;
Mutex mMutex;
Condition mCondition;
pid_t mRenderTid;
volatile bool mExit;
bool mPaused;
volatile bool mRender;
//myPlayer stuff for rendering //------------------------------------------------------------------------------------------//
sp
int mHorstride;
int mVerstride;
int mWidth;
int mHeight;
int mFrameoffset[2];
int mBufindex;
short *mFramebuf;
PixelFormat mPixformat;
//myPlayer stuff //------------------------------------------------------------------------------------------//
}; //class myPlayer
In the MediaPlayerService.cpp add .avi into
extmap
{".mid", SONIVOX_PLAYER},
{".midi", SONIVOX_PLAYER},
{".smf", SONIVOX_PLAYER},
{".xmf", SONIVOX_PLAYER},
{".imy", SONIVOX_PLAYER},
{".rtttl", SONIVOX_PLAYER},
{".rtx", SONIVOX_PLAYER},
{".ota", SONIVOX_PLAYER},
{".ogg", VORBIS_PLAYER},
{".oga", VORBIS_PLAYER},
{".avi", MY_PLAYER},
};
Now add myPlayer instance in createPlayer. We are done. Not to forget to add
static sp
.
.
.
case MY_PLAYER:
LOGE(" create myPlayer");
p = new myPlayer();
break;
.
.
.
}
Now, lets get back to myPlayer.cpp implementation. Lets pay attention only to the following methods, others are just dummy returns No_ERROR. mStatus plays key role here as you can see it changes from method to method.
myPlayer, constructor initializes class variables, here esp, I have set screen size to 320X430. In reality this has to handled at runtime.
myPlayer();
Destructor clears up Surface buffers and frees up thread
virtual ~myPlayer();
Create the render thread and wait for signal, the signal is sent out from start() method.
virtual void onFirstRef();
InitCheck does nothing just pass the mStatus
virtual status_t initCheck();
Set the file URL, here we do nothing but this is the place one can open the file and check it’s valid, do parsing and return OK
virtual status_t setDataSource(const char *url);
virtual status_t setDataSource(int fd, int64_t offset, int64_t length);
Important method, we store the Surface handle and use it for rendering. Also, register our buffers
virtual status_t setVideoSurface(const sp
Called from Java application class MediaPlayer, may be we can prepare CODECs here or initialize e double buffering etc. Now does nothing
virtual status_t prepare();
virtual status_t prepareAsync();
Called from Java application class MediaPlayer, this is where we send single() to render thread to start
virtual status_t start();
Rendering is done through double buffer,
//myPlayer methods //---------------------------------------------------------------------------------------
static int renderThread(void*);
int render();
//-----------------------------------------------------------------------------------------------------------
#include "myPlayer.h"
#ifdef HAVE_GETTID
static pid_t myTid() { return gettid(); }
#else
static pid_t myTid() { return getpid(); }
#endif
/* max frame buffers */
#define
#define WIDTH 320
#define HEIGHT 430
namespace android {
static status_t ERROR_NOT_OPEN = -1;
static status_t ERROR_OPEN_FAILED = -2;
static status_t ERROR_ALLOCATE_FAILED = -4;
static status_t ERROR_NOT_SUPPORTED = -8;
static status_t ERROR_NOT_READY = -16;
static status_t STATE_
static status_t STATE_ERROR = 1;
static status_t STATE_OPEN = 2;
// ----------------------------------------------------------------------------------------------------------------
myPlayer::myPlayer(){
LOGI ("myPlayer Constructor");
mStatus = STATE_ERROR;
mDuration = -1;
mSurface = NULL;
mRenderTid = 0;
mExit = false;
mPaused = false;
mRender = false;
//----------------------------------------------------
//myPlayer stuff initialize
mWidth = (WIDTH + 1) & -2;;
mHeight = (HEIGHT + 1) & -2;;
mHorstride = mWidth;
mVerstride = mHeight;
int frameSize = mWidth*mHeight*2;
/* create frame buffer heap base */
mFrameheap = new MemoryHeapBase(frameSize *
if (mFrameheap->heapID() < 0) {
LOGI("Error creating frame buffer heap!");
}
for ( int i = 0; i<
mFrameoffset[i] = i*frameSize;
}
mBufindex = 0;
mFramebuf = new short[mWidth*mHeight];
mPixformat = PIXEL_FORMAT_
}
// ----------------------------------------------------------------------------------------------------------------
void myPlayer::onFirstRef()
{
LOGV("onFirstRef");
// create playback thread
Mutex::Autolock l(mMutex);
createThreadEtc(renderThread, this, "vorbis decoder");
mCondition.wait(mMutex);
if (mRenderTid > 0) {
LOGV("render thread(%d) started", mRenderTid);
mStatus = STATE_
}
}
// ----------------------------------------------------------------------------------------------------------------
status_t myPlayer::initCheck(){
LOGI ("mStatus = %s\n", (mStatus==NO_ERROR)?"NO_ERROR":"ERROR");
if (mStatus != STATE_ERROR) return NO_ERROR;
return ERROR_NOT_READY;
}
// ----------------------------------------------------------------------------------------------------------------
myPlayer::~myPlayer(){
LOGI ("myPlayer destruct");
mExit=true;
mRender=false;
if (mSurface != NULL){
mSurface->unregisterBuffers();
mSurface.clear();
}
if(mFramebuf)
free(mFramebuf);
mFrameheap.clear();
mFramebuf = NULL;
}
// ----------------------------------------------------------------------------------------------------------------
status_t myPlayer::setDataSource(const char *url){
//now nothing done, here you may add file stream code and paring the file
LOGI("setDataSource: url = %s\n", url);
if (url) {
mStatus = STATE_OPEN;
mExit=false;
}
else {
mStatus = ERROR_NOT_OPEN;
}
return NO_ERROR;
}
// ----------------------------------------------------------------------------------------------------------------
status_t myPlayer::setDataSource(int fd, int64_t offset, int64_t length){
LOGI("fd: %i, offset: %ld, len: %ld\n", fd, (long)offset, (long)length);
return NO_ERROR;
}
// ----------------------------------------------------------------------------------------------------------------
status_t myPlayer::setVideoSurface(const sp
LOGI("ISurface: %p\n", surface.get());
mSurface = surface;
/* create frame buffer heap and register with surfaceflinger */
ISurface::BufferHeap buffers( mWidth, mHeight, mHorstride, mVerstride, mPixformat, mFrameheap);
if (mSurface->registerBuffers(buffers) < 0 ){
LOGI("Cannot register frame buffer!");
mFrameheap.clear();
return STATE_ERROR;
}
mRender = true;
return NO_ERROR;
}
// ----------------------------------------------------------------------------------------------------------------
status_t myPlayer::prepare(){
LOGI("prepare");
mStatus = STATE_OPEN;
mRender = false;
return NO_ERROR;
}
// ----------------------------------------------------------------------------------------------------------------
status_t myPlayer::prepareAsync(){
LOGI("prepareAsync\n");
if (mStatus != STATE_OPEN ) {
sendEvent(MEDIA_ERROR);
return NO_ERROR;
}
sendEvent(MEDIA_PREPARED);
return NO_ERROR;
}
// ----------------------------------------------------------------------------------------------------------------
status_t myPlayer::start(){
LOGI("start");
Mutex::Autolock l(mMutex);
if (mStatus != STATE_OPEN) {
return ERROR_NOT_OPEN;
}
mRender = true;
mCondition.signal();
return NO_ERROR;
}
// ----------------------------------------------------------------------------------------------------------------
int myPlayer::renderThread(void* p) {
LOGI("renderThread");
return ((myPlayer*)p)->render();
}
// ----------------------------------------------------------------------------------------------------------------
int myPlayer::render(){
LOGI("render Enter");
{
Mutex::Autolock l(mMutex);
mRenderTid = myTid();
mCondition.signal();
}
int frame = 0;
// nothing to render, wait for client thread to wake us up
while (1) {
if (!mExit && !mRender) {
Mutex::Autolock l(mMutex);
LOGI("render - signal wait\n");
mCondition.wait(mMutex);
LOGI("render - signal rx'd\n");
}
/// ------// myPlayer stuff -- replace with decoders //--------//
if (++mBufindex ==
mBufindex = 0;
for (int i=0; i< mWidth*mHeight; i++){
mFramebuf[i] = (short)(i+frame*16) ;
}
memcpy (static_cast<unsigned char *>(mFrameheap->base()) + mFrameoffset[mBufindex], mFramebuf, mWidth*mHeight*sizeof(short));
mSurface->postBuffer(mFrameoffset[mBufindex]);
if(++frame == 430)
frame=0;
/// ------// myPlayer stuff end-----------------------------------//
if (mExit) break;
}
{
Mutex::Autolock l(mMutex);
mRenderTid = -1;
mCondition.signal();
}
LOGI("render Exit");
return NO_ERROR;
}
// ----------------------------------------------------------------------------------------------------------------
status_t myPlayer::setAudioStreamType(int type){
mStreamType = type;
LOGI ("mStreamType = %d\n", mStreamType);
return NO_ERROR;
}
// ----------------------------------------------------------------------------------------------------------------
status_t myPlayer::stop(){
LOGI("stop");
mRender = false;
return NO_ERROR;
}
// ----------------------------------------------------------------------------------------------------------------
status_t myPlayer::pause(){
LOGI("stop");
mRender = false;
return NO_ERROR;
}
// ----------------------------------------------------------------------------------------------------------------
bool myPlayer::isPlaying(){
LOGI("isPlaying");
return mRender;
}
// ----------------------------------------------------------------------------------------------------------------
status_t myPlayer::getVideoWidth(int *w){
LOGI("getVideoWidth");
*w = 320;
return NO_ERROR;
}
// ----------------------------------------------------------------------------------------------------------------
status_t myPlayer::getVideoHeight(int *h){
LOGI("getVideoHeight");
*h = 430;
return NO_ERROR;
}
// ----------------------------------------------------------------------------------------------------------------
status_t myPlayer::getCurrentPosition(int *msec){
LOGI("getCurrentPosition");
return NO_ERROR;
}
// ----------------------------------------------------------------------------------------------------------------
status_t myPlayer::getDuration(int *msec){
LOGI("getDuration");
return NO_ERROR;
}
// ----------------------------------------------------------------------------------------------------------------
status_t myPlayer::seekTo(int msec){
LOGI("seekTo");
return NO_ERROR;
}
// ----------------------------------------------------------------------------------------------------------------
status_t myPlayer::reset(){
LOGI("reset");
return NO_ERROR;
}
// ----------------------------------------------------------------------------------------------------------------
status_t myPlayer::setLooping(int loop){
LOGI("setLooping");
return NO_ERROR;
}
}; // namespace android
This class can be used as template for new player development. I created a project under ./external folder and linked this as libmyplayer to libmediaservice library. I used the ApiDemos/medial player to test this myPlayer engine
4 comments:
Thanks a lot... the information is really useful. I am new to Android NDK, can you please mail me the source code / project folder.My email is "jay.chawda at gmail.com". Thanks
wow... kudos
Great Article… I love to read your articles because your writing style is too good, its is very very helpful for all of us and I never get bored while reading your article because, they are becomes a more and more interesting from the starting lines until the end.
rpa training in bangalore
best rpa training in bangalore
RPA training in bangalore
RPA courses in bangalore
Post a Comment