目录
视频源播放。一个视频播放器播放视频文件需要经过的步骤流程。首先经过解协议,解协议通常是指解流媒体协议 HTTP、RTSP、MMS 等,解析为标准封装格式数据输出。我们日常说的 MP4、mkv、avi 等就是封装格式,封装格式的作用就是将已经压缩编码的视频数据和音频数据按照一定的格式组织到一起,因此解封装之后的输出就是音频压缩数据如 aac、ac3 编码,视频压缩数据如 h264 h265 编码等。我们常常把解封装的输出叫做音频轨和视频轨。有了音视频压缩编码文件,下一步就是通过解码 将压缩编码的数据解码输出为原始数据,经过音视频时间戳的同步,音频原始数据PCM通过音频驱动外放,视频原始数据YUV通过显示渲染处理,就完成了一个视频源文件的播放。

安卓最上层的java应用接口是 mediaplayer 和 mediacodec。
mediaplayer 和 mediacodec 是 Google 为多媒体应用开发设计的两套开发接口,函数调用都相对简洁明了。mediaplayer 是纯粹的播放器接口,承担的角色是上图中的所有包含解协议 解封装 解码 音视频同步 外放等。
mediaplayer 提供了一种简单的方式来播放音频和视频。它完全屏蔽掉解码通路过程中的细节。所以如果只是需要播放一个本地或服务器上的音视频文件,可以毫不犹豫的选择 MediaPlayer 接口。
mediacodec 只承担上图中解码的角色,他是多媒体的 “编解码器”,是 Android 底层多媒体支持基础框架的一部分.与 MediaPlayer prepare\start\pause\stop 的使用方式相比较,它的使用方式更为繁琐,但同时为开发者带来了更多的创造性。用户可以根据自己的环境视情况选择这两套接口。
mediaplayer 的分支,从应用接口下来通过 JNI 走到本地框架 native 层,也是通过安卓的 binder 机制,连接到 MediaPlayerService服务,service 进行播放器类型的策略选择,google 原生的 mediaplayer 实现是 nuplayer,视频解码角色由 codec2 承担,codec2 是
android12 硬解的新框架,承担的功能与之前 OMX 一致。
mediaplayerService 另外衍生出来一套 rk mediaplayer 接口实现分支,叫做RockitPlayer,Rockit 是平台开发的多媒体插件系统,向上封装了多套接口,安卓使用的 mediaplayer 插件实现,相比google原生的nuplayer,rockitplayer 支持更全的封装格式解析,rockitPlayer 的视频解码角色由一个系统服务 rockit hw-service 承担。
转到另一边从mediacodec 的分支下来,经过 JNI,mediacodec native 层的接口实现也有两条分支,一条是 ACodec + OMX 的旧分支,另一条则是codec2 新实现,android 12 硬解废弃了旧的 OMX 实现,转由 codec2 实现。



MediaServer:MediaServer进程用于驱动playback和record,例如在组件和进程之间传递和同步缓冲区。流程通过标准的Binder机制进行通信
Application Framework
At the application framework level is application code that utilizes android.media APIs to interact with the multimedia hardware.
Binder IPC
The Binder IPC proxies facilitate communication over process boundaries.
Native Multimedia Framework
At the native level, Android provides MediaPlayerService for application layer services, each mediaplayer has a corresponding client in it and provides a state mechanism. Android provides a multimedia framework that utilizes the Stagefright engine for audio and video recording and playback. Stagefright comes with a default list of supported software codecs and you can implement your own hardware codec by using the OpenMax integration layer standard.
OpenMAX Integration Layer (IL)
The OpenMAX IL provides a standardized way for Stagefright to recognize and use custom hardware-based multimedia codecs called components.


parser模型

encode模型




