- 浏览: 261342 次
- 性别:
- 来自: 济南
文章分类
- 全部博客 (303)
- c (31)
- c++ (16)
- java (18)
- c# (1)
- python (3)
- java web (6)
- oracle (7)
- sqlserver (2)
- mysql (2)
- android (24)
- android系统 (15)
- android多媒体部分 (15)
- android游戏 (12)
- linux (26)
- javaScript (1)
- ajax (1)
- node JS (2)
- html (5)
- apache (3)
- jboss (1)
- weblogic (0)
- 通信协议 (10)
- 云计算 (1)
- 分布式 (5)
- ejb (1)
- webservice (5)
- 设计模式 (16)
- JNI (6)
- swing (13)
- 版本控制 (1)
- UML (1)
- xml (4)
- spring (5)
- hibernate (5)
- struts1 (3)
- struts2 (4)
- ibatis (0)
- tomcat (2)
- 心得体会 (1)
- css (1)
- 嵌入式 (41)
- arm体系结构 (10)
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。
发表评论
-
android系统学习笔记一
2012-09-03 15:01 9081.android的系统架构(linux2.6内核) Lin ... -
android系统学习笔记二
2012-09-03 15:03 913android的linux内核和驱动程序 Androidr专 ... -
android系统学习笔记三
2012-09-03 15:05 839第四章android的底层库和程序 知识点: And ... -
android系统学习笔记四
2012-09-03 15:07 1134Android的GUI系统 AndroidGUI系统综 ... -
android系统学习笔记五
2012-09-03 15:12 2200android中的audio系统 Audo系统主要分如下几 ... -
android系统学习笔记七
2012-09-03 15:18 739Android电话部分综述 主 ... -
android系统学习八
2012-09-03 15:19 755Android的联接部分 Wifi部分 Wif ... -
android系统学习笔记九
2012-09-03 15:21 737蓝牙部分 蓝牙协议栈1.11.22.02.13.0 异步数 ... -
android系统学习笔记十一
2012-09-03 15:22 1502Gps(globalPositionSystem全球定位系统) ... -
android系统学习笔记十二
2012-09-03 15:23 1019Android的传感器系统 ... -
Android编译系统详解(一)
2012-09-10 13:45 829+++++++++++++++++++++++++++++ ... -
android----ServiceManager
2012-09-18 00:33 4319serviceManager 在c++层如何使用servi ... -
android 自定义核心服务
2012-09-18 23:27 797native service zygote 服务 kern ... -
android系统移植学习笔记一
2012-09-21 16:08 627系统框架 applicationjava应用程序 ...
相关推荐
Android学习笔记是Android开发者的必读书籍,书中涵盖了Android系统架构、Activity、Intent、资源管理等多方面的知识。本笔记对应的学习资源《第一行代码》是Android开发者的入门必读书籍,书中系统地介绍了Android...
Android基础学习笔记主要涵盖了一系列关于Android开发的基本概念和关键组件,以下是这些知识点的详细解析: 1. **Activity**: 是Android应用程序的基本单元,它代表用户在屏幕上看到的一个界面。每个Activity都必须...
这篇学习笔记将引导新手入门,通过创建第一个简单的案例来了解Android Studio的基本操作。 首先,让我们了解一下Android Studio的核心功能。它基于IntelliJ IDEA,具备代码自动补全、重构、调试等强大的开发特性。...
Android Activity学习笔记 Android Activity是Android系统中最基本的组件之一,它负责处理用户交互和显示用户界面。本文将深入讲解Activity的生命周期、Activity之间的数据交互、Activity启动模式、Task和BackStack...
- Android系统基于Linux系统开发,拥有开放源码。 - Android系统框架由多个组件构成,其中包括Activity、Intent、Service、ContentProvider、View和BroadcastReceiver。 - Activity作为组件容器,主要负责用户...
根据给定的信息,我们可以从Java和Android学习笔记中提取出一系列重要的知识点,下面将逐一进行详细解释。 ### Java基础知识 #### 1. 命令行基础操作 - **`javacmd`**: 这个命令是Java命令行工具的一部分,用于...
### Android学习笔记知识点详解 #### 一、简介与背景 Android作为全球最受欢迎的移动操作系统之一,自2007年谷歌发布以来,迅速占领市场并持续引领移动技术的发展。随着移动互联网的兴起和发展,Android应用开发...
### Android学习笔记——从HelloWorld开始 #### 一、Google的Android SDK介绍 自从Google发布了Android SDK以来,这款开源移动操作系统迅速吸引了众多开发者的关注。Android SDK为开发者提供了丰富的工具和API,...
Android全程学习笔记旨在提供一个详尽且全面的指南,涵盖了Android开发中的关键技术点和实践案例。以下是关于Android开发的一些核心知识点: 1. **第一个Android应用**:开发Android应用的起点通常是从创建并运行你...
这篇学习笔记主要涵盖了关于布局的一些基本概念,特别是`fill_parent`和`wrap_content`这两种尺寸指定方式,以及如何通过XML布局文件来精确控制组件的位置。 首先,`fill_parent`和`wrap_content`是Android布局中的...
这个"Android_学习笔记.zip"文件很可能包含了一个详细的Android开发学习路径和关键知识点的总结。下面将基于这个主题,详细讲解Android开发的一些核心概念和技术。 首先,Android是Google开发的一款开源操作系统,...
在Android系统相关学习笔记中,我们可以深入探讨这个广泛而复杂的移动操作系统的核心概念和技术。Android以其开源性和灵活性,吸引了大量的开发者和爱好者。以下是一些关键的知识点: 1. **Android架构**:Android...
【Android学习笔记详解】 在移动应用开发领域,Android操作系统占据着重要的地位,为开发者提供了...通过系统学习并实践其中的内容,你将能够逐步掌握Android开发的核心技能,为你的Android开发者之路打下坚实的基础。
这篇学习笔记将深入探讨PreferenceActivity的使用方法、功能以及与源码相关的知识。 首先,PreferenceActivity是Android SDK提供的一种特殊类型的Activity,它允许开发者快速构建具有可配置选项的界面,类似于系统...
### Android开发学习笔记知识点梳理 #### 一、Android概述与架构 - **定义与发布**:Android是由Google在2007年11月5日宣布的基于Linux平台的开源手机操作系统。它不仅用于智能手机,还广泛应用于平板电脑、可穿戴...
Android入门学习笔记主要涵盖了Android开发的基础知识,包括平台概述、环境搭建、基本概念以及实际应用开发的各个方面。这里我们将深入探讨这些关键知识点。 一、Android平台概述 Android是由Google主导开发的一款...