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。