#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
namespace media {
namespace {
#if BUILDFLAG(ENABLE_HLS_DEMUXER) || BUILDFLAG(IS_ANDROID)
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,
};
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
#if BUILDFLAG(ENABLE_FFMPEG)
bool IsLocalFile(const GURL& url) { … }
#endif
}
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;
}
CHECK(data_source_);
bool is_mp = hls_fallback_ == HlsFallbackImplementation::kMediaPlayer;
if (!data_source_->GetAsCrossOriginDataSource() && is_mp) {
return PIPELINE_ERROR_EXTERNAL_RENDERER_FAILED;
}
loaded_url_ = GetDataSourceUrlAfterRedirects().value();
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
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
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
#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
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
void DemuxerManager::DemuxerRequestsSeek(base::TimeDelta time) { … }
}