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的服务进程。

3、多媒体服务部分

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

MediaRecorderClient::MediaRecorderClient(pid_t pid)

{

LOGV("Client constructor");

mPid = pid;

mRecorder = new PVMediaRecorder();

}

三、Opencore

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

我们先从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数据结构中。

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相联系。

更多相关文章

  1. android webview 正常显示 Html中的 Frameset iframe
  2. Android(安卓)调用 Web Service
  3. android 静音与振动
  4. Android的线程
  5. android获取各种系统路径的方法
  6. 浅谈Java中Collections.sort对List排序的两种方法
  7. NPM 和webpack 的基础使用
  8. Python list sort方法的具体使用
  9. 【阿里云镜像】使用阿里巴巴DNS镜像源——DNS配置教程

随机推荐

  1. Android 中数据库查询方法 query() 中的
  2. Android 中加入SD card 应用
  3. Android(安卓)长按Listview显示CheckBox,
  4. Android 事件触发机制
  5. Android关机流程解析
  6. Android不显示开机向导和开机气泡
  7. androidmanifest.xml解析
  8. android context.getCacheDir()缓存目录
  9. Android资源,国内镜像站点,博客文章等
  10. 迅为4418开发板TF卡烧写Android和Qt内容