Chromium源码视频播放分析

news2024/11/17 5:34:35


下载代码,调试方法等见Chromium视频播放相关调试记录_bberdong的博客-CSDN博客

硬解流程

GPU进程

MediaService::CreateInterfaceFactory,然后创建了InterfaceFactoryImpl。

创建解码器

  1. gpu进程收到了一个message创建了一个MojoVideoDecoderService出来
    源码路径: media/mojo/services/interface_factory_impl.cc
void InterfaceFactoryImpl::CreateVideoDecoder(
    mojo::PendingReceiver<mojom::VideoDecoder> receiver,
    mojo::PendingRemote<media::stable::mojom::StableVideoDecoder>
        dst_video_decoder) {
  DVLOG(2) << __func__;
#if BUILDFLAG(ENABLE_MOJO_VIDEO_DECODER)
  video_decoder_receivers_.Add(std::make_unique<MojoVideoDecoderService>(
                                   mojo_media_client_, &cdm_service_context_,
                                   std::move(dst_video_decoder)),
                               std::move(receiver));
#endif  // BUILDFLAG(ENABLE_MOJO_VIDEO_DECODER)
}
  1. MojoVideoDecoderService创建了一个platform video decoder。咱们这里是VadVideoDecoder
    源码路径: media/mojo/services/mojo_video_decoder_service.cc
