Android 音频子系统,音频系统跟应用层直接相关的部分(八)

音频系统跟应用层直接相关的部分

从上层看,跟音频相关的功能概括说就是音频、视频的的录制、回放。与之直接相关的类有MediaPlayer,MediaRecorder。他们具有同时处理音频、视频的能力。

1,  MediaPlayer主要处理音视频的回放,它是一个状态机,依赖于MediaPlayerService来完成具体功能,并项应用程序反馈当前的播放情况。

如下是MediaPlayer的状态迁移图,有状态机约束,不能跨状态操作



下面看Mediaplayer的实现,看它是如何完成应用的播放请求的。

以状态从idle到Initialized为例,触发条件是调用setDataSource函数。

从mediaplayer.java开始,经过jni层,android_media_mediaplayer.cpp,到本地层mediaplayer.cpp。

直接看mediaplayer.cpp的代码:

status_t MediaPlayer::setDataSource(const sp<IDataSource> &source)@ mediaplayer.cpp {
//获取mediaplayerservice的服务。
		const sp<IMediaPlayerService> service(getMediaPlayerService());
//有Mediaplayerservice,创建一个IMediaPlayer实例,这个实例是mediaplayer和mediaplayerservice之间通信的工具。因为mediaplayer是属于应用进程的,而mediaplayerservice属于meidaserver进程,他们之间的通信是跨进程。
		sp<IMediaPlayer> player(service->create(this, mAudioSessionId));
//通过setdatasource把播放源设置进去。
if ((NO_ERROR != doSetRetransmitEndpoint(player)) ||
(NO_ERROR != player->setDataSource(source))) {
 	player.clear();
}
//把这个Imediaplayer实例跟mediaplayer.cpp关联起来,同时把状态从idle转到Initialized。
err = attachNewPlayer(player);
return err;
}

2,  MediaRecorder,音视频的录制。

MediaRecorder也同样有严格的状态机约束。

MediaRecorder的状态迁移图,及相关函数。


音视频录制,涉及的硬件设备,主要包括:

视频:通过视频录制设备(camera)采集数据,并将采集到的图像显示在终端屏幕上同步预览,可以将数据与surfaceView建立关联以实现预览,最后保存到指定的存储设备。

音频:通过音频录制设备(如mic)采集数据,然后保存到指定存储设备中。对于音视频都可以调用MediaRecorder的函数配置各种参数。

编码器:将音视频数据存储为文件之前,需要调用编码器做前期处理。

应用程序负责描述需求(通过MediaRecorder提供的设置函数),MediaRecorder根据这些需求在系统整合、协调相关设备,完成需求。

下图是MediaRecorder的框架图:



MediaPlayerService是底层多媒体的代理者,stageFright是系统的多媒体库,在不同的版本中,多媒体库可能会有变化,mediaplayerservice的存在使得多媒体库的改变不会影响到上层框架。

SurfaceView实现视频录制时的预览功能,同时结合opengl可以对视频数据进行效果调整。

 

下面是一个多媒体录制程序实例,以mediaRecorderTest.java中的实例为参考:

Frameworks/base/media/tests/mediaframeworktest/…/MediaRecorderTest.java

//为了代码清晰,这个方法中函数调用的try-catch异常没加。

public void testPortraitH263() throws Exception {
	mRecorder = new MediaRecorder();
//获取一个Camera实例,可以指定camera编号。
	mCamera = Camera.open(CAMERA_ID);
	Camera.Parameters parameters = mCamera.getParameters();
	parameters.setPreviewSize(352, 288);
	parameters.set("orientation", "portrait");
	mCamera.setParameters(parameters);
//这一步是必不可少的,解锁camera,让另一个进程可以访问他。通常情况下camera是被锁定在拥有活动的camera实例的进程,直到这个进程调用了release,断开并释放相机资源。
但是,为了允许在进程之间快速切换,可以调用unlock方法来释放camera临时的为其他进程使用。
	mCamera.unlock();
//将MediaRecorder与camera建立连接,这个设置会传到stagefright,
	mRecorder.setCamera(mCamera);
	int codec = MediaRecorder.VideoEncoder.H263;
	int frameRate = MediaProfileReader.getMaxFrameRateForCodec(codec);
//调用录制video方法,
	recordVideo(frameRate, 352, 288, codec,
		MediaRecorder.OutputFormat.THREE_GPP,
		MediaNames.RECORDED_PORTRAIT_H263, true);
//资源释放
	mCamera.lock();
	mCamera.release();
	videoRecordedResult =
	validateVideo(MediaNames.RECORDED_PORTRAIT_H263, 352, 288);
}


