chromium/chromecast/media/common/media_resource_tracker.cc

// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chromecast/media/common/media_resource_tracker.h"

#include "base/command_line.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/task/bind_post_task.h"
#include "base/task/single_thread_task_runner.h"
#include "chromecast/public/cast_media_shlib.h"
#include "chromecast/public/volume_control.h"

namespace chromecast {
namespace media {

MediaResourceTracker::ScopedUsage::ScopedUsage(MediaResourceTracker* tracker)
    : tracker_(tracker) {
  DCHECK(tracker_);
  DCHECK(tracker_->media_task_runner_->BelongsToCurrentThread());
  tracker_->IncrementUsageCount();
}

MediaResourceTracker::ScopedUsage::~ScopedUsage() {
  DCHECK(tracker_->media_task_runner_->BelongsToCurrentThread());
  tracker_->DecrementUsageCount();
}

MediaResourceTracker::MediaResourceTracker(
    scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,
    scoped_refptr<base::SingleThreadTaskRunner> media_task_runner)
    : media_use_count_(0),
      media_lib_initialized_(false),
      delete_on_finalize_(false),
      ui_task_runner_(std::move(ui_task_runner)),
      media_task_runner_(std::move(media_task_runner)) {
  DCHECK(ui_task_runner_);
  DCHECK(media_task_runner_);
  DCHECK(ui_task_runner_->BelongsToCurrentThread());
}

MediaResourceTracker::~MediaResourceTracker() {}

void MediaResourceTracker::InitializeMediaLib() {
  DCHECK(ui_task_runner_->BelongsToCurrentThread());
  media_task_runner_->PostTask(
      FROM_HERE,
      base::BindOnce(&MediaResourceTracker::CallInitializeOnMediaThread,
                     base::Unretained(this)));
}

void MediaResourceTracker::FinalizeMediaLib(base::OnceClosure completion_cb) {
  DCHECK(ui_task_runner_->BelongsToCurrentThread());
  DCHECK(!completion_cb.is_null());

  media_task_runner_->PostTask(
      FROM_HERE,
      base::BindOnce(&MediaResourceTracker::MaybeCallFinalizeOnMediaThread,
                     base::Unretained(this), std::move(completion_cb)));
}

void MediaResourceTracker::FinalizeAndDestroy() {
  DCHECK(ui_task_runner_->BelongsToCurrentThread());

  media_task_runner_->PostTask(
      FROM_HERE,
      base::BindOnce(
          &MediaResourceTracker::MaybeCallFinalizeOnMediaThreadAndDeleteSelf,
          base::Unretained(this)));
}

void MediaResourceTracker::IncrementUsageCount() {
  DCHECK(media_task_runner_->BelongsToCurrentThread());
  DCHECK(media_lib_initialized_);
  DCHECK(!finalize_completion_cb_);
  media_use_count_++;
}

void MediaResourceTracker::DecrementUsageCount() {
  DCHECK(media_task_runner_->BelongsToCurrentThread());
  media_use_count_--;

  if (media_use_count_ == 0 &&
      (delete_on_finalize_ || finalize_completion_cb_)) {
    CallFinalizeOnMediaThread();
  }
}

void MediaResourceTracker::CallInitializeOnMediaThread() {
  DCHECK(media_task_runner_->BelongsToCurrentThread());
  if (media_lib_initialized_)
    return;

  DoInitializeMediaLib();
  media_lib_initialized_ = true;
}

void MediaResourceTracker::MaybeCallFinalizeOnMediaThread(
    base::OnceClosure completion_cb) {
  DCHECK(media_task_runner_->BelongsToCurrentThread());
  DCHECK(!finalize_completion_cb_);

  finalize_completion_cb_ =
      base::BindPostTask(ui_task_runner_, std::move(completion_cb));
  if (!media_lib_initialized_) {
    if (finalize_completion_cb_)
      std::move(finalize_completion_cb_).Run();
    return;
  }

  // If there are things using media, we must wait for them to stop.
  // CallFinalize will get called later from DecrementUsageCount when
  // usage count drops to 0.
  if (media_use_count_ == 0)
    CallFinalizeOnMediaThread();
}

void MediaResourceTracker::MaybeCallFinalizeOnMediaThreadAndDeleteSelf() {
  DCHECK(media_task_runner_->BelongsToCurrentThread());

  if (!media_lib_initialized_) {
    ui_task_runner_->DeleteSoon(FROM_HERE, this);
    return;
  }

  delete_on_finalize_ = true;

  // If there are things using media, we must wait for them to stop.
  // CallFinalize will get called later from DecrementUsageCount when
  // usage count drops to 0.
  if (media_use_count_ == 0)
    CallFinalizeOnMediaThread();
}

void MediaResourceTracker::CallFinalizeOnMediaThread() {
  DCHECK(media_task_runner_->BelongsToCurrentThread());
  DCHECK_EQ(media_use_count_, 0ul);
  DCHECK(media_lib_initialized_);

  DoFinalizeMediaLib();
  media_lib_initialized_ = false;

  if (finalize_completion_cb_)
    std::move(finalize_completion_cb_).Run();

  if (delete_on_finalize_)
    ui_task_runner_->DeleteSoon(FROM_HERE, this);
}

void MediaResourceTracker::DoInitializeMediaLib() {
  base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
  media::CastMediaShlib::Initialize(cmd_line->argv());
  VolumeControl::Initialize(cmd_line->argv());
}

void MediaResourceTracker::DoFinalizeMediaLib() {
  CastMediaShlib::Finalize();
  VolumeControl::Finalize();
}

}  // namespace media
}  // namespace chromecast