void MojoVideoDecoderService::Construct(...
{
    ...
    decoder_ = mojo_media_client_->CreateVideoDecoder(...)
    ...
}

// decoder_定义
std::unique_ptr<media::VideoDecoder> decoder_;
// mojo_media_client_定义
// Decoder factory.
 raw_ptr<MojoMediaClient> mojo_media_client_;

而GpuMojoMediaClient继承自MojoMediaClient

std::unique_ptr<VideoDecoder> GpuMojoMediaClient::CreateVideoDecoder(...
{
    ...
    return CreatePlatformVideoDecoder(traits);
    ...
}

调用的CreatePlatformVideoDecoder方法在另外一个文件里
源码路径: media/mojo/services/gpu_mojo_media_client_cros.cc

std::unique_ptr<VideoDecoder> CreatePlatformVideoDecoder(
    const VideoDecoderTraits& traits) {
  switch (GetActualPlatformDecoderImplementation(traits.gpu_preferences,
                                                 traits.gpu_info)) {
    case VideoDecoderType::kVaapi:
    case VideoDecoderType::kV4L2: {
      auto frame_pool = std::make_unique<PlatformVideoFramePool>(
          traits.gpu_memory_buffer_factory);
      auto frame_converter = MailboxVideoFrameConverter::Create(
          base::BindRepeating(&PlatformVideoFramePool::UnwrapFrame,
                              base::Unretained(frame_pool.get())),
          traits.gpu_task_runner, traits.get_command_buffer_stub_cb);
      return VideoDecoderPipeline::Create(
          traits.task_runner, std::move(frame_pool), std::move(frame_converter),
          traits.media_log->Clone());
    }
    // 这里竟然是kVda类型的
    case VideoDecoderType::kVda: {
      return VdaVideoDecoder::Create(
          traits.task_runner, traits.gpu_task_runner, traits.media_log->Clone(),
          *traits.target_color_space, traits.gpu_preferences,
          *traits.gpu_workarounds, traits.get_command_buffer_stub_cb);
    }
    default: {
      return nullptr;
    }
  }
}

VdaVideoDecoder又包装了一个AsyncDestroyVideoDecoder

std::unique_ptr<VideoDecoder> VdaVideoDecoder::Create(
    scoped_refptr<base::SingleThreadTaskRunner> parent_task_runner,
    scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner,
    std::unique_ptr<MediaLog> media_log,
    const gfx::ColorSpace& target_color_space,
    const gpu::GpuPreferences& gpu_preferences,
    const gpu::GpuDriverBugWorkarounds& gpu_workarounds,
    GetStubCB get_stub_cb) {
  auto* decoder = new VdaVideoDecoder(
      std::move(parent_task_runner), std::move(gpu_task_runner),
      std::move(media_log), target_color_space,
      base::BindOnce(&PictureBufferManager::Create),
      base::BindOnce(&CreateCommandBufferHelper, std::move(get_stub_cb)),
      base::BindRepeating(&CreateAndInitializeVda, gpu_preferences,
                          gpu_workarounds),
      GpuVideoAcceleratorUtil::ConvertGpuToMediaDecodeCapabilities(
          GpuVideoDecodeAcceleratorFactory::GetDecoderCapabilities(
              gpu_preferences, gpu_workarounds)));

  return std::make_unique<AsyncDestroyVideoDecoder<VdaVideoDecoder>>(
      base::WrapUnique(decoder));
}

AsyncDestroyVideoDecoder类定义如下:

template <typename T>
class AsyncDestroyVideoDecoder final : public VideoDecoder {
public:
  explicit AsyncDestroyVideoDecoder(std::unique_ptr<T> wrapped_decoder)
      : wrapped_decoder_(std::move(wrapped_decoder)) {
    static_assert(std::is_base_of<VideoDecoder, T>::value,
                  "T must implement 'media::VideoDecoder'");
    DCHECK(wrapped_decoder_);
  }
    ...
    
    ...
    std::unique_ptr<T> wrapped_decoder_;
}

也就是说这里AsyncDestroyVideoDecoder包了一层VdaVideoDecoder,wrapped_decoder_就是一个VdaVideoDecoder::Create通过make_unique创建出来的,VdaVideoDecoder对象的引用。
而VdaVideoDecoder最终在硬解的时候就是使用的VaapiVideoDecodeAccelerator
// TODO 详细展开一下Vda到Vaapi!

解码过程

使用chrome://tracing跟踪代码过程
trace选中的是media和gpu两个模块
chrome进程:
注意,这里是MojoVideoDecoder一直在调用Decode方法
在这里插入图片描述
GPU进程
这里也是MojoVideoDecoder的对端,MojoVideoDecoderService
这里也一直在调用MojoVideoDecoderService::Decode()方法,后面还有VaapiDecoderThread在干具体的工作,调用vaapi接口来进行解码。
在这里插入图片描述
在这里插入图片描述

释放解码器

源码路径: media/mojo/services/mojo_video_decoder_service.cc

MojoVideoDecoderService::~MojoVideoDecoderService() {
    ...
  if (init_cb_) {
    OnDecoderInitialized(DecoderStatus::Codes::kInterrupted);
  }

  if (reset_cb_)
    OnDecoderReset();
    ...
  // Destruct the VideoDecoder here so its destruction duration is included by
  // the histogram timer below.
  weak_factory_.InvalidateWeakPtrs();
  decoder_.reset();
}

前面说过,这里的decoder关联的就是VdaVideoDecoder,但这里调用的不是decoder的Reset方法,那样的话应该写作decoder_->reset(); 这里直接.操作符说明,这里调用的是decoder本身的类型—unique_ptr的reset方法。
也就是说MojoVideoDecoderService的指针被reset了,这个操作直接引发了对应的decoder对象的析构。
源码路径: media/base/async_destroy_video_decoder.h

~AsyncDestroyVideoDecoder() override {
    if (wrapped_decoder_)
      T::DestroyAsync(std::move(wrapped_decoder_));
}

如前面所说,这里通过AsyncDestroyVideoDecoder这个包装实际调用的是下面的函数
源码路径: media/gpu/ipc/service/vda_video_decoder.cc

void VdaVideoDecoder::DestroyAsync(std::unique_ptr<VdaVideoDecoder> decoder) {
  // TODO(sandersd): The documentation says that DestroyAsync() fires any
  // pending callbacks.

  // Prevent any more callbacks to this thread.
  decoder->parent_weak_this_factory_.InvalidateWeakPtrs();

  // Pass ownership of the destruction process over to the GPU thread.
  auto* gpu_task_runner = decoder->gpu_task_runner_.get();
  gpu_task_runner->PostTask(
      FROM_HERE,
      base::BindOnce(&VdaVideoDecoder::CleanupOnGpuThread, std::move(decoder)));
}

void VdaVideoDecoder::CleanupOnGpuThread(
    std::unique_ptr<VdaVideoDecoder> decoder) {
  DVLOG(2) << __func__;
  DCHECK(decoder);
  DCHECK(decoder->gpu_task_runner_->BelongsToCurrentThread());

  // VDA destruction is likely to result in reentrant calls to
  // NotifyEndOfBitstreamBuffer(). Invalidating |gpu_weak_vda_| ensures that we
  // don't call back into |vda_| during its destruction.
  decoder->gpu_weak_vda_factory_ = nullptr;
  decoder->vda_ = nullptr;
  decoder->media_log_ = nullptr;

  // Because |parent_weak_this_| was invalidated in Destroy(), picture buffer
  // dismissals since then have been dropped on the floor.
  decoder->picture_buffer_manager_->DismissAllPictureBuffers();
}

vad_的定义:

// Only written on the GPU thread during initialization, which is mutually
  // exclusive with reads on the parent thread.
  std::unique_ptr<VideoDecodeAccelerator> vda_;

而在硬解的时候,这里VideoDecodeAccelerator的实例即是VaapiVideoDecodeAccelerator
源码路径: media/gpu/vaapi/vaapi_video_decode_accelerator.cc

// Class to provide video decode acceleration for Intel systems with hardware
// support for it, and on which libva is available.
// Decoding tasks are performed in a separate decoding thread.
//
// Threading/life-cycle: this object is created & destroyed on the GPU
// ChildThread.  A few methods on it are called on the decoder thread which is
// stopped during |this->Destroy()|, so any tasks posted to the decoder thread
// can assume |*this| is still alive.  See |weak_this_| below for more details.
class MEDIA_GPU_EXPORT VaapiVideoDecodeAccelerator
    : public VideoDecodeAccelerator,
      public DecodeSurfaceHandler<VASurface>,
      public base::trace_event::MemoryDumpProvider {

在这个对象被置为nullptr的时候,就触发了VaapiVideoDecodeAccelerator的 destructor函数。然后调用了Destroy函数,Destroy中调用的Cleanup才是真正做清理的函数。

void VaapiVideoDecodeAccelerator::Cleanup() {
  DCHECK(task_runner_->BelongsToCurrentThread());

  base::AutoLock auto_lock(lock_);
  if (state_ == kUninitialized || state_ == kDestroying)
    return;
  
    // 在停止播放的时候比较常见的一行log
  VLOGF(2) << "Destroying VAVDA";
  state_ = kDestroying;

  // Call DismissPictureBuffer() to notify |client_| that the picture buffers
  // are no longer used and thus |client_| shall release them. If |client_| has
  // been invalidated in NotifyError(),|client_| will be destroyed shortly. The
  // destruction should release all the PictureBuffers.
  if (client_) {
    for (const auto& id_and_picture : pictures_)
      client_->DismissPictureBuffer(id_and_picture.first);
  }
  pictures_.clear();

  client_ptr_factory_.reset();
  weak_this_factory_.InvalidateWeakPtrs();

  // TODO(mcasas): consider deleting |decoder_| on
  // |decoder_thread_task_runner_|, https://crbug.com/789160.

  // Signal all potential waiters on the decoder_thread_, let them early-exit,
  // as we've just moved to the kDestroying state, and wait for all tasks
  // to finish.
  input_ready_.Signal();
  surfaces_available_.Signal();
  {
    base::AutoUnlock auto_unlock(lock_);
    decoder_thread_.Stop();
  }
  if (buffer_allocation_mode_ != BufferAllocationMode::kNone)
    available_va_surfaces_.clear();

  // Notify |decoder_delegate_| of an imminent VAContextID destruction, so it
  // can destroy any internal structures making use of it. At this point
  // |decoder_thread_| is stopped so we can access |decoder_delegate_| from
  // |task_runner_|.
  decoder_delegate_->OnVAContextDestructionSoon();
  vaapi_wrapper_->DestroyContext();

  if (vpp_vaapi_wrapper_)
    vpp_vaapi_wrapper_->DestroyContext();
  state_ = kUninitialized;
}
RendererImpl::OnRendererEnded
void RendererImpl::OnRendererEnded(DemuxerStream::Type type) {
  ...
  // If all streams are ended, do not propagate a redundant ended event.
  if (state_ != STATE_PLAYING || PlaybackHasEnded())
    return;

  if (type == DemuxerStream::AUDIO) {
    DCHECK(audio_renderer_);
    audio_ended_ = true;
  } else {
    DCHECK(video_renderer_);
    video_ended_ = true;
    video_renderer_->OnTimeStopped();
  }

  RunEndedCallbackIfNeeded();
  ...
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/517941.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

TypeScript 基础语法

(一) 类型 1、基元类型 类型例子描述number1, -33, 2.5任意数字string‘hi’, “hi”, hi任意字符串booleantrue、false布尔值true或falsenull值只能为nullundefined值只能为undefined字面量其本身(详见 面向对象 15泛型)限制变量的值就是该字面量的值any*任意类型unknown*类…

代码随想录算法训练营第二天| 977. 有序数组的平方、209. 长度最小的子数组、59. 螺旋矩阵

有序数组的平方: 题目链接&#xff1a;力扣 知识点&#xff1a;双指针。 思路分析&#xff1a;首先想到的是不怎么动脑子的暴力想法&#xff0c;即先将vector中的各元素用pow函数进行平方&#xff0c;接着再用sort函数对已平方过的数据进行排序。 但是由于sort是基于快速排序…

修复bug:FlexibleButton id相反问题

目录 版本&#xff1a;https://github.com/murphyzhao/FlexibleButton/releases/tag/2.0.1 问题现象 问题描述 查源码&#xff01; 注册 读按键 按键处理 小结 版本&#xff1a;https://github.com/murphyzhao/FlexibleButton/releases/tag/2.0.1https://github.com/mur…

00后自述,生活所迫,不卷一点我能怎么办?

前段时间我去面试了一个软件测试公司&#xff0c;成功拿到了offer&#xff0c;薪资也从10k涨到了15k&#xff0c;对于工作都还没两年的我来说&#xff0c;还是比较满意的&#xff0c;毕竟有些工作了3到4年的可能还没有我的高。 在公司一段时间后大家都说我是卷王&#xff0c;其…

MySQL5.7数据导入/导出、TiDB数据回迁移

一、环境&安装包 CentOS Linux release 7.4.1708 (Core)TDSQL&#xff08;腾讯基于MySQL5.7.x自主研发的数据库&#xff09;Mysqldump&#xff08;MySQL5.7.41备份客户端&#xff09;Dumpling&#xff08;mydumper 的全新升级版&#xff09;Myloader&#xff08;mydumper附…

Flutter 3.10 之 Flutter Web 路线已定,可用性进一步提升,快来尝鲜 WasmGC

随着 Flutter 3.10 发布&#xff0c;Flutter Web 也引来了它最具有「里程碑」意义的更新&#xff0c;这里的「里程碑」不是说这次 Flutter Web 有多么重大的更新&#xff0c;而是 Flutter 官方对于 Web 终于有了明确的定位和方向。 提升 首先我们简单聊提升&#xff0c;这不是…

用处巨广的操作符,快来学学叭(C语言版)

&#x1f929;本文作者&#xff1a;大家好&#xff0c;我是paper jie&#xff0c;感谢你阅读本文&#xff0c;欢迎一建三连哦。 &#x1f970;内容专栏&#xff1a;这里是《C知识系统分享》专栏&#xff0c;笔者用重金(时间和精力)打造&#xff0c;基础知识一网打尽&#xff0c…

buildroot 目录结构

buildroot 目录结构 board 存放着不同厂商相关的文件 buildroot-2023.02/board/sipeed/lichee_rv_dock里面描述了如何连接wifi,蓝牙&#xff0c;烧写sdcard.img 如何配置buildroot和编译等 make sipeed_lichee_rv_dock_defconfig makearch 存放着CPU相关的文件&#xff0c…

SQLite:轻量级嵌入式数据库

原创 | 文 BFT机器人 引言 SQLite是一个进程内的库&#xff0c;实现了自给自足的、无服务器的、零配置的、事务性的 SQL 数据库引擎。它是一个零配置的数据库&#xff0c;这意味着与其他数据库不一样&#xff0c;您不需要在系统中配置。就像其他数据库&#xff0c;SQLite 引擎不…

AI —— 亲测讯飞星火认知大模型,真的很酷!

讯飞星火认知大模型真的很酷&#xff01; 前言&#x1f64c;讯飞星火认知大模型基本的使用介绍与讯飞星火认知大模型基本的对话 总结撒花&#x1f49e; &#x1f60e;博客昵称&#xff1a;博客小梦 &#x1f60a;最喜欢的座右铭&#xff1a;全神贯注的上吧&#xff01;&#xf…

FE_Vue学习笔记 条件渲染[v-show v-if] 列表渲染[v-for] 列表过滤 列表排序

1 条件渲染 v-show v-if 使用template可以使其里面的内容在html的结构中不变。条件渲染&#xff1a; v-if 1&#xff09;v-if“表达式” 2&#xff09;v-else-if“表达式” 3&#xff09;v-else {} 适用于&#xff1a;切换频率较低的场景。特点&#xff1a;不展示的DOM元素直…

rem、px、em的区别 -前端

文章目录 三者的区别特点与换算举例emrem 总结一总结二 三者的区别 在css中单位长度用的最多的是px、em、rem&#xff0c;这三个的区别是&#xff1a; 一、px是固定的像素&#xff0c;一旦设置了就无法因为适应页面大小而改变。 二、em和rem相对于px更具有灵活性&#xff0c;…

消息认证码以及数字签名的认识

文章目录 消息认证码及数字签名1. 消息认证码1.1 消息认证1.2 消息认证码的使用步骤1.3 go中对消息认证码的使用1.4 消息认证码的问题 2.数字签名2.1 数字签名的生成和验证2.2 数字签名的流程2.3 Go使用RSA进行数字签名2.4 Go使用椭圆曲线进行数字签名2.5 数字签名无法解决的问…

dts pinctrl初始gpio0方法

1、在pinctrl-rockchip.c文件中添加头文件 #include <linux/of_gpio.h> 如下 2、 在如下函数添加红色代码 static int rockchip_pinctrl_probe(struct platform_device *pdev) { struct rockchip_pinctrl *info; struct device *dev &pdev->dev; …

Vue3 自定义指令让元素自适应高度,el-table在可视区域内滚起来

我始终坚持&#xff0c;前端开发不能满足于实现功能&#xff0c;而是需要提供优秀的交互与用户体验。即使没有产品没有UI的小项目&#xff0c;也可以自己控制出品质量&#xff0c;做到小而美。所以前端们不仅仅需要了解框架如何用&#xff0c;还要学习一些设计、交互、体验的知…

新入职了一个00后卷王,天天加班12点,太让人崩溃......

在程序员职场上&#xff0c;什么样的人最让人反感呢? 是技术不好的人吗?并不是。技术不好的同事&#xff0c;我们可以帮他。 是技术太强的人吗?也不是。技术很强的同事&#xff0c;可遇不可求&#xff0c;向他学习还来不及呢。 真正让人反感的&#xff0c;是技术平平&…

Android 百度地图SDK闪退

Android 百度地图SDK闪退 通过Android IDE查看APP运行日志Error 1:errorcode: 230 uid: -1 appid -1 msg: APP Mcode码校验失败 在调用百度SDK时出现闪退现象&#xff0c;要知道闪退的具体原因&#xff0c;不然搜了一堆教程&#xff0c;发现纯纯浪费时间&#xff0c;作者也是痛…

知识就是力量,图谱路在何方 | ChatGPT冲击下,招商银行如何“抢救”知识图谱?

“知识就是力量”我们耳熟能详&#xff0c;但培根的这句话其实还有后半句“更重要的是运用知识的技能”。对于人工智能来说&#xff0c;知识图谱就是其如何对知识进行运用的技能体现。在金融领域&#xff0c;如何运用这一技能更好地理解客户需求&#xff0c;提高业务效率和客户…

三层架构与MVC架构区别

三层架构 表示层&#xff1a;主要对用户的请求接受&#xff0c;以及数据的返回&#xff0c;为客户端提供应用程序的访问。 servlet层 业务逻辑层&#xff1a;对我们数据实现业务逻辑的封装 service层 数据访问层&#xff1a;对数据库访问操作 dao层 MVC架构 1.MVC全名是Model …

MATLAB 常用数学函数和数组和字符串、元胞数组和结构体,MATLAB 编程,关系运算符和逻辑变量

目录 MATLAB 零基础学习 简介 使用平台 一、MATLAB 界面 二、基础操作 使用方法 常用数学函数 初等函数 三角函数 指数和对数 复数 标点符号 常用操作 三、文件类型 数组 一、数组基础知识 二、数组创建与操作 创建 数组操作 字符串数组 字符串、元胞数组…