private void recordVideo(int frameRate, int width, int height,
	int videoFormat, int outFormat, String outFile, boolean videoOnly) {
//设置音视频的来源,
	mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
	mRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
//设置文件格式,输出文件名,路径。
	mRecorder.setOutputFormat(outFormat);
	mRecorder.setOutputFile(outFile);
	mRecorder.setVideoFrameRate(frameRate);
	mRecorder.setVideoSize(width, height);
//设置音视频的编码器。
	mRecorder.setVideoEncoder(videoFormat);
	mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
//设置预览窗口。
	mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder();
	mRecorder.setPreviewDisplay(mSurfaceHolder.getSurface());
	mRecorder.prepare();
	mRecorder.start();
	Thread.sleep(MediaNames.RECORDED_TIME);
	mRecorder.stop();
	mRecorder.release();
}

系统支持的音视频来源有哪些,以及是否要添加权限,可以参考MediaRecorder.java中的VideoSource,AudioSource这两个子类

MediaRecorder.java{
	public final class AudioSource {
        /** Default audio source **/
        public static final int DEFAULT = 0;
        /** Microphone audio source */
        public static final int MIC = 1;
        /** Voice call uplink (Tx) audio source.
         * <p>
         * Capturing from <code>VOICE_UPLINK</code> source requires the
         * {@link android.Manifest.permission#CAPTURE_AUDIO_OUTPUT} permission.
         * This permission is reserved for use by system components and is not available to
         * third-party applications.
         * </p>
         */
        public static final int VOICE_UPLINK = 2;
        /** Voice call downlink (Rx) audio source.
         * <p>
         * Capturing from <code>VOICE_DOWNLINK</code> source requires the
         * {@link android.Manifest.permission#CAPTURE_AUDIO_OUTPUT} permission.
         * This permission is reserved for use by system components and is not available to
         * third-party applications.
         * </p>
         */
        public static final int VOICE_DOWNLINK = 3;
        /** Voice call uplink + downlink audio source
         * <p>
         * Capturing from <code>VOICE_CALL</code> source requires the
         * {@link android.Manifest.permission#CAPTURE_AUDIO_OUTPUT} permission.
         * This permission is reserved for use by system components and is not available to
         * third-party applications.
         * </p>
         */
        public static final int VOICE_CALL = 4;
        /** Microphone audio source with same orientation as camera if available, the main
         *  device microphone otherwise */
        public static final int CAMCORDER = 5;
        /** Microphone audio source tuned for voice recognition if available, behaves like
         *  {@link #DEFAULT} otherwise. */
        public static final int VOICE_RECOGNITION = 6;
}
public final class VideoSource {

        public static final int DEFAULT = 0;
        /** Camera video source
         * <p>
         * Using the {@link android.hardware.Camera} API as video source.
         * </p>
         */
        public static final int CAMERA = 1;
        /** Surface video source
         * <p>
         * Using a Surface as video source.
         * </p><p>
         * This flag must be used when recording from an
         * {@link android.hardware.camera2} API source.
         * </p><p>
         * When using this video source type, use {@link MediaRecorder#getSurface()}
         * to retrieve the surface created by MediaRecorder.
         */
        public static final int SURFACE = 2;
}
}

上面是通过MediaRecorder封装的接口,来完成音视频的录制,接着看下mediaRecorder的源码实现。

以前面实例中的一个接口setPreviewDisplay为例:

public void setPreviewDisplay(Surface sv) @MediaRecorder.java{
//这里只是保存了设置的surface对象。
	mSurface = sv;
}

Java层保存的surface对象,什么时候用呢?从上面的框架图可知,要完成音视频的录制是通过MediaRecorder.cpp,进而使用库层的服务,所以这里的surface应该在MediaRecorder.cpp被使用。

