summaryrefslogtreecommitdiff
path: root/chromium/media/base/media_log.h
blob: 957181676fd15f27ef333f6e0667aec6ef65a14e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
// 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/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_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_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 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[];

  // 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 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 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 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();

 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> {
    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;

   protected:
    friend class base::RefCountedThreadSafe<ParentLogRecord>;
    virtual ~ParentLogRecord();

    DISALLOW_COPY_AND_ASSIGN(ParentLogRecord);
  };

  // Use |parent_log_record| instead of making a new one.
  MediaLog(scoped_refptr<ParentLogRecord> parent_log_record);

 private:
  // Allows MediaLogTest to construct MediaLog directly for testing.
  friend class MediaLogTest;
  FRIEND_TEST_ALL_PREFIXES(MediaLogTest, EventsAreForwarded);
  FRIEND_TEST_ALL_PREFIXES(MediaLogTest, EventsAreNotForwardedAfterInvalidate);

  // Helper methods to create events and their parameters.
  std::unique_ptr<MediaLogRecord> CreateRecord(MediaLogRecord::Type type);

  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_;
  DISALLOW_COPY_AND_ASSIGN(MediaLog);
};

// 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) \
  LogHelper((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): "                         \
                             : "")

}  // namespace media

#endif  // MEDIA_BASE_MEDIA_LOG_H_