接续: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<ICameraClient>& cameraClient, int cameraId, const String16& clientPackageName, int clientUid, int clientPid, /*out*/ sp<ICamera>* device) { sp<Client> client = nullptr; ret = connectHelper<ICameraClient,Client>(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>& cameraService, const sp<IInterface>& 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<BasicClient>* 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<MediaSource> &cameraSource, sp<MediaCodecSource> *source) { //根据视频源camerasource,创建视频编码器。 sp<MediaCodecSource> 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<AString> 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<AString> *matches, Vector<AString> *owners) { const sp<IMediaCodecList> list = getInstance(); const sp<MediaCodecInfo> 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<CodecBase::CodecCallback>( new CodecCallback(new AMessage(kWhatCodecNotify, this)))); mBufferChannel = mCodec->getBufferChannel(); mBufferChannel->setCallback( std::unique_ptr<CodecBase::BufferCallback>( new BufferCallback(new AMessage(kWhatCodecNotify, this)))); //在 kWhatInit消息的处理中,分配编解码组件。具体调用是mCodec->initiateAllocateComponent(format); sp<AMessage> msg = new AMessage(kWhatInit, this); }
MediaCodec.cpp status_t MediaCodec::configure( const sp<AMessage> &format, const sp<Surface> &surface, const sp<ICrypto> &crypto, const sp<IDescrambler> &descrambler, uint32_t flags) { //配置,启动编码组件,具体调用mCodec->initiateConfigureComponent(format); sp<AMessage> msg = new AMessage(kWhatConfigure, this); }
Acodec.cpp //这个函数,编解码组件配置都会执行,比较长, status_t ACodec::configureCodec( const char *mime, const sp<AMessage> &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<MediaWriter> writer; sp<MediaSource> mediaSource; err = setupMediaSource(&mediaSource); sp<MediaCodecSource> 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<IMediaSource> &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<MetaData> 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<MetaData> 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<IMemory> &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(); } } 到这里完成了视音频录制的。音频的处理类似。