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

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 using content::RtpDumpType;
16 using content::RTP_DUMP_BOTH;
17 using content::RTP_DUMP_INCOMING;
18 using content::RTP_DUMP_OUTGOING;
19
20 namespace {
21
22 void DeleteFileHelper(const base::FilePath& path) {
23 base::DeleteFile(path, false);
24 }
25
26 static const size_t kMaxOngoingRtpDumpsAllowed = 5;
27
28 // The browser process wide total number of ongoing (i.e. started and not
29 // released) RTP dumps. Incoming and outgoing in one WebRtcDumpHandler are
30 // counted as one dump.
31 // Must be accessed on the browser IO thread.
32 static size_t g_ongoing_rtp_dumps = 0;
33
34 void FireGenericDoneCallback(
35 const WebRtcRtpDumpHandler::GenericDoneCallback& callback,
36 bool success,
37 const std::string& error_message) {
38 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
39 DCHECK(!callback.is_null());
40
41 content::BrowserThread::PostTask(
42 content::BrowserThread::UI,
43 FROM_HERE,
44 base::Bind(callback, success, error_message));
45 }
46
47 bool DumpTypeContainsIncoming(RtpDumpType type) {
48 return type == RTP_DUMP_INCOMING || type == RTP_DUMP_BOTH;
49 }
50
51 bool DumpTypeContainsOutgoing(RtpDumpType type) {
52 return type == RTP_DUMP_OUTGOING || type == RTP_DUMP_BOTH;
53 }
54
55 } // namespace
56
57 WebRtcRtpDumpHandler::WebRtcRtpDumpHandler(const base::FilePath& dump_dir)
58 : dump_dir_(dump_dir),
59 incoming_state_(STATE_NONE),
60 outgoing_state_(STATE_NONE) {
61 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
62 }
63
64 WebRtcRtpDumpHandler::~WebRtcRtpDumpHandler() {
65 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
66
67 // Reset dump writer first to stop writing.
68 if (dump_writer_) {
69 --g_ongoing_rtp_dumps;
70 dump_writer_.reset();
71 }
72
73 if (incoming_state_ != STATE_NONE && !incoming_dump_path_.empty()) {
74 BrowserThread::PostTask(BrowserThread::FILE,
75 FROM_HERE,
76 base::Bind(&DeleteFileHelper, incoming_dump_path_));
77 }
78
79 if (outgoing_state_ != STATE_NONE && !outgoing_dump_path_.empty()) {
80 BrowserThread::PostTask(BrowserThread::FILE,
81 FROM_HERE,
82 base::Bind(&DeleteFileHelper, outgoing_dump_path_));
83 }
84 }
85
86 bool WebRtcRtpDumpHandler::StartDump(RtpDumpType type,
87 std::string* error_message) {
88 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
89
90 if (!dump_writer_ && g_ongoing_rtp_dumps >= kMaxOngoingRtpDumpsAllowed) {
91 *error_message = "Max RTP dump limit reached.";
92 DVLOG(2) << *error_message;
93 return false;
94 }
95
96 // Returns an error if any type of dump specified by the caller cannot be
97 // started.
98 if ((DumpTypeContainsIncoming(type) && incoming_state_ != STATE_NONE) ||
99 (DumpTypeContainsOutgoing(type) && outgoing_state_ != STATE_NONE)) {
100 *error_message = "RTP dump already started.";
101 return false;
102 }
103
104 if (DumpTypeContainsIncoming(type))
105 incoming_state_ = STATE_STARTED;
106
107 if (DumpTypeContainsOutgoing(type))
108 outgoing_state_ = STATE_STARTED;
109
110 DVLOG(2) << "Start RTP dumping: type = " << type;
111
112 if (!dump_writer_) {
113 ++g_ongoing_rtp_dumps;
114
115 static const char kRecvDumpFilePrefix[] = "rtpdump_recv_";
116 static const char kSendDumpFilePrefix[] = "rtpdump_send_";
117 static const size_t kMaxDumpSize = 5 * 1024 * 1024; // 5MB
118
119 std::string dump_id = base::DoubleToString(base::Time::Now().ToDoubleT());
120 incoming_dump_path_ =
121 dump_dir_.AppendASCII(std::string(kRecvDumpFilePrefix) + dump_id)
122 .AddExtension(FILE_PATH_LITERAL(".gz"));
123
124 outgoing_dump_path_ =
125 dump_dir_.AppendASCII(std::string(kSendDumpFilePrefix) + dump_id)
126 .AddExtension(FILE_PATH_LITERAL(".gz"));
127
128 // WebRtcRtpDumpWriter does not support changing the dump path after it's
129 // created. So we assign both incoming and outgoing dump path even if only
130 // one type of dumping has been started.
131 // For "Unretained(this)", see comments StopDump.
132 dump_writer_.reset(new WebRtcRtpDumpWriter(
133 incoming_dump_path_,
134 outgoing_dump_path_,
135 kMaxDumpSize,
136 base::Bind(&WebRtcRtpDumpHandler::OnMaxDumpSizeReached,
137 base::Unretained(this))));
138 }
139
140 return true;
141 }
142
143 void WebRtcRtpDumpHandler::StopDump(RtpDumpType type,
144 const GenericDoneCallback& callback) {
145 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
146
147 // Returns an error if any type of dump specified by the caller cannot be
148 // stopped.
149 if ((DumpTypeContainsIncoming(type) && incoming_state_ != STATE_STARTED) ||
150 (DumpTypeContainsOutgoing(type) && outgoing_state_ != STATE_STARTED)) {
151 if (!callback.is_null()) {
152 FireGenericDoneCallback(
153 callback, false, "RTP dump not started or already stopped.");
154 }
155 return;
156 }
157
158 DVLOG(2) << "Stopping RTP dumping: type = " << type;
159
160 if (DumpTypeContainsIncoming(type))
161 incoming_state_ = STATE_STOPPING;
162
163 if (DumpTypeContainsOutgoing(type))
164 outgoing_state_ = STATE_STOPPING;
165
166 // Using "Unretained(this)" because the this object owns the writer and the
167 // writer is guaranteed to cancel the callback before it goes away. Same for
168 // the other posted tasks bound to the writer.
169 dump_writer_->EndDump(type,
170 base::Bind(&WebRtcRtpDumpHandler::OnDumpEnded,
171 base::Unretained(this),
172 callback,
173 type));
174 }
175
176 WebRtcRtpDumpHandler::ReleasedDumps WebRtcRtpDumpHandler::ReleaseDumps() {
177 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
178
179 ReleasedDumps result;
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 result;
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 result.incoming_dump_path = 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 result.outgoing_dump_path = outgoing_dump_path_;
203 }
204 return result;
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 dump_writer_->WriteRtpPacket(
218 packet_header, header_length, packet_length, incoming);
219 }
220
221 void WebRtcRtpDumpHandler::SetDumpWriterForTesting(
222 scoped_ptr<WebRtcRtpDumpWriter> writer) {
223 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
224
225 dump_writer_ = writer.Pass();
226 g_ongoing_rtp_dumps++;
227
228 incoming_dump_path_ = dump_dir_.AppendASCII("recv");
229 outgoing_dump_path_ = dump_dir_.AppendASCII("send");
230 }
231
232 void WebRtcRtpDumpHandler::OnMaxDumpSizeReached() {
233 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
234
235 RtpDumpType type =
236 (incoming_state_ == STATE_STARTED)
237 ? (outgoing_state_ == STATE_STARTED ? RTP_DUMP_BOTH
238 : RTP_DUMP_INCOMING)
239 : RTP_DUMP_OUTGOING;
240 StopDump(type, GenericDoneCallback());
241 }
242
243 void WebRtcRtpDumpHandler::OnDumpEnded(const GenericDoneCallback& callback,
244 RtpDumpType ended_type,
245 bool incoming_success,
246 bool outgoing_success) {
247 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
248
249 if (DumpTypeContainsIncoming(ended_type)) {
250 DCHECK_EQ(STATE_STOPPING, incoming_state_);
251 incoming_state_ = STATE_STOPPED;
252
253 if (!incoming_success) {
254 BrowserThread::PostTask(
255 BrowserThread::FILE,
256 FROM_HERE,
257 base::Bind(&DeleteFileHelper, incoming_dump_path_));
258
259 DVLOG(2) << "Deleted invalid incoming dump "
260 << incoming_dump_path_.value();
261 incoming_dump_path_.clear();
262 }
263 }
264
265 if (DumpTypeContainsOutgoing(ended_type)) {
266 DCHECK_EQ(STATE_STOPPING, outgoing_state_);
267 outgoing_state_ = STATE_STOPPED;
268
269 if (!outgoing_success) {
270 BrowserThread::PostTask(
271 BrowserThread::FILE,
272 FROM_HERE,
273 base::Bind(&DeleteFileHelper, outgoing_dump_path_));
274
275 DVLOG(2) << "Deleted invalid outgoing dump "
276 << outgoing_dump_path_.value();
277 outgoing_dump_path_.clear();
278 }
279 }
280
281 if (!callback.is_null())
282 FireGenericDoneCallback(callback, true, "");
283
284 // Release the writer when it's no longer needed.
285 if (incoming_state_ != STATE_STOPPING && outgoing_state_ != STATE_STOPPING &&
286 incoming_state_ != STATE_STARTED && outgoing_state_ != STATE_STARTED) {
287 dump_writer_.reset();
288 g_ongoing_rtp_dumps--;
289 }
290 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698