Chromium源码视频播放分析

这篇具有很好参考价值的文章主要介绍了Chromium源码视频播放分析。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


下载代码,调试方法等见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方法
Chromium源码视频播放分析
GPU进程
这里也是MojoVideoDecoder的对端,MojoVideoDecoderService
这里也一直在调用MojoVideoDecoderService::Decode()方法,后面还有VaapiDecoderThread在干具体的工作,调用vaapi接口来进行解码。
Chromium源码视频播放分析
Chromium源码视频播放分析

释放解码器

源码路径: 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才是真正做清理的函数。文章来源地址https://www.toymoban.com/news/detail-441506.html

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();
  ...
}

到了这里,关于Chromium源码视频播放分析的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • TCP/IP协议栈源代码分析:GDB调试环境搭建及源码分析

    Ubuntu 22.04 LTS Linux-5.4.34 busybox-1.36.0 2.1 安装相关工具 axel是一款多线程下载工具,用于下载Linux内核源代码及其他大文件;build-essential软件包里面包含了很多开发必要的软件工具,比如make、gcc等;QEMU是一种通用的开源计算机仿真器和虚拟器,为自己编译构建的Linux系统运行提供

    2024年02月03日
    浏览(47)
  • 如何下载央视网视频,下载视频播放花屏怎么办

    相信有很多人在下载央视网或者央视影音的视频遇到了虽然能下载但是花屏的情况,like this 或许你能找到hls-url,可能也用了猫爪或者video download等工具,但是下载下来的ts或者m3u8文件都是花屏的情况。下图是Opera GX浏览器检查元素界面,在网络-全部-预览当中可以看到类型为

    2024年02月11日
    浏览(35)
  • 【SA8295P 源码分析】83 - SA8295P HQNX + Android 完整源代码下载方法介绍

    因为一些原因,本文需要移除, 对于已经购买的兄弟,不用担心,不是跑路, 我会继续持续提供技术支持, 有什么模块想学习的,或者有什么问题有疑问的, 请私聊我,我们 +VX 沟通技术问题

    2024年02月11日
    浏览(33)
  • 利用远程调试获取Chromium内核浏览器Cookie

    本文将介绍不依靠DPAPI的方式获取Chromium内核浏览器Cookie 首先我们以edge为例。edge浏览器是基于Chromium的,而Chromium是可以开启远程调试的,开启远程调试的官方文档如下: https://blog.chromium.org/2011/05/remote-debugging-with-chrome-developer.html 那么开启远程调试以后可以做什么呢,继续看

    2024年02月15日
    浏览(34)
  • 【Unity声音与视频播放】播放声音视频、代码控制、UI播放视频

    闲谈: 游戏开发比普通软件开发难也是有原因的,第一 游戏功能需求变化多样内部逻辑交错纵横, 而软件相对固定,无非也就是点击跳转、数据存储 第二,游戏需要很多3D数学知识、物理知识,最起码得有高中物理的基础,力、向量、射线,除了这些数据存储一样不少 但是

    2024年02月19日
    浏览(32)
  • 【Unity】代码控制视频的播放(视频播放器-更新)

     结果如上图,之前写过一个使用代码控制视频播放器的Demo,但是好多小伙伴说我附带的链接没法下载,这次我直接做一个完整版的,不用下载,照着一步一步做就能做出来。 之前写了如何设置RawImage进行自动播放,大家可以看一下基础操作这篇文章:,大佬勿怪。 【Unity】

    2024年02月09日
    浏览(45)
  • 下载编译Chromium

    For Mac: https://chromium.googlesource.com/chromium/src/+/main/docs/mac_build_instructions.md Working with Release Branches 一 、 下载编译工具链: deptool git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git export PATH=\\\"$PATH:/Users/yumlu/code/depot_tools\\\" (不过为了后续操作方便,可以将其加入到你的 ~/.zsh

    2024年02月17日
    浏览(32)
  • Java后端接口返回视频流,使用video组件播放视频,实现分段下载

    视频文件保存在不为人知的地方,总之前端不能直接访问的位置,需要通过后端接口取出来再返回给前端。 前端这样子播放 src=后端接口 如果后端直接这样子写 小视频问题不大,视频大的话会卡顿很久,查看请求发现会先请求下载完整视频后开始播放。而且不能拖动进度条

    2024年02月12日
    浏览(32)
  • 萝卜视频源码前后端影视APP源码/更换播放内核到3.2.6/带视频演示

    🎈 限时活动领体验会员:可下载程序+网创项目+短视频素材 🎈 ☑️ 品牌:萝卜视频 ☑️ 语言:PHP ☑️ 类型:影视 ☑️ 支持:APP 🎉 有需要的朋友记得关+赞+评,免费分享需要的文章底部获取!!! 🎉 ✨ 源码介绍 萝卜视频源码前后端,更换播放内核到3.2.6,原版3.0.

    2024年02月15日
    浏览(25)
  • Mac/Windows平台下载编译Chromium

    For Mac: https://chromium.googlesource.com/chromium/src/+/main/docs/mac_build_instructions.md Working with Release Branches 一 、 下载编译工具链: deptool git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git export PATH=\\\"$PATH:/Users/yumlu/code/depot_tools\\\" (不过为了后续操作方便,可以将其加入到你的 ~/.zsh

    2024年02月16日
    浏览(46)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包