您的位置:首页 > 移动开发 > Android开发

android系统学习笔记六

2012-09-03 15:16 197 查看
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();

void died();

void disconnect();

status_t setDataSource(

const char *url,//设置数据源url

const KeyedVector<String8, String8> *headers);

status_t setDataSource(int fd, int64_t offset, int64_t length);//设置数据源文件

status_t setVideoSurface(const sp<Surface>& surface);//设视频输出界面

status_t setListener(const sp<MediaPlayerListener>& listener);//设置临听

status_t prepare();//准备播放

status_t prepareAsync();//异部准备播放

status_t start();//开始

status_t stop();//停止

status_t pause();//暂停

bool isPlaying();//是否正在播放

status_t getVideoWidth(int *w);//获取视频播放的宽

status_t getVideoHeight(int *h);//获取视频播放的高

status_t seekTo(int msec);//跳转到指定位置

status_t getCurrentPosition(int *msec);//取得当前的播放位置

status_t getDuration(int *msec);//播放的持续时间(总时长)

status_t reset();//复位

status_t setAudioStreamType(int type);//设置音频流的格式

status_t setLooping(int loop);// 设置循环

bool isLooping();//是否循环

status_t setVolume(float leftVolume, float rightVolume);//设置音量

void notify(int msg, int ext1, int ext2);//通知函数

static sp<IMemory> decode(const char* url, uint32_t *pSampleRate, int* pNumChannels, int* pFormat);

static sp<IMemory> decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels, int* pFormat);

status_t invoke(const Parcel& request, Parcel *reply);

status_t setMetadataFilter(const Parcel& filter);

status_t getMetadata(bool update_only, bool apply_filter, Parcel *metadata);

status_t suspend();

status_t resume();

status_t setAudioSessionId(int sessionId);

int getAudioSessionId();

status_t setAuxEffectSendLevel(float level);

status_t attachAuxEffect(int effectId);

private:

void clear_l();

status_t seekTo_l(int msec);

status_t prepareAsync_l();

status_t getDuration_l(int *msec);

status_t setDataSource(const sp<IMediaPlayer>& player);

sp<IMediaPlayer> mPlayer;

thread_id_t mLockThreadId;

Mutex mLock;

Mutex mNotifyLock;

Condition mSignal;

sp<MediaPlayerListener> mListener;

void* mCookie;

media_player_states mCurrentState;

int mDuration;

int mCurrentPosition;

int mSeekPosition;

bool mPrepareSync;

status_t mPrepareStatus;

int mStreamType;

bool mLoop;

float mLeftVolume;

float mRightVolume;

int mVideoWidth;

int mVideoHeight;

int mAudioSessionId;

float mSendLevel;

};

IMeciaplayer.h 媒体播和器服务部分的接口(和上层的mediaplay 中的接口方法类似)

被BnMedaplayer继承,提供Binder通信本地实现基础

部分代码如下:

//继承mediaplayerBase,通过autoFlinger输出

class MediaPlayerInterface : public MediaPlayerBase{

public:

virtual ~MediaPlayerInterface() { }

virtual bool hardwareOutput() { return false; }

virtual void setAudioSink(const sp<AudioSink>& audioSink) { mAudioSink = audioSink; }

protected:

sp<AudioSink> mAudioSink;//音频轮输出设备的抽像接口

};

// Implement this class for media players that output directo to hardware

//直接从硬功夫件进行音频输出

class MediaPlayerHWInterface : public MediaPlayerBase//继承mediaplayerBaser

{

public:

virtual ~MediaPlayerHWInterface() {}

virtual bool hardwareOutput() { return true; }

virtual status_t setVolume(float leftVolume, float rightVolume) = 0;

virtual status_t setAudioStreamType(int streamType) = 0;

};

mediaplayerInterface.h

PVPlayer.h是媒体播放器实现层的接口 (是opencore媒体播放器实现的头文件)

继承自MediaPlayerInterface

部分代码如下:

class PVPlayer : public MediaPlayerInterface

