本文主要介绍的是Android中很重要也最为复杂的媒体播放器(MediaPlayer)部分的架构。对于Android这样一个完整又相对复杂的系统,一个MediaPlayer功能的实现不在其具体的功能,而是具体功能如何适应Android系统Android MediaPlayer的主要具体实现在OpenCore的Player中,这部分不是本文的关注点。本文关注的是MediaPlayer系统的架构,其他的一些Android的应用程序也使用类似的架构。
第一部分 MediaPlayer概述
Android的MediaPlayer包含了Audio和video的播放功能,在Android的界面上,Music和Video两个应用程序都是调用MediaPlayer实现的。
MediaPlayer在底层是基于OpenCore(PacketVideo)的库实现的,为了构建一个MediaPlayer程序,上层还包含了进程间通讯等内容,这种进程间通讯的基础是Android基本库中的Binder机制。
以开源的Android为例,MediaPlayer的代码主要在以下的目录中:
JAVA程序的路径:
packages/apps/Music/src/com/android/music/
JAVA类的路径:
frameworks/base/media/java/android/media/MediaPlayer.java
JAVA本地调用部分(JNI):
frameworks/base/media/jni/android_media_MediaPlayer.cpp
这部分内容编译成为目标是libmedia_jni.so。
主要的头文件在以下的目录中:
frameworks/base/include/media/
多媒体底层库在以下的目录中:
frameworks/base/media/libmedia/
这部分的内容被编译成库libmedia.so。
多媒体服务部分:
frameworks/base/media/libmediaplayerservice/
文件为mediaplayerservice.h和mediaplayerservice.cpp
这部分内容被编译成库libmediaplayerservice.so。
基于OpenCore的多媒体播放器部分
external/opencore/
这部分内容被编译成库libopencoreplayer.so。
从程序规模上来看,libopencoreplayer.so是主要的实现部分,而其他的库基本上都是在其上建立的封装和为建立进程间通讯的机制。
第二部分 MediaPlayer的接口与架构
2.1 整体框架图
MediaPlayer的各个库之间的结构比较复杂,可以用下图的表示
在各个库中,libmedia.so位于核心的位置,它对上层的提供的接口主要是MediaPlayer类,类libmedia_jni.so通过调用MediaPlayer类提供对JAVA的接口,并且实现了android.media.MediaPlayer类。
libmediaplayerservice.so是Media的服务器,它通过继承libmedia.so的类实现服务器的功能,而libmedia.so中的另外一部分内容则通过进程间通讯和libmediaplayerservice.so进行通讯。libmediaplayerservice.so的真正功能通过调用OpenCore Player来完成。
MediaPlayer部分的头文件在frameworks/base/include/media/目录中,这个目录是和libmedia.so库源文件的目录frameworks/base/media/libmedia/相对应的。主要的头文件有以下几个:
IMediaPlayerClient.h
mediaplayer.h
IMediaPlayer.h
IMediaPlayerService.h
MediaPlayerInterface.h
在这些头文件mediaplayer.h提供了对上层的接口,而其他的几个头文件都是提供一些接口类(即包含了纯虚函数的类),这些接口类必须被实现类继承才能够使用。
整个MediaPlayer库和调用的关系如下图所示:
整个MediaPlayer在运行的时候,可以大致上分成Client和Server两个部分,它们分别在两个进程中运行,它们之间使用Binder机制实现IPC通讯。从框架结构上来看,IMediaPlayerService.h、IMediaPlayerClient.h和MediaPlayer.h三个类定义了MeidaPlayer的接口和架构,MediaPlayerService.cpp和mediaplayer.coo两个文件用于MeidaPlayer架构的实现,MeidaPlayer的具体功能在PVPlayer(库libopencoreplayer.so)中的实现。
2.2 头文件IMediaPlayerClient.h
IMediaPlayerClient.h用于描述一个MediaPlayer客户端的接口,描述如下所示:
- class IMediaPlayerClient: public IInterface
- {
- public:
- DECLARE_META_INTERFACE(MediaPlayerClient);
- virtual void notify(int msg, int ext1, int ext2) = 0;
- };
- class BnMediaPlayerClient: public BnInterface<IMediaPlayerClient>
- {
- public:
- virtual status_t onTransact( uint32_t code,
- const Parcel& data,
- Parcel* reply,
- uint32_t flags = 0);
- };
在定义中,IMediaPlayerClient类继承IInterface,并定义了一个MediaPlayer客户端的接口,BnMediaPlayerClient继承了BnInterface<IMediaPlayerClient>,这是为基于Android的基础类Binder机制实现在进程通讯而构建的。事实上,根据BnInterface类模版的定义BnInterface<IMediaPlayerClient>类相当于双继承了BnInterface和ImediaPlayerClient。这是Android一种常用的定义方式。
2.3 头文件mediaplayer.h
mediaplayer.h是对外的接口类,它最主要是定义了一个MediaPlayer类:
- class MediaPlayer : public BnMediaPlayerClient
- {
- public:
- MediaPlayer();
- ~MediaPlayer();
- void onFirstRef();
- void disconnect();
- status_t setDataSource(const char *url);
- 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);
- 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);
- static sp<IMemory> decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels);
- //……
- }
从接口中可以看出MediaPlayer类刚好实现了一个MediaPlayer的基本操作,例如播放(start)、停止(stop)、暂停(pause)等。
另外的一个类DeathNotifier在MediaPlayer类中定义,它继承了IBinder类中的DeathRecipient类:
- class DeathNotifier: public IBinder:: DeathRecipient
- {
- public:
- DeathNotifier() {}
- virtual ~DeathNotifier();
- virtual void binderDied(const wp<IBinder>& who);
- };
事实上,MediaPlayer类正是间接地继承了IBinder,而MediaPlayer:: DeathNotifier类继承了IBinder:: DeathRecipient,这都是为了实现进程间通讯而构建的。
2.4 头文件IMediaPlayer.h
IMediaPlayer.h主要的的内容是一个实现MediaPlayer功能的接口,它的主要定义如下所示:
- class IMediaPlayer: public IInterface
- {
- public:
- DECLARE_META_INTERFACE(MediaPlayer);
- virtual void disconnect() = 0;
- virtual status_t setVideoSurface(const sp<ISurface>& surface) = 0;
- virtual status_t prepareAsync() = 0;
- virtual status_t start() = 0;
- virtual status_t stop() = 0;
- virtual status_t pause() = 0;
- virtual status_t isPlaying(bool* state) = 0;
- virtual status_t getVideoSize(int* w, int* h) = 0;
- virtual status_t seekTo(int msec) = 0;
- virtual status_t getCurrentPosition(int* msec) = 0;
- virtual status_t getDuration(int* msec) = 0;
- virtual status_t reset() = 0;
- virtual status_t setAudioStreamType(int type) = 0;
- virtual status_t setLooping(int loop) = 0;
- virtual status_t setVolume(float leftVolume, float rightVolume) = 0;
- };
- class BnMediaPlayer: public BnInterface<IMediaPlayer>
- {
- public:
- virtual status_t onTransact( uint32_t code,
- const Parcel& data,
- Parcel* reply,
- uint32_t flags = 0);
- };
在IMediaPlayer类中,主要定义MediaPlayer的功能接口,这个类必须被继承才能够使用。值得注意的是,这些接口和MediaPlayer类的接口有些类似,但是它们并没有直接的关系。事实上,在MediaPlayer类的各种实现中,一般都会通过调用IMediaPlayer类的实现类来完成。
2.5 头文件IMediaPlayerService.h
IMediaPlayerService.h用于描述一个MediaPlayer的服务,定义方式如下所示:
- class IMediaPlayerService: public IInterface
- {
- public:
- DECLARE_META_INTERFACE(MediaPlayerService);
- virtual sp<IMediaPlayer> create(pid_t pid, const sp<IMediaPlayerClient>& client, const char* url) = 0;
- virtual sp<IMediaPlayer> create(pid_t pid, const sp<IMediaPlayerClient>& client, int fd, int64_t offset, int64_t length) = 0;
- virtual sp<IMemory> decode(const char* url, uint32_t *pSampleRate, int* pNumChannels) = 0;
- virtual sp<IMemory> decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels) = 0;
- };
- class BnMediaPlayerService: public BnInterface<IMediaPlayerService>
- {
- public:
- virtual status_t onTransact( uint32_t code,
- const Parcel& data,
- Parcel* reply,
- uint32_t flags = 0);
- };
由于具有纯虚函数,IMediaPlayerService 以及BnMediaPlayerService必须被继承实现才能够使用,在IMediaPlayerService定义的create和decode等接口,事实上是必须被继承者实现的内容。注意,create的返回值的类型是sp<IMediaPlayer>,这个IMediaPlayer正是提供实现功能的接口。
第三部分 MediaPlayer的主要实现分析
3.1 JAVA程序部分
在packages/apps/Music/src/com/android/music/目录的MediaPlaybackService.java文件中,包含了对MediaPlayer的调用。
在MediaPlaybackService.java中包含对包的引用:
import android.media.MediaPlayer;
在MediaPlaybackService类的内部,定义了MultiPlayer类:
- private class MultiPlayer {
- private MediaPlayer mMediaPlayer = new MediaPlayer();
- }
MultiPlayer类中使用了MediaPlayer类,其中有一些对这个MediaPlayer的调用,调用的过程如下所示:
- mMediaPlayer.reset();
- mMediaPlayer.setDataSource(path);
- mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
reset、setDataSource和setAudioStreamType等接口就是通过JAVA本地调用(JNI)来实现的。
3.2 MediaPlayer的JAVA本地调用部分
MediaPlayer的JAVA本地调用部分在目录frameworks/base/media/jni/的android_media_MediaPlayer.cpp中的文件中实现。
android_media_MediaPlayer.cpp之中定义了一个JNINativeMethod(JAVA本地调用方法)类型的数组gMethods,如下所示:
- static JNINativeMethod gMethods[] = {
- {"setDataSource", "(Ljava/lang/String;)V", (void *)android_media_MediaPlayer_setDataSource},
- {"setDataSource", "(Ljava/io/FileDescriptor;JJ)V", (void *)android_media_MediaPlayer_setDataSourceFD},
- {"prepare", "()V", (void *)android_media_MediaPlayer_prepare},
- {"prepareAsync", "()V", (void *)android_media_MediaPlayer_prepareAsync},
- {"_start", "()V", (void *)android_media_MediaPlayer_start},
- {"_stop", "()V", (void *)android_media_MediaPlayer_stop},
- {"getVideoWidth", "()I", (void *)android_media_MediaPlayer_getVideoWidth},
- {"getVideoHeight", "()I", (void *)android_media_MediaPlayer_getVideoHeight},
- {"seekTo", "(I)V", (void *)android_media_MediaPlayer_seekTo},
- {"_pause", "()V", (void *)android_media_MediaPlayer_pause},
- {"isPlaying", "()Z", (void *)android_media_MediaPlayer_isPlaying},
- {"getCurrentPosition", "()I", (void *)android_media_MediaPlayer_getCurrentPosition},
- {"getDuration", "()I", (void *)android_media_MediaPlayer_getDuration},
- {"_release", "()V", (void *)android_media_MediaPlayer_release},
- {"_reset", "()V", (void *)android_media_MediaPlayer_reset},
- {"setAudioStreamType", "(I)V", (void *)android_media_MediaPlayer_setAudioStreamType},
- {"setLooping", "(Z)V", (void *)android_media_MediaPlayer_setLooping},
- {"setVolume", "(FF)V", (void *)android_media_MediaPlayer_setVolume},
- {"getFrameAt", "(I)Landroid/graphics/Bitmap;", (void *)android_media_MediaPlayer_getFrameAt},
- {"native_setup", "(Ljava/lang/Object;)V", (void *)android_media_MediaPlayer_native_setup},
- {"native_finalize", "()V", (void *)android_media_MediaPlayer_native_finalize},
- }
JNINativeMethod的第一个成员是一个字符串,表示了JAVA本地调用方法的名称,这个名称是在JAVA程序中调用的名称;第二个成员也是一个字符串,表示JAVA本地调用方法的参数和返回值;第三个成员是JAVA本地调用方法对应的C语言函数。
其中android_media_MediaPlayer_reset函数的实现如下所示:
- static void android_media_MediaPlayer_reset(JNIEnv *env, jobject thiz)
- {
- sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
- if (mp == NULL ) {
- jniThrowException(env, "java/lang/IllegalStateException", NULL);
- return;
- }
- process_media_player_call( env, thiz, mp->reset(), NULL, NULL );
- }
在android_media_MediaPlayer_reset的调用中,得到一个MediaPlayer指针,通过对它的调用实现实际的功能。
register_android_media_MediaPlayer用于将gMethods注册为的类"android/media/MediaPlayer",其实现如下所示。
- static int register_android_media_MediaPlayer(JNIEnv *env)
- {
- jclass clazz;
- clazz = env->FindClass("android/media/MediaPlayer");
- // ......
- return AndroidRuntime::registerNativeMethods(env, "android/media/MediaPlayer", gMethods, NELEM(gMethods));
- }
"android/media/MediaPlayer"对应JAVA的类android.media.MediaPlayer。
3.3 mediaplayer的核心库libmedia.so
libs/media/mediaplayer.cpp文件用于实现mediaplayer.h提供的接口,其中一个重要的片段如下所示:
- const sp<IMediaPlayerService>& MediaPlayer::getMediaPlayerService()
- {
- Mutex::Autolock _l(mServiceLock);
- if (mMediaPlayerService.get() == 0) {
- sp<IServiceManager> sm = defaultServiceManager();
- sp<IBinder> binder;
- do {
- binder = sm->getService(String16("media.player"));
- if (binder != 0)
- break;
- LOGW("MediaPlayerService not published, waiting...");
- usleep(500000); // 0.5 s
- } while(true);
- if (mDeathNotifier == NULL) {
- mDeathNotifier = new DeathNotifier();
- }
- binder->linkToDeath(mDeathNotifier);
- mMediaPlayerService = interface_cast<IMediaPlayerService>(binder);
- }
- LOGE_IF(mMediaPlayerService==0, "no MediaPlayerService!?");
- return mMediaPlayerService;
- }
其中最重要的一点是binder = sm->getService(String16("media.player"));这个调用用来得到一个名称为"media.player"的服务,这个调用返回值的类型为IBinder,根据实现将其转换成类型IMediaPlayerService使用。
一个具体的函数setDataSource如下所示:
- status_t MediaPlayer::setDataSource(const char *url)
- {
- LOGV("setDataSource(%s)", url);
- status_t err = UNKNOWN_ERROR;
- if (url != NULL) {
- const sp<IMediaPlayerService>& service(getMediaPlayerService());
- if (service != 0) {
- sp<IMediaPlayer> player(service->create(getpid(), this, url));
- err = setDataSource(player);
- }
- }
- return err;
- }
在函数setDataSource函数中,调用getMediaPlayerService得到了一个IMediaPlayerService,又从IMediaPlayerService中得到了IMediaPlayer类型的指针,通过这个指针进行着具体的操作。
其他一些函数的实现也与setDataSource类似。
libmedia.so中的其他一些文件与头文件的名称相同,它们是:
libs/media/IMediaPlayerClient.cpp
libs/media/IMediaPlayer.cpp
libs/media/IMediaPlayerService.cpp
为了实现Binder的具体功能,在这些类中还需要实现一个BpXXX的类,例如IMediaPlayerClient.cpp的实现如下所示:l
- class BpMediaPlayerClient: public BpInterface<IMediaPlayerClient>
- {
- public:
- BpMediaPlayerClient(const sp<IBinder>& impl)
- : BpInterface<IMediaPlayerClient>(impl){}
- virtual void notify(int msg, int ext1, int ext2)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IMediaPlayerClient::getInterfaceDescriptor());
- data.writeInt32(msg);
- data.writeInt32(ext1);
- data.writeInt32(ext2);
- remote()->transact(NOTIFY, data, &reply, IBinder::FLAG_ONEWAY);
- }
- };
还需要实现定义宏IMPLEMENT_META_INTERFACE,这个宏将被展开,生成几个函数:
IMPLEMENT_META_INTERFACE(MediaPlayerClient, "android.hardware.IMediaPlayerClient");
以上的实现都是基于Binder框架的实现方式,只需要按照模版实现即可。其中BpXXX的类为代理类(proxy),BnXXX的类为本地类(native)。代理类的transact函数和本地类的onTransact函数实现对应的通讯。
3.4 media服务libmediaservice.so
frameworks/base/media\libmediaplayerservice目录中的MediaPlayerService.h和MediaPlayerService.cpp用于实现一个
servers/media/的服务,MediaPlayerService是继承BnMediaPlayerService的实现,在这个类的内部又定义了类Client,MediaPlayerService::Client继承了BnMediaPlayer。
- class MediaPlayerService : public BnMediaPlayerService
- {
- class Client : public BnMediaPlayer
- }
在MediaPlayerService中具有如下一个静态函数instantiate:
- void MediaPlayerService::instantiate() {
- defaultServiceManager()->addService(
- String16("media.player"), new MediaPlayerService());
- }
在instantiate函数中,调用IServiceManager的一个函数addService,向其中增加了一个名为"media.player"的服务。
这个名为"media.player"的服务和mediaplayer.cpp中调用getService中得到的使用一样名称。因此,在这里调用addService增加服务在mediaplayer.cpp中可以按照名称"media.player"来使用。这就是使用Binder实现进程间通讯的(IPC)的作用,事实上这个MediaPlayerService类是在服务中运行的,而mediaplayer.cpp调用的功能在应用中运行,二者并不是一个进程。但是在mediaplayer.cpp却像一个进程的调用一样调用MediaPlayerService的功能。
在MediaPlayerService.cpp中的createPlayer函数如下所示:
- static sp<MediaPlayerBase> createPlayer(player_type playerType, void* cookie, notify_callback_f notifyFunc)
- {
- sp<MediaPlayerBase> p;
- switch (playerType) {
- case PV_PLAYER:
- LOGV(" create PVPlayer");
- p = new PVPlayer();
- break;
- case SONIVOX_PLAYER:
- LOGV(" create MidiFile");
- p = new MidiFile();
- break;
- case VORBIS_PLAYER:
- LOGV(" create VorbisPlayer");
- p = new VorbisPlayer();
- break;
- }
- //……
- return p;
- }
在这里根据playerType的类型建立不同的播放器:对于大多数情况,类型将是PV_PLAYER,这时会调用了new PVPlayer()建立一个PVPlayer,然后将其指针转换成MediaPlayerBase来使用;对于Mini文件的情况,类型为SONIVOX_PLAYER,将会建立一个MidiFile;对于Ogg Vorbis格式的情况,将会建立一个VorbisPlayer。
(OGG Vobis是一种音频压缩格式,与MP3等的音乐格式类似,它具有完全免费、开放和没有专利限制的特点。)
值得注意的是PVPlayer、MidiFile和VorbisPlayer三个类都是继承MediaPlayerInterface得到的,而MediaPlayerInterface又是继承MediaPlayerBase得到的,因此三者具有相同接口类型。只有建立的时候会调用各自的构造函数,在建立之后,将只通过MediaPlayerBase接口来MediaPlayerBase控制它们。
在frameworks/base/media/libmediaplayerservice目录中,MidiFile.h和MidiFile.cpp的实现MidiFile,VorbisPlayer.h和VorbisPlayer.cpp实现一个VorbisPlayer。
3.5 OpenCorePlayer的实现libopencoreplayer.so
OpenCore Player在external/opencore/中实现,这个实现是一个基于OpenCore的Player的实现。具体实现的文件为playerdriver.cpp。其中实现了两个类:PlayerDriver和PVPlayer。PVPlayer通过调用PlayerDriver的函数实现具体的功能。
http://blog.csdn.net/eustoma/article/details/6706492
相关推荐
"Android 的 MediaPlayer 架构介绍" Android 的 MediaPlayer 架构是 Android 系统中非常重要的一部分,本文将对其架构进行详细的介绍。 MediaPlayer 的主要功能是播放音频和视频,提供了 Music 和 Video 两个应用...
对Android的多媒体框架MediaPlayer和基于Opencore的PVplayerEngine之间的调用做了一个较详细的说明。
本篇将详细解释Android MediaPlayer架构中的Binder代理机制以及通过MediaPlayer获取服务播放器的过程。 首先,我们来看Binder代理机制。在Android的跨进程通信中,Binder是关键角色,它允许不同进程间的对象和接口...
一、背景知识 android各版本的多媒体框架介绍 软硬解码概念 二、android Mediaplayer架构介绍 结合代码 三、扩展android视频解码格式 Android源码播放器 第三方库
本文将详细介绍Android多媒体架构的核心组件、优势以及发展历程。 #### Android多媒体应用 Android多媒体应用主要包括音乐播放器、视频播放器、相机、录音机和摄像机等。此外,还提到了视频电话功能,但当时尚未...
Android的MediaPlayer是Android系统中用于处理音频和视频播放的核心组件。它基于OpenCore(PacketVideo)库构建,提供了丰富的多媒体播放功能。MediaPlayer不仅用于Music和Video等内置应用,还可以被开发者用于...
这些组件与Android的多媒体服务层(如MediaPlayer和MediaCodec服务)紧密集成,使得应用程序能够轻松地处理多媒体内容。 Android系统还采用了多种优化策略以提升性能。例如,资源文件在编译时进行优化,XML描述的UI...
- **MediaPlayer API**:Nature音乐播放器使用了Android自带的MediaPlayer类来播放音频文件。这个API支持多种音频格式,如MP3、AAC等,并提供了播放、暂停、停止、快进、后退等基本控制。 - **AudioFocus**:为了...
总的来说,Android音乐播放器的开发不仅涉及到了`MediaPlayer`的使用,还涵盖了Android应用的基础架构、用户交互设计和多媒体处理等多个方面。通过实际操作,开发者能够对Android系统有更全面的理解,为今后的...
在本项目中,我们关注的是一个基于Android的直播系统,其源码涉及到服务端的架构设计,特别是使用了Red5服务器来处理直播流。Red5是一个开源的流媒体服务器,支持实时流传输协议(RTMP)、HLS(HTTP Live Streaming...
OpenCore不仅在Android早期版本中扮演着重要角色,而且在后续版本中与MediaCodec、MediaPlayer等组件一起构建了完整的多媒体处理体系。 OpenCore主要由以下几个模块组成: 1. **PVMF(Platform Video Middleware ...
《Android架构师手册_建模与图形思考》一书通过具体章节介绍了UML的不同类型图表及其在Android应用设计中的应用: 1. **UML类别图**:用于展示类之间的关系,如继承、关联和依赖。通过类别图,架构师可以直观地理解...
它们可能存储在`res/raw`目录下,并通过Android的MediaPlayer API播放。 7. **动画**: - Android提供了丰富的动画支持,包括属性动画和视图动画。飞马的移动、攻击等动作可能通过动画实现。 8. **库和依赖**: ...
- **预处理指令**:为了适应不同架构的Android设备,需要针对armeabi, armeabi-v7a, arm64-v8a, x86, x86_64等架构分别编译FFmpeg库。 2. **视频解码** - **libavcodec**:FFmpeg的编码/解码库,负责视频流的解码...
在处理海康摄像头的视频流时,我们可能需要用到Android的MediaPlayer类,它支持播放多种格式的媒体文件,包括通过HTTP或RTSP协议传输的实时流。此外,SurfaceView或者TextureView可以作为视频的渲染视图,用于显示...
2. **Android架构和组件**:深入讲解了Android系统的架构,包括四大组件:Activity、Service、Broadcast Receiver和Content Provider。这些组件是构建任何Android应用的基础。 3. **UI设计**:介绍如何使用XML布局...
`android.media.MediaPlayer`类提供了完整的音频和视频播放能力。 - **MediaRecorder**:用于录制音频。未来的版本可能会支持视频录制功能。`android.media.MediaRecorder`类提供了音频录制的能力。 ##### 4.1 ...