当前位置: 首页 > news >正文

网站建设和维护试卷aspcms 生成网站地图

网站建设和维护试卷,aspcms 生成网站地图,新冠止咳药哪个效果好,做网站贴吧文章目录 前言音频采集音频初始化AudioRecord 分析AudioSource 采集到音频 音频编码音频编码后数据处理MPEG4Writer写入音频编码后数据到文件MPEG4Writer::Track 取编码后的音频编数据结语 本文首发地址 https://blog.csdn.net/CSqingchen/article/details/134896808 最新更新地… 文章目录 前言音频采集音频初始化AudioRecord 分析AudioSource 采集到音频 音频编码音频编码后数据处理MPEG4Writer写入音频编码后数据到文件MPEG4Writer::Track 取编码后的音频编数据结语 本文首发地址 https://blog.csdn.net/CSqingchen/article/details/134896808 最新更新地址 https://gitee.com/chenjim/chenjimblog 前言 通过安卓MediaRecorder(2)录制源码分析我们知道 MediaRecorder 相关接口是在 StagefrightRecorder.cpp 中实现本文进一步分析音频采集、编码、写入文件详细流程。 音频采集 音频初始化 通过前文我们知道 setupAudioEncoder 在 setupMPEG4orWEBMRecording 中初始化相关源码如下 // frameworks/av/media/libmediaplayerservice/StagefrightRecorder.cpp status_t StagefrightRecorder::setupAudioEncoder() {spMediaCodecSource audioEncoder createAudioSource();return OK; } spMediaCodecSource StagefrightRecorder::createAudioSource() {...// 通过 AVFactory 工厂创建 AudioSource,并初始化 spAudioSource audioSource AVFactory::get()-createAudioSource(attr,mAttributionSource,sourceSampleRate,mAudioChannels,mSampleRate,mSelectedDeviceId,mSelectedMicDirection,mSelectedMicFieldDimension);}那 AudioSource 是如何初始化的呢 // frameworks/av/media/libstagefright/AudioSource.cpp void AudioSource::set(const audio_attributes_t *attr, const AttributionSourceState attributionSource,uint32_t sampleRate, uint32_t channelCount, uint32_t outSampleRate,audio_port_handle_t selectedDeviceId,audio_microphone_direction_t selectedMicDirection,float selectedMicFieldDimension) {...// 构造了 一个 AudioRecord cpp 对象 mRecord new AudioRecord(AUDIO_SOURCE_DEFAULT, sampleRate, AUDIO_FORMAT_PCM_16_BIT,audio_channel_in_mask_from_count(channelCount),attributionSource,(size_t) (bufCount * frameCount),// 采集的音频数据回调 wpAudioRecord::IAudioRecordCallback{this},frameCount /*notificationFrames*/,AUDIO_SESSION_ALLOCATE,AudioRecord::TRANSFER_DEFAULT,AUDIO_INPUT_FLAG_NONE,attr,selectedDeviceId,selectedMicDirection,selectedMicFieldDimension);... }AudioRecord.java 底层的实现也是 AudioSource.cpp AudioRecord 主要是负责从麦克风设备采集音频 PCM 帧 AudioRecord 分析 // frameworks/av/media/libaudioclient/AudioRecord.cpp status_t AudioRecord::set(...) {...if (mCallback ! nullptr) {// 启动录制的线程 mAudioRecordThread new AudioRecordThread(*this);mAudioRecordThread-run(AudioRecord, ANDROID_PRIORITY_AUDIO);}... } bool AudioRecord::AudioRecordThread::threadLoop() {...nsecs_t ns mReceiver.processAudioBuffer();... }nsecs_t AudioRecord::processAudioBuffer() {... // 回调 AudioRecord::IAudioRecordCallback if (newOverrun) {callback-onOverrun();}if (markerReached) {callback-onMarker(markerPosition.value());}while (newPosCount 0) {callback-onNewPos(newPosition.value());newPosition updatePeriod;newPosCount--;}if (mObservedSequence ! sequence) {mObservedSequence sequence;callback-onNewIAudioRecord();}while (mRemainingFrames 0) {// 获取 audioBuffer status_t err obtainBuffer(audioBuffer, requested, NULL, nonContig);// 回调 取到的 buffer 到 AudioSource 中 onMoreData const size_t readSize callback-onMoreData(*buffer);// 释放 buffer releaseBuffer(audioBuffer);} }AudioSource 采集到音频 // frameworks/av/media/libstagefright/AudioSource.cpp size_t AudioSource::onMoreData(const AudioRecord::Buffer audioBuffer) { ...// 将AudioRecord::Buffer 放入 MediaBufferMediaBuffer *buffer new MediaBuffer(audioBuffer.size());memcpy((uint8_t *) buffer-data(),audioBuffer.data(), audioBuffer.size());buffer-set_range(0, audioBuffer.size());// 将 buffer 放入缓存queueInputBuffer_l(buffer, timeUs);return audioBuffer.size(); } void AudioSource::queueInputBuffer_l(MediaBuffer *buffer, int64_t timeUs) {...// 将 buffer 放入缓存 mBuffersReceived 中mBuffersReceived.push_back(buffer);mFrameAvailableCondition.signal(); }// 如下接口可以读取采集到的 buffer status_t AudioSource::read(MediaBufferBase **out, const ReadOptions * /* options */) {...MediaBuffer *buffer *mBuffersReceived.begin();mBuffersReceived.erase(mBuffersReceived.begin());buffer-setObserver(this);...*out buffer; }音频编码 编码器创建如下 spMediaCodecSource StagefrightRecorder::createAudioSource() {spMediaCodecSource audioEncoder MediaCodecSource::Create(mLooper, format, audioSource); } // MediaCodecSource 构造如下 MediaCodecSource::MediaCodecSource(const spALooper looper,const spAMessage outputFormat,const spMediaSource source,const spPersistentSurface persistentSurface,uint32_t flags){if (!(mFlags FLAG_USE_SURFACE_INPUT)) {// 将 AudioSource 放入 Puller 中mPuller new Puller(source);} }MediaCodecSource::start 发送 kWhatStart 消息 status_t MediaCodecSource::start(MetaData* params) {spAMessage msg new AMessage(kWhatStart, mReflector);msg-setObject(meta, params);// 发消息 kWhatStart 到 MediaCodecSource::onMessageReceived // 进而传递到 MediaCodecSource::onStart return postSynchronouslyAndReturnError(msg); } void MediaCodecSource::onMessageReceived(const spAMessage msg) {switch (msg-what()) {case kWhatStart: {spAMessage response new AMessage;// 调用 MediaCodecSource::onStart response-setInt32(err, onStart(params));response-postReply(replyID);}} } status_t MediaCodecSource::onStart(MetaData *params) {...// 创建 kWhatPullerNotify 消息,传入 MediaCodecSource::Puller::start spAMessage notify new AMessage(kWhatPullerNotify, mReflector);err mPuller-start(meta.get(), notify); }MediaCodecSource::Puller::start 流程如下 status_t MediaCodecSource::Puller::start(const spMetaData meta, const spAMessage notify) {mNotify notify;// 发送 kWhatStart 消息 到 MediaCodecSource::Puller::onMessageReceivedspAMessage msg new AMessage(kWhatStart, this);msg-setObject(meta, meta);return postSynchronouslyAndReturnError(msg); } void MediaCodecSource::Puller::onMessageReceived(const spAMessage msg) {switch (msg-what()) {case kWhatStart: {// start后就开始 pull schedulePull();}case kWhatPull:{// 通过上文的 AudioSource::read 读取采集到的数据status_t err mSource-read(mbuf);// 将读取到的 mbuf 放入队列 queue-pushBuffer(mbuf);if (mbuf ! NULL) {// 送到 MediaCodecSource::onMessageReceived 通知编码器 pull 到数据mNotify-post();// 继续 pull msg-post();} else {// 结束 EndOfStream handleEOS();}} }MediaCodecSource::Puller 读取到数据后mNotify 发消息 kWhatPullerNotify 通知编码 void MediaCodecSource::onMessageReceived(const spAMessage msg) {switch (msg-what()) {case kWhatPullerNotify:{...// 收到 通知送去编码feedEncoderInputBuffers();}} } status_t MediaCodecSource::feedEncoderInputBuffers() {// 取数据编码while (!mAvailEncoderInputIndices.empty() mPuller-readBuffer(mbuf)) {...// inbuf 送到编码器status_t err mEncoder-getInputBuffer(bufferIndex, inbuf);...// 编码status_t err mEncoder-queueInputBuffer(bufferIndex, 0, size, timeUs, flags);} }音频编码后数据处理 在创建编码器时把 mEncoderActivityNotify 设置到编码器的 Callback编码器的消息会通过 kWhatEncoderActivity 发送出来 status_t MediaCodecSource::initEncoder() {...mEncoderActivityNotify new AMessage(kWhatEncoderActivity, mReflector);mEncoder-setCallback(mEncoderActivityNotify);... }当编码完成、状态变化会收到 kWhatEncoderActivity 消息通知 void MediaCodecSource::onMessageReceived(const spAMessage msg) {switch (msg-what()) {case kWhatEncoderActivity:{if (cbID MediaCodec::CB_INPUT_AVAILABLE) {// 输入不可用继续给编码器送输入} else if (cbID MediaCodec::CB_OUTPUT_FORMAT_CHANGED) {// 输出格式变化} else if (cbID MediaCodec::CB_OUTPUT_AVAILABLE) {// 正常的输出数据// 获取编码器额输出status_t err mEncoder-getOutputBuffer(index, outbuf);// 将输出 buf 转 MediaBufferMediaBuffer *mbuf new MediaBuffer(outbuf-size());// 提取 MetaDataspMetaData meta new MetaData(mbuf-meta_data());...// 将 编码数据 outbuf 填充到 mbufmemcpy(mbuf-data(), outbuf-data(), outbuf-size());// 将编码后的数据添加到队列output-mBufferQueue.push_back(mbuf);} else if (cbID MediaCodec::CB_ERROR) {// ERROR 异常退出signalEOS(err);}}} }当需要数据时,从输出队列取数据即可 status_t MediaCodecSource::read(MediaBufferBase** buffer, const ReadOptions* /* options */) {MutexedOutput::Locked output(mOutput);*buffer NULL;while (output-mBufferQueue.size() 0 !output-mEncoderReachedEOS) {output.waitForCondition(output-mCond);}if (!output-mEncoderReachedEOS) {*buffer *output-mBufferQueue.begin();output-mBufferQueue.erase(output-mBufferQueue.begin());return OK;}return output-mErrorCode; }MPEG4Writer写入音频编码后数据到文件 通过如下源码我们知道了 MPEG4Writer 创建和写入线程启动 status_t StagefrightRecorder::setupMPEG4orWEBMRecording() {...writer mp4writer new MPEG4Writer(mOutputFd); } status_t StagefrightRecorder::start() {...status mWriter-start(meta.get()); } status_t MPEG4Writer::start(MetaData *param) {...err startWriterThread();...// 这个 startTracks 主要为 MPEG4Writer::Track 做准备 err startTracks(param); } status_t MPEG4Writer::startWriterThread() {mDone false;mIsFirstChunk true;mDriftTimeUs 0;// 将 音、视频 Track 添加到 mChunkInfosfor (ListTrack *::iterator it mTracks.begin();it ! mTracks.end(); it) {ChunkInfo info;info.mTrack *it;info.mPrevChunkTimestampUs 0;info.mMaxInterChunkDurUs 0;mChunkInfos.push_back(info);}...// 启动线程执行 ThreadWrapper pthread_attr_t attr;pthread_attr_init(attr);pthread_attr_setdetachstate(attr, PTHREAD_CREATE_JOINABLE);pthread_create(mThread, attr, ThreadWrapper, this);pthread_attr_destroy(attr);return OK; } void *MPEG4Writer::ThreadWrapper(void *me) {MPEG4Writer *writer static_castMPEG4Writer *(me);// 最终执行的是 threadFunc()writer-threadFunc();return NULL; }写入线程开启后一直循环无数据时等待 void MPEG4Writer::threadFunc() {Mutex::Autolock autoLock(mLock);while (!mDone) {Chunk chunk;bool chunkFound false;// findChunkToWrite 从 mChunkInfos 找到需要写入的 Chunkwhile (!mDone !(chunkFound findChunkToWrite(chunk))) {mChunkReadyCondition.wait(mLock);}// 在实时记录模式下写时不按顺序持有锁, 减少媒体跟踪线程的阻塞时间。// 否则保持锁直到现有的块被写入文件。if (chunkFound) {if (mIsRealTimeRecording) {mLock.unlock();}// 写入 Chunk writeChunkToFile(chunk);if (mIsRealTimeRecording) {mLock.lock();}}}// 写入所有内存writeAllChunks(); }写入到文件是在 writeChunkToFile 中完成 void MPEG4Writer::writeChunkToFile(Chunk* chunk) {while (!chunk-mSamples.empty()) {// 取一个 MediaBuffer ListMediaBuffer *::iterator it chunk-mSamples.begin();...// 写入 MediaBuffer off64_t offset addSample_l(*it, usePrefix, tiffHdrOffset, bytesWritten);...}// 写入后清空chunk-mSamples.clear(); } off64_t MPEG4Writer::addSample_l(MediaBuffer *buffer, bool usePrefix,uint32_t tiffHdrOffset, size_t *bytesWritten) {...writeOrPostError(mFd, (const uint8_t*)buffer-data() buffer-range_offset(),buffer-range_length()); } void MPEG4Writer::writeOrPostError(int fd, const void* buf, size_t count) {...// 真正的写入 buf ssize_t bytesWritten ::write(fd, buf, count);...// IO 异常时 抛出 通过消息传递到上层 spAMessage msg new AMessage(kWhatIOError, mReflector);msg-setInt32(err, ERROR_IO); }MPEG4Writer::Track 取编码后的音频编数据 MPEG4Writer::Track 启动源码如下 status_t MPEG4Writer::startTracks(MetaData *params) {...for (ListTrack *::iterator it mTracks.begin();it ! mTracks.end(); it) {// MPEG4Writer::Track start status_t err (*it)-start(params);...}return OK; } status_t MPEG4Writer::Track::start(MetaData *params) {...// 启动线程执行 ThreadWrapper pthread_create(mThread, attr, ThreadWrapper, this); } void *MPEG4Writer::Track::ThreadWrapper(void *me) {Track *track static_castTrack *(me);status_t err track-threadEntry();return (void *)(uintptr_t)err; }MPEG4Writer::Track::threadEntry 读取编码后的数据 status_t MPEG4Writer::Track::threadEntry() {// mSource-read 也就是 上文 MediaCodecSource::read 一直不停的读取数据到 buffer MediaBufferBase *buffer;while (!mDone (err mSource-read(buffer)) OK buffer ! NULL) {...// 将 buffer 转为 MediaBuffer MediaBuffer *copy new MediaBuffer(buffer-range_length());if (sampleFileOffset ! -1) {copy-meta_data().setInt64(kKeySampleFileOffset, sampleFileOffset);} else {memcpy(copy-data(), (uint8_t*)buffer-data() buffer-range_offset(),buffer-range_length());}...// 将 copy 放入队列 mChunkSamplesmChunkSamples.push_back(copy);...// 将 mChunkSamples 转 为 ChunkbufferChunk(timestampUs);} } void MPEG4Writer::Track::bufferChunk(int64_t timestampUs) {Chunk chunk(this, timestampUs, mChunkSamples);// 也就是 MPEG4Writer::bufferChunk mOwner-bufferChunk(chunk);mChunkSamples.clear(); } void MPEG4Writer::bufferChunk(const Chunk chunk) {Mutex::Autolock autolock(mLock);for (ListChunkInfo::iterator it mChunkInfos.begin();it ! mChunkInfos.end(); it) {if (chunk.mTrack it-mTrack) {// 将 Chunk 放入 ChunkInfo.mChunks 中it-mChunks.push_back(chunk);// 数据准备好了通知 mChunkReadyCondition.wait 继续执行// 进而 由 findChunkToWrite 读取写入文件 mChunkReadyCondition.signal();return;}} }结语 到这里已经完成了 MediaRecorder 音频采集、编码、写入文件详细源码分析。 用一幅图概括如下 #mermaid-svg-3ij6OnkF94m4gjTW {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-3ij6OnkF94m4gjTW .error-icon{fill:#552222;}#mermaid-svg-3ij6OnkF94m4gjTW .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-3ij6OnkF94m4gjTW .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-3ij6OnkF94m4gjTW .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-3ij6OnkF94m4gjTW .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-3ij6OnkF94m4gjTW .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-3ij6OnkF94m4gjTW .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-3ij6OnkF94m4gjTW .marker{fill:#333333;stroke:#333333;}#mermaid-svg-3ij6OnkF94m4gjTW .marker.cross{stroke:#333333;}#mermaid-svg-3ij6OnkF94m4gjTW svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-3ij6OnkF94m4gjTW g.classGroup text{fill:#9370DB;fill:#131300;stroke:none;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:10px;}#mermaid-svg-3ij6OnkF94m4gjTW g.classGroup text .title{font-weight:bolder;}#mermaid-svg-3ij6OnkF94m4gjTW .nodeLabel,#mermaid-svg-3ij6OnkF94m4gjTW .edgeLabel{color:#131300;}#mermaid-svg-3ij6OnkF94m4gjTW .edgeLabel .label rect{fill:#ECECFF;}#mermaid-svg-3ij6OnkF94m4gjTW .label text{fill:#131300;}#mermaid-svg-3ij6OnkF94m4gjTW .edgeLabel .label span{background:#ECECFF;}#mermaid-svg-3ij6OnkF94m4gjTW .classTitle{font-weight:bolder;}#mermaid-svg-3ij6OnkF94m4gjTW .node rect,#mermaid-svg-3ij6OnkF94m4gjTW .node circle,#mermaid-svg-3ij6OnkF94m4gjTW .node ellipse,#mermaid-svg-3ij6OnkF94m4gjTW .node polygon,#mermaid-svg-3ij6OnkF94m4gjTW .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-3ij6OnkF94m4gjTW .divider{stroke:#9370DB;stroke:1;}#mermaid-svg-3ij6OnkF94m4gjTW g.clickable{cursor:pointer;}#mermaid-svg-3ij6OnkF94m4gjTW g.classGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-3ij6OnkF94m4gjTW g.classGroup line{stroke:#9370DB;stroke-width:1;}#mermaid-svg-3ij6OnkF94m4gjTW .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-3ij6OnkF94m4gjTW .classLabel .label{fill:#9370DB;font-size:10px;}#mermaid-svg-3ij6OnkF94m4gjTW .relation{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-3ij6OnkF94m4gjTW .dashed-line{stroke-dasharray:3;}#mermaid-svg-3ij6OnkF94m4gjTW #compositionStart,#mermaid-svg-3ij6OnkF94m4gjTW .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-3ij6OnkF94m4gjTW #compositionEnd,#mermaid-svg-3ij6OnkF94m4gjTW .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-3ij6OnkF94m4gjTW #dependencyStart,#mermaid-svg-3ij6OnkF94m4gjTW .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-3ij6OnkF94m4gjTW #dependencyStart,#mermaid-svg-3ij6OnkF94m4gjTW .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-3ij6OnkF94m4gjTW #extensionStart,#mermaid-svg-3ij6OnkF94m4gjTW .extension{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-3ij6OnkF94m4gjTW #extensionEnd,#mermaid-svg-3ij6OnkF94m4gjTW .extension{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-3ij6OnkF94m4gjTW #aggregationStart,#mermaid-svg-3ij6OnkF94m4gjTW .aggregation{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-3ij6OnkF94m4gjTW #aggregationEnd,#mermaid-svg-3ij6OnkF94m4gjTW .aggregation{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-3ij6OnkF94m4gjTW .edgeTerminals{font-size:11px;}#mermaid-svg-3ij6OnkF94m4gjTW :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} MPEG4Writer::Track 读取编码后内容() AudioSource 采集音频() MediaCodecSource 编码() MPEG4Writer 写入编码后内容() 希望对你有所帮助。如果你在使用MediaRecorder的过程中遇到了其他问题欢迎留言讨论。 如果你觉得本文还不错可以点赞收藏。 相关文章 安卓MediaRecorder(1)录制音频的详细使用 安卓MediaRecorder(2)录制源码分析 安卓MediaRecorder(3)音频采集编码写入详细源码分析 安卓MediaRecorder(4)视频采集编码写入详细源码分析
http://www.pierceye.com/news/414031/

