Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(62)

Side by Side Diff: chrome/browser/media/webrtc_rtp_dump_handler.cc

Issue 264793017: Implements RTP header dumping. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 6 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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.
4
5 #include "chrome/browser/media/webrtc_rtp_dump_handler.h"
6
7 #include "base/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"
13
14 using content::BrowserThread;
15
16 namespace {
17
18 static const size_t kMaxOngoingRtpDumpsAllowed = 5;
19
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;
25
26 void FireGenericDoneCallback(
27 const WebRtcRtpDumpHandler::GenericDoneCallback& callback,
28 bool success,
29 const std::string& error_message) {
30 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
31 DCHECK(!callback.is_null());
32
33 content::BrowserThread::PostTask(
34 content::BrowserThread::UI,
35 FROM_HERE,
36 base::Bind(callback, success, error_message));
37 }
38
39 bool DumpTypeContainsIncoming(RtpDumpType type) {
40 return type == RTP_DUMP_INCOMING || type == RTP_DUMP_BOTH;
41 }
42
43 bool DumpTypeContainsOutgoing(RtpDumpType type) {
44 return type == RTP_DUMP_OUTGOING || type == RTP_DUMP_BOTH;
45 }
46
47 } // namespace
48
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(BrowserThread::CurrentlyOn(BrowserThread::IO));
55 }
56
57 WebRtcRtpDumpHandler::~WebRtcRtpDumpHandler() {
58 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
59
60 // Reset dump writer first to stop writing.
61 if (dump_writer_) {
62 --g_ongoing_rtp_dumps;
63 dump_writer_.reset();
64 }
65
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));
72 }
73
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));
80 }
81 }
82
83 bool WebRtcRtpDumpHandler::StartDump(RtpDumpType type,
84 std::string* error_message) {
85 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
86
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;
91 }
92
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 = "RTP dump already started.";
98 return false;
99 }
100
101 if (DumpTypeContainsIncoming(type))
102 incoming_state_ = STATE_STARTED;
103
104 if (DumpTypeContainsOutgoing(type))
105 outgoing_state_ = STATE_STARTED;
106
107 DVLOG(2) << "Start RTP dumping: type = " << type;
108
109 if (!dump_writer_) {
110 ++g_ongoing_rtp_dumps;
111
112 static const char kRecvDumpFilePrefix[] = "rtpdump_recv_";
113 static const char kSendDumpFilePrefix[] = "rtpdump_send_";
114 static const size_t kMaxDumpSize = 5 * 1024 * 1024; // 5MB
115
116 std::string dump_id = base::DoubleToString(base::Time::Now().ToDoubleT());
117 incoming_dump_path_ =
118 dump_dir_.AppendASCII(std::string(kRecvDumpFilePrefix) + dump_id)
119 .AddExtension(FILE_PATH_LITERAL(".gz"));
120
121 outgoing_dump_path_ =
122 dump_dir_.AppendASCII(std::string(kSendDumpFilePrefix) + dump_id)
123 .AddExtension(FILE_PATH_LITERAL(".gz"));
124
125 // WebRtcRtpDumpWriter does not support changing the dump path after it's
126 // created. So we assign both incoming and outgoing dump path even if only
127 // one type of dumping has been started.
128 // For "Unretained(this)", see comments StopDump.
129 dump_writer_.reset(new WebRtcRtpDumpWriter(
130 incoming_dump_path_,
131 outgoing_dump_path_,
132 kMaxDumpSize,
133 base::Bind(&WebRtcRtpDumpHandler::OnMaxDumpSizeReached,
134 base::Unretained(this))));
135 }
136
137 return true;
138 }
139
140 void WebRtcRtpDumpHandler::StopDump(RtpDumpType type,
141 const GenericDoneCallback& callback) {
142 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
143
144 // Returns an error if any type of dump specified by the caller cannot be
145 // stopped.
146 if ((DumpTypeContainsIncoming(type) && incoming_state_ != STATE_STARTED) ||
147 (DumpTypeContainsOutgoing(type) && outgoing_state_ != STATE_STARTED)) {
148 if (!callback.is_null()) {
149 FireGenericDoneCallback(
150 callback, false, "RTP dump not started or already stopped.");
151 }
152 return;
153 }
154
155 DVLOG(2) << "Stopping RTP dumping: type = " << type;
156
157 if (DumpTypeContainsIncoming(type))
158 incoming_state_ = STATE_STOPPING;
159
160 if (DumpTypeContainsOutgoing(type))
161 outgoing_state_ = STATE_STOPPING;
162
163 // Using "Unretained(this)" because the this object owns the writer and the
164 // writer is guaranteed to cancel the callback before it goes away. Same for
165 // the other posted tasks bound to the writer.
166 dump_writer_->EndDump(
167 type,
168 base::Bind(&WebRtcRtpDumpHandler::OnDumpEnded,
169 base::Unretained(this),
170 callback.is_null()
171 ? base::Closure()
172 : base::Bind(&FireGenericDoneCallback, callback, true, ""),
173 type));
174 }
175
176 WebRtcRtpDumpHandler::ReleasedDumps WebRtcRtpDumpHandler::ReleaseDumps() {
177 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
178
179 base::FilePath incoming_dump, outgoing_dump;
180
181 // All types of dumps must have been stopped, or one stopped and the other not
182 // started.
183 if (incoming_state_ == STATE_STARTED || incoming_state_ == STATE_STOPPING ||
184 outgoing_state_ == STATE_STARTED || outgoing_state_ == STATE_STOPPING ||
185 (incoming_state_ == STATE_NONE && outgoing_state_ == STATE_NONE)) {
186 DVLOG(2) << "ReleaseDumps called in invalid state: incoming_state = "
187 << incoming_state_ << ", outgoing_state = " << outgoing_state_;
188 return ReleasedDumps(incoming_dump, outgoing_dump);
189 }
190
191 if (incoming_state_ == STATE_STOPPED) {
192 DVLOG(2) << "Incoming RTP dumps released: " << incoming_dump_path_.value();
193
194 incoming_state_ = STATE_NONE;
195 incoming_dump = incoming_dump_path_;
196 }
197
198 if (outgoing_state_ == STATE_STOPPED) {
199 DVLOG(2) << "Outgoing RTP dumps released: " << outgoing_dump_path_.value();
200
201 outgoing_state_ = STATE_NONE;
202 outgoing_dump = outgoing_dump_path_;
203 }
204 return ReleasedDumps(incoming_dump, outgoing_dump);
205 }
206
207 void WebRtcRtpDumpHandler::OnRtpPacket(const uint8* packet_header,
208 size_t header_length,
209 size_t packet_length,
210 bool incoming) {
211 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
212
213 if ((incoming && incoming_state_ != STATE_STARTED) ||
214 (!incoming && outgoing_state_ != STATE_STARTED)) {
215 return;
216 }
217
218 dump_writer_->WriteRtpPacket(
219 packet_header, header_length, packet_length, incoming);
220 }
221
222 void WebRtcRtpDumpHandler::StopOngoingDumps(const base::Closure& callback) {
223 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
224 DCHECK(!callback.is_null());
225
226 // No ongoing dumps, return directly.
227 if ((incoming_state_ == STATE_NONE || incoming_state_ == STATE_STOPPED) &&
228 (outgoing_state_ == STATE_NONE || outgoing_state_ == STATE_STOPPED)) {
229 callback.Run();
230 return;
231 }
232
233 // If the FILE thread is working on stopping the dumps, wait for the FILE
234 // thread to return and check the states again.
235 if (incoming_state_ == STATE_STOPPING || outgoing_state_ == STATE_STOPPING) {
236 BrowserThread::PostTaskAndReply(
237 BrowserThread::FILE,
238 FROM_HERE,
239 base::Bind(&base::DoNothing),
240 base::Bind(&WebRtcRtpDumpHandler::StopOngoingDumps,
241 weak_ptr_factory_.GetWeakPtr(),
242 callback));
243 return;
244 }
245
246 // Either incoming or outgoing dump must be ongoing.
247 RtpDumpType type =
248 (incoming_state_ == STATE_STARTED)
249 ? (outgoing_state_ == STATE_STARTED ? RTP_DUMP_BOTH
250 : RTP_DUMP_INCOMING)
251 : RTP_DUMP_OUTGOING;
252
253 if (incoming_state_ == STATE_STARTED)
254 incoming_state_ = STATE_STOPPING;
255
256 if (outgoing_state_ == STATE_STARTED)
257 outgoing_state_ = STATE_STOPPING;
258
259 DVLOG(2) << "Stopping ongoing dumps: type = " << type;
260
261 dump_writer_->EndDump(type,
262 base::Bind(&WebRtcRtpDumpHandler::OnDumpEnded,
263 base::Unretained(this),
264 callback,
265 type));
266 }
267
268 void WebRtcRtpDumpHandler::SetDumpWriterForTesting(
269 scoped_ptr<WebRtcRtpDumpWriter> writer) {
270 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
271
272 dump_writer_ = writer.Pass();
273 ++g_ongoing_rtp_dumps;
274
275 incoming_dump_path_ = dump_dir_.AppendASCII("recv");
276 outgoing_dump_path_ = dump_dir_.AppendASCII("send");
277 }
278
279 void WebRtcRtpDumpHandler::OnMaxDumpSizeReached() {
280 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
281
282 RtpDumpType type =
283 (incoming_state_ == STATE_STARTED)
284 ? (outgoing_state_ == STATE_STARTED ? RTP_DUMP_BOTH
285 : RTP_DUMP_INCOMING)
286 : RTP_DUMP_OUTGOING;
287 StopDump(type, GenericDoneCallback());
288 }
289
290 void WebRtcRtpDumpHandler::OnDumpEnded(const base::Closure& callback,
291 RtpDumpType ended_type,
292 bool incoming_success,
293 bool outgoing_success) {
294 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
295
296 if (DumpTypeContainsIncoming(ended_type)) {
297 DCHECK_EQ(STATE_STOPPING, incoming_state_);
298 incoming_state_ = STATE_STOPPED;
299
300 if (!incoming_success) {
301 BrowserThread::PostTask(BrowserThread::FILE,
302 FROM_HERE,
303 base::Bind(base::IgnoreResult(&base::DeleteFile),
304 incoming_dump_path_,
305 false));
306
307 DVLOG(2) << "Deleted invalid incoming dump "
308 << incoming_dump_path_.value();
309 incoming_dump_path_.clear();
310 }
311 }
312
313 if (DumpTypeContainsOutgoing(ended_type)) {
314 DCHECK_EQ(STATE_STOPPING, outgoing_state_);
315 outgoing_state_ = STATE_STOPPED;
316
317 if (!outgoing_success) {
318 BrowserThread::PostTask(BrowserThread::FILE,
319 FROM_HERE,
320 base::Bind(base::IgnoreResult(&base::DeleteFile),
321 outgoing_dump_path_,
322 false));
323
324 DVLOG(2) << "Deleted invalid outgoing dump "
325 << outgoing_dump_path_.value();
326 outgoing_dump_path_.clear();
327 }
328 }
329
330 if (!callback.is_null())
331 callback.Run();
332
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;
338 }
339 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698