蛇怕雄黄吗:OpenCore代码分析
来源:百度文库 编辑:九乡新闻网 时间:2024/05/04 07:06:35
上次静下心来看opencore大概是半年前,看的云里雾里的,最近为了一个在线的视频调试再次看看opencore,感觉明朗了很多,下面的东西虽然大部分都是代码,但是代码中的注释和对整个opencore框架的解释我觉得都是合情合理的,有兴趣的人可以参考一下。
Opencore小结
在android框架层,他认识的就是MediaPlayerInterface。对于一个能够播放音乐的东西,我们看google是怎么抽象的。
首先是音频设备的抽象:
// AudioSink: abstraction layer for audio output
class AudioSink : public RefBase
{
public:
typedef void (*AudioCallback)(AudioSink *audioSink, void *buffer, size_t size, void *cookie);
virtual ~AudioSink() {} //析构函数一般为虚函数
virtual bool ready() const = 0; // audio output is open and ready
virtual bool realtime() const = 0; // audio output is real-time output
virtual ssize_t bufferSize() const = 0;
virtual ssize_t frameCount() const = 0;
virtual ssize_t channelCount() const = 0;
virtual ssize_t frameSize() const = 0;
virtual uint32_t latency() const = 0;
virtual float msecsPerFrame() const = 0;
// If no callback is specified, use the "write" API below to submit
// audio data. Otherwise return a full buffer of audio data on each
// callback. 播放音乐的两种方式,一个是write输出
//一个是通过callback,当底层觉得音频不够的时候就会调用这个callback
//这个时候,callback返回一个缓存给下面就行了
//打开一个设备
virtual status_t open(uint32_t sampleRate, int channelCount,int format=AudioSystem::PCM_16_BIT,int bufferCount=DEFAULT_AUDIOSINK_BUFFERCOUNT,AudioCallback cb = NULL,void *cookie = NULL) = 0;
//开始播放
virtual void start() = 0;
//写入数据
virtual ssize_t write(const void* buffer, size_t size) = 0;
//停止
virtual void stop() = 0;
//释放掉缓存
virtual void flush() = 0;
//暂停
virtual void pause() = 0;
//关闭
virtual void close() = 0;};
然后是播放一个文件的接口
MediaPlayerBase() : mCookie(0), mNotify(0) {}
virtual ~MediaPlayerBase() {}
virtual status_t initCheck() = 0; //初始化检查
virtual bool hardwareOutput() = 0; //是否直接音频硬件输出 或者使用audioFlinger
virtual status_t setDataSource(const char *url) = 0; //通过字符串设置播放源
virtual status_t setDataSource(int fd, int64_t offset, int64_t length) = 0;//通过文件句柄和偏移量来设置
virtual status_t setVideoSurface(const sp& surface) = 0;//设置我们视频输出的surfacevirtual status_t prepare() = 0;
//开始准备
virtual status_t prepareAsync() = 0; //异步准备,这个准备是可以直接返回,准备好了通过消息机制回调上层,这个接口是为了兼容那些网络媒体文件,准备时间比较长
virtual status_t start() = 0; //开始播放
virtual status_t stop() = 0;//停止播放
virtual status_t pause() = 0;//暂停
virtual bool isPlaying() = 0; //是否在播放
virtual status_t seekTo(int msec) = 0;//跳转
virtual status_t getCurrentPosition(int *msec) = 0;//获得当期位置
virtual status_t getDuration(int *msec) = 0;//获得总长度
virtual status_t reset() = 0;//重置
virtual status_t setLooping(int loop) = 0; //设置循环
virtual player_type playerType() = 0; //播放类型 应该是2.1新加函数
virtual void setNotifyCallback(void* cookie, notify_callback_f notifyFunc) {mCookie = cookie; mNotify = notifyFunc; }//回调函数
// Invoke a generic method on the player by using opaque parcels
// for the request and reply. 通过不透明的包裹调用播放器的通用方法//
// @param request Parcel that is positioned at the start of the
// data sent by the java layer.
//来自java层的数据
// @param[out] reply Parcel to hold the reply data. Cannot be null.
// @return OK if the call was successful.
virtual status_t invoke(const Parcel& request, Parcel *reply) = 0;
// The Client in the MetadataPlayerService calls this method on
// the native player to retrieve all or a subset of metadata.//
// @param ids SortedList of metadata ID to be fetch. If empty, all
// the known metadata should be returned.
// @param[inout] records Parcel where the player appends its metadata.
// @return OK if the call was successful.
virtual status_t getMetadata(constmedia::Metadata::Filter& ids, Parcel *records) { returnINVALID_OPERATION; };
protected:
//这个是一个保护的函数,只能子类和自己可以使用
virtual voidsendEvent(int msg, int ext1=0, int ext2=0) { if (mNotify)mNotify(mCookie, msg, ext1, ext2); }
void* mCookie; notify_callback_fmNotify; }
看完了android对其的封装,我们看看opencore是如何实现的。
首先简单的说明几个概念,opencore下面会有很多的编解码方式,文件的播放流程类似于DShow的流水线,读取 解码显示等几个不同的模块,每一个模块都称为node,播放之前,有一个文件类型识别的子模块,识别之后才能搭建整个流水线。
在opencore下面有这样的一个类:
class PVPlayer : public MediaPlayerInterface
{
public:
PVPlayer();
virtual ~PVPlayer();
virtual status_t initCheck();
virtual status_t setDataSource(const char *url);
virtual status_tsetDataSource(int fd, int64_t offset, int64_t length);
virtual status_tsetVideoSurface(const sp& surface);
virtual status_t prepare();
virtual status_t prepareAsync();
virtual status_t start();
virtualstatus_t stop();
virtual status_t pause();
virtual bool isPlaying();
virtual status_t seekTo(int msec);
virtual status_tgetCurrentPosition(int *msec);
virtual status_t getDuration(int *msec);
virtual status_t reset();
virtual status_t setLooping(int loop);
virtual player_type playerType() { return PV_PLAYER; }
virtual status_tinvoke(const Parcel& request, Parcel *reply);
virtual status_tgetMetadata( const SortedVector& ids, Parcel *records); // makeavailable to PlayerDriver
void sendEvent(int msg, int ext1=0, intext2=0) { MediaPlayerBase::sendEvent(msg, ext1, ext2); }
//上面的函数都不用看,我们主要看看它有这样的 几个私有的静态函数
//静态函数在类中,一般都是作为线程函数的,或者做一些与本身关系不大的操作。
private:
static voiddo_nothing(status_t s, void *cookie, bool cancelled) { } //真的什么都没有做
static void run_init(status_t s, void *cookie, bool cancelled);
staticvoid run_set_video_surface(status_t s, void *cookie, bool cancelled);
static void run_set_audio_output(status_t s, void *cookie, boolcancelled);
static void run_prepare(status_t s, void *cookie, boolcancelled);
static void check_for_live_streaming(status_t s, void*cookie, bool cancelled);
//注意下面的几个东东 PlayerDriver* mPlayerDriver;
//这个是一个中间机制很重要,维护着一个消息队列,后面会讲述 char * mDataSourcePath;
//记录着播放的源 boolmIsDataSourceSet; //这个不是很清楚 下面见到再说 sp mSurface;
//一般用opencore基本上都会有视频
int mSharedFd;
//文件句柄
status_t mInit;
//状态
int mDuration;
//长度
#ifdefMAX_OPENCORE_INSTANCES
static volatile int32_t sNumInstances;
//同时可以有几个实例存在
#endif };
//这里cookie是一个PVPlayer的指针,通过PVPlayer得到PlayerDriver的指针,然后往这个消息队列中放松一个消息。
注意我们发送的是一个PlayerInit的消息,这个消息析构的时候,传递了两个变量,后面的cookie是PVPlayer的指针,前面的是一个函数指针,当处理完这个消息的时候,如果发现这个回调的函数指针还存在,就会继续调用这个函数。
voidPVPlayer::run_init(status_t s, void *cookie, bool cancelled)
{
LOGV("run_init s=%d, cancelled=%d", s, cancelled);
if (s == NO_ERROR&& !cancelled)
{
PVPlayer *p = (PVPlayer*)cookie;
p->mPlayerDriver->enqueueCommand(newPlayerInit(run_set_video_surface, cookie));
}
}
我们接着看run_set_video_surface,当我们的消息队列处理了PlayerInit这个消息,然后就会跳入这个函数。注意这个处理过程都是在另外一个线程,往消息队列添加的线程和处理消息的线程一般是不一样的线程。
voidPVPlayer::run_set_video_surface(status_t s, void *cookie, boolcancelled)
{
LOGV("run_set_video_surface s=%d, cancelled=%d", s,cancelled);
if (s == NO_ERROR && !cancelled)
{
// If we don'thave a video surface, just skip to the next step.
PVPlayer *p =(PVPlayer*)cookie;
if (p->mSurface == NULL)
{
//如果只有音频,注意这里在前面的初始化的时候,我们就应该知道这个视频的具体的信息了,是否有音频或者视频。
run_set_audio_output(s, cookie, false);
} else {
//如果有视频就会先初始化视频然后跳转到音频处理,注意这里的每一个函数都不是实际的处理地方,只是发送一条消息。
p->mPlayerDriver->enqueueCommand(new PlayerSetVideoSurface(p->mSurface,run_set_audio_output, cookie));
}
}
}
可以看到这个函数,往自身队列中又加入了一个消息PlayerSetVideoSurface,这个消息是如何处理的我们后面再看如何初始化音频。
void PVPlayer::run_set_audio_output(status_t s, void *cookie, boolcancelled)
{
LOGV("run_set_audio_output s=%d, cancelled=%d", s,cancelled);
if (s == NO_ERROR && !cancelled)
{
PVPlayer *p =(PVPlayer*)cookie;
p->mPlayerDriver->enqueueCommand(newPlayerSetAudioSink(p->mAudioSink, run_prepare, cookie));
}
}
等音频设备也初始化之后,我们跳进run_prepare,表示视频播放链路已经搭建成功,下面你可以选择开始播放或者暂停了。
voidPVPlayer::run_prepare(status_t s, void *cookie, bool cancelled)
{
LOGV("run_prepare s=%d, cancelled=%d", s, cancelled);
if (s == NO_ERROR&& !cancelled)
{
PVPlayer *p = (PVPlayer*)cookie;
p->mPlayerDriver->enqueueCommand(newPlayerPrepare(check_for_live_streaming, cookie));
}
}//如果是流媒体文件,这里我们还要有多余的处理,这些处理都是发送消息到队列。
voidPVPlayer::check_for_live_streaming(status_t s, void *cookie, boolcancelled)
{
LOGV("check_for_live_streaming s=%d, cancelled=%d", s,cancelled);
if (s == NO_ERROR && !cancelled)
{
PVPlayer *p =(PVPlayer*)cookie;
if ( (p->mPlayerDriver->getFormatType() ==PVMF_MIME_DATA_SOURCE_RTSP_URL) ||(p->mPlayerDriver->getFormatType() ==PVMF_MIME_DATA_SOURCE_MS_HTTP_STREAMING_URL) )
{
p->mPlayerDriver->enqueueCommand(new PlayerCheckLiveStreaming(do_nothing, NULL));
}
}
}
这个完成之后就是donothing,表示我们的一条处理链路完成。
我们发现其实这个run_init就完成文件识别和链路构架,音视频输出设备的基本工作,我们要看的重点就在这几个消息的处理。那么这个run_init上面是怎么调用下来的呢?上层的调用肯定是通过基类的函数,因为android只认识基类。找了半天可以看到:
status_tPVPlayer::prepareAsync() {
LOGV("prepareAsync");
status_t ret = OK;
//如果发现还没有初始化视频源,首先初始化视频源,当你都不知道要播放什么后面的工作基本上是无效的。一般我们的Run_init是紧接这设置PlayerSetDataSource这个消息后面的。如果发现,视频源已经被设置,那么我们也知道我们的init功能基本已经被调用过。
if(!mIsDataSourceSet)
{
// If data source has NOT been set.
// Set ourdata source as cached in setDataSource() above.
LOGV(" data source =%s", mDataSourcePath);
ret = mPlayerDriver->enqueueCommand(newPlayerSetDataSource(mDataSourcePath,run_init,this));
mIsDataSourceSet =true;
} else {
// If data source has been already set.
// No need torun a sequence of commands.
// The only code needed to run isPLAYER_PREPARE.
//这个时候只用让视频开始准备,然后check是否为流媒体
ret =mPlayerDriver->enqueueCommand(newPlayerPrepare(check_for_live_streaming, this));
}
return ret;
}
初始化音频设备和视频设备只能调用一次,后面的prepare 和 live_stream可以多次的调用。
好了这个类几个私有函数基本说完,我们回到正轨,上层的调用。
首先看构造函数:
PVPlayer::PVPlayer() {
LOGV("PVPlayer constructor");
mDataSourcePath = NULL;
mSharedFd = -1;
mIsDataSourceSet = false;
mDuration = -1;
mPlayerDriver = NULL;//连这个都是空的 消息队列线程都不存在
#ifdef MAX_OPENCORE_INSTANCES//如果有最大实例的限制,那么在构造的时候就会有一个判断,然后将mInit 设置为繁忙
if(android_atomic_inc(&sNumInstances) >= MAX_OPENCORE_INSTANCES) {
LOGW("Exceeds maximum number of OpenCore instances");
mInit = -EBUSY;return;
}
#endif
//然后就是一些初始化的工作
LOGV("construct PlayerDriver");
//Driver的初始化,实际上就是构建了一条消息队列
mPlayerDriver = new PlayerDriver(this);
LOGV("send PLAYER_SETUP");
//往这个消息队列发送的第一个消息就是setup
PlayerSetup* setup= new PlayerSetup(0,0);
mInit =mPlayerDriver->enqueueCommand(setup);
if (mInit == NO_INIT) { deletesetup; }
}
//Setup之后调用这个函数 看是否初始化成功 这个函数在opencore中不重要
//只是为了照顾借口的完整性
status_t PVPlayer::initCheck() { return mInit; }
下面先看看析构,当我们的播放结束之后,是如何析构的,
PVPlayer::~PVPlayer() {
LOGV("PVPlayerdestructor");
if (mPlayerDriver != NULL) {
PlayerQuit quit =PlayerQuit(0,0);
mPlayerDriver->enqueueCommand(&quit);
// willwait on mSyncSem, signaled by player thread
//这个消息会产生等待结束工作完成。
}
free(mDataSourcePath);
if (mSharedFd >= 0) { close(mSharedFd); }
#ifdef MAX_OPENCORE_INSTANCES
android_atomic_dec(&sNumInstances);
#endif
}
然后就是setdatabase,这两个函数都是比较简单的,只是简单的赋值。
status_tPVPlayer::setDataSource(const char *url) {
LOGV("setDataSource(%s)", url);
if (mSharedFd >= 0) {close(mSharedFd); mSharedFd = -1; }
free(mDataSourcePath);
mDataSourcePath = NULL;
// Don't let somebody trick us in to readingsome random block of memory
if (strncmp("sharedfd://", url, 11) == 0)
return android::UNKNOWN_ERROR;
mDataSourcePath = strdup(url);
returnOK;
}
status_t PVPlayer::setDataSource(int fd, int64_t offset, int64_tlength) {
// This is all a big hack to allow PV to play from a filedescriptor.
// Eventually we'll fix PV to use a file descriptordirectly instead
// of using mmap().
LOGV("setDataSource(%d, %lld,%lld)", fd, offset, length);
if (mSharedFd >= 0) { close(mSharedFd);mSharedFd = -1; }
free(mDataSourcePath);
mDataSourcePath = NULL;
charbuf[80];
mSharedFd = dup(fd);
sprintf(buf, "sharedfd://%d:%lld:%lld",mSharedFd, offset, length);
mDataSourcePath = strdup(buf);
return OK;
}
status_t PVPlayer::setVideoSurface(const sp& surface) {
LOGV("setVideoSurface(%p)", surface.get()); mSurface = surface;
returnOK;
}
有人会问为什么没有设置音频,因为音频对于开发者来说过于简单,一个设备就一个喇叭,音频只能往那里输出,但是对于这么大的一块屏幕,视频是怎么输出,甚至是输出到overlay上面,所以非常有必要有上面的一个函数。
下面是一个较为重要的函数:
status_tPVPlayer::prepare() {
status_t ret;
// We need to differentiate the twovalid use cases for prepare():
// 1. newPVPlayer/reset()->setDataSource()->prepare()
// 2. newPVPlayer/reset()->setDataSource()->prepare()/prepareAsync()
//->start()->...->stop()->prepare()
// If data source hasalready been set previously, no need to run
// a sequence of commandsand only the PLAYER_PREPARE code needs
// to be run.
//首先我们的视频源有没有被初始化过,如果没有这个时候就要初始化,注意这个时候Cmmand的callback参数为空,表示这个调用是阻塞的。因为我们的prepare本身就是阻塞的。 if (!mIsDataSourceSet) {
// set data source
LOGV("prepare");
LOGV(" data source = %s", mDataSourcePath);
ret =mPlayerDriver->enqueueCommand(newPlayerSetDataSource(mDataSourcePath,0,0));
//这里等到消息处理之后再返回 阻塞
if (ret !=OK)
return ret;
// init
LOGV(" init");
ret =mPlayerDriver->enqueueCommand(new PlayerInit(0,0));
//同样 有一个阻塞调用
if(ret != OK)
return ret;
// set video surface, if there is one
if(mSurface != NULL) {
LOGV(" set video surface");
//阻塞调用设置视频
ret =mPlayerDriver->enqueueCommand(newPlayerSetVideoSurface(mSurface,0,0));
if (ret != OK) return ret;
}
//set audio output
// If we ever need to expose selectable audio outputsetup, this can be broken
// out. In the meantime, however, systemaudio routing APIs should suffice.
LOGV(" set audio sink");
//阻塞调用设置视频
ret = mPlayerDriver->enqueueCommand(newPlayerSetAudioSink(mAudioSink,0,0));
if (ret != OK)
return ret;
// Newdata source has been set successfully.
mIsDataSourceSet = true;
}
//prepare 最后一个是非阻塞,无论PlayerPrepare有没有处理,我们这个函数都返回
LOGV(" prepare");
return mPlayerDriver->enqueueCommand(newPlayerPrepare(check_for_live_streaming, this));
}
然后使一些播放的控制接口startstop pause,这里这些函数不是很难,简单的省略。
status_t PVPlayer::start() {
LOGV("start");
return mPlayerDriver->enqueueCommand(newPlayerStart(0,0));
}
status_t PVPlayer::stop() {
LOGV("stop");
returnmPlayerDriver->enqueueCommand(new PlayerStop(0,0));
}
status_tPVPlayer::pause() {
LOGV("pause");
returnmPlayerDriver->enqueueCommand(new PlayerPause(0,0));
}
boolPVPlayer::isPlaying() {
int status = 0;
if(mPlayerDriver->enqueueCommand(new PlayerGetStatus(&status,0,0))== NO_ERROR)
{ return (status == PVP_STATE_STARTED); }
return false;
}
status_t PVPlayer::getCurrentPosition(int *msec) {
returnmPlayerDriver->enqueueCommand(new PlayerGetPosition(msec,0,0));
}
status_t PVPlayer::getDuration(int *msec) {
status_t ret =mPlayerDriver->enqueueCommand(new PlayerGetDuration(msec,0,0));
if(ret == NO_ERROR) mDuration = *msec;
return ret;
}
上面的函数全部阻塞。但是这个跳转函数是非阻塞的,因为可能跳转会很耗时
status_t PVPlayer::seekTo(int msec) {
LOGV("seekTo(%d)", msec);
// can't always seek to end of streams - sowe fudge a little
if ((msec == mDuration) && (mDuration >0)) {
msec--;
LOGV("Seek adjusted 1 msec from end");
}
returnmPlayerDriver->enqueueCommand(new PlayerSeek(msec,do_nothing,0));
}
播放器重置: //阻塞的调用
status_t PVPlayer::reset() {
LOGV("reset");
status_t ret= mPlayerDriver->enqueueCommand(new PlayerCancelAllCommands(0,0));
// Log failure from CancelAllCommands() and call Reset() regardless.
if(ret != NO_ERROR) {
LOGE("failed to cancel all exiting PV player enginecommands with error code (%d)", ret);
}
ret =mPlayerDriver->enqueueCommand(new PlayerReset(0,0));
// We shouldnever fail in Reset(), but logs the failure just in case.
if (ret !=NO_ERROR) {
LOGE("failed to reset PV player engine with error code(%d)", ret);
} else {
ret = mPlayerDriver->enqueueCommand(newPlayerRemoveDataSource(0,0));
}
mSurface.clear();
LOGV("unmap file");
if (mSharedFd >= 0) { close(mSharedFd); mSharedFd = -1; }
mIsDataSourceSet = false; return ret;
}
上面我们看到最多就是enqueueCommand这个函数,下面我们来简单的说一下我们的PlayerDriver,这个类是联系PVPlayer和下层的OMX的中间层,主要的作用是降低耦合,上面的控制者(PVPlayer),只需阻塞或者非阻塞的往这个中间层发送命令,下面具体怎么做我一概不知,google构架就是牛x。这个类实际上就是一个消息队列。这个类后面再说。