android系统学习笔记六
android 的多媒体系统
多媒体系统的结构和业务
多媒体系统的宏鸡观结构
应用层,java框架层,c 语言层,硬件抽像层,其中输入输出由HAL层,处理环节由packetView的OpenCore实现,
多媒体业备有以下几种:
musicPlayer(音频播放器)
viderPlayer(视频播放器)
Camera(照相机)
soundRecord(录音机)
videoCamera (摄像机)
Media metadata(媒体元信息)
核心是媒体的播放和录制,分别由下层的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.
部分代码如下:
class MediaPlayer : public BnMediaPlayerClient,
public virtual IMediaDeathNotifier
{
public:
MediaPlayer();
~MediaPlayer();
voiddied();
voiddisconnect();
status_tsetDataSource(
const char *url,//设置数据源url
const KeyedVector *headers);
status_tsetDataSource(int fd, int64_t offset, int64_t length);
//设置数据源文件
status_tsetVideoSurface(const sp& surface);
//设视频输出界面
status_tsetListener(const sp
status_tprepare();
//准备播放
status_tprepareAsync();
//异部准备播放
status_tstart();
//开始
status_tstop();
//停止
status_tpause();
//暂停
boolisPlaying();
//是否正在播放
status_tgetVideoWidth(int *w);
//获取视频播放的宽
status_tgetVideoHeight(int *h);
//获取视频播放的高
status_tseekTo(int msec);
//跳转到指定位置
status_tgetCurrentPosition(int *msec);
//取得当前的播放位置
status_tgetDuration(int *msec);
//播放的持续时间(总时长)
status_treset();
//复位
status_tsetAudioStreamType(int type);
//设置音频流的格式
status_tsetLooping(int loop);
// 设置循环
boolisLooping();
//是否循环
status_tsetVolume(float leftVolume, float rightVolume);
//设置音量
voidnotify(int msg, int ext1, int ext2);
//通知函数
staticsp
staticsp
status_tinvoke(const Parcel& request, Parcel *reply);
status_tsetMetadataFilter(const Parcel& filter);
status_tgetMetadata(bool update_only, bool apply_filter, Parcel *metadata);
status_tsuspend();
status_tresume();
status_tsetAudioSessionId(int sessionId);
intgetAudioSessionId();
status_tsetAuxEffectSendLevel(float level);
status_tattachAuxEffect(int effectId);
private:
voidclear_l();
status_tseekTo_l(int msec);
status_tprepareAsync_l();
status_tgetDuration_l(int *msec);
status_tsetDataSource(const sp
sp
thread_id_tmLockThreadId;
MutexmLock;
MutexmNotifyLock;
ConditionmSignal;
sp
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输出
class MediaPlayerInterface : public MediaPlayerBase{
public:
virtual~MediaPlayerInterface() { }
virtual boolhardwareOutput() { return false;
}
virtual voidsetAudioSink(const sp& audioSink) { mAudioSink = audioSink;
}
protected:
spmAudioSink;
//音频轮输出设备的抽像接口
};
// Implement this class for media players that output directo to hardware
//直接从硬功夫件进行音频输出
class MediaPlayerHWInterface : public MediaPlayerBase//继承mediaplayerBaser
{
public:
virtual~MediaPlayerHWInterface() {}
virtual boolhardwareOutput() { return true;
}
virtual status_tsetVolume(float leftVolume, float rightVolume) = 0;
virtual status_tsetAudioStreamType(int streamType) = 0;
};
mediaplayerInterface.h
PVPlayer.h是媒体播放器实现层的接口 (是opencore媒体播放器实现的头文件)
继承自MediaPlayerInterface
部分代码如下:
class PVPlayer : public MediaPlayerInterface
{
public:
PVPlayer();
virtual~PVPlayer();
virtual status_tinitCheck();
virtual status_tsetDataSource(
const char *url, const KeyedVector *headers);
virtual status_tsetDataSource(int fd, int64_t offset, int64_t length);
virtual status_tsetVideoSurface(const sp
virtual status_tprepare();
virtual status_tprepareAsync();
virtual status_tstart();
virtual status_tstop();
virtual status_tpause();
virtual boolisPlaying();
virtual status_tseekTo(int msec);
virtual status_tgetCurrentPosition(int *msec);
virtual status_tgetDuration(int *msec);
virtual status_treset();
virtual status_tsetLooping(int loop);
virtual player_type playerType() { return PV_PLAYER;
}
virtual status_tinvoke(const Parcel& request, Parcel *reply);
virtual status_tgetMetadata(
const SortedVector
Parcel *records);
// make available to PlayerDriver
voidsendEvent(int msg, int ext1=0, int ext2=0) { MediaPlayerBase::sendEvent(msg, ext1, ext2);
}
private:
static voiddo_nothing(status_t s, void *cookie, bool cancelled) { }
static voidrun_init(status_t s, void *cookie, bool cancelled);
static voidrun_set_video_surface(status_t s, void *cookie, bool cancelled);
static voidrun_set_audio_output(status_t s, void *cookie, bool cancelled);
static voidrun_prepare(status_t s, void *cookie, bool cancelled);
static voidcheck_for_live_streaming(status_t s, void *cookie, bool cancelled);
PlayerDriver*mPlayerDriver;
char *mDataSourcePath;
boolmIsDataSourceSet;
sp
intmSharedFd;
status_tmInit;
intmDuration;
#ifdef MAX_OPENCORE_INSTANCES
static volatile int32_tsNumInstances;
#endif
};
ImeciaplayerClient.h 多媒体的客户端(定义了媒体的客户端)主要用作通知函数
Mediaplayer继承ImediaplayerClient 所以可以得到下层传弟的信息
部分代码如下:
class IMediaPlayerClient: public IInterface
{
public:
DECLARE_META_INTERFACE(MediaPlayerClient);
virtual void notify(int msg, int ext1, int ext2) = 0;
//通知的信息是个消息
};
Imediaplayerservice.h 多媒体的服务(定义了多媒体服务的接口,由下层服务去实现)
部分代码如下:
class IMediaPlayerService: public IInterface
{
public:
DECLARE_META_INTERFACE(MediaPlayerService);
/
virtual sp
virtual sp
/创建IMediaPlayer
virtual sp
const char* url, const KeyedVector *headers = NULL,
int audioSessionId = 0) = 0;
virtual sp
int fd, int64_t offset, int64_t length, int audioSessionId) = 0;
//用于直接解码
virtual sp
virtual sp
virtual sp
};
// ----------------------------------------------------------------------------
class BnMediaPlayerService: public BnInterface
{
public:
virtual status_tonTransact( uint32_t code,
const Parcel& data,
Parcel* reply,
uint32_t flags = 0);
};
源文件的目录
\frameworks\base\media\libmedia
媒体记录器
头文件和实现文件的代码路径:
\frameworks\base\include\media
主要的头文件有:
MediaRecorder.h 媒体记录器的上层拉接口,每个函数都调用IMediaRecord来实现, 他也继承了BnMediaPlayerClient用于接收下层返回的通知
部分代码如下:
class MediaRecorder : public BnMediaRecorderClient,
public virtual IMediaDeathNotifier
{
public:
MediaRecorder();
~MediaRecorder();
voiddied();
status_tinitCheck();
status_tsetCamera(const sp
status_tsetPreviewSurface(const sp& surface);
//设置视频预览界面
status_tsetVideoSource(int vs);
//视频数据源( 枚举值)
status_tsetAudioSource(int as);
//音频数据源 (同上)
status_tsetOutputFormat(int of);
//设置输出格式
status_tsetVideoEncoder(int ve);
//设置视频编码格式
status_tsetAudioEncoder(int ae);
//设置音频编码格式
status_tsetOutputFile(const char* path);
//设置输出文件路径
status_tsetOutputFile(int fd, int64_t offset, int64_t length);
//设置输出文件的文件描述符
status_tsetVideoSize(int width, int height);
//设置视频尺寸
status_tsetVideoFrameRate(int frames_per_second);
//设置视频帧率
status_tsetParameters(const String8& params);
//设置其他参数
status_tsetListener(const sp
status_tprepare();
//准备录制
status_tgetMaxAmplitude(int* max);
//获得最大增益
status_tstart();
//开始
status_tstop();
//停止
status_treset();
//复位
status_tinit();
//初始化记录器
status_tclose();
//关闭记录器
status_trelease();
//释放资源
voidnotify(int msg, int ext1, int ext2);
private:
voiddoCleanUp();
status_tdoReset();
sp
sp
media_recorder_statesmCurrentState;
boolmIsAudioSourceSet;
boolmIsVideoSourceSet;
boolmIsAudioEncoderSet;
boolmIsVideoEncoderSet;
boolmIsOutputFileSet;
MutexmLock;
MutexmNotifyLock;
};
};
IMediaRecorder.h 媒体记录器的部分实现接口
部分代码如下:
class IMediaRecorder: public IInterface
{
public:
DECLARE_META_INTERFACE(MediaRecorder);
virtual status_t setCamera(const sp
virtual status_t setPreviewSurface(const sp
virtual status_t setVideoSource(int vs) = 0;
virtual status_t setAudioSource(int as) = 0;
virtual status_t setOutputFormat(int of) = 0;
virtual status_t setVideoEncoder(int ve) = 0;
virtual status_t setAudioEncoder(int ae) = 0;
virtual status_t setOutputFile(const char* path) = 0;
virtual status_t setOutputFile(int fd, int64_t offset, int64_t length) = 0;
virtual status_t setVideoSize(int width, int height) = 0;
virtual status_t setVideoFrameRate(int frames_per_second) = 0;
virtualstatus_tsetParameters(const String8& params) = 0;
virtualstatus_tsetListener(const sp
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;
virtual status_tinit() = 0;
virtual status_tclose() = 0;
virtual status_t release() = 0;
};
// ----------------------------------------------------------------------------
class BnMediaRecorder: public BnInterface
{
public:
virtual status_tonTransact( uint32_t code,
const Parcel& data,
Parcel* reply,
uint32_t flags = 0);
};
};
PVMediaRecorder.h 下层接口,由openCore实现
媒体元信息和扫描器
主要的头文件有:
MediaMetadataRetriever.h
部分代码如下;
class MediaMetadataRetriever: public RefBase
{
public:
MediaMetadataRetriever();
~MediaMetadataRetriever();
void disconnect();
status_t setDataSource(const char* dataSourceUrl);
//设置数据源(url)
status_t setDataSource(int fd, int64_t offset, int64_t length);
//设置数据源(文件描述符)
sp
sp
const char* extractMetadata(int keyCode);
//抽取元信息
IMediaMetadataRetriever
MediaMetadataRetrieverInterface.h 实现的接口文件
PVMetadataRetriever.h下层实现的接口
class MediaMetadataRetrieverBase : public RefBase{}
class MediaMetadataRetrieverInterface : public MediaMetadataRetrieverBase{}
class PVMetadataRetriever : public MediaMetadataRetrieverInterface{}
媒体扫描器的头文件
MediaScanner .hscanner的接口扫描一个文件或者一个文件夹取得文件格式,会调用MediaScannerClient
部分代码如下:
struct MediaScanner {
MediaScanner();
virtual ~MediaScanner();
virtual status_t processFile(
const char *path, const char *mimeType,
MediaScannerClient &client) = 0;
typedef bool (*ExceptionCheck)(void* env);
virtual status_t processDirectory(
const char *path, const char *extensions,
MediaScannerClient &client,
ExceptionCheck exceptionCheck, void *exceptionEnv);
void setLocale(const char *locale);
// extracts album art as a block of data
virtual char *extractAlbumArt(int fd) = 0;
}
class MediaScannerClient
{
public:
MediaScannerClient();
virtual ~MediaScannerClient();
void setLocale(const char* locale);
void beginFile();
bool addStringTag(const char* name, const char* value);
void endFile();
virtual bool scanFile(const char* path, long long lastModified, long long fileSize) = 0;
virtual bool handleStringTag(const char* name, const char* value) = 0;
virtual bool setMimeType(const char* mimeType) = 0;
virtual bool addNoMediaFolder(const char* path) = 0;
}
多媒体服务
他包含媒体揪放器, 媒体记录器,媒体元信息管理 他和他的调用者是在两个不同的进程中,使用binder进行IPC通信
多媒体服务的守护进程main_mediaserver.cpp
代码中和路径: \frameworks\base\media\mediaserver
部分代码如下:
int main(int argc, char** argv)
{
sp proc(ProcessState::self());
sp
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中具有如下定义
Service media /system/bin/mediaserver
User media
Group system audio camera graphics inet net_bt net_bt_admin
由于没有定义oneshot,所以这个进程一直存在,如果被杀死,init会将其重新启动
多媒体服务的实现
多媒体服务的路径:\frameworks\base\media\libmediaplayerservice
mediaPlayerService.h头文件中定义了 ,是IMediaplayer的实现
class MediaPlayerService : public BnMediaPlayerService{
class AudioOutput : public MediaPlayerBase::AudioSink {}
class AudioCache : public MediaPlayerBase::AudioSink{}
class Client : public BnMediaPlayer {}
}
是IMeciaRecorder的实现
class MediaRecorderClient : public BnMediaRecorder{}
是IMediadataRetriever的实现
class MetadataRetrieverClient : public BnMediaMetadataRetriever{}
MediaPlayerService.cpp中定义了取得媒体记录器(IMediaRecorder>)的接口
sp
{
sp
wp
Mutex::Autolock lock(mLock);
mMediaRecorderClients.add(w);
LOGV("Create new media recorder client from pid %d", pid);
return recorder;
}
取得媒体播放器的媒体元信息
sp
{
sp
LOGV("Create new media retriever from pid %d", pid);
return retriever;
}
MediaPlayService 类中创建媒体播放器的过程:
1
sp
int fd, int64_t offset, int64_t length, int audioSessionId)
{
int32_t connId = android_atomic_inc(&mNextConnId);
//创建mediaPlayerService::Client类
sp
LOGV("Create new client(%d) from pid %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
Mutex::Autolock lock(mLock);
mClients.add(w);
}
::close(fd);
return c;
}
static sp
notify_callback_f notifyFunc)
{
sp
switch (playerType) {//根据playerType的类型建立不同的播放器
#ifndef NO_OPENCORE
case PV_PLAYER:
LOGV(" create PVPlayer");
p = new PVPlayer();
break;
#endif
case SONIVOX_PLAYER:
LOGV(" create MidiFile");
p = new MidiFile();
break;
case STAGEFRIGHT_PLAYER:
LOGV(" create StagefrightPlayer");
p = new StagefrightPlayer;
break;
case TEST_PLAYER:
LOGV("Create Test Player stub");
p = new TestPlayerStub();
break;
}
if (p != NULL) {
if (p->initCheck() == NO_ERROR) {
p->setNotifyCallback(cookie, notifyFunc);
} else {
p.clear();
}
}
if (p == NULL) {
LOGE("Failed to create player object");
}
return p;
}
PVPlayerMidiFile和VorbisPlayer三个都继承MediaPlayInterface得到的,MediaPlayerInterface是继承MediaPlayerBase得到,三者具有相同的接口类型,三者在建立之后通过MediaPlayerBase接口来控制他们
媒体播放器的实现结构如下图所示
MediaPlayerService::AudioOutput实现audio输出环节的封装,由Audio系统来实现,主要是调用AudioTrakc的接口
status_t MediaPlayerService::AudioOutput::open(
uint32_t sampleRate, int channelCount, int format, int bufferCount,
AudioCallback cb, void *cookie)
{
mCallback = cb;
mCallbackCookie = cookie;
// Check argument "bufferCount" against the mininum buffer count
if (bufferCount < mMinBufferCount) {
LOGD("bufferCount (%d) is too small and increased to %d", bufferCount, mMinBufferCount);
bufferCount = mMinBufferCount;
}
LOGV("open(%u, %d, %d, %d, %d)", sampleRate, channelCount, format, bufferCount,mSessionId);
if (mTrack) close();
int afSampleRate;
int afFrameCount;
int frameCount;
if (AudioSystem::getOutputFrameCount(&afFrameCount, mStreamType) != NO_ERROR) {
return NO_INIT;
}
if (AudioSystem::getOutputSamplingRate(&afSampleRate, mStreamType) != NO_ERROR) {
return NO_INIT;
}
//获得帧数和采样率
frameCount = (sampleRate*afFrameCount*bufferCount)/afSampleRate;
AudioTrack *t;
if (mCallback != NULL) {
t = new AudioTrack(
mStreamType,
sampleRate,
format,
(channelCount == 2) ? AudioSystem::CHANNEL_OUT_STEREO : AudioSystem::CHANNEL_OUT_MONO,
frameCount,
0 /* flags */,
CallbackWrapper,
this,
0,
mSessionId);
} else {
t = new AudioTrack(
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("Unable to create audio track");
delete t;
return NO_INIT;
}
LOGV("setVolume");
t->setVolume(mLeftVolume, mRightVolume);
mMsecsPerFrame = 1.e3 / (float) sampleRate;
mLatency = t->latency();
mTrack = t;
t->setAuxEffectSendLevel(mSendLevel);
return t->attachAuxEffect(mAuxEffectId);
;
}
音频输出的接口
ssize_t MediaPlayerService::AudioOutput::write(const void* buffer, size_t size)
{
LOG_FATAL_IF(mCallback != NULL, "Don't call write if supplying a callback.");
//LOGV("write(%p, %u)", buffer, size);
if (mTrack) {
ssize_t ret = mTrack->write(buffer, size);
return ret;
}
return NO_INIT;
}
多媒体的JNI部分
本地调用部分的代码路径为:
Frameworks/base/media/jni
主要文件有:
Android 2。3后改用stagefright
\frameworks\base\media\libstagefright
两者的处理机制不同
openCore 的处理流程如下:
Stagefright部分的处理流程如下:
从上面可以看出
1 OpenCore的 parser和 dec是分离的,各行其职,stagefright则是邦在一起作为一个独立的原子操作
2 stagefright通过callback 和videoevent 来驱动数据输出, openCore是通过sink-node节点控制输出
3 Opencore中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()函数中直接从环境中得到并设置了。
static void
android_media_MediaPlayer_prepare(JNIEnv *env, jobject thiz)
{
sp
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", "Prepare failed." );
}
static void
android_media_MediaRecorder_prepare(JNIEnv *env, jobject thiz)
{
LOGV("prepare");
sp
jobject surface = env->GetObjectField(thiz, fields.surface);
if (surface != NULL) {
const sp native_surface = get_surface(env, surface);
// The application may misbehave and
// the preview surface becomes unavailable
if (native_surface.get() == 0) {
LOGE("Application lost the surface");
jniThrowException(env, "java/io/IOException", "invalid preview surface");
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", "setPreviewSurface failed.")) {
return;
}
}
process_media_recorder_call(env, mr->prepare(), "java/io/IOException", "prepare failed.");
}
多媒体部分的java部分代码
Java框架类的路径为:
frameworks\base\media\java\android\media
主要文介绍;
:
MediaFile.java 文件提供了媒体文件的文件类型,
MediaPlayerMediaRecorderMediaMetadataRecorder 等类基本上和JNI层的内容一一对应
MediaScanner在这里实现有客户端内容比较多
其中MedisPlayer中对视频输出和取景器预览的接口
public void setDisplay(SurfaceHolder sh) {
mSurfaceHolder = sh;
if (sh != null) {
mSurface = sh.getSurface();
} else {
mSurface = null;
}
_setVideoSurface();
updateSurfaceScreenOn();
}
MediaRecorder中对视频输出和取景器预览的接口
public void setPreviewDisplay(Surface sv) {
mSurface = sv;
}
Java框架层没有直接使用传递参数的方式,而是使用了保存在环境中再传递的方式
Android.widgetVideoView类. 是一个UI元素
代码路径为:frameworks\base\core\java\android\widget
使用该类,可以不用再调用MediaPlayer类,节省了一些中间环节
public class VideoView extends SurfaceView implements MediaPlayerControl {
public void setVideoPath(String path) { }//设置源文件路径
public void setVideoURI(Uri uri) {}//设置视频的URL
public void start() {}//开始播放
public void stopPlayback() {}//停止播放
public void pause() { }//暂停播放
public void seekTo(int msec) {}//更改播放位置
多媒体实现的核心部分OpenCore
多媒体系统框架PacketVideo的开源版本OpenCore是android 多媒体本地实现在的核心
它为android提供的引警如下
PVPlayer 媒体播放器的功能音频和视频的回放功能
PVAuthor 媒体记录器功能音频和视频的录制
OpenCore的层次结构
自上而下分为
OSCL(operation system compatibilitylibrary ,操作系统兼容库) 类似一个基础的c++库
PVMF(packetVideoMultimediaFramework多媒体框架)packetVideo 的基本框架, 例如nodea基类,输入输出的抽象类
文件格式处理,文件解析(parser)和组成(composer)两个部分,
各种Node,是packetVideo 中的基本功能模块,
播放器(Player Engine) 播放器引擎
记录器 (author Engine) 媒体记录器引擎
注: 在openCore2.X之后, 开始提供了2-way engine 两路引擎用于构建视频电话
在使用OpenCore的SDK时,需要在应用层实现一个适配器
PVPlaytr和PVAuthor就是基于OpenCore的下层功能和接口构建军的应用层的库
在android 系统中OpenCore的代码路径为:externam/opencore/
Stagefright整体框图:
Android froyo版本对多媒体引擎作了变动.新添加了stagefright框架,但并没有完全抛弃opencore
主要是作了一个omx 层,仅是对opencore的omx-component部分作了引用,,它在android 系统中作为
共享库(libstagefright.so)存在,其中的module--awesomePlayer用来播放video/audio
Awesomeplayer 提供的API可以供上次的应用(java/JNI)来调用
StageFrigtht数据流封装
1 MediaExtractor.cpp根据数据源DataSource生成MediaExtractor
具体实现是通过调用(代码路径为;frameworks\base\media\libstagefright)
sp
通过DateSource的source->sniff(&tmp, &confidence, &meta)来探测数据类型
2AwesomePlayer.cpp把音视频轨道分离,生成mVideoTrack 和MediaSource
部分代码如下:
if (!haveVideo && !strncasecmp(mime, "video/", 6)) {
setVideoSource(extractor->getTrack(i));
haveVideo = true;
} else if (!haveAudio && !strncasecmp(mime, "audio/", 6)) {
setAudioSource(extractor->getTrack(i));
haveAudio = true;
if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_VORBIS)) {
// Only do this for vorbis audio, none of the other audio
// formats even support this ringtone specific hack and
// retrieving the metadata on some extractors may turn out
// to be very expensive.
sp
int32_t loop;
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)
4 stageFright的decode
经过流的封装得到两个MediaSource ,其实是两个OMXCodec,
AwesomePlayer和mAudioPlayer都是从mediaSource中得到数据进行播放, 最终需要渲染的原始视频数据,也就是说OMXCodec中得到的是原始数据
部分代码如下:
sp
const sp
const sp
//他的对象成员是一个keyedVector
//里面存放的是代表mediaSource格式信息的键值对
const sp
const char *matchComponentName, //指定一种codec用于生成omxcodec
uint32_t flags) {
//首先调用findMatchingCodecs()方法,找到对应的Codec,
findMatchingCodecs(
mime, createEncoder, matchComponentName, flags, &matchingCodecs);
//找到以后为当前的IOMX分配并注册监听事件,
status_t err = omx->allocateNode(componentName, observer, &node);
//这样就得到了OMXCodec
sp
omx, node, quirks,
createEncoder, mime, componentName,
source);
}
在AwesomePlayer中得到这个OMXCodec后,首先调用mVideoSource->start()进行初始化,主要有两件事
1向openMAX发送命令
err = mOMX->sendCommand(mNode, OMX_CommandStateSet, OMX_StateIdle);
2 err = allocateBuffers();
分配两个缓冲区,freeBuffersOnPort()分别用于输入和输出
当awesomePlayer开始播放以后,通过mVideoSource->read(&mVideoBuffer,&options) 读取数据
OMXCodec.read分两部来实现数据读取,
1 通过调用draininputBuffers()对mPortBuffers[kPortindexOutput]进行填充,这一步完成parse
由OpenMAX从数据源把demux后的数据读取到输入缓,作为OpenMAX的输入
2 通过fillOutputBuffers()对mPortBuffers[kPortIndexInput]进行填充, 这一步完成decode,
由OpenMAX对输入缓冲区的数据进行解码
3 AwesomePlayer通过mVideoRenderer->reder()对经过parse和decode处理的数据进行渲染
mVideoRenderer = new AwesomeLocalRenderer(
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------------>mVideoTrack mAudioTrack(音视频数据流)--------------->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/下运行./adb shell文件,再运行logcat
n打开Gallery选择视频文件运行,并同步查看log。
【android系统学习笔记六】转载于:https://www.cnblogs.com/retacn-yue/archive/2012/09/03/2761392.html
推荐阅读
- android第三方框架(五)ButterKnife
- 由浅入深理解AOP
- 继续努力,自主学习家庭Day135(20181015)
- python学习之|python学习之 实现QQ自动发送消息
- Android中的AES加密-下
- 带有Hilt的Android上的依赖注入
- 一起来学习C语言的字符串转换函数
- 定制一套英文学习方案
- 如何在Mac中的文件选择框中打开系统隐藏文件夹
- 漫画初学者如何学习漫画背景的透视画法(这篇教程请收藏好了!)