blob: 937991a801923ca3d42a6469ebaa199e8e402c4f [file] [log] [blame]
// Copyright (c) 2012 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 <stddef.h>
#include <stdint.h>
#include <memory>
#include <sstream>
#include <string>
#include "base/logging.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/thread_annotations.h"
#include "media/base/buffering_state.h"
#include "media/base/media_export.h"
#include "media/base/media_log_event.h"
#include "media/base/pipeline_impl.h"
#include "media/base/pipeline_status.h"
#include "url/gurl.h"
namespace media {
// Interface for media components to log to chrome://media-internals log.
// To provide a logging implementation, derive from MediaLog instead.
// Implementations only need to implement AddEventLocked(), which must be thread
// safe in the sense that it may be called from multiple threads, though it will
// not be called concurrently. See below for more details.
// Implementations should also call InvalidateLog during destruction, to signal
// to any child logs that the underlying log is no longer available.
class MEDIA_EXPORT MediaLog {
enum MediaLogLevel {
// Fatal error, e.g. cause of playback failure. Since this is also used to
// form MediaError.message, do NOT use this for non-fatal errors to avoid
// contaminating MediaError.message.
// Warning about non-fatal issues, e.g. quality of playback issues such as
// audio/video out of sync.
// General info useful for Chromium and/or web developers, testers and even
// users, e.g. audio/video codecs used in a playback instance.
// Misc debug info for Chromium developers.
// Convert various enums to strings.
static std::string MediaLogLevelToString(MediaLogLevel level);
static MediaLogEvent::Type MediaLogLevelToEventType(MediaLogLevel level);
static std::string EventTypeToString(MediaLogEvent::Type type);
// Returns a string version of the status, unique to each PipelineStatus, and
// not including any ':'. This makes it suitable for usage in
// MediaError.message as the UA-specific-error-code.
static std::string PipelineStatusToString(PipelineStatus status);
static std::string BufferingStateToString(BufferingState state);
static std::string MediaEventToLogString(const MediaLogEvent& event);
// Returns a string usable as part of a MediaError.message, for only
// PIPELINE_ERROR or MEDIA_ERROR_LOG_ENTRY events, with any newlines replaced
// with whitespace in the latter kind of events.
static std::string MediaEventToMessageString(const MediaLogEvent& event);
// Constructor is protected, see below.
virtual ~MediaLog();
// Add an event to this log. Inheritors should override AddEventLocked to
// do something.
void AddEvent(std::unique_ptr<MediaLogEvent> event);
// Returns a string usable as the contents of a MediaError.message.
// This method returns an incomplete message if it is called before the
// pertinent events for the error have been added to the log.
// Note: The base class definition only produces empty messages. See
// RenderMediaLog for where this method is meaningful.
// Inheritors should override GetErrorMessageLocked().
std::string GetErrorMessage();
// Records the domain and registry of the current frame security origin to a
// Rappor privacy-preserving metric. See:
// Inheritors should override RecordRapportWithSecurityOriginLocked().
void RecordRapporWithSecurityOrigin(const std::string& metric);
// Helper methods to create events and their parameters.
std::unique_ptr<MediaLogEvent> CreateEvent(MediaLogEvent::Type type);
std::unique_ptr<MediaLogEvent> CreateBooleanEvent(MediaLogEvent::Type type,
const std::string& property,
bool value);
std::unique_ptr<MediaLogEvent> CreateCreatedEvent(
const std::string& origin_url);
std::unique_ptr<MediaLogEvent> CreateStringEvent(MediaLogEvent::Type type,
const std::string& property,
const std::string& value);
std::unique_ptr<MediaLogEvent> CreateTimeEvent(MediaLogEvent::Type type,
const std::string& property,
base::TimeDelta value);
std::unique_ptr<MediaLogEvent> CreateTimeEvent(MediaLogEvent::Type type,
const std::string& property,
double value);
std::unique_ptr<MediaLogEvent> CreateLoadEvent(const std::string& url);
std::unique_ptr<MediaLogEvent> CreatePipelineStateChangedEvent(
PipelineImpl::State state);
std::unique_ptr<MediaLogEvent> CreatePipelineErrorEvent(PipelineStatus error);
std::unique_ptr<MediaLogEvent> CreateVideoSizeSetEvent(size_t width,
size_t height);
std::unique_ptr<MediaLogEvent> CreateBufferingStateChangedEvent(
const std::string& property,
BufferingState state);
// Report a log message at the specified log level.
void AddLogEvent(MediaLogLevel level, const std::string& message);
// Report a property change without an accompanying event.
void SetStringProperty(const std::string& key, const std::string& value);
void SetDoubleProperty(const std::string& key, double value);
void SetBooleanProperty(const std::string& key, bool value);
// Getter for |id_|. Used by MojoMediaLogService to construct MediaLogEvents
// to log into this MediaLog. Also used in trace events to associate each
// event with a specific media playback.
int32_t id() const { return id_; }
// Provide a MediaLog which can have a separate lifetime from this one, but
// still write to the same log. It is not guaranteed that this will log
// forever; it might start silently discarding log messages if the original
// log is closed by whoever owns it.
virtual std::unique_ptr<MediaLog> Clone();
// Ensures only subclasses and factories (e.g. Clone()) can create MediaLog.
// Methods that may be overridden by inheritors. All calls may arrive on any
// thread, but will be synchronized with respect to any other *Locked calls on
// any other thread, and with any parent log invalidation.
// Please see the documentation for the corresponding public methods.
virtual void AddEventLocked(std::unique_ptr<MediaLogEvent> event);
virtual std::string GetErrorMessageLocked();
virtual void RecordRapporWithSecurityOriginLocked(const std::string& metric);
// Notify all child logs that they should stop working. This should be called
// to guarantee that no further calls into AddEvent should be allowed.
// Further, since calls into this log may happen on any thread, it's important
// to call this while the log is still in working order. For example, calling
// it immediately during destruction is a good idea.
void InvalidateLog();
struct ParentLogRecord : base::RefCountedThreadSafe<ParentLogRecord> {
ParentLogRecord(MediaLog* log);
// |lock_| protects the rest of this structure.
base::Lock lock;
// Original media log, or null.
MediaLog* media_log GUARDED_BY(lock) = nullptr;
friend class base::RefCountedThreadSafe<ParentLogRecord>;
virtual ~ParentLogRecord();
// Use |parent_log_record| instead of making a new one.
MediaLog(scoped_refptr<ParentLogRecord> parent_log_record);
// Allows MediaLogTest to construct MediaLog directly for testing.
friend class MediaLogTest;
FRIEND_TEST_ALL_PREFIXES(MediaLogTest, EventsAreForwarded);
FRIEND_TEST_ALL_PREFIXES(MediaLogTest, EventsAreNotForwardedAfterInvalidate);
enum : size_t {
// Max length of URLs in Created/Load events. Exceeding triggers truncation.
kMaxUrlLength = 1000,
// URLs (for Created and Load events) may be of arbitrary length from the
// untrusted renderer. This method truncates to |kMaxUrlLength| before storing
// the event, and sets the last 3 characters to an ellipsis.
static std::string TruncateUrlString(std::string log_string);
// The underlying media log.
scoped_refptr<ParentLogRecord> parent_log_record_;
// A unique (to this process) id for this MediaLog.
int32_t id_;
// Helper class to make it easier to use MediaLog like DVLOG().
class MEDIA_EXPORT LogHelper {
LogHelper(MediaLog::MediaLogLevel level, MediaLog* media_log);
LogHelper(MediaLog::MediaLogLevel level,
const std::unique_ptr<MediaLog>& media_log);
std::ostream& stream() { return stream_; }
const MediaLog::MediaLogLevel level_;
MediaLog* const media_log_;
std::stringstream stream_;
// Provides a stringstream to collect a log entry to pass to the provided
// MediaLog at the requested level.
#define MEDIA_LOG(level, media_log) \
LogHelper((MediaLog::MEDIALOG_##level), (media_log)).stream()
// Logs only while |count| < |max|, increments |count| for each log, and warns
// in the log if |count| has just reached |max|.
// Multiple short-circuit evaluations are involved in this macro:
// 1) LAZY_STREAM avoids wasteful MEDIA_LOG and evaluation of subsequent stream
// arguments if |count| is >= |max|, and
// 2) the |condition| given to LAZY_STREAM itself short-circuits to prevent
// incrementing |count| beyond |max|.
// Note that LAZY_STREAM guarantees exactly one evaluation of |condition|, so
// |count| will be incremented at most once each time this macro runs.
// The "|| true" portion of |condition| lets logging occur correctly when
// |count| < |max| and |count|++ is 0.
// TODO(wolenetz,chcunningham): Consider using a helper class instead of a macro
// to improve readability.
#define LIMITED_MEDIA_LOG(level, media_log, count, max) \
LAZY_STREAM(MEDIA_LOG(level, media_log), \
(count) < (max) && ((count)++ || true)) \
<< (((count) == (max)) ? "(Log limit reached. Further similar entries " \
"may be suppressed): " \
: "")
} // namespace media