chromium/media/filters/demuxer_manager.cc

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

#include "media/filters/demuxer_manager.h"

#include <string_view>

#include "base/feature_list.h"
#include "base/functional/callback.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/strings/string_util.h"
#include "base/task/bind_post_task.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/task_runner.h"
#include "media/base/cross_origin_data_source.h"
#include "media/base/data_source.h"
#include "media/base/media_switches.h"
#include "media/base/media_url_demuxer.h"
#include "media/filters/chunk_demuxer.h"
#include "media/filters/ffmpeg_demuxer.h"
#include "net/storage_access_api/status.h"
#include "url/gurl.h"

#if BUILDFLAG(ENABLE_HLS_DEMUXER)
#include "media/filters/hls_manifest_demuxer_engine.h"
#include "media/filters/manifest_demuxer.h"
#endif  // BUILDFLAG(ENABLE_HLS_DEMUXER)

namespace media {

namespace {

#if BUILDFLAG(ENABLE_HLS_DEMUXER) || BUILDFLAG(IS_ANDROID)

// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
enum class MimeType {
  kOtherMimeType = 0,
  kApplicationDashXml = 1,
  kApplicationOgg = 2,
  kApplicationMpegUrl = 3,
  kApplicationVndAppleMpegUrl = 4,
  kApplicationXMpegUrl = 5,
  kAudioMpegUrl = 6,
  kAudioXMpegUrl = 7,
  kNonspecificAudio = 8,
  kNonspecificImage = 9,
  kNonspecificVideo = 10,
  kTextVtt = 11,
  kMaxValue = kTextVtt,  // For UMA histograms.
};

MimeType TranslateMimeTypeToHistogramEnum(const std::string_view mime_type) {
  constexpr auto kCaseInsensitive = base::CompareCase::INSENSITIVE_ASCII;
  if (base::StartsWith(mime_type, "application/dash+xml", kCaseInsensitive)) {
    return MimeType::kApplicationDashXml;
  }
  if (base::StartsWith(mime_type, "application/ogg", kCaseInsensitive)) {
    return MimeType::kApplicationOgg;
  }
  if (base::StartsWith(mime_type, "application/mpegurl", kCaseInsensitive)) {
    return MimeType::kApplicationMpegUrl;
  }
  if (base::StartsWith(mime_type, "application/vnd.apple.mpegurl",
                       kCaseInsensitive)) {
    return MimeType::kApplicationVndAppleMpegUrl;
  }
  if (base::StartsWith(mime_type, "application/x-mpegurl", kCaseInsensitive)) {
    return MimeType::kApplicationXMpegUrl;
  }

  if (base::StartsWith(mime_type, "audio/mpegurl", kCaseInsensitive)) {
    return MimeType::kAudioMpegUrl;
  }
  if (base::StartsWith(mime_type, "audio/x-mpegurl", kCaseInsensitive)) {
    return MimeType::kAudioXMpegUrl;
  }

  if (base::StartsWith(mime_type, "audio/", kCaseInsensitive)) {
    return MimeType::kNonspecificAudio;
  }
  if (base::StartsWith(mime_type, "image/", kCaseInsensitive)) {
    return MimeType::kNonspecificImage;
  }
  if (base::StartsWith(mime_type, "video/", kCaseInsensitive)) {
    return MimeType::kNonspecificVideo;
  }

  if (base::StartsWith(mime_type, "text/vtt", kCaseInsensitive)) {
    return MimeType::kTextVtt;
  }

  return MimeType::kOtherMimeType;
}

HlsFallbackImplementation SelectHlsFallbackImplementation() {
#if BUILDFLAG(ENABLE_HLS_DEMUXER)
  if (base::FeatureList::IsEnabled(kBuiltInHlsPlayer)) {
    return HlsFallbackImplementation::kBuiltinHlsPlayer;
  }
#endif

#if BUILDFLAG(IS_ANDROID)
  if (base::FeatureList::IsEnabled(kHlsPlayer)) {
    return HlsFallbackImplementation::kMediaPlayer;
  }
#endif

  return HlsFallbackImplementation::kNone;
}

#endif  // BUILDFLAG(ENABLE_HLS_DEMUXER) || BUILDFLAG(IS_ANDROID)

#if BUILDFLAG(ENABLE_FFMPEG)
// Returns true if `url` represents (or is likely to) a local file.
bool IsLocalFile(const GURL& url) {}
#endif

}  // namespace

DemuxerManager::DemuxerManager(
    Client* client,
    scoped_refptr<base::SequencedTaskRunner> media_task_runner,
    MediaLog* log,
    net::SiteForCookies site_for_cookies,
    url::Origin top_frame_origin,
    net::StorageAccessApiStatus storage_access_api_status,
    bool enable_instant_source_buffer_gc,
    std::unique_ptr<Demuxer> demuxer_override)
    :{}

DemuxerManager::~DemuxerManager() {}

void DemuxerManager::InvalidateWeakPtrs() {}

void DemuxerManager::RestartClientForHLS() {}

void DemuxerManager::OnPipelineError(PipelineStatus error) {}

void DemuxerManager::FreeResourcesAfterMediaThreadWait(base::OnceClosure cb) {}

void DemuxerManager::DisallowFallback() {}

void DemuxerManager::SetLoadedUrl(GURL url) {}

const GURL& DemuxerManager::LoadedUrl() const {}

#if BUILDFLAG(ENABLE_HLS_DEMUXER) || BUILDFLAG(IS_ANDROID)

void DemuxerManager::PopulateHlsHistograms(bool cryptographic_url) {
  DCHECK(data_source_);

  if (auto* co_data_source = data_source_->GetAsCrossOriginDataSource()) {
    MimeType mime_type =
        TranslateMimeTypeToHistogramEnum(co_data_source->GetMimeType());
    base::UmaHistogramEnumeration("Media.WebMediaPlayerImpl.HLS.MimeType",
                                  mime_type);

    bool is_cross_origin = co_data_source->IsCorsCrossOrigin();
    UMA_HISTOGRAM_BOOLEAN("Media.WebMediaPlayerImpl.HLS.IsCorsCrossOrigin",
                          is_cross_origin);
    if (is_cross_origin) {
      UMA_HISTOGRAM_BOOLEAN("Media.WebMediaPlayerImpl.HLS.HasAccessControl",
                            co_data_source->HasAccessControl());
      base::UmaHistogramEnumeration(
          "Media.WebMediaPlayerImpl.HLS.CorsCrossOrigin.MimeType", mime_type);
    }
  } else {
    UMA_HISTOGRAM_BOOLEAN("Media.WebMediaPlayerImpl.HLS.IsCorsCrossOrigin",
                          false);
  }

  bool is_mixed_content =
      cryptographic_url &&
      (!loaded_url_.SchemeIsCryptographic() ||
       GetDataSourceUrlAfterRedirects()->SchemeIsCryptographic());
  UMA_HISTOGRAM_BOOLEAN("Media.WebMediaPlayerImpl.HLS.IsMixedContent",
                        is_mixed_content);
}

PipelineStatus DemuxerManager::SelectHlsFallbackMechanism(
    bool cryptographic_url) {
  hls_fallback_ = SelectHlsFallbackImplementation();
  if (hls_fallback_ == HlsFallbackImplementation::kNone) {
    return DEMUXER_ERROR_DETECTED_HLS;
  }

  // If we've gotten a request to start HLS fallback and logging, we can assert
  // that data source has been set.
  CHECK(data_source_);

  // |data_source_| might be a MemoryDataSource if our URL is a data:// url.
  // Since MediaPlayer doesn't support this type of URL, we can't fall back to
  // android's HLS implementation. Since HLS is enabled, we should report a
  // failed external renderer, since we know MediaPlayerRenderer would fail
  // anyway here.
  bool is_mp = hls_fallback_ == HlsFallbackImplementation::kMediaPlayer;
  if (!data_source_->GetAsCrossOriginDataSource() && is_mp) {
    // Media player requires that the data source not be a data:// url.
    return PIPELINE_ERROR_EXTERNAL_RENDERER_FAILED;
  }

  loaded_url_ = GetDataSourceUrlAfterRedirects().value();

  // We do not support using blob and filesystem schemes with the Android
  // MediaPlayer. Fail now rather than during MediaPlayerRender initialization.
  if (is_mp &&
      (loaded_url_.SchemeIsBlob() || loaded_url_.SchemeIsFileSystem())) {
    return PIPELINE_ERROR_EXTERNAL_RENDERER_FAILED;
  }

  PopulateHlsHistograms(cryptographic_url);

  if (client_) {
    client_->UpdateLoadedUrl(loaded_url_);
  }

  return OkStatus();
}

#endif  // BUILDFLAG(ENABLE_HLS_DEMUXER) || BUILDFLAG(IS_ANDROID)

std::optional<double> DemuxerManager::GetDemuxerDuration() {}

std::optional<DemuxerType> DemuxerManager::GetDemuxerType() const {}

std::optional<container_names::MediaContainerName>
DemuxerManager::GetContainerForMetrics() {}

void DemuxerManager::RespondToDemuxerMemoryUsageReport(
    base::OnceCallback<void(int64_t)> cb) {}

void DemuxerManager::DisableDemuxerCanChangeType() {}

PipelineStatus DemuxerManager::CreateDemuxer(
    bool load_media_source,
    DataSource::Preload preload,
    bool needs_first_frame,
    DemuxerManager::DemuxerCreatedCB on_demuxer_created,
    base::flat_map<std::string, std::string> headers) {}

#if BUILDFLAG(IS_ANDROID)
void DemuxerManager::SetAllowMediaPlayerRendererCredentials(bool allow) {
  allow_media_player_renderer_credentials_ = allow;
}
#endif  // BUILDFLAG(IS_ANDROID)

DataSource* DemuxerManager::GetDataSourceForTesting() const {}

void DemuxerManager::SetDataSource(std::unique_ptr<DataSource> data_source) {}

void DemuxerManager::OnBufferingHaveEnough(bool enough) {}

void DemuxerManager::SetPreload(DataSource::Preload preload) {}

void DemuxerManager::StopAndResetClient() {}

int64_t DemuxerManager::GetDataSourceMemoryUsage() {}

void DemuxerManager::OnDataSourcePlaybackRateChange(double rate, bool paused) {}

void DemuxerManager::DurationChanged() {}

bool DemuxerManager::WouldTaintOrigin() const {}

bool DemuxerManager::HasDataSource() const {}

bool DemuxerManager::HasDemuxer() const {}

bool DemuxerManager::HasDemuxerOverride() const {}

std::optional<GURL> DemuxerManager::GetDataSourceUrlAfterRedirects() const {}

bool DemuxerManager::DataSourceFullyBuffered() const {}

bool DemuxerManager::IsStreaming() const {}

bool DemuxerManager::PassedDataSourceTimingAllowOriginCheck() const {}

bool DemuxerManager::IsLiveContent() const {}

std::unique_ptr<Demuxer> DemuxerManager::CreateChunkDemuxer() {}

#if BUILDFLAG(ENABLE_FFMPEG)
std::unique_ptr<Demuxer> DemuxerManager::CreateFFmpegDemuxer() {}
#endif  // BUILDFLAG(ENABLE_FFMPEG)

#if BUILDFLAG(ENABLE_HLS_DEMUXER)
std::tuple<raw_ptr<DataSourceInfo>, std::unique_ptr<Demuxer>>
DemuxerManager::CreateHlsDemuxer() {
  bool would_taint_origin =
      data_source_info_ ? data_source_info_->WouldTaintOrigin() : false;
  auto engine = std::make_unique<HlsManifestDemuxerEngine>(
      client_->GetHlsDataSourceProvider(), media_task_runner_,
      would_taint_origin, loaded_url_, media_log_.get());
  raw_ptr<DataSourceInfo> datasource_info = engine.get();
  return std::make_tuple(
      datasource_info,
      std::make_unique<ManifestDemuxer>(
          media_task_runner_,
          base::BindPostTaskToCurrentDefault(
              base::BindRepeating(&DemuxerManager::DemuxerRequestsSeek,
                                  weak_factory_.GetWeakPtr())),
          std::move(engine), media_log_.get()));
}
#endif

#if BUILDFLAG(IS_ANDROID)
std::unique_ptr<Demuxer> DemuxerManager::CreateMediaUrlDemuxer(
    bool expect_hls_content,
    base::flat_map<std::string, std::string> headers) {
#if BUILDFLAG(ENABLE_HLS_DEMUXER)
  if (base::FeatureList::IsEnabled(kMediaPlayerHlsStatistics)) {
    media_player_hls_tag_recorder_ =
        std::make_unique<HlsMediaPlayerTagRecorder>(
            std::make_unique<HlsNetworkAccessImpl>(
                client_->GetHlsDataSourceProvider()));
    media_player_hls_tag_recorder_->Start(loaded_url_);
  }
#endif

  std::unique_ptr<MediaUrlDemuxer> media_url_demuxer =
      std::make_unique<MediaUrlDemuxer>(
          media_task_runner_, loaded_url_, site_for_cookies_, top_frame_origin_,
          storage_access_api_status_, allow_media_player_renderer_credentials_,
          expect_hls_content);
  media_url_demuxer->SetHeaders(std::move(headers));
  return media_url_demuxer;
}
#endif  // BUILDFLAG(IS_ANDROID)

void DemuxerManager::SetDemuxer(std::unique_ptr<Demuxer> demuxer) {}

void DemuxerManager::OnEncryptedMediaInitData(
    EmeInitDataType init_data_type,
    const std::vector<uint8_t>& init_data) {}

void DemuxerManager::OnMemoryPressure(
    base::MemoryPressureListener::MemoryPressureLevel level) {}

void DemuxerManager::OnChunkDemuxerOpened() {}

void DemuxerManager::OnProgress() {}

#if BUILDFLAG(ENABLE_FFMPEG)
void DemuxerManager::OnFFmpegMediaTracksUpdated(
    std::unique_ptr<MediaTracks> tracks) {}
#endif  // BUILDFLAG(ENABLE_FFMPEG)

void DemuxerManager::DemuxerRequestsSeek(base::TimeDelta time) {}

}  // namespace media