Source:数据的来源不仅是本地文件,而且是Internet上的各种协议,如:http、rtsp等。Source的任务是对数据源进行抽象,为Demux模块提供稳定的数据流,主要功能有:多媒体文件的格式检测、读取和解析文件
Parser/Demux:视频文件通常由音频流和视频流通过一些规则编织。这种规则就是容器规则。现在有许多不同的容器格式。如ts、mp4、flv、mkv、avi、rmvb等。demux的功能是将音频和视频流从容器中剥离出来,并将其发送到不同的解码器。Demux将为解码器提供解码数据流。
Decoder:player的核心模块。分为音频和视频解码器。音频和视频解码器的作用是恢复这些压缩数据(包括MPEG1 (VCD)、MPEG2 (DVD)、MPEG4、H.264等)。得到原始音频和视频数据。
Renderer:从功能上看,Renderer主要有几个功能:音频和视频原始数据的缓冲操作、音频回放(到声卡)、视频显示(到显卡)、音频和视频同步以及其他辅助回放控制操作。
在这个播放框架中,NuPlayer是Source, Demux, Decoder和Renderer之间的链接。
MediaCodec是一种通过硬件加速解码和编码的编解码器。它为芯片供应商和应用程序开发人员建立了统一的接口。MediaCodec几乎是所有Android播放器的标准。
APP开发者可以采用两种多媒体JAVA库开发多媒体播放服务,分别为MediaPlayer,MediaCodec。
MediaCodec的C++层是对ACodec的封装;
ACodec完成的工作是向下调用OpenMAX的IL接口,实现音视频的编解码模块管理工作,作为媒体中间层的中间力量;
OpenMAX IL层统一了音视频编解码的调用接口,所有的芯片厂商都实现一套标准的接口,以供不同的上层调用,包括ACodec,OMXCodec。
MediaCodec与MediaPlayer/VideoView等high-level APIs相比,MediaCodec是low-level APIs,因此它提供了更加完善、灵活、丰富的接口,开发者可以实现更加灵活的功能。从API 16开始,Android提供了Mediacodec类以便开发者更加灵活的处理音视频的编解码。
Android提供了一个MediaCodecList,用于枚举设备支持的编解码器的名称和功能,以查找适当的编解码器
异步播放的方法是注册一个回调到MediaCodec,它被通知 MediaCodec:当输入缓冲区可用时,输出缓冲区可用,格式改变时。 我们需要做的是,当输入缓冲区可用时,通过MediaExtractor将数据填充到指定的缓冲区;当输出缓冲区可用时,决定是否显示帧。
使用MediaCodec解码usecase 

在一个简单的APK DEMO中,一般需要包括这些主要操作方法。创建一个MediaCodec解码器实例可以采用两种方法,一种是CreateByType,一种是CreateByComponentName。
dequeueInputBuffer是从MediaCodec解码器取得一个空的Buffer,queueInputBuffer是将一个填充好码流数据的Buffer送给MediaCodec解码器,dequeueOutputBuffer是从MediaCodec解码器取得一个存放一帧图像的Buffer,renderOutputBufferAndRelease是将一个存放有一帧图像的Buffer送给GPU显示并且在显示完毕后还给MediaCodec解码器。
当应用层将最后一笔码流准备送给MediaCodec解码器后,必须再送一个空的Input Buffer并带上一个码流结束标志BUFFER_FLAG_END_OF_STREAM,这种通常用于解码。signalEndOfInputStream的作用类似于前述的操作,但通常用于录像(Record)的情况,并且录像的码流输入采用Surface的方式,当录像结束时,调用该方法告诉编码器,码流结束了。 
requestActivityNotification的意思是,应用层调用该方法以得知MediaCodec解码器拥有多少个已经解码好的Output Buffer,它的一个意义在于,应用层应当尽快地取走解码好的图像数据,不要慢吞吞的,以致于有可能出现可用于解码的Buffer数量不够而使内部解码引擎解码效率降低。
releaseOutputBuffer的作用在于,取到的这个Output Buffer不送去显示而直接还给MediaCodec解码器。



