Popular sites on the NTP: check that experiment group StartsWith (rather than IS...
[chromium-blink-merge.git] / chrome / browser / media / webrtc_rtp_dump_handler.cc
blob7fb2613139164a6023a1970cfd9479dac56d9846
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/media/webrtc_rtp_dump_handler.h"
7 #include "base/files/file_util.h"
8 #include "base/logging.h"
9 #include "base/strings/string_number_conversions.h"
10 #include "base/time/time.h"
11 #include "chrome/browser/media/webrtc_rtp_dump_writer.h"
12 #include "content/public/browser/browser_thread.h"
14 using content::BrowserThread;
16 namespace {
18 static const size_t kMaxOngoingRtpDumpsAllowed = 5;
20 // The browser process wide total number of ongoing (i.e. started and not
21 // released) RTP dumps. Incoming and outgoing in one WebRtcDumpHandler are
22 // counted as one dump.
23 // Must be accessed on the browser IO thread.
24 static size_t g_ongoing_rtp_dumps = 0;
26 void FireGenericDoneCallback(
27 const WebRtcRtpDumpHandler::GenericDoneCallback& callback,
28 bool success,
29 const std::string& error_message) {
30 DCHECK_CURRENTLY_ON(BrowserThread::IO);
31 DCHECK(!callback.is_null());
33 content::BrowserThread::PostTask(
34 content::BrowserThread::UI,
35 FROM_HERE,
36 base::Bind(callback, success, error_message));
39 bool DumpTypeContainsIncoming(RtpDumpType type) {
40 return type == RTP_DUMP_INCOMING || type == RTP_DUMP_BOTH;
43 bool DumpTypeContainsOutgoing(RtpDumpType type) {
44 return type == RTP_DUMP_OUTGOING || type == RTP_DUMP_BOTH;
47 } // namespace
49 WebRtcRtpDumpHandler::WebRtcRtpDumpHandler(const base::FilePath& dump_dir)
50 : dump_dir_(dump_dir),
51 incoming_state_(STATE_NONE),
52 outgoing_state_(STATE_NONE),
53 weak_ptr_factory_(this) {
54 DCHECK_CURRENTLY_ON(BrowserThread::IO);
57 WebRtcRtpDumpHandler::~WebRtcRtpDumpHandler() {
58 DCHECK_CURRENTLY_ON(BrowserThread::IO);
60 // Reset dump writer first to stop writing.
61 if (dump_writer_) {
62 --g_ongoing_rtp_dumps;
63 dump_writer_.reset();
66 if (incoming_state_ != STATE_NONE && !incoming_dump_path_.empty()) {
67 BrowserThread::PostTask(
68 BrowserThread::FILE,
69 FROM_HERE,
70 base::Bind(
71 base::IgnoreResult(&base::DeleteFile), incoming_dump_path_, false));
74 if (outgoing_state_ != STATE_NONE && !outgoing_dump_path_.empty()) {
75 BrowserThread::PostTask(
76 BrowserThread::FILE,
77 FROM_HERE,
78 base::Bind(
79 base::IgnoreResult(&base::DeleteFile), outgoing_dump_path_, false));
83 bool WebRtcRtpDumpHandler::StartDump(RtpDumpType type,
84 std::string* error_message) {
85 DCHECK_CURRENTLY_ON(BrowserThread::IO);
87 if (!dump_writer_ && g_ongoing_rtp_dumps >= kMaxOngoingRtpDumpsAllowed) {
88 *error_message = "Max RTP dump limit reached.";
89 DVLOG(2) << *error_message;
90 return false;
93 // Returns an error if any type of dump specified by the caller cannot be
94 // started.
95 if ((DumpTypeContainsIncoming(type) && incoming_state_ != STATE_NONE) ||
96 (DumpTypeContainsOutgoing(type) && outgoing_state_ != STATE_NONE)) {
97 *error_message =
98 "RTP dump already started for type " + base::IntToString(type);
99 return false;
102 if (DumpTypeContainsIncoming(type))
103 incoming_state_ = STATE_STARTED;
105 if (DumpTypeContainsOutgoing(type))
106 outgoing_state_ = STATE_STARTED;
108 DVLOG(2) << "Start RTP dumping: type = " << type;
110 if (!dump_writer_) {
111 ++g_ongoing_rtp_dumps;
113 static const char kRecvDumpFilePrefix[] = "rtpdump_recv_";
114 static const char kSendDumpFilePrefix[] = "rtpdump_send_";
115 static const size_t kMaxDumpSize = 5 * 1024 * 1024; // 5MB
117 std::string dump_id = base::DoubleToString(base::Time::Now().ToDoubleT());
118 incoming_dump_path_ =
119 dump_dir_.AppendASCII(std::string(kRecvDumpFilePrefix) + dump_id)
120 .AddExtension(FILE_PATH_LITERAL(".gz"));
122 outgoing_dump_path_ =
123 dump_dir_.AppendASCII(std::string(kSendDumpFilePrefix) + dump_id)
124 .AddExtension(FILE_PATH_LITERAL(".gz"));
126 // WebRtcRtpDumpWriter does not support changing the dump path after it's
127 // created. So we assign both incoming and outgoing dump path even if only
128 // one type of dumping has been started.
129 // For "Unretained(this)", see comments StopDump.
130 dump_writer_.reset(new WebRtcRtpDumpWriter(
131 incoming_dump_path_,
132 outgoing_dump_path_,
133 kMaxDumpSize,
134 base::Bind(&WebRtcRtpDumpHandler::OnMaxDumpSizeReached,
135 base::Unretained(this))));
138 return true;
141 void WebRtcRtpDumpHandler::StopDump(RtpDumpType type,
142 const GenericDoneCallback& callback) {
143 DCHECK_CURRENTLY_ON(BrowserThread::IO);
145 // Returns an error if any type of dump specified by the caller cannot be
146 // stopped.
147 if ((DumpTypeContainsIncoming(type) && incoming_state_ != STATE_STARTED) ||
148 (DumpTypeContainsOutgoing(type) && outgoing_state_ != STATE_STARTED)) {
149 if (!callback.is_null()) {
150 FireGenericDoneCallback(
151 callback,
152 false,
153 "RTP dump not started or already stopped for type " +
154 base::IntToString(type));
156 return;
159 DVLOG(2) << "Stopping RTP dumping: type = " << type;
161 if (DumpTypeContainsIncoming(type))
162 incoming_state_ = STATE_STOPPING;
164 if (DumpTypeContainsOutgoing(type))
165 outgoing_state_ = STATE_STOPPING;
167 // Using "Unretained(this)" because the this object owns the writer and the
168 // writer is guaranteed to cancel the callback before it goes away. Same for
169 // the other posted tasks bound to the writer.
170 dump_writer_->EndDump(
171 type,
172 base::Bind(&WebRtcRtpDumpHandler::OnDumpEnded,
173 base::Unretained(this),
174 callback.is_null()
175 ? base::Closure()
176 : base::Bind(&FireGenericDoneCallback, callback, true, ""),
177 type));
180 bool WebRtcRtpDumpHandler::ReadyToRelease() const {
181 DCHECK_CURRENTLY_ON(BrowserThread::IO);
183 return incoming_state_ != STATE_STARTED &&
184 incoming_state_ != STATE_STOPPING &&
185 outgoing_state_ != STATE_STARTED && outgoing_state_ != STATE_STOPPING;
188 WebRtcRtpDumpHandler::ReleasedDumps WebRtcRtpDumpHandler::ReleaseDumps() {
189 DCHECK_CURRENTLY_ON(BrowserThread::IO);
190 DCHECK(ReadyToRelease());
192 base::FilePath incoming_dump, outgoing_dump;
194 if (incoming_state_ == STATE_STOPPED) {
195 DVLOG(2) << "Incoming RTP dumps released: " << incoming_dump_path_.value();
197 incoming_state_ = STATE_NONE;
198 incoming_dump = incoming_dump_path_;
201 if (outgoing_state_ == STATE_STOPPED) {
202 DVLOG(2) << "Outgoing RTP dumps released: " << outgoing_dump_path_.value();
204 outgoing_state_ = STATE_NONE;
205 outgoing_dump = outgoing_dump_path_;
207 return ReleasedDumps(incoming_dump, outgoing_dump);
210 void WebRtcRtpDumpHandler::OnRtpPacket(const uint8* packet_header,
211 size_t header_length,
212 size_t packet_length,
213 bool incoming) {
214 DCHECK_CURRENTLY_ON(BrowserThread::IO);
216 if ((incoming && incoming_state_ != STATE_STARTED) ||
217 (!incoming && outgoing_state_ != STATE_STARTED)) {
218 return;
221 dump_writer_->WriteRtpPacket(
222 packet_header, header_length, packet_length, incoming);
225 void WebRtcRtpDumpHandler::StopOngoingDumps(const base::Closure& callback) {
226 DCHECK_CURRENTLY_ON(BrowserThread::IO);
227 DCHECK(!callback.is_null());
229 // No ongoing dumps, return directly.
230 if ((incoming_state_ == STATE_NONE || incoming_state_ == STATE_STOPPED) &&
231 (outgoing_state_ == STATE_NONE || outgoing_state_ == STATE_STOPPED)) {
232 callback.Run();
233 return;
236 // If the FILE thread is working on stopping the dumps, wait for the FILE
237 // thread to return and check the states again.
238 if (incoming_state_ == STATE_STOPPING || outgoing_state_ == STATE_STOPPING) {
239 BrowserThread::PostTaskAndReply(
240 BrowserThread::FILE,
241 FROM_HERE,
242 base::Bind(&base::DoNothing),
243 base::Bind(&WebRtcRtpDumpHandler::StopOngoingDumps,
244 weak_ptr_factory_.GetWeakPtr(),
245 callback));
246 return;
249 // Either incoming or outgoing dump must be ongoing.
250 RtpDumpType type =
251 (incoming_state_ == STATE_STARTED)
252 ? (outgoing_state_ == STATE_STARTED ? RTP_DUMP_BOTH
253 : RTP_DUMP_INCOMING)
254 : RTP_DUMP_OUTGOING;
256 if (incoming_state_ == STATE_STARTED)
257 incoming_state_ = STATE_STOPPING;
259 if (outgoing_state_ == STATE_STARTED)
260 outgoing_state_ = STATE_STOPPING;
262 DVLOG(2) << "Stopping ongoing dumps: type = " << type;
264 dump_writer_->EndDump(type,
265 base::Bind(&WebRtcRtpDumpHandler::OnDumpEnded,
266 base::Unretained(this),
267 callback,
268 type));
271 void WebRtcRtpDumpHandler::SetDumpWriterForTesting(
272 scoped_ptr<WebRtcRtpDumpWriter> writer) {
273 DCHECK_CURRENTLY_ON(BrowserThread::IO);
275 dump_writer_ = writer.Pass();
276 ++g_ongoing_rtp_dumps;
278 incoming_dump_path_ = dump_dir_.AppendASCII("recv");
279 outgoing_dump_path_ = dump_dir_.AppendASCII("send");
282 void WebRtcRtpDumpHandler::OnMaxDumpSizeReached() {
283 DCHECK_CURRENTLY_ON(BrowserThread::IO);
285 RtpDumpType type =
286 (incoming_state_ == STATE_STARTED)
287 ? (outgoing_state_ == STATE_STARTED ? RTP_DUMP_BOTH
288 : RTP_DUMP_INCOMING)
289 : RTP_DUMP_OUTGOING;
290 StopDump(type, GenericDoneCallback());
293 void WebRtcRtpDumpHandler::OnDumpEnded(const base::Closure& callback,
294 RtpDumpType ended_type,
295 bool incoming_success,
296 bool outgoing_success) {
297 DCHECK_CURRENTLY_ON(BrowserThread::IO);
299 if (DumpTypeContainsIncoming(ended_type)) {
300 DCHECK_EQ(STATE_STOPPING, incoming_state_);
301 incoming_state_ = STATE_STOPPED;
303 if (!incoming_success) {
304 BrowserThread::PostTask(BrowserThread::FILE,
305 FROM_HERE,
306 base::Bind(base::IgnoreResult(&base::DeleteFile),
307 incoming_dump_path_,
308 false));
310 DVLOG(2) << "Deleted invalid incoming dump "
311 << incoming_dump_path_.value();
312 incoming_dump_path_.clear();
316 if (DumpTypeContainsOutgoing(ended_type)) {
317 DCHECK_EQ(STATE_STOPPING, outgoing_state_);
318 outgoing_state_ = STATE_STOPPED;
320 if (!outgoing_success) {
321 BrowserThread::PostTask(BrowserThread::FILE,
322 FROM_HERE,
323 base::Bind(base::IgnoreResult(&base::DeleteFile),
324 outgoing_dump_path_,
325 false));
327 DVLOG(2) << "Deleted invalid outgoing dump "
328 << outgoing_dump_path_.value();
329 outgoing_dump_path_.clear();
333 // Release the writer when it's no longer needed.
334 if (incoming_state_ != STATE_STOPPING && outgoing_state_ != STATE_STOPPING &&
335 incoming_state_ != STATE_STARTED && outgoing_state_ != STATE_STARTED) {
336 dump_writer_.reset();
337 --g_ongoing_rtp_dumps;
340 // This object might be deleted after running the callback.
341 if (!callback.is_null())
342 callback.Run();