{

public:

PVPlayer();

virtual ~PVPlayer();

virtual status_t initCheck();

virtual status_t setDataSource(

const char *url, const KeyedVector<String8, String8> *headers);

virtual status_t setDataSource(int fd, int64_t offset, int64_t length);

virtual status_t setVideoSurface(const sp<ISurface>& surface);

virtual status_t prepare();

virtual status_t prepareAsync();

virtual status_t start();

virtual status_t stop();

virtual status_t pause();

virtual bool isPlaying();

virtual status_t seekTo(int msec);

virtual status_t getCurrentPosition(int *msec);

virtual status_t getDuration(int *msec);

virtual status_t reset();

virtual status_t setLooping(int loop);

virtual player_type playerType() { return PV_PLAYER; }

virtual status_t invoke(const Parcel& request, Parcel *reply);

virtual status_t getMetadata(

const SortedVector<media::Metadata::Type>& ids,

Parcel *records);

// make available to PlayerDriver

void sendEvent(int msg, int ext1=0, int ext2=0) { MediaPlayerBase::sendEvent(msg, ext1, ext2); }

private:

static void do_nothing(status_t s, void *cookie, bool cancelled) { }

static void run_init(status_t s, void *cookie, bool cancelled);

static void run_set_video_surface(status_t s, void *cookie, bool cancelled);

static void run_set_audio_output(status_t s, void *cookie, bool cancelled);

static void run_prepare(status_t s, void *cookie, bool cancelled);

static void check_for_live_streaming(status_t s, void *cookie, bool cancelled);

PlayerDriver* mPlayerDriver;

char * mDataSourcePath;

bool mIsDataSourceSet;

sp<ISurface> mSurface;

int mSharedFd;

status_t mInit;

int mDuration;

#ifdef MAX_OPENCORE_INSTANCES

static volatile int32_t sNumInstances;

#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<IMediaRecorder> createMediaRecorder(pid_t pid) = 0;

virtual sp<IMediaMetadataRetriever> createMetadataRetriever(pid_t pid) = 0;

/创建IMediaPlayer

virtual sp<IMediaPlayer> create(pid_t pid, const sp<IMediaPlayerClient>& client,

const char* url, const KeyedVector<String8, String8> *headers = NULL,

int audioSessionId = 0) = 0;

virtual sp<IMediaPlayer> create(pid_t pid, const sp<IMediaPlayerClient>& client,

int fd, int64_t offset, int64_t length, int audioSessionId) = 0;

//用于直接解码

virtual sp<IMemory> decode(const char* url, uint32_t *pSampleRate, int* pNumChannels, int* pFormat) = 0;

virtual sp<IMemory> decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels, int* pFormat) = 0;

virtual sp<IOMX> getOMX() = 0;

};

// ----------------------------------------------------------------------------

class BnMediaPlayerService: public BnInterface<IMediaPlayerService>

{

public:

virtual status_t onTransact( 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();

void died();

status_t initCheck();

status_t setCamera(const sp<ICamera>& camera); //设置camera作为输入设备

status_t setPreviewSurface(const sp<Surface>& surface); //设置视频预览界面

status_t setVideoSource(int vs); //视频数据源( 枚举值)

status_t setAudioSource(int as); //音频数据源 (同上)

status_t setOutputFormat(int of); //设置输出格式

status_t setVideoEncoder(int ve); //设置视频编码格式

status_t setAudioEncoder(int ae); //设置音频编码格式

status_t setOutputFile(const char* path); //设置输出文件路径

status_t setOutputFile(int fd, int64_t offset, int64_t length); //设置输出文件的文件描述符

status_t setVideoSize(int width, int height); //设置视频尺寸

status_t setVideoFrameRate(int frames_per_second); //设置视频帧率

status_t setParameters(const String8& params); //设置其他参数

status_t setListener(const sp<MediaRecorderListener>& listener); //设置临听

status_t prepare(); //准备录制

status_t getMaxAmplitude(int* max); //获得最大增益

status_t start(); //开始

status_t stop(); //停止

status_t reset(); //复位

status_t init(); //初始化记录器

status_t close(); //关闭记录器

status_t release(); //释放资源

void notify(int msg, int ext1, int ext2);

private:

void doCleanUp();

status_t doReset();

sp<IMediaRecorder> mMediaRecorder;

sp<MediaRecorderListener> mListener;

media_recorder_states mCurrentState;

bool mIsAudioSourceSet;

bool mIsVideoSourceSet;

bool mIsAudioEncoderSet;

bool mIsVideoEncoderSet;

bool mIsOutputFileSet;

Mutex mLock;

Mutex mNotifyLock;

};

};

IMediaRecorder.h 媒体记录器的部分实现接口

