blob: 297ff6827f975a310d22c6943822841d9a632e86 [file] [log] [blame]
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "media/blink/webmediaplayer_util.h"
#include <math.h>
#include <stddef.h>
#include <string>
#include <utility>
#include "base/metrics/histogram.h"
#include "media/base/bind_to_current_loop.h"
#include "media/base/media_client.h"
#include "third_party/WebKit/public/platform/URLConversion.h"
#include "third_party/WebKit/public/platform/WebMediaPlayerEncryptedMediaClient.h"
namespace media {
blink::WebTimeRanges ConvertToWebTimeRanges(
const Ranges<base::TimeDelta>& ranges) {
blink::WebTimeRanges result(ranges.size());
for (size_t i = 0; i < ranges.size(); ++i) {
result[i].start = ranges.start(i).InSecondsF();
result[i].end = ranges.end(i).InSecondsF();
}
return result;
}
blink::WebMediaPlayer::NetworkState PipelineErrorToNetworkState(
PipelineStatus error) {
switch (error) {
case PIPELINE_ERROR_NETWORK:
case PIPELINE_ERROR_READ:
case CHUNK_DEMUXER_ERROR_EOS_STATUS_NETWORK_ERROR:
return blink::WebMediaPlayer::NetworkStateNetworkError;
case PIPELINE_ERROR_INITIALIZATION_FAILED:
case PIPELINE_ERROR_COULD_NOT_RENDER:
case DEMUXER_ERROR_COULD_NOT_OPEN:
case DEMUXER_ERROR_COULD_NOT_PARSE:
case DEMUXER_ERROR_NO_SUPPORTED_STREAMS:
case DECODER_ERROR_NOT_SUPPORTED:
return blink::WebMediaPlayer::NetworkStateFormatError;
case PIPELINE_ERROR_DECODE:
case PIPELINE_ERROR_ABORT:
case PIPELINE_ERROR_INVALID_STATE:
case CHUNK_DEMUXER_ERROR_APPEND_FAILED:
case CHUNK_DEMUXER_ERROR_EOS_STATUS_DECODE_ERROR:
case AUDIO_RENDERER_ERROR:
case AUDIO_RENDERER_ERROR_SPLICE_FAILED:
return blink::WebMediaPlayer::NetworkStateDecodeError;
case PIPELINE_OK:
NOTREACHED() << "Unexpected status! " << error;
}
return blink::WebMediaPlayer::NetworkStateFormatError;
}
namespace {
// Helper enum for reporting scheme histograms.
enum URLSchemeForHistogram {
kUnknownURLScheme,
kMissingURLScheme,
kHttpURLScheme,
kHttpsURLScheme,
kFtpURLScheme,
kChromeExtensionURLScheme,
kJavascriptURLScheme,
kFileURLScheme,
kBlobURLScheme,
kDataURLScheme,
kFileSystemScheme,
kMaxURLScheme = kFileSystemScheme // Must be equal to highest enum value.
};
URLSchemeForHistogram URLScheme(const GURL& url) {
if (!url.has_scheme()) return kMissingURLScheme;
if (url.SchemeIs("http")) return kHttpURLScheme;
if (url.SchemeIs("https")) return kHttpsURLScheme;
if (url.SchemeIs("ftp")) return kFtpURLScheme;
if (url.SchemeIs("chrome-extension")) return kChromeExtensionURLScheme;
if (url.SchemeIs("javascript")) return kJavascriptURLScheme;
if (url.SchemeIs("file")) return kFileURLScheme;
if (url.SchemeIs("blob")) return kBlobURLScheme;
if (url.SchemeIs("data")) return kDataURLScheme;
if (url.SchemeIs("filesystem")) return kFileSystemScheme;
return kUnknownURLScheme;
}
std::string LoadTypeToString(blink::WebMediaPlayer::LoadType load_type) {
switch (load_type) {
case blink::WebMediaPlayer::LoadTypeURL:
return "SRC";
case blink::WebMediaPlayer::LoadTypeMediaSource:
return "MSE";
case blink::WebMediaPlayer::LoadTypeMediaStream:
return "MS";
}
NOTREACHED();
return "Unknown";
}
} // namespace
void ReportMetrics(blink::WebMediaPlayer::LoadType load_type,
const GURL& url,
const blink::WebSecurityOrigin& security_origin) {
// Report URL scheme, such as http, https, file, blob etc.
UMA_HISTOGRAM_ENUMERATION("Media.URLScheme", URLScheme(url),
kMaxURLScheme + 1);
// Report load type, such as URL, MediaSource or MediaStream.
UMA_HISTOGRAM_ENUMERATION("Media.LoadType", load_type,
blink::WebMediaPlayer::LoadTypeMax + 1);
// Report the origin from where the media player is created.
if (GetMediaClient()) {
GURL security_origin_url(
blink::WebStringToGURL(security_origin.toString()));
GetMediaClient()->RecordRapporURL(
"Media.OriginUrl." + LoadTypeToString(load_type), security_origin_url);
// For MSE, also report usage by secure/insecure origin.
if (load_type == blink::WebMediaPlayer::LoadTypeMediaSource) {
if (security_origin.isPotentiallyTrustworthy()) {
GetMediaClient()->RecordRapporURL("Media.OriginUrl.MSE.Secure",
security_origin_url);
} else {
GetMediaClient()->RecordRapporURL("Media.OriginUrl.MSE.Insecure",
security_origin_url);
}
}
}
}
void ReportPipelineError(blink::WebMediaPlayer::LoadType load_type,
const blink::WebSecurityOrigin& security_origin,
PipelineStatus error) {
DCHECK_NE(PIPELINE_OK, error);
// Report the origin from where the media player is created.
if (!GetMediaClient())
return;
GetMediaClient()->RecordRapporURL(
"Media.OriginUrl." + LoadTypeToString(load_type) + "PipelineError",
blink::WebStringToGURL(security_origin.toString()));
}
void RecordOriginOfHLSPlayback(const GURL& origin_url) {
if (media::GetMediaClient())
GetMediaClient()->RecordRapporURL("Media.OriginUrl.HLS", origin_url);
}
EmeInitDataType ConvertToEmeInitDataType(
blink::WebEncryptedMediaInitDataType init_data_type) {
switch (init_data_type) {
case blink::WebEncryptedMediaInitDataType::Webm:
return EmeInitDataType::WEBM;
case blink::WebEncryptedMediaInitDataType::Cenc:
return EmeInitDataType::CENC;
case blink::WebEncryptedMediaInitDataType::Keyids:
return EmeInitDataType::KEYIDS;
case blink::WebEncryptedMediaInitDataType::Unknown:
return EmeInitDataType::UNKNOWN;
}
NOTREACHED();
return EmeInitDataType::UNKNOWN;
}
blink::WebEncryptedMediaInitDataType ConvertToWebInitDataType(
EmeInitDataType init_data_type) {
switch (init_data_type) {
case EmeInitDataType::WEBM:
return blink::WebEncryptedMediaInitDataType::Webm;
case EmeInitDataType::CENC:
return blink::WebEncryptedMediaInitDataType::Cenc;
case EmeInitDataType::KEYIDS:
return blink::WebEncryptedMediaInitDataType::Keyids;
case EmeInitDataType::UNKNOWN:
return blink::WebEncryptedMediaInitDataType::Unknown;
}
NOTREACHED();
return blink::WebEncryptedMediaInitDataType::Unknown;
}
namespace {
// This class wraps a scoped blink::WebSetSinkIdCallbacks pointer such that
// copying objects of this class actually performs moving, thus
// maintaining clear ownership of the blink::WebSetSinkIdCallbacks pointer.
// The rationale for this class is that the SwichOutputDevice method
// can make a copy of its base::Callback parameter, which implies
// copying its bound parameters.
// SwitchOutputDevice actually wants to move its base::Callback
// parameter since only the final copy will be run, but base::Callback
// does not support move semantics and there is no base::MovableCallback.
// Since scoped pointers are not copyable, we cannot bind them directly
// to a base::Callback in this case. Thus, we use this helper class,
// whose copy constructor transfers ownership of the scoped pointer.
class SetSinkIdCallback {
public:
explicit SetSinkIdCallback(blink::WebSetSinkIdCallbacks* web_callback)
: web_callback_(web_callback) {}
SetSinkIdCallback(const SetSinkIdCallback& other)
: web_callback_(std::move(other.web_callback_)) {}
~SetSinkIdCallback() {}
friend void RunSetSinkIdCallback(const SetSinkIdCallback& callback,
OutputDeviceStatus result);
private:
// Mutable is required so that Pass() can be called in the copy
// constructor.
mutable std::unique_ptr<blink::WebSetSinkIdCallbacks> web_callback_;
};
void RunSetSinkIdCallback(const SetSinkIdCallback& callback,
OutputDeviceStatus result) {
if (!callback.web_callback_)
return;
switch (result) {
case OUTPUT_DEVICE_STATUS_OK:
callback.web_callback_->onSuccess();
break;
case OUTPUT_DEVICE_STATUS_ERROR_NOT_FOUND:
callback.web_callback_->onError(blink::WebSetSinkIdError::NotFound);
break;
case OUTPUT_DEVICE_STATUS_ERROR_NOT_AUTHORIZED:
callback.web_callback_->onError(blink::WebSetSinkIdError::NotAuthorized);
break;
case OUTPUT_DEVICE_STATUS_ERROR_INTERNAL:
callback.web_callback_->onError(blink::WebSetSinkIdError::Aborted);
break;
default:
NOTREACHED();
}
callback.web_callback_ = nullptr;
}
} // namespace
OutputDeviceStatusCB ConvertToOutputDeviceStatusCB(
blink::WebSetSinkIdCallbacks* web_callbacks) {
return media::BindToCurrentLoop(
base::Bind(RunSetSinkIdCallback, SetSinkIdCallback(web_callbacks)));
}
} // namespace media