| OLD | NEW |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/media/webrtc_rtp_dump_writer.h" | 5 #include "chrome/browser/media/webrtc_rtp_dump_writer.h" |
| 6 | 6 |
| 7 #include <string.h> |
| 8 |
| 7 #include "base/big_endian.h" | 9 #include "base/big_endian.h" |
| 8 #include "base/files/file_util.h" | 10 #include "base/files/file_util.h" |
| 9 #include "base/logging.h" | 11 #include "base/logging.h" |
| 12 #include "base/macros.h" |
| 10 #include "content/public/browser/browser_thread.h" | 13 #include "content/public/browser/browser_thread.h" |
| 11 #include "third_party/zlib/zlib.h" | 14 #include "third_party/zlib/zlib.h" |
| 12 | 15 |
| 13 using content::BrowserThread; | 16 using content::BrowserThread; |
| 14 | 17 |
| 15 namespace { | 18 namespace { |
| 16 | 19 |
| 17 static const size_t kMinimumGzipOutputBufferSize = 256; // In bytes. | 20 static const size_t kMinimumGzipOutputBufferSize = 256; // In bytes. |
| 18 | 21 |
| 19 const unsigned char kRtpDumpFileHeaderFirstLine[] = "#!rtpplay1.0 0.0.0.0/0\n"; | 22 const unsigned char kRtpDumpFileHeaderFirstLine[] = "#!rtpplay1.0 0.0.0.0/0\n"; |
| 20 static const size_t kRtpDumpFileHeaderSize = 16; // In bytes. | 23 static const size_t kRtpDumpFileHeaderSize = 16; // In bytes. |
| 21 | 24 |
| 22 // A helper for writing the header of the dump file. | 25 // A helper for writing the header of the dump file. |
| 23 void WriteRtpDumpFileHeaderBigEndian(base::TimeTicks start, | 26 void WriteRtpDumpFileHeaderBigEndian(base::TimeTicks start, |
| 24 std::vector<uint8>* output) { | 27 std::vector<uint8_t>* output) { |
| 25 size_t buffer_start_pos = output->size(); | 28 size_t buffer_start_pos = output->size(); |
| 26 output->resize(output->size() + kRtpDumpFileHeaderSize); | 29 output->resize(output->size() + kRtpDumpFileHeaderSize); |
| 27 | 30 |
| 28 char* buffer = reinterpret_cast<char*>(&(*output)[buffer_start_pos]); | 31 char* buffer = reinterpret_cast<char*>(&(*output)[buffer_start_pos]); |
| 29 | 32 |
| 30 base::TimeDelta delta = start - base::TimeTicks(); | 33 base::TimeDelta delta = start - base::TimeTicks(); |
| 31 uint32 start_sec = delta.InSeconds(); | 34 uint32_t start_sec = delta.InSeconds(); |
| 32 base::WriteBigEndian(buffer, start_sec); | 35 base::WriteBigEndian(buffer, start_sec); |
| 33 buffer += sizeof(start_sec); | 36 buffer += sizeof(start_sec); |
| 34 | 37 |
| 35 uint32 start_usec = | 38 uint32_t start_usec = |
| 36 delta.InMilliseconds() * base::Time::kMicrosecondsPerMillisecond; | 39 delta.InMilliseconds() * base::Time::kMicrosecondsPerMillisecond; |
| 37 base::WriteBigEndian(buffer, start_usec); | 40 base::WriteBigEndian(buffer, start_usec); |
| 38 buffer += sizeof(start_usec); | 41 buffer += sizeof(start_usec); |
| 39 | 42 |
| 40 // Network source, always 0. | 43 // Network source, always 0. |
| 41 base::WriteBigEndian(buffer, uint32(0)); | 44 base::WriteBigEndian(buffer, uint32_t(0)); |
| 42 buffer += sizeof(uint32); | 45 buffer += sizeof(uint32_t); |
| 43 | 46 |
| 44 // UDP port, always 0. | 47 // UDP port, always 0. |
| 45 base::WriteBigEndian(buffer, uint16(0)); | 48 base::WriteBigEndian(buffer, uint16_t(0)); |
| 46 buffer += sizeof(uint16); | 49 buffer += sizeof(uint16_t); |
| 47 | 50 |
| 48 // 2 bytes padding. | 51 // 2 bytes padding. |
| 49 base::WriteBigEndian(buffer, uint16(0)); | 52 base::WriteBigEndian(buffer, uint16_t(0)); |
| 50 } | 53 } |
| 51 | 54 |
| 52 // The header size for each packet dump. | 55 // The header size for each packet dump. |
| 53 static const size_t kPacketDumpHeaderSize = 8; // In bytes. | 56 static const size_t kPacketDumpHeaderSize = 8; // In bytes. |
| 54 | 57 |
| 55 // A helper for writing the header for each packet dump. | 58 // A helper for writing the header for each packet dump. |
| 56 // |start| is the time when the recording is started. | 59 // |start| is the time when the recording is started. |
| 57 // |dump_length| is the length of the packet dump including this header. | 60 // |dump_length| is the length of the packet dump including this header. |
| 58 // |packet_length| is the length of the RTP packet header. | 61 // |packet_length| is the length of the RTP packet header. |
| 59 void WritePacketDumpHeaderBigEndian(const base::TimeTicks& start, | 62 void WritePacketDumpHeaderBigEndian(const base::TimeTicks& start, |
| 60 uint16 dump_length, | 63 uint16_t dump_length, |
| 61 uint16 packet_length, | 64 uint16_t packet_length, |
| 62 std::vector<uint8>* output) { | 65 std::vector<uint8_t>* output) { |
| 63 size_t buffer_start_pos = output->size(); | 66 size_t buffer_start_pos = output->size(); |
| 64 output->resize(output->size() + kPacketDumpHeaderSize); | 67 output->resize(output->size() + kPacketDumpHeaderSize); |
| 65 | 68 |
| 66 char* buffer = reinterpret_cast<char*>(&(*output)[buffer_start_pos]); | 69 char* buffer = reinterpret_cast<char*>(&(*output)[buffer_start_pos]); |
| 67 | 70 |
| 68 base::WriteBigEndian(buffer, dump_length); | 71 base::WriteBigEndian(buffer, dump_length); |
| 69 buffer += sizeof(dump_length); | 72 buffer += sizeof(dump_length); |
| 70 | 73 |
| 71 base::WriteBigEndian(buffer, packet_length); | 74 base::WriteBigEndian(buffer, packet_length); |
| 72 buffer += sizeof(packet_length); | 75 buffer += sizeof(packet_length); |
| 73 | 76 |
| 74 uint32 elapsed = | 77 uint32_t elapsed = |
| 75 static_cast<uint32>((base::TimeTicks::Now() - start).InMilliseconds()); | 78 static_cast<uint32_t>((base::TimeTicks::Now() - start).InMilliseconds()); |
| 76 base::WriteBigEndian(buffer, elapsed); | 79 base::WriteBigEndian(buffer, elapsed); |
| 77 } | 80 } |
| 78 | 81 |
| 79 // Append |src_len| bytes from |src| to |dest|. | 82 // Append |src_len| bytes from |src| to |dest|. |
| 80 void AppendToBuffer(const uint8* src, | 83 void AppendToBuffer(const uint8_t* src, |
| 81 size_t src_len, | 84 size_t src_len, |
| 82 std::vector<uint8>* dest) { | 85 std::vector<uint8_t>* dest) { |
| 83 size_t old_dest_size = dest->size(); | 86 size_t old_dest_size = dest->size(); |
| 84 dest->resize(old_dest_size + src_len); | 87 dest->resize(old_dest_size + src_len); |
| 85 memcpy(&(*dest)[old_dest_size], src, src_len); | 88 memcpy(&(*dest)[old_dest_size], src, src_len); |
| 86 } | 89 } |
| 87 | 90 |
| 88 } // namespace | 91 } // namespace |
| 89 | 92 |
| 90 // This class is running on the FILE thread for compressing and writing the | 93 // This class is running on the FILE thread for compressing and writing the |
| 91 // dump buffer to disk. | 94 // dump buffer to disk. |
| 92 class WebRtcRtpDumpWriter::FileThreadWorker { | 95 class WebRtcRtpDumpWriter::FileThreadWorker { |
| (...skipping 18 matching lines...) Expand all Loading... |
| 111 DCHECK(thread_checker_.CalledOnValidThread()); | 114 DCHECK(thread_checker_.CalledOnValidThread()); |
| 112 | 115 |
| 113 // Makes sure all allocations are freed. | 116 // Makes sure all allocations are freed. |
| 114 deflateEnd(&stream_); | 117 deflateEnd(&stream_); |
| 115 } | 118 } |
| 116 | 119 |
| 117 // Compresses the data in |buffer| and write to the dump file. If |end_stream| | 120 // Compresses the data in |buffer| and write to the dump file. If |end_stream| |
| 118 // is true, the compression stream will be ended and the dump file cannot be | 121 // is true, the compression stream will be ended and the dump file cannot be |
| 119 // written to any more. | 122 // written to any more. |
| 120 void CompressAndWriteToFileOnFileThread( | 123 void CompressAndWriteToFileOnFileThread( |
| 121 scoped_ptr<std::vector<uint8> > buffer, | 124 scoped_ptr<std::vector<uint8_t>> buffer, |
| 122 bool end_stream, | 125 bool end_stream, |
| 123 FlushResult* result, | 126 FlushResult* result, |
| 124 size_t* bytes_written) { | 127 size_t* bytes_written) { |
| 125 DCHECK(thread_checker_.CalledOnValidThread()); | 128 DCHECK(thread_checker_.CalledOnValidThread()); |
| 126 | 129 |
| 127 // This is called either when the in-memory buffer is full or the dump | 130 // This is called either when the in-memory buffer is full or the dump |
| 128 // should be ended. | 131 // should be ended. |
| 129 DCHECK(!buffer->empty() || end_stream); | 132 DCHECK(!buffer->empty() || end_stream); |
| 130 | 133 |
| 131 *result = FLUSH_RESULT_SUCCESS; | 134 *result = FLUSH_RESULT_SUCCESS; |
| 132 *bytes_written = 0; | 135 *bytes_written = 0; |
| 133 | 136 |
| 134 // There may be nothing to compress/write if there is no RTP packet since | 137 // There may be nothing to compress/write if there is no RTP packet since |
| 135 // the last flush. | 138 // the last flush. |
| 136 if (!buffer->empty()) { | 139 if (!buffer->empty()) { |
| 137 *bytes_written = CompressAndWriteBufferToFile(buffer.get(), result); | 140 *bytes_written = CompressAndWriteBufferToFile(buffer.get(), result); |
| 138 } else if (!base::PathExists(dump_path_)) { | 141 } else if (!base::PathExists(dump_path_)) { |
| 139 // If the dump does not exist, it means there is no RTP packet recorded. | 142 // If the dump does not exist, it means there is no RTP packet recorded. |
| 140 // Return FLUSH_RESULT_NO_DATA to indicate no dump file created. | 143 // Return FLUSH_RESULT_NO_DATA to indicate no dump file created. |
| 141 *result = FLUSH_RESULT_NO_DATA; | 144 *result = FLUSH_RESULT_NO_DATA; |
| 142 } | 145 } |
| 143 | 146 |
| 144 if (end_stream && !EndDumpFile()) | 147 if (end_stream && !EndDumpFile()) |
| 145 *result = FLUSH_RESULT_FAILURE; | 148 *result = FLUSH_RESULT_FAILURE; |
| 146 } | 149 } |
| 147 | 150 |
| 148 private: | 151 private: |
| 149 // Helper for CompressAndWriteToFileOnFileThread to compress and write one | 152 // Helper for CompressAndWriteToFileOnFileThread to compress and write one |
| 150 // dump. | 153 // dump. |
| 151 size_t CompressAndWriteBufferToFile(std::vector<uint8>* buffer, | 154 size_t CompressAndWriteBufferToFile(std::vector<uint8_t>* buffer, |
| 152 FlushResult* result) { | 155 FlushResult* result) { |
| 153 DCHECK(thread_checker_.CalledOnValidThread()); | 156 DCHECK(thread_checker_.CalledOnValidThread()); |
| 154 DCHECK(buffer->size()); | 157 DCHECK(buffer->size()); |
| 155 | 158 |
| 156 *result = FLUSH_RESULT_SUCCESS; | 159 *result = FLUSH_RESULT_SUCCESS; |
| 157 | 160 |
| 158 std::vector<uint8> compressed_buffer; | 161 std::vector<uint8_t> compressed_buffer; |
| 159 if (!Compress(buffer, &compressed_buffer)) { | 162 if (!Compress(buffer, &compressed_buffer)) { |
| 160 DVLOG(2) << "Compressing buffer failed."; | 163 DVLOG(2) << "Compressing buffer failed."; |
| 161 *result = FLUSH_RESULT_FAILURE; | 164 *result = FLUSH_RESULT_FAILURE; |
| 162 return 0; | 165 return 0; |
| 163 } | 166 } |
| 164 | 167 |
| 165 int bytes_written = -1; | 168 int bytes_written = -1; |
| 166 | 169 |
| 167 if (base::PathExists(dump_path_)) { | 170 if (base::PathExists(dump_path_)) { |
| 168 bytes_written = | 171 bytes_written = |
| (...skipping 13 matching lines...) Expand all Loading... |
| 182 DVLOG(2) << "Writing file failed: " << dump_path_.value(); | 185 DVLOG(2) << "Writing file failed: " << dump_path_.value(); |
| 183 *result = FLUSH_RESULT_FAILURE; | 186 *result = FLUSH_RESULT_FAILURE; |
| 184 return 0; | 187 return 0; |
| 185 } | 188 } |
| 186 | 189 |
| 187 DCHECK_EQ(static_cast<size_t>(bytes_written), compressed_buffer.size()); | 190 DCHECK_EQ(static_cast<size_t>(bytes_written), compressed_buffer.size()); |
| 188 return bytes_written; | 191 return bytes_written; |
| 189 } | 192 } |
| 190 | 193 |
| 191 // Compresses |input| into |output|. | 194 // Compresses |input| into |output|. |
| 192 bool Compress(std::vector<uint8>* input, std::vector<uint8>* output) { | 195 bool Compress(std::vector<uint8_t>* input, std::vector<uint8_t>* output) { |
| 193 DCHECK(thread_checker_.CalledOnValidThread()); | 196 DCHECK(thread_checker_.CalledOnValidThread()); |
| 194 int result = Z_OK; | 197 int result = Z_OK; |
| 195 | 198 |
| 196 output->resize(std::max(kMinimumGzipOutputBufferSize, input->size())); | 199 output->resize(std::max(kMinimumGzipOutputBufferSize, input->size())); |
| 197 | 200 |
| 198 stream_.next_in = &(*input)[0]; | 201 stream_.next_in = &(*input)[0]; |
| 199 stream_.avail_in = input->size(); | 202 stream_.avail_in = input->size(); |
| 200 stream_.next_out = &(*output)[0]; | 203 stream_.next_out = &(*output)[0]; |
| 201 stream_.avail_out = output->size(); | 204 stream_.avail_out = output->size(); |
| 202 | 205 |
| 203 result = deflate(&stream_, Z_SYNC_FLUSH); | 206 result = deflate(&stream_, Z_SYNC_FLUSH); |
| 204 DCHECK_EQ(Z_OK, result); | 207 DCHECK_EQ(Z_OK, result); |
| 205 DCHECK_EQ(0U, stream_.avail_in); | 208 DCHECK_EQ(0U, stream_.avail_in); |
| 206 | 209 |
| 207 output->resize(output->size() - stream_.avail_out); | 210 output->resize(output->size() - stream_.avail_out); |
| 208 | 211 |
| 209 stream_.next_in = NULL; | 212 stream_.next_in = NULL; |
| 210 stream_.next_out = NULL; | 213 stream_.next_out = NULL; |
| 211 stream_.avail_out = 0; | 214 stream_.avail_out = 0; |
| 212 return true; | 215 return true; |
| 213 } | 216 } |
| 214 | 217 |
| 215 // Ends the compression stream and completes the dump file. | 218 // Ends the compression stream and completes the dump file. |
| 216 bool EndDumpFile() { | 219 bool EndDumpFile() { |
| 217 DCHECK(thread_checker_.CalledOnValidThread()); | 220 DCHECK(thread_checker_.CalledOnValidThread()); |
| 218 | 221 |
| 219 std::vector<uint8> output_buffer; | 222 std::vector<uint8_t> output_buffer; |
| 220 output_buffer.resize(kMinimumGzipOutputBufferSize); | 223 output_buffer.resize(kMinimumGzipOutputBufferSize); |
| 221 | 224 |
| 222 stream_.next_in = NULL; | 225 stream_.next_in = NULL; |
| 223 stream_.avail_in = 0; | 226 stream_.avail_in = 0; |
| 224 stream_.next_out = &output_buffer[0]; | 227 stream_.next_out = &output_buffer[0]; |
| 225 stream_.avail_out = output_buffer.size(); | 228 stream_.avail_out = output_buffer.size(); |
| 226 | 229 |
| 227 int result = deflate(&stream_, Z_FINISH); | 230 int result = deflate(&stream_, Z_FINISH); |
| 228 DCHECK_EQ(Z_STREAM_END, result); | 231 DCHECK_EQ(Z_STREAM_END, result); |
| 229 | 232 |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 267 | 270 |
| 268 bool success = BrowserThread::DeleteSoon( | 271 bool success = BrowserThread::DeleteSoon( |
| 269 BrowserThread::FILE, FROM_HERE, incoming_file_thread_worker_.release()); | 272 BrowserThread::FILE, FROM_HERE, incoming_file_thread_worker_.release()); |
| 270 DCHECK(success); | 273 DCHECK(success); |
| 271 | 274 |
| 272 success = BrowserThread::DeleteSoon( | 275 success = BrowserThread::DeleteSoon( |
| 273 BrowserThread::FILE, FROM_HERE, outgoing_file_thread_worker_.release()); | 276 BrowserThread::FILE, FROM_HERE, outgoing_file_thread_worker_.release()); |
| 274 DCHECK(success); | 277 DCHECK(success); |
| 275 } | 278 } |
| 276 | 279 |
| 277 void WebRtcRtpDumpWriter::WriteRtpPacket(const uint8* packet_header, | 280 void WebRtcRtpDumpWriter::WriteRtpPacket(const uint8_t* packet_header, |
| 278 size_t header_length, | 281 size_t header_length, |
| 279 size_t packet_length, | 282 size_t packet_length, |
| 280 bool incoming) { | 283 bool incoming) { |
| 281 DCHECK(thread_checker_.CalledOnValidThread()); | 284 DCHECK(thread_checker_.CalledOnValidThread()); |
| 282 | 285 |
| 283 static const size_t kMaxInMemoryBufferSize = 65536; | 286 static const size_t kMaxInMemoryBufferSize = 65536; |
| 284 | 287 |
| 285 std::vector<uint8>* dest_buffer = | 288 std::vector<uint8_t>* dest_buffer = |
| 286 incoming ? &incoming_buffer_ : &outgoing_buffer_; | 289 incoming ? &incoming_buffer_ : &outgoing_buffer_; |
| 287 | 290 |
| 288 // We use the capacity of the buffer to indicate if the buffer has been | 291 // We use the capacity of the buffer to indicate if the buffer has been |
| 289 // initialized and if the dump file header has been created. | 292 // initialized and if the dump file header has been created. |
| 290 if (!dest_buffer->capacity()) { | 293 if (!dest_buffer->capacity()) { |
| 291 dest_buffer->reserve(std::min(kMaxInMemoryBufferSize, max_dump_size_)); | 294 dest_buffer->reserve(std::min(kMaxInMemoryBufferSize, max_dump_size_)); |
| 292 | 295 |
| 293 start_time_ = base::TimeTicks::Now(); | 296 start_time_ = base::TimeTicks::Now(); |
| 294 | 297 |
| 295 // Writes the dump file header. | 298 // Writes the dump file header. |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 346 } | 349 } |
| 347 | 350 |
| 348 WebRtcRtpDumpWriter::EndDumpContext::~EndDumpContext() { | 351 WebRtcRtpDumpWriter::EndDumpContext::~EndDumpContext() { |
| 349 } | 352 } |
| 350 | 353 |
| 351 void WebRtcRtpDumpWriter::FlushBuffer(bool incoming, | 354 void WebRtcRtpDumpWriter::FlushBuffer(bool incoming, |
| 352 bool end_stream, | 355 bool end_stream, |
| 353 const FlushDoneCallback& callback) { | 356 const FlushDoneCallback& callback) { |
| 354 DCHECK(thread_checker_.CalledOnValidThread()); | 357 DCHECK(thread_checker_.CalledOnValidThread()); |
| 355 | 358 |
| 356 scoped_ptr<std::vector<uint8> > new_buffer(new std::vector<uint8>()); | 359 scoped_ptr<std::vector<uint8_t>> new_buffer(new std::vector<uint8_t>()); |
| 357 | 360 |
| 358 if (incoming) { | 361 if (incoming) { |
| 359 new_buffer->reserve(incoming_buffer_.capacity()); | 362 new_buffer->reserve(incoming_buffer_.capacity()); |
| 360 new_buffer->swap(incoming_buffer_); | 363 new_buffer->swap(incoming_buffer_); |
| 361 } else { | 364 } else { |
| 362 new_buffer->reserve(outgoing_buffer_.capacity()); | 365 new_buffer->reserve(outgoing_buffer_.capacity()); |
| 363 new_buffer->swap(outgoing_buffer_); | 366 new_buffer->swap(outgoing_buffer_); |
| 364 } | 367 } |
| 365 | 368 |
| 366 scoped_ptr<FlushResult> result(new FlushResult(FLUSH_RESULT_FAILURE)); | 369 scoped_ptr<FlushResult> result(new FlushResult(FLUSH_RESULT_FAILURE)); |
| (...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 437 base::Bind(&WebRtcRtpDumpWriter::OnDumpEnded, | 440 base::Bind(&WebRtcRtpDumpWriter::OnDumpEnded, |
| 438 weak_ptr_factory_.GetWeakPtr(), | 441 weak_ptr_factory_.GetWeakPtr(), |
| 439 context, | 442 context, |
| 440 false)); | 443 false)); |
| 441 return; | 444 return; |
| 442 } | 445 } |
| 443 | 446 |
| 444 // This object might be deleted after running the callback. | 447 // This object might be deleted after running the callback. |
| 445 context.callback.Run(context.incoming_succeeded, context.outgoing_succeeded); | 448 context.callback.Run(context.incoming_succeeded, context.outgoing_succeeded); |
| 446 } | 449 } |
| OLD | NEW |