`
isiqi
  • 浏览: 16483049 次
  • 性别: Icon_minigender_1
  • 来自: 济南
社区版块
存档分类
最新评论

MediaRecorder流程分析

阅读更多

MediaRecorder流程分析

目录

一、 java层

media recorder state machine:

1、java应用层

java应用层主要是一些接口的调用,它并没有具体功能代码的实现,java应用层的代码路径为:

android/packages/apps/SoundRecorder/src/com/android/soundrecorder/

该目录下有文件: SoundRecorder.java Recorder.java VUMeter.java

soundrecorder.java是程序的入口文件,我们在可以在里面设置文件输出编码格式的格式,现在系统默认支持两种格式amr和3gpp格式。设置代码如下:

mRequestedType =AUDIO_3GPP; //02 AUDIO_AMR;

接着运行mRecorder = new Recorder();创建一个Recorder类。Recorder类在Recorder.java中定义。

Recorder的startRecording方法启动了java层的录音。startRecording方法中首先创建一个Mediarecorder的类,然后调用Mediarecorder的方法完成设置audio源、设置输出文件格式、audio编码格式、设置输出文件,然后检查MediaRecorder是否准备好了。如果准备好就启动。如果没有准备好就抛出异常然后重新设置MediaRecorder和释放MediaRecorder。代码如下所示:

mRecorder = new MediaRecorder();

mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);

mRecorder.setOutputFormat(outputfileformat);

mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);

mRecorder.setOutputFile(mSampleFile.getAbsolutePath());

// Handle IOException

try {

mRecorder.prepare();

} catch(IOException exception) {

setError(INTERNAL_ERROR);

mRecorder.reset();

mRecorder.release();

mRecorder = null;

return;

}

mRecorder.start();

2、JAVA Framework层

Java的framework层代码位于:

frameworks/base/media/java/android/media/MediaRecorder.java

它没有具体的实现只是一个接口而已。

3、JAVA本地调用部分(JNI):

frameworks/base/media/jni/android_media_MediaRecorder.cpp

jni层的只是实现了方法的注册,为java层调用C++程序提供一种注册。

这三给部分的程序会编译成一个libmedia_jni.so库,java层序的调用都是调用该库中的接口。具体的实现要要在我们的多媒体底层库。

二、 多媒体底层库

1、ImediaRecorder.cpp

Imediarecorder.cpp文件中实现了BP功能。BP和BN是通过binder来通信的。Bp主要是用来处理java层传下来的服务请求。然后通过transact将处理请求传给bn(通过binder)。其接口如下所示:

class BpMediaRecorder: public BpInterface

{

BpMediaRecorder(const sp& impl) : BpInterface(impl) {}

status_t setCamera(const sp& camera);

status_t setPreviewSurface(const sp& surface);

status_t init();

status_t setVideoSource(int vs);

status_t setAudioSource(int as);

status_t setOutputFormat(int of);

status_t setAudioEncoder(int ae);

status_t setOutputFile(const char* path);

status_t prepare();

status_t getMaxAmplitude(int* max);

…………………………

}

上面的每个函数中都用transact方法来向bn发出请求。然后调用return reply.readInt32();将从bn返回的数据传送个他们的调用函数。

2、Mediarecorder.cpp

Bn的实现是在Mediarecorder.cpp文件中。BN是用来处理bp的请求,当bn将数将处理完后将数据通过transact传给回bp(通过binder)。MediaRecorder.cpp文件的实现方法与ImediaRecorder,cpp对应,主要是用来接收ImediaRecorder发送过来的请求。

MediaRecorder::MediaRecorder()

{ LOGV("constructor");

sp sm = defaultServiceManager();

sp binder;

do {

binder = sm->getService(String16("media.player"));

if (binder != NULL) {

break;

}

usleep(500000); // 0.5 s

} while(true);

sp service = interface_cast(binder);

if (service != NULL) {

mMediaRecorder = service->createMediaRecorder(getpid());

}

if (mMediaRecorder != NULL) {

mCurrentState = MEDIA_RECORDER_IDLE;

}

doCleanUp();

}

该文件操作的方法是mMediaRecorder的方法,它主要是同过binder机制将请求传输送给mediarecorder的服务进程。

clip_image001

3、多媒体服务部分

mediaRecorder的服务文件是MediaRecorderClient.cpp,它主要调用的是PVMediaRecorder的实现方法,在此请求opencore的服务。

MediaRecorderClient::MediaRecorderClient(pid_t pid)

{

LOGV("Client constructor");

mPid = pid;

mRecorder = new PVMediaRecorder();

}

三、Opencore

Opencore的大致框架图如下所示:

clip_image003

我们先从pvmediarecorder.cpp文件分析。

在PVMediaRecorder中首先创建一个AuthorDriverWrapper的对象。PVMediaRecorder将它的方法通过author_command包装。然后通过AuthorDriverWrapper的enqueueCommand将命令发送请求队列中。

PVMediaRecorder的setOutputFile方法会打开我们上面指定的文件路径下的文件,为写文件作好准备。代码如下:

int fd = open(path, O_RDWR | O_CREAT );

接着分析authordriver.cpp文件

AuthorDriverWrapper::AuthorDriverWrapper()

{

mAuthorDriver = new AuthorDriver();

}

我们在AuthorDriverWrapper首先创建一个AuthorDriver的对象.。我们来看AuthorDriverWrapper的enqueueCommand方法,可以看到,我们在pvmediarecorder中调用的enqueuecommand实际上调用的是authordriver的enqueuecommand方法。

status_t AuthorDriverWrapper::enqueueCommand(author_command *ac, media_completion_f comp, void *cookie)

{ if (mAuthorDriver) {

return mAuthorDriver->enqueueCommand(ac, comp, cookie);

}

return NO_INIT;

}

四、audioflinger层和audiorecord

1、AudioRecord

音频系统的对外接口是AudioRecord,它通过iBinder来远程调用Audioflinger的openRecorder函数。AudioRecord构造函数如下:

1:AudioRecord

AudioRecord::AudioRecord(

int streamType,

uint32_t sampleRate,

int format,

int channelCount,

int frameCount,

uint32_t flags,

callback_t cbf,

void* user,

int notificationFrames)

: mStatus(NO_INIT)

{

log_wj("ENTER IN::--%s---%s---\n",__FILE__,__FUNCTION__);

mStatus = set(streamType, sampleRate, format, channelCount,

frameCount, flags, cbf, user, notificationFrames);

}

调用:

status_t AudioRecord::set(int streamType,

uint32_t sampleRate,

int format,

int channelCount,

int frameCount,

uint32_t flags,

callback_t cbf,

void* user,

int notificationFrames,

bool threadCanCallJava)

{

const sp& audioFlinger = AudioSystem::get_audio_flinger();

//获取缓存大小,间接调用我们修改过该函数(经过三次调用中转),返回值为//channelCount*320

AudioSystem::getInputBufferSize(sampleRate, format, channelCount, &inputBuffSizeInBytes);

//远程调用audioFlinger的openrecord函数,openRecord相当于audioflinger为audioRecord

//开辟相应的服务窗口

sp record = audioFlinger->openRecord(getpid(), streamType,

sampleRate, format,

channelCount,

frameCount,

((uint16_t)flags) << 16,

&status);

//创建一个线程用来处理

mClientRecordThread = new ClientRecordThread(*this, threadCanCallJava);

}

AudioRecord相当于一个代理,它的线程是用来处理其它客户的请求。

2、AudioFlinger

sp AudioFlinger::openRecord(

pid_t pid,

int streamType,

uint32_t sampleRate,

int format,

int channelCount,

int frameCount,

uint32_t flags,

status_t *status)

{

// AudioRecord线程

if (mAudioRecordThread == 0) {

LOGE("Audio record thread not started");

lStatus = NO_INIT;

goto Exit;

}

// add client to list

{ // scope for mLock

Mutex::Autolock _l(mLock);

wclient = mClients.valueFor(pid);

if (wclient != NULL) {

client = wclient.promote();

} else {

client = new Client(this, pid);

mClients.add(pid, client);

}

// create new record track. The record track uses one track in mHardwareMixerThread by //convention.

//生成一个recordTrack用来作为数据的中转(audioflinger与audiorecord之间)。

//他们使用audio_track_cblk_t数据结构来传输数据。

recordTrack = new MixerThread::RecordTrack(mHardwareMixerThread, client, streamType, sampleRate, format, channelCount, frameCount, flags);

if (recordTrack->getCblk() == NULL) {

recordTrack.clear();

lStatus = NO_MEMORY;

goto Exit;

}

// return to handle to client------我们的audiorecord。

recordHandle = new RecordHandle(recordTrack);

}

AudioRecord和AudioFlinger操作的都是RecordTrack实例,AudioRecord通过它的执行控制操作(start/stop)和读取操作(read)。Audiorecord的start/stop操作可以理解为一个开关,控制的是AudiorecordThread的运行与否。

Audioflinger则负责从音频设备读取数据放置到audio_track_cblk_t数据结构中。

clip_image005

Audioflinger对数据的读取在AudioFlinger::AudioRecordThread::threadLoop()函数中。在第一次启动的时候会打开一个AudioStreamIn的对象,并设置参数。

input = mAudioHardware->openInputStream(mRecordTrack->format(),

mRecordTrack->channelCount(), mRecordTrack->sampleRate(),

&mStartStatus,

(AudioSystem::audio_in_acoustics)(mRecordTrack->mFlags >> 16));

读取数据的代码如下:

if (LIKELY(mRecordTrack->getNextBuffer(&buffer) == NO_ERROR&&

(int)buffer.frameCount == inFrameCount) ) {

ssize_t bytesRead = input->read(buffer.raw, inBufferSize);

mRecordTrack->releaseBuffer(&buffer);

mRecordTrack->overflow();

}

首先从audio_track_cblk_t取得缓冲区,然后调用input的read方法读取数据,最后释放缓冲区,检查是否溢出。

五、 硬件抽象层

硬件抽象层主要实现了AudioStreamInALSA和AudioStreamOutALSA两个类,这两个类又会调用该文件下的ALSAStreamOps类的方法。AudioStreamInALSA是录音部分调用的路径。在AudioStreamInALSA的构造函数中会对alsa进行一些初始化参数设置。AudioStreamInALSA的read方法是最主要的方法,audioflinger层的read调用就是对AudioStreamInALSA的read的调用。由于录音部分出现单声道和双声道数据传输的问题,修改read方法如下,即可实现了录音功能正常,避免了在编码的时候修改数据时其他编码仍不能工作的弊端。

ssize_t AudioStreamInALSA::read(void *buffer, ssize_t bytes)

{ snd_pcm_sframes_t n;

status_t err;

short int *tmp1,*tmp2;

int i;

AutoMutex lock(mLock);

tmp1=(short int *)malloc(bytes*2);

n = snd_pcm_readi(mHandle, tmp1, snd_pcm_bytes_to_frames(mHandle, bytes*2));

if (n < 0 && mHandle) {

n = snd_pcm_recover(mHandle, n, 0);

}

tmp2=(short int *)buffer;

for(i=0;i

{

tmp2[i]=tmp1[2*i];

}

free(tmp1);

return static_cast(n/2);

}

snd_pcm_readi调用的是alsa库函数,跟踪执行最终会调用alsa库下的snd_pcm_hw_readi函数。snd_pcm_hw_readi会调用err = ioctl(fd, SNDRV_PCM_IOCTL_READI_FRAMES, &xferi);最终与kernel相联系。

分享到:
评论

相关推荐

    Android应用源码之MediaRecorder.zip

    总之,通过深入研究“Android应用源码之MediaRecorder.zip”,开发者不仅可以了解`MediaRecorder`的工作流程,还能掌握Android多媒体录制的核心技术,为实现高质量的音视频应用提供坚实的基础。在实际开发中,结合...

    安卓录音软件(MediaRecorder)

    通过分析和学习RecordAudioDemo的源代码,开发者可以更深入地理解如何在实际项目中使用MediaRecorder来实现音频录制功能。 ### 5. 进阶话题 - **音频质量调整**:可以通过调整音频编码的比特率、采样率和声道数来...

    MediaRecorder视频录制

    MediaRecorder的工作流程包括初始化、设置、准备和开始录制等步骤。 **2. 初始化与设置** 在使用MediaRecorder之前,需要先进行初始化。这通常包括设置输出文件路径、视频编码格式(如H.264)、音频编码格式(如AAC...

    FFmpeg实时解码MediaRecorder MP4视频流

    6. 渲染或处理解码后的帧:这一步可以是显示在屏幕上,或者进行进一步的处理,如视频分析。 7. 清理:完成解码后,需要释放资源,调用avcodec_close_context关闭解码器,av_free释放解码器上下文。 在这个过程中,...

    RtspMediaRecorder:libstreaming 源码分析一之RTSP连接,]libstreaming源码分析二之MediaRecorder编码

    而在分析二中,我们将探讨`MediaRecorder`的初始化、参数设置和编码流程,以及如何将编码后的数据整合到RTSP流中。 在`RtspMediaRecorder-master`这个项目中,你将找到`libstreaming`的完整源代码,包括相关的类和...

    Android多媒体功能开发-使用MediaRecorder类录制音频

    通过阅读和分析这个示例,开发者可以更好地理解MediaRecorder的工作流程和具体实现细节。 综上所述,MediaRecorder是Android平台进行音频录制的核心工具。通过正确地配置和使用它,开发者可以创建功能丰富的多媒体...

    Android安卓经典设计例程源代码-MediaRecorder.rar

    《Android MediaRecorder深度解析——基于源代码学习》 在Android应用开发中,MediaRecorder是一个至关重要的组件,它允许...结合源代码,我们可以更深入地了解MediaRecorder的工作流程,提高在实际项目中的运用能力。

    android_media_MediaRecorder.rar_camera

    通过对这部分源码的分析,我们可以更深入地理解 `MediaRecorder` 在Android系统内部的工作原理。 7. **优化与注意事项** 在实际应用中,正确管理和释放 `MediaRecorder` 和 `Camera` 资源是非常重要的,因为不...

    浅析Android录屏 MediaRecorder

    录屏流程包括:启动授权请求Activity,获取`MediaProjection`对象,创建`MediaRecorder`,设置输出文件路径,创建`VirtualDisplay`以捕获屏幕内容,然后开始和停止录制。在这个过程中,需要用户授权录音和数据读写...

    Camera2 录像 修复了点击 stop 时 不能继续预览

    总之,修复"点击stop时不能继续预览"的问题需要对Camera2 API有深入的理解,包括录像流程、资源管理以及异常处理。通过分析和学习开源示例,如android-Camera2Video-master项目,开发者可以更好地掌握这些问题,并...

    android 录音

    在Android平台上,录音功能是通过Android ...综上所述,Android录音涉及到Android权限管理、MediaRecorder类的使用、录音流程控制以及资源管理等多个方面。通过深入学习和实践,开发者可以创建出高效、稳定的录音应用。

    前端调用麦克风获取实时音频流和录音并上传至后台

    你可以在这个处理链中添加各种节点,如分析器(analyser)来获取实时音频数据,或者增益节点(gainNode)来调整音量。 接下来,我们讨论录音功能。HTML5的`MediaRecorder` API使得在浏览器中录制音频变得简单。在...

    android 录制视频

    二、MediaRecorder的使用流程 1. 初始化:创建`MediaRecorder`实例。 2. 配置:设置输入源(如摄像头)、输出格式(如MP4)、编码器(如H.264)、音频源和音频编码等。 3. 准备:调用`prepare()`方法,准备录制环境...

    Android 录音程序源码.zip

    通过分析和学习这个源码,我们可以深入了解Android录音API的使用,以及如何在Android应用中集成录音功能。 Android系统提供了MediaRecorder类来处理音频录制。MediaRecorder是一个用于准备和控制媒体记录的类,可以...

    Android MIUI小米录音机源码-IT计算机-毕业设计.zip

    在小米录音机的源码中,我们可以通过阅读代码来了解这两个类的具体用法和实际工作流程。 其次,MIUI小米录音机源码中还包含了界面设计的部分,这展示了如何根据MIUI的风格定制Android应用。MIUI是小米公司基于...

    Android应用源码之录音程序源码.zip

    6. **录音操作流程**:源码将展示标准的录音操作步骤,包括`MediaRecorder.prepare()`预处理,`MediaRecorder.start()`开始录音,`MediaRecorder.stop()`停止录音,以及最后的`MediaRecorder.release()`释放资源。...

    关于android电话录音问题的详细分析.doc

    本文将深入探讨Android电话录音的问题,并详细分析其背后的技术原理。 首先,让我们了解一下Android录音的基本流程。在Android中,录音操作通常通过`MediaRecorder`类来实现。在创建`MediaRecorder`实例后,我们...

    Android 录音程序源码.rar

    通过分析`Android 录音程序源码`,你可以学习到如何在Android应用中实现从开始录音、保存文件到停止录音的完整流程,并了解相关的权限管理和错误处理。这将有助于你开发具有多媒体功能的Android应用。

    Android高级应用源码-简易录音机.zip

    通过分析这个"简易录音机"的源码,开发者可以学习到如何在Android环境中集成录音功能,同时对Android系统的多媒体框架、文件操作、权限管理等方面有更深入的理解。这不仅有助于开发类似的应用,也为其他更复杂的...

    Android 音视频录制

    总结,Android音视频录制涉及MediaRecorder的使用、参数配置、录制流程控制,以及可能的LocalSocket流媒体录制。RecordDemo应该是一个实现这些功能的简单示例,对于理解Android音视频录制具有参考价值。对于...

Global site tag (gtag.js) - Google Analytics