相关文章:

  • 如何查看网站开发语言百度排行榜风云榜
  • 泉州 网站建设公司首选广告设计公司名字有寓意有创意
  • 天津个人做网站慈利网站制作
  • 专门做推广的网站吗宿迁房价2023年最新房价
  • 0基础12天精通网站建设网站建设 全网推广
  • 东莞网站营销推广公司移动应用开发案例
  • 妇科医院网站建设怎么做网站建设培训心得体会
  • 网站建设 管理正能量网站入口地址
  • 做网站没有创意Wordpress国际收款
  • 网站推广关键词工具wap网站分享到微信
  • 哪个网站可以给图片做链接做网站的公司在哪
  • 搬瓦工可以长期做网站广告制作开票大类是什么
  • 高级网站开发工信部小企业门户网站建设
  • 网站建站知识秦皇岛汽车网站制作
  • 建站之星极速版app开发需求
  • .net域名可以做银行网站吗做网站用模版
  • 嘉兴市平湖市建设局网站品牌设计公司 知乎
  • jfinal网站开发模板app开发网站
  • 成都和奇乐网站建设公司怎么样研发网站要多久
  • 蓬莱做网站北京宣传片
  • 网站建设 部署与发布wordpress多说插件
  • 池州做网站的公司哪里有网站开发技术
  • 网站建设内容策划外贸软件排行榜前十名
  • 微信官方网站公众平台郸城建设银行网站
  • .net 微信网站开发免费网站建设制作
  • 做网站需要啥备案之类的嘛传统的网站开发模式
  • 杭州网站seo优化最适合女生的专业排名
  • 广州市酒店网站设计交易平台网站怎么做
  • 江苏省示范校建设专题网站网站网页制作公司网站
  • 前海艾爻网站 建设磐安住房和城乡建设部网站