//在这个jni的初始化函数中,会通过name和类型签名,找到mediaRecorder(java)中mSurface变量,然后保存到fields(fields_t结构体)中。

static void android_media_MediaRecorder_native_init(JNIEnv *env)
@ android_media_MediaRecorder.cpp{
	clazz = env->FindClass("android/media/MediaRecorder");
	fields.surface = env->GetFieldID(clazz, "mSurface", "Landroid/view/Surface;");
}

static void android_media_MediaRecorder_prepare(JNIEnv *env, jobject thiz)
@ android_media_MediaRecorder.cpp {
//这里得到是mediarecorder(cpp)对象,这个对象是在native_setup中创建的,然后存在了fields的fields.context中。
	sp<MediaRecorder> mr = getMediaRecorder(env, thiz);
//先获取java层的对象mSurface,进一步获取native_surface。
	jobject surface = env->GetObjectField(thiz, fields.surface);
	const sp<Surface> native_surface = get_surface(env, surface);
//调用MediaRecorder.cpp中的setPreviewSurface,最后把surface设置到stageFrightRecorder中。
if (process_media_recorder_call
(env, mr->setPreviewSurface(native_surface->getIGraphicBufferProducer()), 
"java/lang/RuntimeException", "setPreviewSurface failed.")) {
            return;
        }
}
 

//这里的mMediaRecorder是sp<IMediaRecorder>  类型的对象。前面说过IMediaRecorder是应用程序跟MediaPlayerService之间的桥梁。mMediaRecorder是在mediaRecorder.cpp的构造函数中创建的。

status_t MediaRecorder::setPreviewSurface(const sp<IGraphicBufferProducer>& surface)
@ mediarecorder.cpp {
	status_t ret = mMediaRecorder->setPreviewSurface(surface);
}

//通过mediaplayerService创建了mMediaRecorder对象。

MediaRecorder::MediaRecorder(const String16& opPackageName) @mediarecorder.cpp {
	const sp<IMediaPlayerService> service(getMediaPlayerService());
	mMediaRecorder = service->createMediaRecorder(opPackageName);
//同时把状态置为IDLE,状态机开始运转。
    if (mMediaRecorder != NULL) {
        mCurrentState = MEDIA_RECORDER_IDLE;
    }
}


//看下MediaPlayerService是如何生成mMediaRecorder对象的。

sp<IMediaRecorder> MediaPlayerService::createMediaRecorder(const String16 
&opPackageName)@ MediaPlayerService.cpp {
//这里生成是一个MediaRecorderClient对象,并把它加到了mMediaRecorderClients列表中,以做全局管理。所以IMediaRecorder对应的实现类是MediaRecorderClient。应用进程中MediaRecorder向Mediaplayerservice发起的业务请求都是通过MediaRecorderClient。
	sp<MediaRecorderClient> recorder = new MediaRecorderClient(this, pid, opPackageName);
	wp<MediaRecorderClient> w = recorder;
	mMediaRecorderClients.add(w);
	return recorder;
}

看下MediaRecorderClient的继承关系:

class MediaRecorderClient : publicBnMediaRecorder…

class BnMediaRecorder: publicBnInterface<IMediaRecorder>…

template<typename INTERFACE>

class BnInterface : public INTERFACE,public BBinder

所以是一个多继承 MediaRecorderClient:publicIMediaRecorder, public BBinder…

一方面具有了IMediaRecorder的功能,另一方面BBinder实现了跨进程的能力。

 

接着前面MediaRecorder.cpp中对setPreviewSurface的实现,就是调用了mMediaRecorder的中setPreviewSurface函数。


status_t MediaRecorderClient::setPreviewSurface(const sp<IGraphicBufferProducer>& surface)
@ MediaRecorderClient.cpp {
	return mRecorder->setPreviewSurface(surface);
}


这里的mRecorder是MediaRecorderBase      *mRecorder;类型。

StagefrightRecorder *AVMediaServiceFactory::createStagefrightRecorder(
        const String16 &opPackageName) @ AVMediaServiceFactory.cpp {
    return new StagefrightRecorder(opPackageName);
}

具体的实现类是StagefrightRecorder,从前面的框架图看出,stagefright是android的多媒体库。




  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值