OLD | NEW |
(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 // |path| must be a copy of FilePath, not a reference. |
| 19 void DeleteFileHelper(base::FilePath path) { |
| 20 base::DeleteFile(path, false); |
| 21 } |
| 22 |
| 23 static const size_t kMaxOngoingRtpDumpsAllowed = 5; |
| 24 |
| 25 // The browser process wide total number of ongoing (i.e. started and not |
| 26 // released) RTP dumps. Incoming and outgoing in one WebRtcDumpHandler are |
| 27 // counted as one dump. |
| 28 // Must be accessed on the browser IO thread. |
| 29 static size_t g_ongoing_rtp_dumps = 0; |
| 30 |
| 31 } // namespace |
| 32 |
| 33 WebRtcRtpDumpHandler::WebRtcRtpDumpHandler(const base::FilePath& dump_dir) |
| 34 : dump_dir_(dump_dir), |
| 35 incoming_state_(STATE_NONE), |
| 36 outgoing_state_(STATE_NONE) { |
| 37 } |
| 38 |
| 39 WebRtcRtpDumpHandler::~WebRtcRtpDumpHandler() { |
| 40 if (!incoming_dump_path_.empty()) { |
| 41 BrowserThread::PostTask(BrowserThread::FILE, |
| 42 FROM_HERE, |
| 43 base::Bind(&DeleteFileHelper, incoming_dump_path_)); |
| 44 } |
| 45 |
| 46 if (!outgoing_dump_path_.empty()) { |
| 47 BrowserThread::PostTask(BrowserThread::FILE, |
| 48 FROM_HERE, |
| 49 base::Bind(&DeleteFileHelper, outgoing_dump_path_)); |
| 50 } |
| 51 |
| 52 if (dump_writer_) |
| 53 g_ongoing_rtp_dumps--; |
| 54 } |
| 55 |
| 56 bool WebRtcRtpDumpHandler::StartDump(const PacketType& type) { |
| 57 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 58 |
| 59 bool succeeded = false; |
| 60 |
| 61 if (type.incoming && incoming_state_ == STATE_NONE && |
| 62 outgoing_state_ <= STATE_STARTED) { |
| 63 incoming_state_ = STATE_STARTED; |
| 64 succeeded = true; |
| 65 } |
| 66 if (type.outgoing && outgoing_state_ == STATE_NONE && |
| 67 incoming_state_ <= STATE_STARTED) { |
| 68 outgoing_state_ = STATE_STARTED; |
| 69 succeeded = true; |
| 70 } |
| 71 |
| 72 if (!dump_writer_ && g_ongoing_rtp_dumps >= kMaxOngoingRtpDumpsAllowed) { |
| 73 DVLOG(2) << "Max RTP dump limit reached."; |
| 74 return false; |
| 75 } |
| 76 |
| 77 DVLOG(2) << "Start RTP dumping: incoming = " << type.incoming |
| 78 << ", outgoing = " << type.outgoing; |
| 79 |
| 80 if (succeeded && !dump_writer_) { |
| 81 g_ongoing_rtp_dumps++; |
| 82 |
| 83 static const char kRecvDumpFilePrefix[] = "rtpdump_recv_"; |
| 84 static const char kSendDumpFilePrefix[] = "rtpdump_send_"; |
| 85 static const char kDumpFileExtension[] = ".gz"; |
| 86 static const size_t kMaxDumpSize = 5 * 1024 * 1024; // 5MB |
| 87 |
| 88 std::string dump_id = base::DoubleToString(base::Time::Now().ToDoubleT()); |
| 89 incoming_dump_path_ = |
| 90 dump_dir_.AppendASCII(std::string(kRecvDumpFilePrefix) + dump_id) |
| 91 .AddExtension(FILE_PATH_LITERAL(kDumpFileExtension)); |
| 92 |
| 93 outgoing_dump_path_ = |
| 94 dump_dir_.AppendASCII(std::string(kSendDumpFilePrefix) + dump_id) |
| 95 .AddExtension(FILE_PATH_LITERAL(kDumpFileExtension)); |
| 96 |
| 97 // WebRtcRtpDumpWriter does not support changing the dump path after it's |
| 98 // created. So we assign both incoming and outgoing dump path even if only |
| 99 // one type of dumping has been started. |
| 100 dump_writer_.reset(new WebRtcRtpDumpWriter( |
| 101 incoming_dump_path_, |
| 102 outgoing_dump_path_, |
| 103 kMaxDumpSize, |
| 104 base::Bind(&WebRtcRtpDumpHandler::OnMaxDumpSizeReached, |
| 105 base::Unretained(this)))); |
| 106 } |
| 107 return succeeded; |
| 108 } |
| 109 |
| 110 bool WebRtcRtpDumpHandler::StopDump(const PacketType& type) { |
| 111 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 112 |
| 113 bool succeeded = false; |
| 114 if (type.incoming && incoming_state_ == STATE_STARTED) { |
| 115 incoming_state_ = STATE_STOPPED; |
| 116 succeeded = true; |
| 117 } |
| 118 if (type.outgoing && outgoing_state_ == STATE_STARTED) { |
| 119 outgoing_state_ = STATE_STOPPED; |
| 120 succeeded = true; |
| 121 } |
| 122 |
| 123 DVLOG(2) << "Stop RTP dumping: incoming = " << type.incoming |
| 124 << ", outgoing = " << type.outgoing; |
| 125 return succeeded; |
| 126 } |
| 127 |
| 128 bool WebRtcRtpDumpHandler::ReleaseDump(const ReleaseDumpCallback& callback) { |
| 129 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 130 |
| 131 // All types of dumps must have been stopped or not started. |
| 132 if (incoming_state_ == STATE_STARTED || incoming_state_ == STATE_RELEASING || |
| 133 outgoing_state_ == STATE_STARTED || outgoing_state_ == STATE_RELEASING || |
| 134 (incoming_state_ == STATE_NONE && outgoing_state_ == STATE_NONE) || |
| 135 !dump_writer_) |
| 136 return false; |
| 137 |
| 138 if (incoming_state_ == STATE_STOPPED) |
| 139 incoming_state_ = STATE_RELEASING; |
| 140 |
| 141 if (outgoing_state_ == STATE_STOPPED) |
| 142 outgoing_state_ = STATE_RELEASING; |
| 143 |
| 144 dump_writer_->EndDump(base::Bind( |
| 145 &WebRtcRtpDumpHandler::OnDumpEnded, base::Unretained(this), callback)); |
| 146 |
| 147 DVLOG(2) << "Releasing RTP dump."; |
| 148 |
| 149 return true; |
| 150 } |
| 151 |
| 152 void WebRtcRtpDumpHandler::OnRtpPacket(const uint8* packet_header, |
| 153 size_t header_length, |
| 154 size_t packet_length, |
| 155 bool incoming) { |
| 156 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 157 |
| 158 if ((incoming && incoming_state_ != STATE_STARTED) || |
| 159 (!incoming && outgoing_state_ != STATE_STARTED)) |
| 160 return; |
| 161 |
| 162 dump_writer_->WriteRtpPacket( |
| 163 packet_header, header_length, packet_length, incoming); |
| 164 } |
| 165 |
| 166 void WebRtcRtpDumpHandler::SetDumpWriterForTesting( |
| 167 scoped_ptr<WebRtcRtpDumpWriter> writer) { |
| 168 DCHECK(!dump_writer_.get()); |
| 169 dump_writer_ = writer.Pass(); |
| 170 g_ongoing_rtp_dumps++; |
| 171 } |
| 172 |
| 173 void WebRtcRtpDumpHandler::OnMaxDumpSizeReached() { |
| 174 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 175 |
| 176 PacketType type = {true, true}; |
| 177 StopDump(type); |
| 178 } |
| 179 |
| 180 void WebRtcRtpDumpHandler::OnDumpEnded(const ReleaseDumpCallback& callback, |
| 181 bool succeeded) { |
| 182 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 183 |
| 184 dump_writer_.reset(); |
| 185 g_ongoing_rtp_dumps--; |
| 186 |
| 187 ReleasedDumps dumps; |
| 188 if (succeeded) { |
| 189 dumps.incoming_dump_path = incoming_dump_path_; |
| 190 dumps.outgoing_dump_path = outgoing_dump_path_; |
| 191 callback.Run(dumps); |
| 192 |
| 193 DVLOG(2) << "RTP dump released: " << incoming_dump_path_.value() << ", " |
| 194 << outgoing_dump_path_.value(); |
| 195 } else { |
| 196 BrowserThread::PostTask(BrowserThread::FILE, |
| 197 FROM_HERE, |
| 198 base::Bind(&DeleteFileHelper, incoming_dump_path_)); |
| 199 |
| 200 BrowserThread::PostTask(BrowserThread::FILE, |
| 201 FROM_HERE, |
| 202 base::Bind(&DeleteFileHelper, outgoing_dump_path_)); |
| 203 |
| 204 // Call back with an empty list. |
| 205 callback.Run(dumps); |
| 206 DVLOG(2) << "Releasing RTP dump failed."; |
| 207 } |
| 208 |
| 209 incoming_state_ = STATE_NONE; |
| 210 outgoing_state_ = STATE_NONE; |
| 211 incoming_dump_path_.clear(); |
| 212 outgoing_dump_path_.clear(); |
| 213 } |
OLD | NEW |