MediaCodec实质是对ACodec的封装与管理,MediaCodec需要与ACodec进行通讯交互,包括MediaCodec直接调用ACodec方法,而ACodec采用AMessage向MediaCodec进行状态反馈与通知。
MediaCodec调用ACodec的initateConfigureComponent方法,ACodec完成OMX的组件配置工作后,将一个kWhatComponentAllocated事件放在一个AMessage消息中,即notify对象,并将该消息post出去,post的“目的地”为CodecBase(具体可见ACodec的onAllocateComponent函数),而CodecBase是继承于AHandler的虚类,它会将消息“转发”给MediaCodec
CodecBase基于AHandler构造,而MediaCodec亦基于AHandler构造,该消息将在MediaCodec的onMessageReceived中接收到并进行处理。这样的消息反馈机制



三种设计原则:
封装变化
多用组合,少用继承
针对接口编程,不针对实现编程
「针对接口编程」真正的意思是「针对超类型(supertype)编程」。这里所谓的「接口」有多个含义,接口是一个「概念」,也是一种 Java 的 interface 构造。你可以在不涉及 Java interface 的情况下「针对接口编程」,关键就在多态。利用多态,程序可以针对超类型编程,执行时会根据实际状况执行到真正的行为。







NuPlayer是在Stagefright类的基础上构建的,使用了一个较低级的alooper - ahandler - message机制来异步处理消息。
AMessage作为消息的载体,并保存相关的信息;
ALooper是一个循环,它运行一个后台线程来循环处理接收到的消息(将信息传递给AHandler进行处理,相当于一个中继站); AHandler是一个handler,是消息的最终处理。
在NuPlayer中大量使用异步模式,因为在媒体相关的地方,很多操作都是耗时的操作,但用户对画面平滑度的容忍度很低。


流水线:Thread 包裹:AMessage
皮带:MessageQueue
转运洞口:Handler
包裹分拣器:Looper


buffer轮转模型


在Android Q之前,Android的两套多媒体框架分别为MediaPlayer与MediaCodec,后者只负责解码与渲染工作,解封装工作由MediaExtractor代劳,MediaCodec经由ACodec层调用第三方编解码标准接口OpenMAX IL,实现硬件编解码。芯片厂商只需要支持上Khronos 制定的OpenMAX接口,就可以实现MediaCodec的硬件编解码。谷歌在Android Q上推出了Codec2.0,旨在于取代ACodec与OpenMAX,它可以看作是一套新的对接MediaCodec的中间件,往上对接MediaCodec Native层,往下提供新的API标准供编解码使用,相当于ACodec 2.0。 
Enable new use cases, such as pre/post processing
新增 预处理/后处理
Improved performance by on-demand buffer allocation and buffer sharing
按需分配内存和内存共享改善性能
Simpler states and simpler processing
简化状态和处理过程
More precise (frame-based) processing and parameter descriptions (dependencies and supported values)
更精确的(基于单帧)处理过程和参数描述(依赖关系和支持的值)
More flexible buffers and configuration (best-effort ⇒ always valid)
更灵活的缓冲区和配置
Standard way of exposing vendor-specific extensions (settings and codecs) without modifying the media framework
标准化供应商的扩展(设置和编解码器)方法而不用修改媒体框架代码
Codec2.0和ACodec OMX的关系如下图所示:

Codec2.0的代码目录位于/frameworks/av/media/codec2。目录结构如下: 
sfplugin/CCodec.cpp是顶层实现,它提供的接口为MediaCodec Native层所调用,与libstagefright/ACodec接口一致,都继承自CodecBase,如下所示:
virtual std::shared_ptr<BufferChannelBase> getBufferChannel() override;
virtual void initiateAllocateComponent(const sp<AMessage> &msg) override;
virtual void initiateConfigureComponent(const sp<AMessage> &msg) override;
virtual void initiateCreateInputSurface() override;
virtual void initiateSetInputSurface(const sp<PersistentSurface> &surface) override;
virtual void initiateStart() override;
virtual void initiateShutdown(bool keepComponentAllocated = false) override;
virtual status_t setSurface(const sp<Surface> &surface) override;
virtual void signalFlush() override;
virtual void signalResume() override;
virtual void signalSetParameters(const sp<AMessage> ¶ms) override;
virtual void signalEndOfInputStream() override;
virtual void signalRequestIDRFrame() override;
virtual status_t querySupportedParameters(std::vector<std::string> *names) override;
virtual status_t describeParameter(
const std::string &name, CodecParameterDescriptor *desc) override;
virtual status_t subscribeToParameters(const std::vector<std::string> &names) override;
virtual status_t unsubscribeFromParameters(const std::vector<std::string> &names) override;
RK Codec2.0的代码目录位于vendor/rockchip/hardware/interfaces/codec2。目录结构如下:
1)自定义系统服务创建,用于应用程序和框架之间的交互
2)开发HIDL接口
3)通过HIDL接口创建与系统服务交互的供应商服务
4) SE政策变化

