拓冰建站拓冰建站
首页 / 资讯中心 / 正文

第 16 章:媒体与视频流水线

Android 的媒体框架是 AOSP 中架构最复杂的子系统之一。它覆盖高层 Java API(MediaPlayer、MediaCodec、MediaRecorder),向下延伸至原生 C++ 栈,包含 Stagefright、Codec2 框架、NuPlayer、相机服务、媒体解析器,以及直接与厂商提供的编解码器、相机硬件交互的硬件抽象层。核心流水线由约 5 万行 C++ 代码构成,你观看的每一帧视频、听到的每一段音频采样、拍摄的每一张照片,都会经过本章所描述的这套机制。16.1 媒体架构概述16.1.1 分层架构Android 媒体栈分为 5 个独立层级。最上层为应用开发者使用的 Java 与 NDK API。其下是原生服务层,负责管理编解码器实例、播放会话与录制流水线。核心编解码器抽象层同时包含传统 Stagefright/OMX 路径与现代 Codec2 路径,完成服务层与实际编解码器实现之间的转换。再往下,HAL(硬件抽象层)定义厂商对接契约。最底层是硬件本身:DSP、专用视频编解码器、相机传感器以及 ISP。上图体现 Android 媒体架构的核心要点:编解码器层存在两条并行路径。传统 OMX 路径(ACodec)起源于 Android 1.0,对 OpenMAX IL 组件进行封装。现代 Codec2 路径(CCodec)自 Android 10 引入,如今是谷歌提供的全部软件编解码器、绝大多数厂商硬件编解码器的首选路径。两条路径均被封装在 MediaCodec API 之下,应用无需感知底层实际使用哪一条路径。16.1.2 关键进程与服务媒体框架运行在多个系统进程中:进程服务二进制文件mediaserverMediaPlayerService、MediaRecorderService/system/bin/mediaservermedia.codecCodec2 组件服务/vendor/bin/hw/android.hardware.media.c2‑servicemedia.extractorMediaExtractorService/system/bin/mediaextractorcameraserverCameraService/system/bin/cameraservermedia.resource_managerResourceManagerServicemediaserver 的一部分这种进程隔离是刻意设计:媒体解析器运行在沙箱进程,限制解析不可信媒体文件带来的安全风险。编解码器组件可运行在厂商进程,实现厂商代码与框架代码的隔离。16.1.3 视频帧的流转为理解整套架构,下面来看播放过程中单个视频帧的生命周期:该端到端流程至少涉及 3 个进程(应用、编解码器服务、SurfaceFlinger),仅编解码器部分通常就要经过两次 Binder/AIDL 跨进程调用。16.1.4 源码目录布局frameworks/av/下相关源码目录汇总:frameworks/av/ media/ libstagefright/ # Stagefright 核心库 MediaCodec.cpp # 8234 行 — MediaCodec 状态机 ACodec.cpp # 9458 行 — OMX 编解码器封装 MPEG4Writer.cpp # 6070 行 — MP4 合成器 NuMediaExtractor.cpp # 896 行 — 解析器封装 MediaExtractorFactory.cpp # 395 行 — 解析器插件加载 codec2/ components/ # 23 套以上软件编解码器 aac/ amr_nb_wb/ aom/ apv/ avc/ base/ dav1d/ flac/ g711/ gav1/ gsm/ hevc/ iamf/ mp3/ mpeg2/ mpeg4_h263/ opus/ raw/ vorbis/ vpx/ xaac/ sfplugin/ # Codec2 对接 Stagefright 的桥接层 CCodec.cpp # 3849 行 CCodecBufferChannel.cpp # 3428 行 CCodecConfig.cpp Codec2Buffer.cpp hal/ # Codec2 HAL 实现 aidl/ hidl/ services/ core/ # Codec2 核心接口 libmediaplayerservice/ MediaPlayerService.cpp # 3114 行 StagefrightRecorder.cpp # 2759 行 nuplayer/ NuPlayer.cpp # 3265 行 NuPlayerDecoder.cpp # 1394 行 NuPlayerRenderer.cpp # 2239 行 NuPlayerDriver.cpp # 1240 行 libmedia/ VideoCapabilities.cpp # 1966 行 MediaProfiles.cpp # 1521 行 services/ camera/ libcameraservice/ CameraService.cpp # 7019 行 device3/ # Camera HAL3 设备实现 api1/ # 传统相机 API api2/ # Camera2 API(CameraDeviceClient)16.2 MediaCodec 与 Stagefright16.2.1 MediaCodec:核心状态机MediaCodec 是 Android 媒体框架中最重要的类。frameworks/av/media/libstagefright/MediaCodec.cpp共 8234 行,实现一套复杂异步状态机,管理系统中每一个编解码器实例的生命周期,涵盖音频、视频,编码器、解码器,硬件、软件类型。该类提供如下工厂方法:// frameworks/av/media/libstagefright/MediaCodec.cpp, 第1214行 // static spMediaCodec MediaCodec::CreateByType( const spALooper looper, const AString mime, bool encoder, status_t *err, pid_t pid, uid_t uid) { spAMessage format; return CreateByType(looper, mime, encoder, err, pid, uid, format); } spMediaCodec MediaCodec::CreateByType( const spALooper looper, const AString mime, bool encoder, status_t *err, pid_t pid, uid_t uid, spAMessage format) { VectorAString matchingCodecs; MediaCodecList::findMatchingCodecs( mime.c_str(), encoder, 0, format, matchingCodecs); if (err != NULL) { *err = NAME_NOT_FOUND; } for (size_t i = 0; i matchingCodecs.size(); ++i) { spMediaCodec codec = new MediaCodec(looper, pid, uid); AString componentName = matchingCodecs[i]; status_t ret = codec-init(componentName); if (err != NULL) { *err = ret; } if (ret == OK) { return codec; } ALOGD("Allocating component '%s' failed (%d), try next one.", componentName.c_str(), ret); } return NULL; }该工厂模式至关重要:CreateByType查询 MediaCodecList,获取所有支持指定 MIME 类型的编解码器,按优先级顺序尝试实例化。若硬件编解码器分配失败(例如全部硬件实例已被占用),系统会回退到软件编解码器。状态机MediaCodec 实现定义完备的状态机,状态如下:状态切换由 MediaCodec.cpp 第 862 行定义的内部消息码驱动:// frameworks/av/media/libstagefright/MediaCodec.cpp, 第862行 enum { kWhatFillThisBuffer = 'fill', kWhatDrainThisBuffer = 'drai', kWhatEOS = 'eos ', kWhatStartCompleted = 'Scom', kWhatStopCompleted = 'scom', kWhatReleaseCompleted = 'rcom', kWhatFlushCompleted = 'fcom', kWhatError = 'erro', kWhatCryptoError = 'ercp', kWhatComponentAllocated = 'cAll', kWhatComponentConfigured = 'cCon', kWhatInputSurfaceCreated = 'isfc', kWhatInputSurfaceAccepted = 'isfa', kWhatSignaledInputEOS = 'seos', kWhatOutputFramesRendered = 'outR', kWhatOutputBuffersChanged = 'outC', kWhatFirstTunnelFrameReady = 'ftfR', kWhatPollForRenderedBuffers = 'plrb', kWhatMetricsUpdated = 'mtru', kWhatRequiredResourcesChanged = 'reqR', };使用四字符码(FourCC)作为消息标识符是 Stagefright 框架标志性模式。这类编码让调试日志具备可读性:日志中看到fill,就代表 “填充此缓冲区” 消息。16.2.2 MediaCodec 初始化init()方法(第 2531 行)完成关键步骤:选择并实例化底层编解码器实现,打通抽象 MediaCodec API 与具体编解码器后端。// frameworks/av/media/libstagefright/MediaCodec.cpp, 第2531行 status_t MediaCodec::init(const AString name) { ScopedTrace trace(ATRACE_TAG, "MediaCodec::Init#native"); status_t err = mResourceManagerProxy-init(); if (err != OK) { mErrorLog.log(LOG_TAG, base::StringPrintf( "Fatal error: failed to initialize ResourceManager (err=%d)", err)); mCodec = NULL; // remove the codec return err; } // save init parameters for reset mInitName = name; mCodecInfo.clear(); bool secureCodec = false; const char *owner = ""; if (!name.startsWith("android.filter.")) { err = mGetCodecInfo(name, mCodecInfo); // ... error handling ... secureCodec = name.endsWith(".secure"); VectorAString mediaTypes; mCodecInfo-getSupportedMediaTypes(mediaTypes); for (size_t i = 0; i mediaTypes.size(); ++i) { if (mediaTypes[i].startsWith("video/")) { mDomain = DOMAIN_VIDEO; break; } else if (mediaTypes[i].startsWith("audio/")) { mDomain = DOMAIN_AUDIO; break; } else if (mediaTypes[i].startsWith("image/")) { mDomain = DOMAIN_IMAGE; break; } } owner = mCodecInfo-getOwnerName(); } mCodec = mGetCodecBase(name, owner);此处有若干重要细节:资源管理器集成:分配任何编解码器之前,先初始化 ResourceManager。该服务跟踪系统全部编解码器实例,资源紧张时可从低优先级应用手中回收编解码器。域检测:编解码器通过自身支持的 MIME 类型判断处理的是视频、音频还是图像数据。视频编解码器会获得独立 ALooper 线程(CodecLooper),因为视频解码不能共享主事件队列,否则会发生卡顿。编解码器基类选择:回调mGetCodecBase根据 MediaCodecInfo 的 owner 字段,创建 ACodec(OMX 组件)或者 CCodec(Codec2 组件)。安全编解码器处理:名称以.secure结尾的编解码器代表 DRM 保护内容通路,需要特殊硬件支持以及额外安全校验。16.2.3 配置与资源管理configure()方法(第 2856 行)根据格式参数与输出 Surface 完成编解码器配置。// frameworks/av/media/libstagefright/MediaCodec.cpp, 第2856行 status_t MediaCodec::configure( const spAMessage format, const spSurface surface, const spICrypto crypto, const spIDescrambler descrambler, uint32_t flags) { ScopedTrace trace(ATRACE_TAG, "MediaCodec::configure#native"); // Update the codec importance. updateCodecImportance(format); // ... spAMessage msg = new AMessage(kWhatConfigure, this); msg-setMessage("format", format); msg-setInt32("flags", flags); msg-setObject("surface", surface); if (crypto != NULL || descrambler != NULL) { if (crypto != NULL) { msg-setPointer("crypto", crypto.get()); } else { msg-setPointer("descrambler", descrambler.get()); } }配置流程包含带资源回收的重试机制。若初次配置因资源不足失败(例如全部硬件编解码器实例被占用),MediaCodec 会请求 ResourceManagerService 从低优先级进程回收编解码器,然后重试。for (int i = 0; i = kMaxRetry; ++i) { spAMessage response; err = PostAndAwaitResponse(msg, response); if (err != OK err != INVALID_OPERATION) { if (isResourceError(err) !mResourceManagerProxy-reclaimResource(resources)) { break; } // ...reset and retry... } if (!isResourceError(err)) { break; } }常量kMaxRetry设置为 2(第 337 行),代表配置最多尝试 3 次。16.2.4 资源管理器ResourceManagerServiceProxy(自第 415 行开始定义)是系统媒体资源管理器的复杂封装,负责:资源注册:每个编解码器实例向 ResourceManager 注册自身资源占用信息(类型、硬件 / 软件、安全 / 非安全)。资源回收:资源耗尽时,ResourceManager 识别优先级最低的客户端,向其发起reclaimResource()调用。Binder 死亡处理:若 ResourceManager 进程消亡,代理自动重连并重新注册全部资源。// frameworks/av/media/libstagefright/MediaCodec.cpp, 第349行 struct ResourceManagerClient : public BnResourceManagerClient { explicit ResourceManagerClient(MediaCodec* codec, int32_t pid, int32_t uid) : mMediaCodec(codec), mPid(pid), mUid(uid) {} Status reclaimResource(bool* _aidl_return) override { spMediaCodec codec = mMediaCodec.promote(); if (codec == NULL) { // Codec is already gone, so remove the resources as well // ... *_aidl_return = true; return Status::ok(); } status_t err = codec-reclaim(); if (err == WOULD_BLOCK) { ALOGD("Wait for the client to release codec."); usleep(kMaxReclaimWaitTimeInUs); ALOGD("Try to reclaim again."); err = codec-reclaim(true /* force */); } // ... }回收机制对于硬件编解码器资源有限的移动设备尤为重要。一片 SoC 通常仅支持同时 2‑4 路硬件解码会话。当请求第 5 路会话时,ResourceManager 必须决定驱逐哪一个已有会话。优先级依据进程 OOM 调整分数,该分数反映应用对用户的可见度与重要程度。16.2.5 MediaCodec 指标与遥测MediaCodec 实现了完备遥测能力,文件头部约 100 个指标常量(第 111‑287 行)可以佐证。指标覆盖:编解码器标识:名称、MIME 类型、模式(音频 / 视频 / 图像)、编码器 / 解码器、硬件 / 软件、安全模式、隧道模式性能:时延(最小 / 最大 / 平均 / 直方图)、帧率、码率质量:卡顿事件(次数、时长、得分)、抖动事件(次数、得分)渲染质量:已释放帧、已渲染帧、丢弃帧、跳过帧、停滞帧HDR 元数据:色彩标准、色彩范围、传递函数、HDR10+ 信息错误追踪:错误码、错误状态渲染质量检测逻辑十分完善,同时实现卡顿检测(帧未按时渲染)与抖动检测(帧间隔不均匀)。这些指标上报至平台 MediaMetrics 系统,用于大规模监控视频播放质量。16.2.6 启动状态下的缓冲区流转编解码器启动后,客户端与编解码器之间以乒乓模式流转缓冲区:BufferCallback类(第 968 行)把编解码器内部缓冲区通知,转换为驱动 MediaCodec 状态机的 AMessage 事件。// frameworks/av/media/libstagefright/MediaCodec.cpp, 第984行 void BufferCallback::onInputBufferAvailable( size_t index, const spMediaCodecBuffer buffer) { spAMessage notify(mNotify-dup()); notify-setInt32("what", kWhatFillThisBuffer); notify-setSize("index", index); notify-setObject("buffer", buffer); notify-post(); } void BufferCallback::onOutputBufferAvailable( size_t index, const spMediaCodecBuffer buffer) { spAMessage notify(mNotify-dup()); notify-setInt32("what", kWhatDrainThisBuffer); notify-setSize("index", index); notify-setObject("buffer", buffer); notify-post(); }16.2.7 ACodec:OMX 桥接层frameworks/av/media/libstagefright/ACodec.cpp中的 ACodec 是 MediaCodec 与 OpenMAX IL(OMX)组件之间的传统桥接层。文件共 9458 行,属于媒体框架中体量最大的单个源文件。虽逐步被 Codec2 替代,但为兼容老旧厂商 OMX 实现,ACodec 依旧十分重要。ACodec 使用AHierarchicalStateMachine模式实现自身嵌套状态机,每个状态是一个嵌套类:// frameworks/av/media/libstagefright/ACodec.cpp, 第276行 struct ACodec::BaseState : public AState { explicit BaseState(ACodec *codec, const spAState parentState = NULL); protected: enum PortMode { KEEP_BUFFERS, RESUBMIT_BUFFERS, FREE_BUFFERS, }; ACodec *mCodec; virtual PortMode getPortMode(OMX_U32 portIndex); virtual void stateExited(); virtual bool onMessageReceived(const spAMessage msg); virtual bool onOMXEvent(OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2); virtual void onOutputBufferDrained(const spAMessage msg); virtual void onInputBufferFilled(const spAMessage msg); };ACodec 状态层级:CodecObserver类(第 192 行)接收 OMX 回调消息,转换为 AMessage 事件。// frameworks/av/media/libstagefright/ACodec.cpp, 第192行 struct CodecObserver : public BnOMXObserver { explicit CodecObserver(const spAMessage msg) : mNotify(msg) {} virtual void onMessages(const std::listomx_message messages) { if (messages.empty()) { return; } spAMessage notify = mNotify-dup(); spMessageList msgList = new MessageList(); for (std::listomx_message::const_iterator it = messages.cbegin(); it != messages.cend(); ++it) { const omx_message omx_msg = *it; spAMessage msg = new AMessage; msg-setInt32("type", omx_msg.type); switch (omx_msg.type) { case omx_message::EVENT: msg-setInt32("event", omx_msg.u.event_data.event); msg-setInt32("data1", omx_msg.u.event_data.data1); msg-setInt32("data2", omx_msg.u.event_data.data2); break; case omx_message::EMPTY_BUFFER_DONE: msg-setInt32("buffer", omx_msg.u.buffer_data.buffer); msg-setInt32("fence_fd", omx_msg.fenceFd); break; case omx_message::FILL_BUFFER_DONE: // ... range_offset, range_length, flags, timestamp, fence_fd break; case omx_message::FRAME_RENDERED: // ... media_time_us, system_nano break; } msgList-getList().push_back(msg); } notify-setObject("messages", msgList); notify-post(); } };OMX 消息类型直接对应 OpenMAX IL 规范:EMPTY_BUFFER_DONE:编解码器消费完输入缓冲区,将其归还FILL_BUFFER_DONE:编解码器在缓冲区生成输出数据EVENT:状态变更通知、错误事件、端口设置变更FRAME_RENDERED:帧已经渲染到输出 SurfaceACodec 同时完成 Android API 常量与 OMXOMX_VIDEO_CONTROLRATETYPE码率控制模式之间的转换。16.2.8 MPEG4Writer:容器合成器frameworks/av/media/libstagefright/MPEG4Writer.cpp中的 MPEG4Writer 实现 ISO 14496(MP4/3GP)容器格式写入逻辑。负责复杂任务:音视频轨道交错、元数据框写入、维护 MP4 文件的原子树结构。内部类 Track(第 117 行)维护单条轨道状态:// frameworks/av/media/libstagefright/MPEG4Writer.cpp, 第117行 class MPEG4Writer::Track { public: Track(MPEG4Writer *owner, const spMediaSource source, uint32_t aTrackId); ~Track(); status_t start(MetaData *params); status_t stop(bool stopSource = true); status_t pause(); bool reachedEOS(); int64_t getDurationUs() const; int64_t getEstimatedTrackSizeBytes() const; void writeTrackHeader(); // ... bool isAvc() const { return mIsAvc; } bool isHevc() const { return mIsHevc; } bool isAv1() const { return mIsAv1; } bool isApv() const { return mIsApv; } bool isHeic() const { return mIsHeic; } bool isAvif() const { return mIsAvif; } bool isHeif() const { return mIsHeif; } bool isAudio() const { return mIsAudio; } bool isMPEG4() const { return mIsMPEG4; } bool usePrefix() const { return mIsAvc || mIsHevc || mIsHeic || mIsDovi; }Track 类支持大量编解码器:AVC (H.264)、HEVC (H.265)、AV1、APV、HEIC、AVIF、HEIF、杜比视界,以及传统 MPEG‑4 Part2。关键常量定义运行限制:// frameworks/av/media/libstagefright/MPEG4Writer.cpp, 第74行 static const int64_t kMinStreamableFileSizeInBytes = 5 * 1024 * 1024; static const uint8_t kNalUnitTypeSeqParamSet = 0x07; static const uint8_t kNalUnitTypePicParamSet = 0x08; static const int64_t kInitialDelayTimeUs = 700000LL; static const int64_t kMaxMetadataSize = 0x4000000LL; // 64MB max per‑frame metadata size static const int64_t kMaxCttsOffsetTimeUs = 30 * 60 * 1000000LL; // 30 minutesMPEG4Writer 同时处理 HEIF/AVIF 图像写入以及增益图(HDR)元数据,这对新版 Ultra HDR 照片格式至关重要。轨道 ID 体系使用 TrackId 结构体(第 118 行)强制执行 ISO 14496‑12 约束:轨道 ID 不能为 0;搭配 MediaRecorder 使用时,轨道 ID 被限制为 4 比特(取值 1‑15)。16.2.9 AMessage 模式媒体框架内部组件之间通信广泛使用AMessage/AHandler/ALooper模式。这是 Stagefright 自研轻量 Actor 模型:ALooper:线程,处理队列中的消息AHandler:接收 looper 分发过来的消息AMessage:带类型的键值容器,可以投递到 Handler该模式几乎出现在 MediaCodec 的每一个方法中。例如start():// frameworks/av/media/libstagefright/MediaCodec.cpp, 第3552行 status_t MediaCodec::start() { ScopedTrace trace(ATRACE_TAG, "MediaCodec::start#native"); spAMessage msg = new AMessage(kWhatStart, this); // ...resource checking and retry logic... spAMessage response; err = PostAndAwaitResponse(msg, response); // ... }PostAndAwaitResponse是同步封装:将消息投递到 looper 线程,阻塞调用线程直到收到应答。所以对外看MediaCodec::start()是同步调用,内部实际运行在 looper 线程,以此保证对 MediaCodec 状态的线程安全访问。16.3 Codec2 框架16.3.1 架构与设计理念Codec2(常简写为 C2)是 Android 现代编解码器框架,目标替换老旧 OMX IL 接口。代码位于frameworks/av/media/codec2/,包含 11 个子目录:核心 API、23 套以上软件编解码器、HAL 层、对接 Stagefright 的 sfplugin 桥接层。对比 OMX,关键设计改进:强类型参数系统:不再使用 OMX 扁平索引式参数,Codec2 使用强类型、可反射的参数体系(C2Param),编译期即可捕获配置错误。基于 Work 的处理模型:摒弃 OMX 相互独立的输入输出缓冲区队列,Codec2 使用统一 C2Work 结构体把输入输出绑定在一起,简化缓冲区生命周期跟踪。灵活缓冲区管理:通过统一 C2Buffer 抽象,支持多种分配器后端(Gralloc、ION/DMA‑buf、blob)。组件存储:通过 C2ComponentStore 接口发现编解码器,而非全局 OMX 节点注册表,实现更好隔离性,便于厂商扩展。16.3.2 CCodec:Codec2 对接 Stagefright 的桥接层 ¶frameworks/av/media/codec2/sfplugin/CCodec.cpp中的 CCodec 实现 CodecBase 接口,让 MediaCodec 可以使用 Codec2 组件,相当于 Codec2 侧的 ACodec。// frameworks/av/media/codec2/sfplugin/CCodec.cpp, 第18‑19行 #define LOG_TAG "CCodec" #define ATRACE_TAG ATRACE_TAG_VIDEOCCodec 内置看门狗,用于检测卡死的编解码器:// frameworks/av/media/codec2/sfplugin/CCodec.cpp, 第90行 class CCodecWatchdog : public AHandler { private: enum { kWhatWatch, }; constexpr static int64_t kWatchIntervalUs = 3300000; // 3.3 secs public: static spCCodecWatchdog getInstance() { static spCCodecWatchdog sInstance = [] { spCCodecWatchdog instance = new CCodecWatchdog; instance-incStrong((void *)CCodecWatchdog::getInstance); instance-init(); return instance; }(); return sInstance; } void watch(spCCodec codec) { bool shouldPost = false; { Mutexedstd::setwpCCodec::Locked codecs(mCodecsToWatch); shouldPost = codecs-empty(); codecs-emplace(codec); } if (shouldPost) { ALOGV("posting watch message"); (new AMessage(kWhatWatch, this))-post(kWatchIntervalUs); } }看门狗运行在单例 looper 线程。每 3.3 秒遍历全部注册的 CCodec 实例,对疑似卡死的实例调用initiateReleaseIfStuck()。这对鲁棒性至关重要:一旦厂商编解码器 HAL 卡死,看门狗保证系统最终恢复,而不是让 MediaCodec 永久卡死。16.3.3 CCodecBufferChannelframeworks/av/media/codec2/sfplugin/CCodecBufferChannel.cpp的 CCodecBufferChannel 管理 MediaCodec 与 Codec2 组件之间的缓冲区流水线,负责:缓冲区分配与池化MediaCodec 的 MediaCodecBuffer 与 Codec2 的 C2Buffer 之间转换视频输出 Surface 缓冲区管理DRM / 加密缓冲区处理大帧音频缓冲区管理下面是 MediaCodec 与 Codec2 缓冲区标志转换示例:// frameworks/av/media/codec2/sfplugin/CCodecBufferChannel.cpp, 第101行 constexpr static std::initializer_liststd::pairuint32_t, uint32_t flagList = { {BUFFER_FLAG_CODEC_CONFIG, C2FrameData::FLAG_CODEC_CONFIG}, {BUFFER_FLAG_END_OF_STREAM, C2FrameData::FLAG_END_OF_STREAM}, {BUFFER_FLAG_DECODE_ONLY, C2FrameData::FLAG_DROP_FRAME} }; static uint32_t convertFlags(uint32_t flags, bool toC2) { return std::transform_reduce( flagList.begin(), flagList.end(), 0u, std::bit_or{}, [flags, toC2](const std::pairuint32_t, uint32_t entry) { if (toC2) { return (flags entry.first) ? entry.second : 0; } else { return (flags entry.second) ? entry.first : 0; } }); }SurfaceCallbackHandler(第 121 行)处理 Surface 缓冲区异步事件:// frameworks/av/media/codec2/sfplugin/CCodecBufferChannel.cpp, 第121行 class SurfaceCallbackHandler { public: enum callback_type_t { ON_BUFFER_RELEASED = 0, ON_BUFFER_ATTACHED }; void post(callback_type_t callback, std::shared_ptrCodec2Client::Component component, uint32_t generation) { // ...post callback to handler thread... } };关键运行常量:// frameworks/av/media/codec2/sfplugin/CCodecBufferChannel.cpp, 第88行 constexpr size_t kSmoothnessFactor = 4; const static size_t kDequeueTimeoutNs = 0;kSmoothnessFactor取值为 4,缓冲区通道分配最小所需缓冲区数量的 4 倍,为解码时延波动留出余量,保障播放流畅。16.3.4 C2InputSurface 封装类对于输入来自 Surface 的编码场景(录屏、相机录制),CCodec 使用C2InputSurfaceWrapper。// frameworks/av/media/codec2/sfplugin/CCodec.cpp, 第164行 class C2InputSurfaceWrapper : public InputSurfaceWrapper { public: explicit C2InputSurfaceWrapper( const std::shared_ptrCodec2Client::InputSurface surface, uint32_t width, uint32_t height, uint64_t usage) : mSurface(surface), mWidth(width), mHeight(height) { mDataSpace = HAL_DATASPACE_BT709; mConfig.mUsage = usage; } status_t connect(const std::shared_ptrCodec2Client::Component comp) override { // Configure block size, count, usage, dataspace C2StreamBlockSizeInfo::output blockSize{0u, mWidth, mHeight}; C2StreamBlockCountInfo::output blockCount{0u, getInputBufferCount(comp)}; C2StreamUsageTuning::output usage{0u, mConfig.mUsage}; C2StreamDataSpaceInfo::output dataspace{0u, mDataSpace}; c2_status_t err = mSurface-config( {blockSize, blockCount, usage, dataspace}, C2_MAY_BLOCK, failures); // ... return mSurface-connect(comp, mConnection); }该封装类配置输入 Surface 的缓冲区尺寸、数量、使用标志,然后直接连接 Codec2 组件。由此实现零拷贝编码通路:相机或 GPU 输出直接送入编码器,无需 CPU 侧缓冲区拷贝。16.3.5 软件编解码器组件(23 套以上)frameworks/av/media/codec2/components/目录存放谷歌软件编解码器实现,按编解码器家族划分。每个组件遵循命名规范:c2.android.codec.encoder|decoder。完整 23 套以上组件家族:目录编解码器类型源文件aac/AAC音频 编 + 解C2SoftAacDec.cpp、C2SoftAacEnc.cppamr_nb_wb/AMR‑NB、AMR‑WB音频 编 + 解C2SoftAmrDec.cpp、C2SoftAmrNbEnc.cpp、C2SoftAmrWb
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门