接续:https://blog.csdn.net/lin20044140410/article/details/80057981
前面的过程漏了一点,就是camera设备的打开过程,也就是mCamera=Camera.open(CAMERA_ID),分析的recorder.setCamera(mCamera);中的参数就是open的返回值。open()的过程还是很长的,下面只关注跟cameraService,ICameraClient相关一小部分,以便跟前面的connect中的描述可以串起来。
连接camera设备的过程,是先获取cameraservice的句柄,然后执行cameraservice的connect方法。这个过程会创建一个cameraclient实例CameraDeviceClient,并作为出参回传给调用者,也就是device指向指针的指针。
Frameworks/av/service/camera/libcameraservice/cameraservice.cpp
Status CameraService::connect(
const sp& cameraClient,
int cameraId,
const String16& clientPackageName,
int clientUid,
int clientPid,
/*out*/
sp* device) {
sp client = nullptr;
ret = connectHelper(cameraClient, id,
CAMERA_HAL_API_VERSION_UNSPECIFIED, clientPackageName, clientUid, clientPid, API_1,
/*legacyMode*/ false, /*shimUpdateOnly*/ false,
/*out*/client);
*device = client;
}
cameraclient的创建会根据hal版本和api版本,来确定具体的实例。
CameraService.cpp
Status CameraService::makeClient(const sp& cameraService,
const sp& cameraCb, const String16& packageName, const String8& cameraId,
int facing, int clientPid, uid_t clientUid, int servicePid, bool legacyMode,
int halVersion, int deviceVersion, apiLevel effectiveApiLevel,
/*out*/sp* client) {
//默认配置下创建的是 CameraClient
*client = new CameraClient(cameraService, tmp, packageName, cameraIdToInt(cameraId),
facing, clientPid, clientUid, getpid(), legacyMode);
//对于hal 3,api1,创建的 Camera2Client
*client = new Camera2Client(cameraService, tmp, packageName, cameraIdToInt(cameraId),
facing, clientPid, clientUid, servicePid, legacyMode);
//对于hal 3,api2,创建的 CameraDeviceClient
*client = new CameraDeviceClient(cameraService, tmp, packageName, cameraId,
facing, clientPid, clientUid, servicePid);
}
接着执行cameraclient的初始化,这个过程中会打开camera硬件设备。
设置camerasource的最后一步,camera的配置。
status_t CameraSource::configureCamera(
CameraParameters* params, int32_t width, int32_t height, int32_t frameRate) {
//这个过程,主要是用要求的视频大小,帧率来配置camera,如果没有请求视频宽高,帧率,将跳过,使用camera setting中的默认值。
if (width != -1 && height != -1) {…}
if (frameRate != -1) {….}
}
这段分析都是从prepare()开始,到这里视频source已经准备好了。
5,继续prepare(),看下视频编码器的创建。
StagefrightRecorder.cpp
status_t StagefrightRecorder::setupVideoEncoder(
const sp &cameraSource,
sp *source) {
//根据视频源camerasource,创建视频编码器。
sp encoder = MediaCodecSource::Create(
mLooper, format, cameraSource, mPersistentSurface, flags);
//保存bufferproducer,用于buffer的申请。
if (cameraSource == NULL) {
mGraphicBufferProducer = encoder->getGraphicBufferProducer();
}
}
MediaCodecSource::Create()创建一个MediaCodecSource实例,给一些变量赋值,然后初始化init()-->initEncoder()。
MediaCodecSource.cpp
status_t MediaCodecSource::initEncoder() {
Vector matchingCodecs;
//根据设置的mime类型(MEDIA_MIMETYPE_VIDEO_MPEG4或者MEDIA_MIMETYPE_VIDEO_AVC),查找匹配的编码器。
MediaCodecList::findMatchingCodecs(
outputMIME.c_str(), true /* encoder */,
((mFlags & FLAG_PREFER_SOFTWARE_CODEC) ? MediaCodecList::kPreferSoftwareCodecs : 0),
&matchingCodecs);
//创建解码组件,执行其init(),configure()。
mEncoder = MediaCodec::CreateByComponentName(mCodecLooper, matchingCodecs[ix]);
//kWhatEncoderActivity 消息的处理,通过feedEncoderInputBuffers填充编码输入buffer。
mEncoderActivityNotify = new AMessage(kWhatEncoderActivity, mReflector);
mEncoder→setCallback(mEncoderActivityNotify);
//注意configure()的最后一个参数 MediaCodec::CONFIGURE_FLAG_ENCODE表示接下来要创建的是编码组件。
err = mEncoder->configure( mOutputFormat, NULL /* nativeWindow */,
NULL /* crypto */, MediaCodec::CONFIGURE_FLAG_ENCODE);
//start(),表示编码组件初始化已经成功完成,可以准备编码工作了,也就是将其OMX_CommandStateSet状态置为OMX_StateIdle。
最终调用的是OMXNodeInstance.cpp中方法sendCommand()。
err = mEncoder->start();
}
//这个方法会列出所有匹配mime格式的编码器,即是其中的参数matches,同时也会考虑优先软解,还是强制硬解。
void MediaCodecList::findMatchingCodecs(
const char *mime, bool encoder, uint32_t flags,
Vector *matches, Vector *owners) {
const sp list = getInstance();
const sp info = list→getCodecInfo(matchIndex);
AString componentName = info→getCodecName();
matches->push(componentName);
}
MediaCodec.cpp
status_t MediaCodec::init(const AString &name, bool nameIsType, bool encoder) {
//这里返回的实际是ACodec实例。
mCodec = GetCodecBase(name, nameIsType);
mCodec->setCallback(
std::unique_ptr(
new CodecCallback(new AMessage(kWhatCodecNotify, this))));
mBufferChannel = mCodec->getBufferChannel();
mBufferChannel->setCallback(
std::unique_ptr(
new BufferCallback(new AMessage(kWhatCodecNotify, this))));
//在 kWhatInit消息的处理中,分配编解码组件。具体调用是mCodec->initiateAllocateComponent(format);
sp msg = new AMessage(kWhatInit, this);
}
MediaCodec.cpp
status_t MediaCodec::configure( const sp &format, const sp &surface,
const sp &crypto, const sp &descrambler, uint32_t flags) {
//配置,启动编码组件,具体调用mCodec->initiateConfigureComponent(format);
sp msg = new AMessage(kWhatConfigure, this);
}
Acodec.cpp
//这个函数,编解码组件配置都会执行,比较长,
status_t ACodec::configureCodec( const char *mime, const sp &msg) {
if (video) {
//确定是否使用软件渲染,是解码需要的。
bool usingSwRenderer = false;
if (haveNativeWindow && mComponentName.startsWith("OMX.google.")) {
usingSwRenderer = true;
(void)setPortMode(kPortIndexOutput, IOMX::kPortModePresetByteBuffer);
}else if (haveNativeWindow && !storingMetadataInDecodedBuffers()) {
err = setPortMode(kPortIndexOutput, IOMX::kPortModePresetANWBuffer);
}
//创建编码或者解码器。
if (encoder) {
err = setupVideoEncoder(mime, msg, outputFormat, inputFormat);
}else {
err = setupVideoDecoder(mime, msg, haveNativeWindow, usingSwRenderer, outputFormat);
}
}
…...
}
再回到StagefrightRecorder.cpp中,前面编码组件的构建是从setupVideoEncoder()开始的,
StagefrightRecorder.cpp
status_t StagefrightRecorder::setupMPEG4orWEBMRecording() {
sp writer;
sp mediaSource;
err = setupMediaSource(&mediaSource);
sp encoder;
err = setupVideoEncoder(mediaSource, &encoder);
//前面设置视频source(camerasource),设置编码器都执行完了,接着是创建视频track,writer是Mpeg4Writer类型实例,
Mpeg4Writer的作用就是分别把视频track和音频track写入到chunck中,然后打包写入到文件容器。
writer->addSource(encoder);
}
MPEG4Writer.cpp
status_t MPEG4Writer::addSource(const sp &source) {
const char *mime;
source->getFormat()->findCString(kKeyMIMEType, &mime);
bool isAudio = !strncasecmp(mime, "audio/", 6);
//source是视频encorder,所以创建的视频track,如果是setupAudioEncoder()中调用addSource,传入的source是音频encorder,创建的就是音频track。
Track *track = new Track(this, source, 1 + mTracks.size());
mTracks.push_back(track);
}
prepare()执行完,接下来就是start()方法的调用。
StagefrightRecorder.cpp
status_t StagefrightRecorder::start() {
switch (mOutputFormat) {
case OUTPUT_FORMAT_DEFAULT:
case OUTPUT_FORMAT_THREE_GPP:
case OUTPUT_FORMAT_MPEG_4:
case OUTPUT_FORMAT_WEBM:{
sp meta = new MetaData;
setupMPEG4orWEBMMetaData(&meta);
//调用mepg4writer的start()方法。
status = mWriter->start(meta.get());
}
}
}
对文件的读写,前提是先了解这个文件格式。
mpeg4文件格式解析,参考:https://blog.csdn.net/chenchong_219/article/details/44263691
MPEG4Writer.cpp
status_t MPEG4Writer::start(MetaData *param) {
//先写入文件格式。
writeFtypBox(param);
//为这个视频track创建一个线程,并从ThreadWrapper()函数开始运行,线程实际执行的是MPEG4Writer::threadFunc()函数,执行mp4文件的chunks的写入。
status_t err = startWriterThread();
//将会调用前面创建的视频track中的start()方法。
err = startTracks(param);
}
重点看下Mpeg4Writer::Track的start()方法,媒体元数据的输入输出都在这里完成。
MPEG4Writer.cpp
status_t MPEG4Writer::Track::start(MetaData *params) {
sp meta = new MetaData;
//mSource是前面StagefrightRecorder::setupVideoEncoder()的返回值MediaCodecSource实例。
status_t err = mSource->start(meta.get());
pthread_create(&mThread, &attr, ThreadWrapper, this);
}
从MediaCodecSource.cpp的start()方法,间接通过,MediaCodecSource::Puller的start(),调用了CameraSource.cpp的start()方法。
CameraSource.cpp
status_t CameraSource::start(MetaData *meta) {
if ((err = startCameraRecording()) == OK) {
mStarted = true;
}
}
CameraSource.cpp
status_t CameraSource::startCameraRecording() {
//创建存储的buffers的内存块,作为本地视频元数据。
createVideoBufferMemoryHeap(sizeof(VideoNativeHandleMetadata), kDefaultVideoBufferCount);
//启动camera录制,同时设置监听ProxyListener ,通过其回调dataCallbackTimestamp获取元数据。
if ((err = mCameraRecordingProxy->startRecording(new ProxyListener(this))) != OK) {...}
}
void CameraSource::dataCallbackTimestamp(int64_t timestampUs,
int32_t msgType __unused, const sp &data) {
//camera返回的数据放在列表 mFramesReceived,然后通过mFrameAvailableCondition 唤醒一个线程。
mFramesReceived.push_back(data);
mFrameTimes.push_back(timeUs);
mFrameAvailableCondition.signal();
}
mFrameAvailableCondition.signal();唤醒了那个线程呢?前面在MPEG4Writer::Track::start()创建了线程ThreadWrapper,这个线程运行后,通过mSource->read(&buffer)来读取camerasource中的数据,当列表无数据可读时,这个线程就会在read时进入wait,然后等camera返回的数据填充到列表mFramesReceived时,在唤醒,继续读取。这样camera返回的数据就到了MPEG4Writer::Track中。
ThreadWrapper直接调用了threadEntry。
status_t MPEG4Writer::Track::threadEntry() {
//循环执行,通过camerasource的read拿到元数据。
while (!mDone && (err = mSource->read(&buffer)) == OK) {
//执行编码。
f (mIsMPEG4) { copyCodecSpecificData((const uint8_t *)buffer->data() +
buffer->range_offset(), buffer->range_length());
}
//中间略去了很多代码,这里是根据timestampUs ,mChunkSamples 生成chunk。 先是调用MPEG4Writer::Track的bufferChunk()方法,
生成一个chunk后,再调用MPEG4Writer的bufferChunk()方法,同时把生成的chunk作为参数传递到MPEG4Writer中,
同时在MPEG4Writer::bufferChunk()中通过mChunkReadyCondition.signal()唤醒了另一个处于wait的线程。
mChunkSamples.push_back(copy);
bufferChunk(timestampUs);
}
}
mChunkReadyCondition.signal()唤醒的是MPEG4Writer中线程ThreadWrapper,也就是MPEG4Writer中的threadFunc()方法。
void MPEG4Writer::threadFunc() {
while (!mDone) {
//循环执行,读取MPEG4Writer::Track的bufferChunk()方法生成的chunk,
while (!mDone && !(chunkFound = findChunkToWrite(&chunk))) {
mChunkReadyCondition.wait(mLock);
}
//将chunk写入文件容器。
writeChunkToFile(&chunk);
writeAllChunks();
}
}
到这里完成了视音频录制的。音频的处理类似。
MediaWriter是把音视频字幕track打包到容器中,播放时的逆过程是通过MediaExtractor来提取分离出容器中的音视频track。
对代码中类的命名说几句题外话,感觉这些大牛之所以是大牛,不仅仅在于专业技术够强,还在于对技术外的生活有很好的体察,所以他们定义的类名、方法名等做到了恰到好处,能让人望文生义,比如:MediaExtractor,其中的Extractor意思是萃取,萃取就是分离混合物中的单元,所以即使在没有具体了解MediaExtractor前,通过名字也能大概知道它的功能,就是萃取多媒体中的资源,多媒体资源都有那些呢,音频、视频、字幕,所以它的功能就是从容器中把这些资源分离出来。
再比如Choreographer,就是画面的渲染需要vsync信号的触发,应用的画面,包括动画的步进主要是通过注册callback到Choreographer中,然后Choreographer在vsync到来时,通知感兴趣的注册者。Choreographer意思是编舞的人,就像舞台上表演者什么时候该做什么动作,收腹提臀挺胸啥的,是由编舞的人事先定好的。类比过来,手机屏幕就是舞台,该显示什么内容也是要有"舞台指导"的,也即是Choreographer,所以看的出来,写这块代码的人一定是个热爱艺术的人。