blob: 89d44be5f92c31cd973d70cec914770bb9b49b0f [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.
#ifndef MEDIA_BASE_MEDIA_LOG_H_
#define MEDIA_BASE_MEDIA_LOG_H_
#include <stddef.h>
#include <stdint.h>
#include <memory>
#include <sstream>
#include <string>
#include <utility>
#include "base/gtest_prod_util.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/thread_annotations.h"
#include "build/build_config.h"
#include "media/base/buffering_state.h"
#include "media/base/media_export.h"
#include "media/base/media_log_events.h"
#include "media/base/media_log_message_levels.h"
#include "media/base/media_log_properties.h"
#include "media/base/media_log_record.h"
#include "media/base/pipeline_status.h"
#include "url/gurl.h"
namespace media {
#if defined(STARBOARD)
// A stub MediaLog implementation.
// TODO(b/230916218): Consider making use of MediaLog for debugging.
class MEDIA_EXPORT MediaLog {
public: template <MediaLogProperty P, typename T>
void SetProperty(const T& value) {}
MediaLog() {}
virtual ~MediaLog() {}
virtual void AddLogRecordLocked(
std::unique_ptr<media::MediaLogRecord> event) {
NOTREACHED();
}
template <MediaLogEvent E, typename... T>
void AddEvent(const T&... value) {
}
template <MediaLogEvent E, typename... T>
int CreateEvent(const T&... value) {
return 0;
}
void NotifyError(PipelineStatus status) {}
};
#define MEDIA_LOG(level, media_log) LOG(INFO)
#define LIMITED_MEDIA_LOG(level, media_log, count, max) DLOG(INFO)
#else // defined(STARBOARD)
// 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 AddLogRecordLocked(), 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 {
public:
static const char kEventKey[];
static const char kStatusText[];
// Maximum limit for the total number of logs kept per renderer. At the time of
// writing, 512 events of the kind: { "property": value } together consume ~88kb
// of memory on linux.
#if defined(OS_ANDROID)
static constexpr size_t kLogLimit = 128;
#else
static constexpr size_t kLogLimit = 512;
#endif
MediaLog(const MediaLog&) = delete;
MediaLog& operator=(const MediaLog&) = delete;
// Constructor is protected, see below.
virtual ~MediaLog();
// Report a log message at the specified log level.
void AddMessage(MediaLogMessageLevel level, std::string message);
// Typechecked property setter, since all properties must take values.
// For example, MediaLogProperty::kResolution supports only gfx::Size as
// an argument (see media_log_properties.h for this), so calling
// media_log->SetProperty<MediaLogProperty::kResolution>(1);
// would lead to a compile error, while
// gfx::Size rect = {100, 100};
// media_log->SetProperty<MediaLogProperty::kResolution>(rect);
// is correct.
template <MediaLogProperty P, typename T>
void SetProperty(const T& value) {
AddLogRecord(CreatePropertyRecord<P, T>(value));
}
// TODO(tmathmeyer) add the ability to report events with a separated
// start and end time.
// Send an event to the media log that may or may not have attached data.
// For example, MediaLogEvent::kPlay takes no arguments, while
// MediaLogEvent::kSeek takes a double as an argument, representing the time.
// A proper way to add either of these events would be
// media_log->AddEvent<MediaLogEvent::kPlay>();
// media_log->AddEvent<MediaLogEvent::kSeek>(1.99);
template <MediaLogEvent E, typename... T>
void AddEvent(const T&... value) {
std::unique_ptr<MediaLogRecord> record = CreateEventRecord<E, T...>();
MediaLogEventTypeSupport<E, T...>::AddExtraData(&record->params, value...);
AddLogRecord(std::move(record));
}
// TODO(tmathmeyer) replace with Status when that's ready.
void NotifyError(PipelineStatus status);
// Notify a non-ok Status. This method Should _not_ be given an OK status.
void NotifyError(Status status);
// Notify the media log that the player is destroyed. Some implementations
// will want to change event handling based on this.
void OnWebMediaPlayerDestroyed();
// 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().
// TODO(tmathmeyer) Use a media::Status when that is ready.
std::string GetErrorMessage();
// Getter for |id_|. Used by MojoMediaLogService to construct MediaLogRecords
// to log into this MediaLog. Also used in trace events to associate each
// event with a specific media playback.
int32_t id() const { return parent_log_record_->id; }
// Add a record to this log. Inheritors should override AddLogRecordLocked to
// do something. This needs to be public for MojoMediaLogService to use it.
void AddLogRecord(std::unique_ptr<MediaLogRecord> event);
// Provide a MediaLog which can have a separate lifetime from this one, but
// still write to the same player's 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. However, it's safe to use it
// even if this occurs, in the "won't crash" sense.
virtual std::unique_ptr<MediaLog> Clone();
protected:
// Ensures only subclasses and factories (e.g. Clone()) can create MediaLog.
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 AddLogRecordLocked(std::unique_ptr<MediaLogRecord> event);
virtual void OnWebMediaPlayerDestroyedLocked();
virtual std::string GetErrorMessageLocked();
// MockMediaLog also needs to call this method.
template <MediaLogProperty P, typename T>
std::unique_ptr<MediaLogRecord> CreatePropertyRecord(const T& value) {
auto record = CreateRecord(MediaLogRecord::Type::kMediaPropertyChange);
record->params.SetKey(MediaLogPropertyKeyToString(P),
MediaLogPropertyTypeSupport<P, T>::Convert(value));
return record;
}
template <MediaLogEvent E, typename... Opt>
std::unique_ptr<MediaLogRecord> CreateEventRecord() {
std::unique_ptr<MediaLogRecord> record(
CreateRecord(MediaLogRecord::Type::kMediaEventTriggered));
record->params.SetString(MediaLog::kEventKey,
MediaLogEventTypeSupport<E, Opt...>::TypeName());
return record;
}
// Notify all child logs that they should stop working. This should be called
// to guarantee that no further calls into AddLogRecord 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> {
explicit ParentLogRecord(MediaLog* log);
// A unique (to this process) id for this MediaLog.
int32_t id;
// |lock_| protects the rest of this structure.
base::Lock lock;
// Original media log, or null.
MediaLog* media_log GUARDED_BY(lock) = nullptr;
protected:
friend class base::RefCountedThreadSafe<ParentLogRecord>;
virtual ~ParentLogRecord();
DISALLOW_COPY_AND_ASSIGN(ParentLogRecord);
};
private:
// Allows MediaLogTest to construct MediaLog directly for testing.
friend class MediaLogTest;
FRIEND_TEST_ALL_PREFIXES(MediaLogTest, EventsAreForwarded);
FRIEND_TEST_ALL_PREFIXES(MediaLogTest, EventsAreNotForwardedAfterInvalidate);
// Use |parent_log_record| instead of making a new one.
explicit MediaLog(scoped_refptr<ParentLogRecord> parent_log_record);
// Helper methods to create events and their parameters.
std::unique_ptr<MediaLogRecord> CreateRecord(MediaLogRecord::Type type);
// The underlying media log.
scoped_refptr<ParentLogRecord> parent_log_record_;
};
// Helper class to make it easier to use MediaLog like DVLOG().
class MEDIA_EXPORT LogHelper {
public:
LogHelper(MediaLogMessageLevel level, MediaLog* media_log);
LogHelper(MediaLogMessageLevel level,
const std::unique_ptr<MediaLog>& media_log);
~LogHelper();
std::ostream& stream() { return stream_; }
private:
const MediaLogMessageLevel 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) \
media::LogHelper((media::MediaLogMessageLevel::k##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): " \
: "")
#endif // defined(STARBOARD)
} // namespace media
#endif // MEDIA_BASE_MEDIA_LOG_H_