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
media / base / media_log.cc [blame]
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "media/base/media_log.h"
#include <utility>
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/strings/string_util.h"
#include "base/values.h"
#include "media/base/media_switches.h"
namespace media {
// Static declaration for dictionary keys that we expect to be used inside
// different |MediaLogRecord|s. We declare them here so if they change, its
// only in one spot.
const char MediaLog::kEventKey[] = "event";
MediaLog::MediaLog() : MediaLog(new ParentLogRecord(this)) {}
MediaLog::MediaLog(scoped_refptr<ParentLogRecord> parent_log_record)
: parent_log_record_(std::move(parent_log_record)) {}
MediaLog::~MediaLog() {
// If we are the underlying log, then somebody should have called
// InvalidateLog before now. Otherwise, there could be concurrent calls into
// this after we're destroyed. Note that calling it here isn't really much
// better, since there could be concurrent calls into the now destroyed
// derived class.
//
// However, we can't DCHECK on it, since lots of folks create a base Medialog
// implementation temporarily. So, the best we can do is invalidate the log.
// We could get around this if we introduce a new NullMediaLog that handles
// log invalidation, so we could dcheck here. However, that seems like a lot
// of boilerplate.
InvalidateLog();
}
// Default *Locked implementations
void MediaLog::AddLogRecordLocked(std::unique_ptr<MediaLogRecord> event) {}
std::string MediaLog::GetErrorMessageLocked() {
return "";
}
// Default implementation.
void MediaLog::Stop() {}
bool MediaLog::ShouldLogToDebugConsole() const {
#if DCHECK_IS_ON()
return true;
#else
return false;
#endif
}
void MediaLog::AddMessage(MediaLogMessageLevel level, std::string message) {
std::unique_ptr<MediaLogRecord> record(
CreateRecord(MediaLogRecord::Type::kMessage));
if (!base::IsStringUTF8AllowingNoncharacters(message))
message = "WARNING: system message could not be rendered!";
record->params.Set(MediaLogMessageLevelToString(level), std::move(message));
AddLogRecord(std::move(record));
}
void MediaLog::OnWebMediaPlayerDestroyedLocked() {}
void MediaLog::OnWebMediaPlayerDestroyed() {
AddEvent<MediaLogEvent::kWebMediaPlayerDestroyed>();
base::AutoLock auto_lock(parent_log_record_->lock);
// Forward to the parent log's implementation.
if (parent_log_record_->media_log)
parent_log_record_->media_log->OnWebMediaPlayerDestroyedLocked();
}
std::string MediaLog::GetErrorMessage() {
base::AutoLock auto_lock(parent_log_record_->lock);
// Forward to the parent log's implementation.
if (parent_log_record_->media_log)
return parent_log_record_->media_log->GetErrorMessageLocked();
return "";
}
std::unique_ptr<MediaLog> MediaLog::Clone() {
// Protected ctor, so we can't use std::make_unique.
return base::WrapUnique(new MediaLog(parent_log_record_));
}
void MediaLog::AddLogRecord(std::unique_ptr<MediaLogRecord> record) {
base::AutoLock auto_lock(parent_log_record_->lock);
// Forward to the parent log's implementation.
if (parent_log_record_->media_log)
parent_log_record_->media_log->AddLogRecordLocked(std::move(record));
}
void MediaLog::EmitConsoleErrorLog(base::Value::Dict status_dict) {
auto stack = status_dict.Extract(StatusConstants::kStackKey);
DCHECK(stack);
DCHECK(stack->is_list());
DCHECK(!stack->GetList().empty());
DCHECK(stack->GetList().front().is_dict());
auto file =
stack->GetList().front().GetDict().Extract(StatusConstants::kFileKey);
DCHECK(file);
DCHECK(file->is_string());
auto line =
stack->GetList().front().GetDict().Extract(StatusConstants::kLineKey);
DCHECK(line);
DCHECK(line->is_int());
auto log_writer = logging::LogMessage(file->GetString().c_str(),
line->GetInt(), logging::LOGGING_ERROR);
if (auto message = status_dict.Extract(StatusConstants::kMsgKey);
message && message->is_string()) {
auto message_str = message->GetString();
if (!message_str.empty()) {
log_writer.stream() << message_str << ": ";
}
}
log_writer.stream() << base::WriteJson(status_dict).value_or(std::string());
}
std::unique_ptr<MediaLogRecord> MediaLog::CreateRecord(
MediaLogRecord::Type type) {
auto record = std::make_unique<MediaLogRecord>();
// Record IDs are populated by event handlers before they are sent to various
// log viewers, such as the media-internals page, or devtools.
record->id = 0;
record->type = type;
record->time = base::TimeTicks::Now();
return record;
}
void MediaLog::InvalidateLog() {
base::AutoLock auto_lock(parent_log_record_->lock);
// Do nothing if this log didn't create the record, i.e.
// it's not the parent log. The parent log should invalidate itself.
if (parent_log_record_->media_log == this)
parent_log_record_->media_log = nullptr;
// Keep |parent_log_record_| around, since the lock must keep working.
}
MediaLog::ParentLogRecord::ParentLogRecord(MediaLog* log) : media_log(log) {}
MediaLog::ParentLogRecord::~ParentLogRecord() = default;
LogHelper::LogHelper(MediaLogMessageLevel level,
MediaLog* media_log,
const char* file,
int line,
std::optional<logging::SystemErrorCode> code)
: file_(file),
line_(line),
level_(level),
media_log_(media_log),
code_(code) {
DCHECK(media_log_);
}
LogHelper::LogHelper(MediaLogMessageLevel level,
const std::unique_ptr<MediaLog>& media_log,
const char* file,
int line,
std::optional<logging::SystemErrorCode> code)
: LogHelper(level, media_log.get(), file, line, code) {}
LogHelper::~LogHelper() {
if (code_) {
stream_ << ": ";
auto err_string = logging::SystemErrorCodeToString(*code_);
if (!base::IsStringUTF8AllowingNoncharacters(err_string)) {
stream_ << *code_;
} else {
stream_ << err_string;
}
}
const auto log = stream_.str();
if (media_log_->ShouldLogToDebugConsole()) {
switch (level_) {
case MediaLogMessageLevel::kERROR:
// ERRORs are always logged regardless of kMediaLogToConsole value.
if (DLOG_IS_ON(ERROR)) {
logging::LogMessage(file_, line_, logging::LOGGING_ERROR).stream()
<< log;
}
break;
case MediaLogMessageLevel::kWARNING:
if (DLOG_IS_ON(WARNING) &&
base::FeatureList::IsEnabled(kMediaLogToConsole)) {
logging::LogMessage(file_, line_, logging::LOGGING_WARNING).stream()
<< log;
}
break;
case MediaLogMessageLevel::kINFO:
case MediaLogMessageLevel::kDEBUG:
if (DLOG_IS_ON(INFO) &&
base::FeatureList::IsEnabled(kMediaLogToConsole)) {
logging::LogMessage(file_, line_, logging::LOGGING_INFO).stream()
<< log;
}
break;
}
}
media_log_->AddMessage(level_, log);
}
} // namespace media