部分代码如下:

class IMediaRecorder: public IInterface

{

public:

DECLARE_META_INTERFACE(MediaRecorder);

virtual status_t setCamera(const sp<ICamera>& camera) = 0;

virtual status_t setPreviewSurface(const sp<ISurface>& surface) = 0;

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;

virtual status_t setParameters(const String8& params) = 0;

virtual status_t setListener(const sp<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;

virtual status_t init() = 0;

virtual status_t close() = 0;

virtual status_t release() = 0;

};

// ----------------------------------------------------------------------------

class BnMediaRecorder: public BnInterface<IMediaRecorder>

{

public:

virtual status_t onTransact( 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<IMemory> getFrameAtTime(int64_t timeUs, int option); //捕获帧

sp<IMemory> extractAlbumArt(); // 抽取

const char* extractMetadata(int keyCode); //抽取元信息

IMediaMetadataRetriever

MediaMetadataRetrieverInterface.h 实现的接口文件

PVMetadataRetriever.h 下层实现的接口

class MediaMetadataRetrieverBase : public RefBase{}

class MediaMetadataRetrieverInterface : public MediaMetadataRetrieverBase{}

class PVMetadataRetriever : public MediaMetadataRetrieverInterface{}

媒体扫描器的头文件

MediaScanner .h scanner的接口扫描一个文件或者一个文件夹取得文件格式,会调用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<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中具有如下定义

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<IMediaRecorder> MediaPlayerService::createMediaRecorder(pid_t pid)

{

sp<MediaRecorderClient> recorder = new MediaRecorderClient(this, pid);

wp<MediaRecorderClient> w = recorder;

Mutex::Autolock lock(mLock);

mMediaRecorderClients.add(w);

LOGV("Create new media recorder client from pid %d", pid);

return recorder;

}

取得媒体播放器的媒体元信息

sp<IMediaMetadataRetriever> MediaPlayerService::createMetadataRetriever(pid_t pid)

{

sp<MetadataRetrieverClient> retriever = new MetadataRetrieverClient(pid);

LOGV("Create new media retriever from pid %d", pid);

return retriever;

}

MediaPlayService 类中创建媒体播放器的过程:

1

sp<IMediaPlayer> MediaPlayerService::create(pid_t pid, const sp<IMediaPlayerClient>& client,

int fd, int64_t offset, int64_t length, int audioSessionId)

{

int32_t connId = android_atomic_inc(&mNextConnId);

//创建mediaPlayerService::Client类

sp<Client> c = new Client(this, pid, connId, client, audioSessionId);

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<Client> w = c;

Mutex::Autolock lock(mLock);

mClients.add(w);

}

::close(fd);

return c;

}

static sp<MediaPlayerBase> createPlayer(player_type playerType, void* cookie,

notify_callback_f notifyFunc)

{

sp<MediaPlayerBase> p;

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;

}

PVPlayer MidiFile和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<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", "Prepare failed." );

}

static void

android_media_MediaRecorder_prepare(JNIEnv *env, jobject thiz)

{

LOGV("prepare");

sp<MediaRecorder> mr = getMediaRecorder(env, thiz);

jobject surface = env->GetObjectField(thiz, fields.surface);

if (surface != NULL) {

const sp<Surface> 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 文件提供了媒体文件的文件类型,

MediaPlayer MediaRecorder MediaMetadataRecorder 等类基本上和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 compatibility library ,操作系统兼容库) 类似一个基础的c++库

PVMF (packetVideo Multimedia Framework 多媒体框架) 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<MediaExtractor> MediaExtractor::Create( const sp<DataSource> &source, const char *mime) {}

通过DateSource的source->sniff(&tmp, &confidence, &meta)来探测数据类型

2 AwesomePlayer.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<MetaData> fileMeta = extractor->getMetaData();

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<MediaSource> OMXCodec::Create(

const sp<IOMX> &omx, //OMXNodeInstance对象的实例

const sp<MetaData> &meta, bool createEncoder, //由MediaSource.getFormat获取得到,

//他的对象成员是一个keyedVector<uint32_t,typed_data>

//里面存放的是代表mediaSource格式信息的键值对

const sp<MediaSource> &source, //mediaExtractor

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<OMXCodec> codec = new OMXCodec(

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(音视频数据流)--------------->mVideoSource mAudioSource(音视频解码器)

注: 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。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: