android的多媒体系统
多媒体系统的结构和业务
多媒体系统的宏鸡观结构
应用层,java框架层,c语言层,硬件抽像层,其中输入输出由HAL层,处理环节由packetView的OpenCore实现,
多媒体业备有以下几种:
musicPlayer(音频播放器)
viderPlayer(视频播放器)
Camera(照相机)
soundRecord(录音机)
videoCamera(摄像机)
Mediametadata(媒体元信息)
核心是媒体的播放和录制,分别由下层的OpenCore的PVPlayer和PVAuthor来实现
多媒体的java类:
\frameworks\base\media\java\android\media
Jni部分的代码路径:
\frameworks\base\media\jni最终编译生成libMedia_jni.so
多媒体的本地框架:
\frameworks\base\include\media
\frameworks\base\media\libmedia
最终被子编译成libmedia.so
多媒体的服务部分
库的代码路径:\frameworks\base\media\libmediaplayerservice
最后编译生成:libmediaplayerservice.so
守护进程的代码路径:\frameworks\base\media\mediaserver
多媒体的实现部分:
多媒体的各种业务
多媒体从实现角度看,分为两部分:
输入/输出环节
中间处理环节(文件格式的处理和编解码环节)
例如一个mp3的播放
Mp3格式文件的解析,mp3编码流的解码,pcm输出的播放
媒体播放器涉及内容
本地媒体揪放器部分;
PVPlayer(实现的核心部分)
音频视频的编解码
音频输出环节
视频输出环节(surface或者是overlay)
Android.media.mediaplayer
Android.view.surface
Andorid.widget.videoview
数据流在android媒体播放器中的运行情况是:
上层的java应用程序将媒体的URI设置到媒体播放器中.
Java框架架----->JNI-------->本地框架------->PVPlayer中,
PVPlayer解析后,将媒体分为音频流和视频流
经过编码器的处理和同步(AVSync),转换成原始数据(音频是PCM,视频一般是YUV或者是RGB)
照相机的统结构
录音机的系统结构
本地媒体框架中的媒体记录器部分
PVPlayer
音频编码模块
音频输入环节
android.media.mediaRecorder
soundRecorder
摄像机的系统结构
本地框加的媒体记录器部分
PVAuthor
音频/视频编码模块
音频输入环节
Camera的本地接口
视频输出环节
android.media.MediaRecorder
Android.view.surface
Andoird.widget.videoview
Music包和camera包
多媒体系统的各个层次
libMedia的框架部分
媒体播放器
头文件的目录
\frameworks\base\include\media
主要的头文件有:
Mediaplayer.h媒体播放器本地部分的上层接口
提供了对上层的调用,通过JNI将接口给java调用,其实都是调用下层的mediaplayer
继承自Bnmediaplayerclient.
部分代码如下:
classMediaPlayer:publicBnMediaPlayerClient,
publicvirtualIMediaDeathNotifier
{
public:
MediaPlayer();
~MediaPlayer();
voiddied();
voiddisconnect();
status_tsetDataSource(
constchar*url,//设置数据源url
constKeyedVector<String8,String8>*headers);
status_tsetDataSource(intfd,int64_toffset,int64_tlength);//设置数据源文件
status_tsetVideoSurface(constsp<Surface>&surface);//设视频输出界面
status_tsetListener(constsp<MediaPlayerListener>&listener);//设置临听
status_tprepare();//准备播放
status_tprepareAsync();//异部准备播放
status_tstart();//开始
status_tstop();//停止
status_tpause();//暂停
boolisPlaying();//是否正在播放
status_tgetVideoWidth(int*w);//获取视频播放的宽
status_tgetVideoHeight(int*h);//获取视频播放的高
status_tseekTo(intmsec);//跳转到指定位置
status_tgetCurrentPosition(int*msec);//取得当前的播放位置
status_tgetDuration(int*msec);//播放的持续时间(总时长)
status_treset();//复位
status_tsetAudioStreamType(inttype);//设置音频流的格式
status_tsetLooping(intloop);//设置循环
boolisLooping();//是否循环
status_tsetVolume(floatleftVolume,floatrightVolume);//设置音量
voidnotify(intmsg,intext1,intext2);//通知函数
staticsp<IMemory>decode(constchar*url,uint32_t*pSampleRate,int*pNumChannels,int*pFormat);
staticsp<IMemory>decode(intfd,int64_toffset,int64_tlength,uint32_t*pSampleRate,int*pNumChannels,int*pFormat);
status_tinvoke(constParcel&request,Parcel*reply);
status_tsetMetadataFilter(constParcel&filter);
status_tgetMetadata(boolupdate_only,boolapply_filter,Parcel*metadata);
status_tsuspend();
status_tresume();
status_tsetAudioSessionId(intsessionId);
intgetAudioSessionId();
status_tsetAuxEffectSendLevel(floatlevel);
status_tattachAuxEffect(inteffectId);
private:
voidclear_l();
status_tseekTo_l(intmsec);
status_tprepareAsync_l();
status_tgetDuration_l(int*msec);
status_tsetDataSource(constsp<IMediaPlayer>&player);
sp<IMediaPlayer>mPlayer;
thread_id_tmLockThreadId;
MutexmLock;
MutexmNotifyLock;
ConditionmSignal;
sp<MediaPlayerListener>mListener;
void*mCookie;
media_player_statesmCurrentState;
intmDuration;
intmCurrentPosition;
intmSeekPosition;
boolmPrepareSync;
status_tmPrepareStatus;
intmStreamType;
boolmLoop;
floatmLeftVolume;
floatmRightVolume;
intmVideoWidth;
intmVideoHeight;
intmAudioSessionId;
floatmSendLevel;
};
IMeciaplayer.h媒体播和器服务部分的接口(和上层的mediaplay中的接口方法类似)
被BnMedaplayer继承,提供Binder通信本地实现基础
部分代码如下:
//继承mediaplayerBase,通过autoFlinger输出
classMediaPlayerInterface:publicMediaPlayerBase{
public:
virtual~MediaPlayerInterface(){}
virtualboolhardwareOutput(){returnfalse;}
virtualvoidsetAudioSink(constsp<AudioSink>&audioSink){mAudioSink=audioSink;}
protected:
sp<AudioSink>mAudioSink;//音频轮输出设备的抽像接口
};
//Implementthisclassformediaplayersthatoutputdirectotohardware
//直接从硬功夫件进行音频输出
classMediaPlayerHWInterface:publicMediaPlayerBase//继承mediaplayerBaser
{
public:
virtual~MediaPlayerHWInterface(){}
virtualboolhardwareOutput(){returntrue;}
virtualstatus_tsetVolume(floatleftVolume,floatrightVolume)=0;
virtualstatus_tsetAudioStreamType(intstreamType)=0;
};
mediaplayerInterface.h
PVPlayer.h是媒体播放器实现层的接口(是opencore媒体播放器实现的头文件)
继承自MediaPlayerInterface
部分代码如下:
classPVPlayer:publicMediaPlayerInterface
{
public:
PVPlayer();
virtual~PVPlayer();
virtualstatus_tinitCheck();
virtualstatus_tsetDataSource(
constchar*url,constKeyedVector<String8,String8>*headers);
virtualstatus_tsetDataSource(intfd,int64_toffset,int64_tlength);
virtualstatus_tsetVideoSurface(constsp<ISurface>&surface);
virtualstatus_tprepare();
virtualstatus_tprepareAsync();
virtualstatus_tstart();
virtualstatus_tstop();
virtualstatus_tpause();
virtualboolisPlaying();
virtualstatus_tseekTo(intmsec);
virtualstatus_tgetCurrentPosition(int*msec);
virtualstatus_tgetDuration(int*msec);
virtualstatus_treset();
virtualstatus_tsetLooping(intloop);
virtualplayer_typeplayerType(){returnPV_PLAYER;}
virtualstatus_tinvoke(constParcel&request,Parcel*reply);
virtualstatus_tgetMetadata(
constSortedVector<media::Metadata::Type>&ids,
Parcel*records);
//makeavailabletoPlayerDriver
voidsendEvent(intmsg,intext1=0,intext2=0){MediaPlayerBase::sendEvent(msg,ext1,ext2);}
private:
staticvoiddo_nothing(status_ts,void*cookie,boolcancelled){}
staticvoidrun_init(status_ts,void*cookie,boolcancelled);
staticvoidrun_set_video_surface(status_ts,void*cookie,boolcancelled);
staticvoidrun_set_audio_output(status_ts,void*cookie,boolcancelled);
staticvoidrun_prepare(status_ts,void*cookie,boolcancelled);
staticvoidcheck_for_live_streaming(status_ts,void*cookie,boolcancelled);
PlayerDriver*mPlayerDriver;
char*mDataSourcePath;
boolmIsDataSourceSet;
sp<ISurface>mSurface;
intmSharedFd;
status_tmInit;
intmDuration;
#ifdefMAX_OPENCORE_INSTANCES
staticvolatileint32_tsNumInstances;
#endif
};
ImeciaplayerClient.h多媒体的客户端(定义了媒体的客户端)主要用作通知函数
Mediaplayer继承ImediaplayerClient所以可以得到下层传弟的信息
部分代码如下:
classIMediaPlayerClient:publicIInterface
{
public:
DECLARE_META_INTERFACE(MediaPlayerClient);
virtualvoidnotify(intmsg,intext1,intext2)=0;//通知的信息是个消息
};
Imediaplayerservice.h多媒体的服务(定义了多媒体服务的接口,由下层服务去实现)
部分代码如下:
classIMediaPlayerService:publicIInterface
{
public:
DECLARE_META_INTERFACE(MediaPlayerService);
/
virtualsp<IMediaRecorder>createMediaRecorder(pid_tpid)=0;
virtualsp<IMediaMetadataRetriever>createMetadataRetriever(pid_tpid)=0;
/创建IMediaPlayer
virtualsp<IMediaPlayer>create(pid_tpid,constsp<IMediaPlayerClient>&client,
constchar*url,constKeyedVector<String8,String8>*headers=NULL,
intaudioSessionId=0)=0;
virtualsp<IMediaPlayer>create(pid_tpid,constsp<IMediaPlayerClient>&client,
intfd,int64_toffset,int64_tlength,intaudioSessionId)=0;
//用于直接解码
virtualsp<IMemory>decode(constchar*url,uint32_t*pSampleRate,int*pNumChannels,int*pFormat)=0;
virtualsp<IMemory>decode(intfd,int64_toffset,int64_tlength,uint32_t*pSampleRate,int*pNumChannels,int*pFormat)=0;
virtualsp<IOMX>getOMX()=0;
};
//----------------------------------------------------------------------------
classBnMediaPlayerService:publicBnInterface<IMediaPlayerService>
{
public:
virtualstatus_tonTransact(uint32_tcode,
constParcel&data,
Parcel*reply,
uint32_tflags=0);
};
源文件的目录
\frameworks\base\media\libmedia
媒体记录器
头文件和实现文件的代码路径:
\frameworks\base\include\media
主要的头文件有:
MediaRecorder.h媒体记录器的上层拉接口,每个函数都调用IMediaRecord来实现,他也继承了BnMediaPlayerClient用于接收下层返回的通知
部分代码如下:
classMediaRecorder:publicBnMediaRecorderClient,
publicvirtualIMediaDeathNotifier
{
public:
MediaRecorder();
~MediaRecorder();
voiddied();
status_tinitCheck();
status_tsetCamera(constsp<ICamera>&camera);//设置camera作为输入设备
status_tsetPreviewSurface(constsp<Surface>&surface);//设置视频预览界面
status_tsetVideoSource(intvs);//视频数据源(枚举值)
status_tsetAudioSource(intas);//音频数据源(同上)
status_tsetOutputFormat(intof);//设置输出格式
status_tsetVideoEncoder(intve);//设置视频编码格式
status_tsetAudioEncoder(intae);//设置音频编码格式
status_tsetOutputFile(constchar*path);//设置输出文件路径
status_tsetOutputFile(intfd,int64_toffset,int64_tlength);//设置输出文件的文件描述符
status_tsetVideoSize(intwidth,intheight);//设置视频尺寸
status_tsetVideoFrameRate(intframes_per_second);//设置视频帧率
status_tsetParameters(constString8¶ms);//设置其他参数
status_tsetListener(constsp<MediaRecorderListener>&listener);//设置临听
status_tprepare();//准备录制
status_tgetMaxAmplitude(int*max);//获得最大增益
status_tstart();//开始
status_tstop();//停止
status_treset();//复位
status_tinit();//初始化记录器
status_tclose();//关闭记录器
status_trelease();//释放资源
voidnotify(intmsg,intext1,intext2);
private:
voiddoCleanUp();
status_tdoReset();
sp<IMediaRecorder>mMediaRecorder;
sp<MediaRecorderListener>mListener;
media_recorder_statesmCurrentState;
boolmIsAudioSourceSet;
boolmIsVideoSourceSet;
boolmIsAudioEncoderSet;
boolmIsVideoEncoderSet;
boolmIsOutputFileSet;
MutexmLock;
MutexmNotifyLock;
};
};
IMediaRecorder.h媒体记录器的部分实现接口
部分代码如下:
classIMediaRecorder:publicIInterface
{
public:
DECLARE_META_INTERFACE(MediaRecorder);
virtual status_t setCamera(constsp<ICamera>&camera)=0;
virtual status_t setPreviewSurface(constsp<ISurface>&surface)=0;
virtual status_t setVideoSource(intvs)=0;
virtual status_t setAudioSource(intas)=0;
virtual status_t setOutputFormat(intof)=0;
virtual status_t setVideoEncoder(intve)=0;
virtual status_t setAudioEncoder(intae)=0;
virtual status_t setOutputFile(constchar*path)=0;
virtual status_t setOutputFile(intfd,int64_toffset,int64_tlength)=0;
virtual status_t setVideoSize(intwidth,intheight)=0;
virtual status_t setVideoFrameRate(intframes_per_second)=0;
virtualstatus_tsetParameters(constString8¶ms)=0;
virtualstatus_tsetListener(constsp<IMediaRecorderClient>&listener)=0;
virtual status_t prepare()=0;
virtual status_t getMaxAmplitude(int*max)=0;
virtual status_t start()=0;
virtual status_t stop()=0;
virtual status_t reset()=0;
virtualstatus_tinit()=0;
virtualstatus_tclose()=0;
virtual status_t release()=0;
};
//----------------------------------------------------------------------------
classBnMediaRecorder:publicBnInterface<IMediaRecorder>
{
public:
virtualstatus_tonTransact(uint32_tcode,
constParcel&data,
Parcel*reply,
uint32_tflags=0);
};
};
PVMediaRecorder.h下层接口,由openCore实现
媒体元信息和扫描器
主要的头文件有:
MediaMetadataRetriever.h
部分代码如下;
classMediaMetadataRetriever:publicRefBase
{
public:
MediaMetadataRetriever();
~MediaMetadataRetriever();
voiddisconnect();
status_tsetDataSource(constchar*dataSourceUrl);//设置数据源(url)
status_tsetDataSource(intfd,int64_toffset,int64_tlength);//设置数据源(文件描述符)
sp<IMemory>getFrameAtTime(int64_ttimeUs,intoption);//捕获帧
sp<IMemory>extractAlbumArt();//抽取
constchar*extractMetadata(intkeyCode);//抽取元信息
IMediaMetadataRetriever
MediaMetadataRetrieverInterface.h实现的接口文件
PVMetadataRetriever.h下层实现的接口
classMediaMetadataRetrieverBase:publicRefBase{}
classMediaMetadataRetrieverInterface:publicMediaMetadataRetrieverBase{}
classPVMetadataRetriever:publicMediaMetadataRetrieverInterface{}
媒体扫描器的头文件
MediaScanner.hscanner的接口扫描一个文件或者一个文件夹取得文件格式,会调用MediaScannerClient
部分代码如下:
structMediaScanner{
MediaScanner();
virtual~MediaScanner();
virtualstatus_tprocessFile(
constchar*path,constchar*mimeType,
MediaScannerClient&client)=0;
typedefbool(*ExceptionCheck)(void*env);
virtualstatus_tprocessDirectory(
constchar*path,constchar*extensions,
MediaScannerClient&client,
ExceptionCheckexceptionCheck,void*exceptionEnv);
voidsetLocale(constchar*locale);
//extractsalbumartasablockofdata
virtualchar*extractAlbumArt(intfd)=0;
}
classMediaScannerClient
{
public:
MediaScannerClient();
virtual~MediaScannerClient();
voidsetLocale(constchar*locale);
voidbeginFile();
booladdStringTag(constchar*name,constchar*value);
voidendFile();
virtualboolscanFile(constchar*path,longlonglastModified,longlongfileSize)=0;
virtualboolhandleStringTag(constchar*name,constchar*value)=0;
virtualboolsetMimeType(constchar*mimeType)=0;
virtualbooladdNoMediaFolder(constchar*path)=0;
}
多媒体服务
他包含媒体揪放器,媒体记录器,媒体元信息管理他和他的调用者是在两个不同的进程中,使用binder进行IPC通信
多媒体服务的守护进程main_mediaserver.cpp
代码中和路径:\frameworks\base\media\mediaserver
部分代码如下:
intmain(intargc,char**argv)
{
sp<ProcessState>proc(ProcessState::self());
sp<IServiceManager>sm=defaultServiceManager();
LOGI("ServiceManager:%p",sm.get());
AudioFlinger::instantiate();//用于声音的混合
MediaPlayerService::instantiate();//用于音频播放
CameraService::instantiate();//摄像头相关服务
AudioPolicyService::instantiate();
ProcessState::self()->startThreadPool();
IPCThreadState::self()->joinThreadPool();
}
audioFlinger是通过defaultServiceMannager获取IServiceMamager接口通过addService方法注册为
Media.audido_flinger
Mediaserver作为一个守护进程,在android的init.rc中具有如下定义
Servicemedia/system/bin/mediaserver
Usermedia
Groupsystemaudiocameragraphicsinetnet_btnet_bt_admin
由于没有定义oneshot,所以这个进程一直存在,如果被杀死,init会将其重新启动
多媒体服务的实现
多媒体服务的路径:\frameworks\base\media\libmediaplayerservice
mediaPlayerService.h头文件中定义了,是IMediaplayer的实现
classMediaPlayerService:publicBnMediaPlayerService{
classAudioOutput:publicMediaPlayerBase::AudioSink{}
classAudioCache:publicMediaPlayerBase::AudioSink{}
classClient:publicBnMediaPlayer{}
}
是IMeciaRecorder的实现
classMediaRecorderClient:publicBnMediaRecorder{}
是IMediadataRetriever的实现
classMetadataRetrieverClient:publicBnMediaMetadataRetriever{}
MediaPlayerService.cpp中定义了取得媒体记录器(IMediaRecorder>)的接口
sp<IMediaRecorder>MediaPlayerService::createMediaRecorder(pid_tpid)
{
sp<MediaRecorderClient>recorder=newMediaRecorderClient(this,pid);
wp<MediaRecorderClient>w=recorder;
Mutex::Autolocklock(mLock);
mMediaRecorderClients.add(w);
LOGV("Createnewmediarecorderclientfrompid%d",pid);
returnrecorder;
}
取得媒体播放器的媒体元信息
sp<IMediaMetadataRetriever>MediaPlayerService::createMetadataRetriever(pid_tpid)
{
sp<MetadataRetrieverClient>retriever=newMetadataRetrieverClient(pid);
LOGV("Createnewmediaretrieverfrompid%d",pid);
returnretriever;
}
MediaPlayService类中创建媒体播放器的过程:
1
sp<IMediaPlayer>MediaPlayerService::create(pid_tpid,constsp<IMediaPlayerClient>&client,
intfd,int64_toffset,int64_tlength,intaudioSessionId)
{
int32_tconnId=android_atomic_inc(&mNextConnId);
//创建mediaPlayerService::Client类
sp<Client>c=newClient(this,pid,connId,client,audioSessionId);
LOGV("Createnewclient(%d)frompid%d,fd=%d,offset=%lld,length=%lld,audioSessionId=%d",
connId,pid,fd,offset,length,audioSessionId);
//设置源的url
if(NO_ERROR!=c->setDataSource(fd,offset,length)){//根据setDataSource()时根据输入的类型创建不同的mediaPlayBase,接着调用下面的createPlayer方法创建不同的player
c.clear();
}else{
wp<Client>w=c;
Mutex::Autolocklock(mLock);
mClients.add(w);
}
::close(fd);
returnc;
}
staticsp<MediaPlayerBase>createPlayer(player_typeplayerType,void*cookie,
notify_callback_fnotifyFunc)
{
sp<MediaPlayerBase>p;
switch(playerType){//根据playerType的类型建立不同的播放器
#ifndefNO_OPENCORE
casePV_PLAYER:
LOGV("createPVPlayer");
p=newPVPlayer();
break;
#endif
caseSONIVOX_PLAYER:
LOGV("createMidiFile");
p=newMidiFile();
break;
caseSTAGEFRIGHT_PLAYER:
LOGV("createStagefrightPlayer");
p=newStagefrightPlayer;
break;
caseTEST_PLAYER:
LOGV("CreateTestPlayerstub");
p=newTestPlayerStub();
break;
}
if(p!=NULL){
if(p->initCheck()==NO_ERROR){
p->setNotifyCallback(cookie,notifyFunc);
}else{
p.clear();
}
}
if(p==NULL){
LOGE("Failedtocreateplayerobject");
}
returnp;
}
PVPlayerMidiFile和VorbisPlayer三个都继承MediaPlayInterface得到的,MediaPlayerInterface是继承MediaPlayerBase得到,三者具有相同的接口类型,三者在建立之后通过MediaPlayerBase接口来控制他们
媒体播放器的实现结构如下图所示
MediaPlayerService::AudioOutput实现audio输出环节的封装,由Audio系统来实现,主要是调用AudioTrakc的接口
status_tMediaPlayerService::AudioOutput::open(
uint32_tsampleRate,intchannelCount,intformat,intbufferCount,
AudioCallbackcb,void*cookie)
{
mCallback=cb;
mCallbackCookie=cookie;
//Checkargument"bufferCount"againstthemininumbuffercount
if(bufferCount<mMinBufferCount){
LOGD("bufferCount(%d)istoosmallandincreasedto%d",bufferCount,mMinBufferCount);
bufferCount=mMinBufferCount;
}
LOGV("open(%u,%d,%d,%d,%d)",sampleRate,channelCount,format,bufferCount,mSessionId);
if(mTrack)close();
intafSampleRate;
intafFrameCount;
intframeCount;
if(AudioSystem::getOutputFrameCount(&afFrameCount,mStreamType)!=NO_ERROR){
returnNO_INIT;
}
if(AudioSystem::getOutputSamplingRate(&afSampleRate,mStreamType)!=NO_ERROR){
returnNO_INIT;
}
//获得帧数和采样率
frameCount=(sampleRate*afFrameCount*bufferCount)/afSampleRate;
AudioTrack*t;
if(mCallback!=NULL){
t=newAudioTrack(
mStreamType,
sampleRate,
format,
(channelCount==2)?AudioSystem::CHANNEL_OUT_STEREO:AudioSystem::CHANNEL_OUT_MONO,
frameCount,
0/*flags*/,
CallbackWrapper,
this,
0,
mSessionId);
}else{
t=newAudioTrack(
mStreamType,
sampleRate,
format,
(channelCount==2)?AudioSystem::CHANNEL_OUT_STEREO:AudioSystem::CHANNEL_OUT_MONO,
frameCount,
0,
NULL,
NULL,
0,
mSessionId);
}
if((t==0)||(t->initCheck()!=NO_ERROR)){
LOGE("Unabletocreateaudiotrack");
deletet;
returnNO_INIT;
}
LOGV("setVolume");
t->setVolume(mLeftVolume,mRightVolume);
mMsecsPerFrame=1.e3/(float)sampleRate;
mLatency=t->latency();
mTrack=t;
t->setAuxEffectSendLevel(mSendLevel);
returnt->attachAuxEffect(mAuxEffectId);;
}
音频输出的接口
ssize_tMediaPlayerService::AudioOutput::write(constvoid*buffer,size_tsize)
{
LOG_FATAL_IF(mCallback!=NULL,"Don'tcallwriteifsupplyingacallback.");
//LOGV("write(%p,%u)",buffer,size);
if(mTrack){
ssize_tret=mTrack->write(buffer,size);
returnret;
}
returnNO_INIT;
}
多媒体的JNI部分
本地调用部分的代码路径为:
Frameworks/base/media/jni
主要文件有:
Android2。3后改用stagefright
\frameworks\base\media\libstagefright
两者的处理机制不同
openCore的处理流程如下:
Stagefright部分的处理流程如下:
从上面可以看出
1OpenCore的parser和dec是分离的,各行其职,stagefright则是邦在一起作为一个独立的原子操作
2stagefright通过callback和videoevent来驱动数据输出,openCore是通过sink-node节点控制输出
3Opencore中parser/dec/sink是并行处理的stagefright是串行处理android_media_MediaPlayer.cpp//媒体播放器
android_media_MediaRecorder.cpp//媒体记录器
android_media_MediaMetadataRetriever.cpp//媒体元信息工具
android_media_MediaScanner.cpp//媒体扫描器
这部分内容最终编译成libmedia_jni.so,
设置surface作为视频输出和取景器预览的接口没有对java提供,而是在preapare()函数中直接从环境中得到并设置了。
staticvoid
android_media_MediaPlayer_prepare(JNIEnv*env,jobjectthiz)
{
sp<MediaPlayer>mp=getMediaPlayer(env,thiz);
if(mp==NULL){
jniThrowException(env,"java/lang/IllegalStateException",NULL);
return;
}
setVideoSurface(mp,env,thiz);//调用mediaplayer函数作视频输出设置
process_media_player_call(env,thiz,mp->prepare(),"java/io/IOException","Preparefailed.");
}
staticvoid
android_media_MediaRecorder_prepare(JNIEnv*env,jobjectthiz)
{
LOGV("prepare");
sp<MediaRecorder>mr=getMediaRecorder(env,thiz);
jobjectsurface=env->GetObjectField(thiz,fields.surface);
if(surface!=NULL){
constsp<Surface>native_surface=get_surface(env,surface);
//Theapplicationmaymisbehaveand
//thepreviewsurfacebecomesunavailable
if(native_surface.get()==0){
LOGE("Applicationlostthesurface");
jniThrowException(env,"java/io/IOException","invalidpreviewsurface");
return;
}
LOGI("prepare:surface=%p(identity=%d)",native_surface.get(),native_surface->getIdentity());
//调用mediaplayer函数作视频输出设置
if(process_media_recorder_call(env,mr->setPreviewSurface(native_surface),"java/lang/RuntimeException","setPreviewSurfacefailed.")){
return;
}
}
process_media_recorder_call(env,mr->prepare(),"java/io/IOException","preparefailed.");
}
多媒体部分的java部分代码
Java框架类的路径为:
frameworks\base\media\java\android\media
主要文介绍;:
MediaFile.java文件提供了媒体文件的文件类型,
MediaPlayerMediaRecorderMediaMetadataRecorder等类基本上和JNI层的内容一一对应
MediaScanner在这里实现有客户端内容比较多
其中MedisPlayer中对视频输出和取景器预览的接口
publicvoidsetDisplay(SurfaceHoldersh){
mSurfaceHolder=sh;
if(sh!=null){
mSurface=sh.getSurface();
}else{
mSurface=null;
}
_setVideoSurface();
updateSurfaceScreenOn();
}
MediaRecorder中对视频输出和取景器预览的接口
publicvoidsetPreviewDisplay(Surfacesv){
mSurface=sv;
}
Java框架层没有直接使用传递参数的方式,而是使用了保存在环境中再传递的方式
Android.widgetVideoView类.是一个UI元素
代码路径为:frameworks\base\core\java\android\widget
使用该类,可以不用再调用MediaPlayer类,节省了一些中间环节
publicclassVideoViewextendsSurfaceViewimplementsMediaPlayerControl{
publicvoidsetVideoPath(Stringpath){}//设置源文件路径
publicvoidsetVideoURI(Uriuri){}//设置视频的URL
publicvoidstart(){}//开始播放
publicvoidstopPlayback(){}//停止播放
publicvoidpause(){}//暂停播放
publicvoidseekTo(intmsec){}//更改播放位置
多媒体实现的核心部分OpenCore
多媒体系统框架PacketVideo的开源版本OpenCore是android多媒体本地实现在的核心
它为android提供的引警如下
PVPlayer媒体播放器的功能 音频和视频的回放功能
PVAuthor媒体记录器功能音频和视频的录制
OpenCore的层次结构
自上而下分为
OSCL(operationsystemcompatibilitylibrary,操作系统兼容库)类似一个基础的c++库
PVMF (packetVideoMultimediaFramework多媒体框架)packetVideo的基本框架,例如nodea基类,输入输出的抽象类
文件格式处理,文件解析(parser)和组成(composer)两个部分,
各种Node,是packetVideo中的基本功能模块,
播放器(PlayerEngine)播放器引擎
记录器(authorEngine)媒体记录器引擎
注:在openCore2.X之后,开始提供了2-wayengine两路引擎用于构建视频电话
在使用OpenCore的SDK时,需要在应用层实现一个适配器
PVPlaytr和PVAuthor就是基于OpenCore的下层功能和接口构建军的应用层的库
在android系统中OpenCore的代码路径为:externam/opencore/
Stagefright整体框图:
Androidfroyo版本对多媒体引擎作了变动.新添加了stagefright框架,但并没有完全抛弃opencore
主要是作了一个omx层,仅是对opencore的omx-component部分作了引用,,它在android系统中作为
共享库(libstagefright.so)存在,其中的module--awesomePlayer用来播放video/audio
Awesomeplayer提供的API可以供上次的应用(java/JNI)来调用
StageFrigtht数据流封装
1MediaExtractor.cpp根据数据源DataSource生成MediaExtractor
具体实现是通过调用(代码路径为;frameworks\base\media\libstagefright)
sp<MediaExtractor>MediaExtractor::Create(constsp<DataSource>&source,constchar*mime){}
通过DateSource的source->sniff(&tmp,&confidence,&meta)来探测数据类型
2AwesomePlayer.cpp把音视频轨道分离,生成mVideoTrack和MediaSource
部分代码如下:
if(!haveVideo&&!strncasecmp(mime,"video/",6)){
setVideoSource(extractor->getTrack(i));
haveVideo=true;
}elseif(!haveAudio&&!strncasecmp(mime,"audio/",6)){
setAudioSource(extractor->getTrack(i));
haveAudio=true;
if(!strcasecmp(mime,MEDIA_MIMETYPE_AUDIO_VORBIS)){
//Onlydothisforvorbisaudio,noneoftheotheraudio
//formatsevensupportthisringtonespecifichackand
//retrievingthemetadataonsomeextractorsmayturnout
//tobeveryexpensive.
sp<MetaData>fileMeta=extractor->getMetaData();
int32_tloop;
if(fileMeta!=NULL
&&fileMeta->findInt32(kKeyAutoLoop,&loop)&&loop!=0){
mFlags|=AUTO_LOOPING;
}
}
}
3得到的两个mediaSource只具有parser功能,,没有decode功能,还需要对两个MediaSource做进一步的包装
mAudioSource=OMXCodec::Create(
mClient.interface(),mAudioTrack->getFormat(),
false,//createEncoder
mAudioTrack);
mVideoSource=OMXCodec::Create(
mClient.interface(),mVideoTrack->getFormat(),
false,//createEncoder
mVideoTrack,
NULL,flags);
当调用mediaSource.start()方法后,就会开始从数据源获取数据并解析,等到缓冲区满后就停止
awesomePlayer就可以调用mediaSource的read方法读取解码后的数据
对于mVideoSource来说,读取数据mVideoource->read(&mVideoBuffer,&options)交给显示模块进行渲染,mVideoRenderer->render(mVideoBufer)
4stageFright的decode
经过流的封装得到两个MediaSource,其实是两个OMXCodec,
AwesomePlayer和mAudioPlayer都是从mediaSource中得到数据进行播放,最终需要渲染的原始视频数据,也就是说OMXCodec中得到的是原始数据
部分代码如下:
sp<MediaSource>OMXCodec::Create(
constsp<IOMX>&omx,//OMXNodeInstance对象的实例
constsp<MetaData>&meta,boolcreateEncoder,//由MediaSource.getFormat获取得到,
//他的对象成员是一个keyedVector<uint32_t,typed_data>
//里面存放的是代表mediaSource格式信息的键值对
constsp<MediaSource>&source,//mediaExtractor
constchar*matchComponentName,//指定一种codec用于生成omxcodec
uint32_tflags){
//首先调用findMatchingCodecs()方法,找到对应的Codec,
findMatchingCodecs(
mime,createEncoder,matchComponentName,flags,&matchingCodecs);
//找到以后为当前的IOMX分配并注册监听事件,
status_terr=omx->allocateNode(componentName,observer,&node);
//这样就得到了OMXCodec
sp<OMXCodec>codec=newOMXCodec(
omx,node,quirks,
createEncoder,mime,componentName,
source);
}
在AwesomePlayer中得到这个OMXCodec后,首先调用mVideoSource->start()进行初始化,主要有两件事
1向openMAX发送命令
err=mOMX->sendCommand(mNode,OMX_CommandStateSet,OMX_StateIdle);
2err=allocateBuffers();分配两个缓冲区,freeBuffersOnPort()分别用于输入和输出
当awesomePlayer开始播放以后,通过mVideoSource->read(&mVideoBuffer,&options)读取数据
OMXCodec.read分两部来实现数据读取,
1通过调用draininputBuffers()对mPortBuffers[kPortindexOutput]进行填充,这一步完成parse
由OpenMAX从数据源把demux后的数据读取到输入缓,作为OpenMAX的输入
2通过fillOutputBuffers()对mPortBuffers[kPortIndexInput]进行填充,这一步完成decode,
由OpenMAX对输入缓冲区的数据进行解码
3AwesomePlayer通过mVideoRenderer->reder()对经过parse和decode处理的数据进行渲染
mVideoRenderer=newAwesomeLocalRenderer(
false,//previewOnly
component,
(OMX_COLOR_FORMATTYPE)format,
mISurface,
mVideoWidth,mVideoHeight,
decodedWidth,decodedHeight,rotationDegrees);
StageFright的处理流程
Audioplayer是awesomePlayer的成员,audioplayer通过callback来驱动数据的获取,
Awesomeplayer则是通过videoevent来驱动,数据获取由mSource->Read()来完成,
Read内部将parset和decod在一起
两者进行同步部分audio完全是callback驱动数据流,
Video部分在onvideoEvent会读取audio的时间戳,是传统的AV时间戳同步
AwesomePlayer的Video主要有以下几个成员
mVideoSource(解码视频)
mVideoTeack(从媒体文件中读取视频数据)
mVideoRenderer(对解码好的视频进行格式转换,android使用的格式为RGB565)
mlSurface(重绘图层)
mQueue(event事件对列)
Audio部分的抽像流程如下:
设置mUrl路径
启动mQueue,创建一个线程threadEntry(timedEventQueue,这个线程就是event调度器)
打开mUrl指定文件头部,根据不同类型选择不同的分离器(例如:MPEG4Extractor)
使用分离器(MPEG4Extractor对MP4进行音视频轨道的分离,返回MPEG4Source类型的视频轨道给mVideoTrack
根据mVideoTrack中的编码类型来选择解码器,avc的编码类型会选择AVCDecoder,并返回给mVideoSource并设置mVideoSource中的mSource为mVideoTrack
插入到onVideoEvent到queue中,开始解码播放
通过mVideoSource对象来读取解析好的视频buffer,如果解析好的buffer还没到AV时间戳同步的时刻,则推迟到下一轮操作,
mVideoRenderer为空,则进行初始化(如果不使用,OMX会将mVideoRenderer设置为AwesomeLocalRenderer)
通过mVideoRenderer对象将解析好的视频buffer转换成RGB565格式,并发给display模块进行图像绘制
将onVideoEvent重新插入event调度器来循环
数据源到最终解码后的流程如下
URI,FD------->DataSource---------->MediaExtractor------------>mVideoTrackmAudioTrack(音视频数据流)--------------->mVideoSourcemAudioSource(音视频解码器)
注:URI可以为;http://rtsp://等
FD是本地文件描述符
打开log日志
代码标记Log
依据第4》项StageFright描述的Vide视频播放流程,作Log标记跟踪视频DATA获取、CODEC过程。从AwesomePlayer.cpp中方法着手,步骤如下:
n在修改的/mydroid/frameworks/base/media/libstagefrigh/下,用mm编译,并调试直到生成相应的.so文件。注:允许单模块编译时,需事先在/mydroid下允许../build/envsetup.sh文件。
n在/mydroid/目录下make进行整体编译,生成system.img文件。说明:先单模块编译,后再整体编译的好处是,可以缩短调试编译的时间。
n将system.img文件copy到/android-sdk-linux/platforms/android-8/下。注意:事先备份原有的system.img。
n带sdcard启动模拟器,在/android-sdk-linux/tools/下运行./adbshell文件,再运行logcat
n打开Gallery选择视频文件运行,并同步查看log。