理解代码中这些关键点,能大体知道codec2.0硬件插件是如何运行的。
struct C2Work {
//链信息,插件中暂未使用
//additional work chain info not part of this work
std::shared_ptr<C2WorkChainInfo> chainInfo;
//输入待处理数据:解码输入pkt/编码输入frm
C2FrameData input;
//组件传递链路,里面包含输出缓存池的id,编解码的输出数据通过此链路往上传递
std::list<std::unique_ptr<C2Worklet>> worklets;
//处理完毕的工作片数目
uint32_t workletsProcessed;
//work处理结果,正确处理返回0
c2_status_t result;
};

//获取缓存池Id,判断是否bufferqueue类型,如果是bufferqueue类型,说明上层有创建surface,否则没有创建
getLocalId()
//解码输出buffer申请
fetchGraphicBlock
//编码输出buffer申请
fetchLinearBlock






uint8_t *inData = NULL;
size_t inSize = 0u;
C2ReadView rView = mDummyReadView;
if (!work->input.buffers.empty()) {
rView = work->input.buffers[0]->data().linearBlocks().front().map().get();
inSize = rView.capacity();
}
inData = const_cast<uint8_t *>(rView.data());
...
//获取到输入数据后送进mpp
// may block, quit util enqueue success.
err = sendpacket(inData, inSize, frameIndex, timestamp, flags);
//C2GraphicBlock block是由fetchGraphicBlock申请的,其中包含mpp解码输出buffer对应fd
std::shared_ptr<C2Buffer> buffer
= createGraphicBuffer(std::move(block),
C2Rect(mWidth, mHeight).at(left, top));
mOutBlock = nullptr;
{
if (mCodingType == MPP_VIDEO_CodingAVC ||
mCodingType == MPP_VIDEO_CodingHEVC ||
mCodingType == MPP_VIDEO_CodingMPEG2) {
IntfImpl::Lock lock = mIntf->lock();
buffer->setInfo(mIntf->getColorAspects_l());//将color信息设置到buffer中
}
}
...
//buffer打包好后,最终会push进work中返回给上层送显或传递
work->worklets.front()->output.buffers.push_back(mBuffer);
上层如果有创建surface,申请外部buffer时会默认使用BUFFERQUEUE类型的buffer pool,这个是一个环形缓存池,可以将pool中每一个buffer对应的fd送到mpp中,实现buffer的环形轮转,整个过程没有发生拷贝动作。
有关 BufferQueue 通信过程,请参见下图。

BufferQueue 包含将图像流生产方与图像流消耗方结合在一起的逻辑。本文图像生产方Codec2.0,图像来源于mpp解码输出,图像消耗方 SurfaceFlinger。下图可以清楚展示数据流方向:
关于BufferQueue详细可浏览图形 | Android 开源项目 | Android Open Source Project学习。
......
......
......
......
参考链接:
从使用Handler致内存泄漏角度源码追踪Handler工作机制(配动画)_亦游的博客-CSDN博客
BufferQueue详解 原理_王瑞-程序员资料_bufferqueue - 程序员资料
Codec2入门:框架解析_Kayson12345的博客-CSDN博客_android codec2
Codec2入门:解码组件_Kayson12345的博客-CSDN博客_codec2.0
http://www.cjcbill.com/2022/03/12/codec2-analyse/
Android OMX介绍(总括)_Android系统攻城狮的博客-CSDN博客_omx解码
我正在尝试测试是否存在表单。我是Rails新手。我的new.html.erb_spec.rb文件的内容是:require'spec_helper'describe"messages/new.html.erb"doit"shouldrendertheform"dorender'/messages/new.html.erb'reponse.shouldhave_form_putting_to(@message)with_submit_buttonendendView本身,new.html.erb,有代码:当我运行rspec时,它失败了:1)messages/new.html.erbshou
我在从html页面生成PDF时遇到问题。我正在使用PDFkit。在安装它的过程中,我注意到我需要wkhtmltopdf。所以我也安装了它。我做了PDFkit的文档所说的一切......现在我在尝试加载PDF时遇到了这个错误。这里是错误:commandfailed:"/usr/local/bin/wkhtmltopdf""--margin-right""0.75in""--page-size""Letter""--margin-top""0.75in""--margin-bottom""0.75in""--encoding""UTF-8""--margin-left""0.75in""-
我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t
我有一个对象has_many应呈现为xml的子对象。这不是问题。我的问题是我创建了一个Hash包含此数据,就像解析器需要它一样。但是rails自动将整个文件包含在.........我需要摆脱type="array"和我该如何处理?我没有在文档中找到任何内容。 最佳答案 我遇到了同样的问题;这是我的XML:我在用这个:entries.to_xml将散列数据转换为XML,但这会将条目的数据包装到中所以我修改了:entries.to_xml(root:"Contacts")但这仍然将转换后的XML包装在“联系人”中,将我的XML代码修改为
为了将Cucumber用于命令行脚本,我按照提供的说明安装了arubagem。它在我的Gemfile中,我可以验证是否安装了正确的版本并且我已经包含了require'aruba/cucumber'在'features/env.rb'中为了确保它能正常工作,我写了以下场景:@announceScenario:Testingcucumber/arubaGivenablankslateThentheoutputfrom"ls-la"shouldcontain"drw"假设事情应该失败。它确实失败了,但失败的原因是错误的:@announceScenario:Testingcucumber/ar
我在我的项目中添加了一个系统来重置用户密码并通过电子邮件将密码发送给他,以防他忘记密码。昨天它运行良好(当我实现它时)。当我今天尝试启动服务器时,出现以下错误。=>BootingWEBrick=>Rails3.2.1applicationstartingindevelopmentonhttp://0.0.0.0:3000=>Callwith-dtodetach=>Ctrl-CtoshutdownserverExiting/Users/vinayshenoy/.rvm/gems/ruby-1.9.3-p0/gems/actionmailer-3.2.1/lib/action_mailer
我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server
在MRIRuby中我可以这样做:deftransferinternal_server=self.init_serverpid=forkdointernal_server.runend#Maketheserverprocessrunindependently.Process.detach(pid)internal_client=self.init_client#Dootherstuffwithconnectingtointernal_server...internal_client.post('somedata')ensure#KillserverProcess.kill('KILL',
我已经从我的命令行中获得了一切,所以我可以运行rubymyfile并且它可以正常工作。但是当我尝试从sublime中运行它时,我得到了undefinedmethod`require_relative'formain:Object有人知道我的sublime设置中缺少什么吗?我正在使用OSX并安装了rvm。 最佳答案 或者,您可以只使用“require”,它应该可以正常工作。我认为“require_relative”仅适用于ruby1.9+ 关于ruby-主要:Objectwhenrun
我花了三天的时间用头撞墙,试图弄清楚为什么简单的“rake”不能通过我的规范文件。如果您遇到这种情况:任何文件夹路径中都不要有空格!。严重地。事实上,从现在开始,您命名的任何内容都没有空格。这是我的控制台输出:(在/Users/*****/Desktop/LearningRuby/learn_ruby)$rake/Users/*******/Desktop/LearningRuby/learn_ruby/00_hello/hello_spec.rb:116:in`require':cannotloadsuchfile--hello(LoadError) 最佳