Chromium Code Reviews| 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_writer.h" | |
| 6 | |
| 7 #include "base/files/file.h" | |
| 8 #include "base/logging.h" | |
| 9 #include "content/public/browser/browser_thread.h" | |
| 10 | |
| 11 using content::BrowserThread; | |
| 12 | |
| 13 namespace { | |
| 14 | |
| 15 // The header of the dump file. | |
| 16 struct RtpDumpFileHeader { | |
| 17 static const unsigned char kFirstLine[]; | |
| 18 | |
| 19 explicit RtpDumpFileHeader(const base::TimeTicks& start) | |
| 20 : start_sec(0), | |
| 21 start_usec(0), | |
| 22 source(0), | |
| 23 port(0), | |
| 24 padding(0) { | |
| 25 base::TimeDelta interval(start - base::TimeTicks()); | |
| 26 start_sec = interval.InSeconds(); | |
| 27 start_usec = | |
| 28 interval.InMilliseconds() * base::Time::kMicrosecondsPerMillisecond; | |
| 29 } | |
| 30 | |
| 31 uint32 start_sec; // start of recording, the seconds part. | |
| 32 uint32 start_usec; // start of recording, the microseconds part. | |
| 33 uint32 source; // network source (multicast address). Always 0. | |
| 34 uint16 port; // UDP port. Always 0. | |
| 35 uint16 padding; // 2 bytes padding. | |
| 36 }; | |
| 37 const unsigned char RtpDumpFileHeader::kFirstLine[] = | |
| 38 "#!rtpplay1.0 0.0.0.0/0\n"; | |
| 39 | |
| 40 // The header for each packet dump. | |
| 41 struct PacketDumpHeader { | |
| 42 PacketDumpHeader(const base::TimeTicks& start, | |
| 43 uint16 dump_length, | |
| 44 uint16 packet_length) | |
| 45 : packet_dump_length(dump_length), | |
| 46 packet_length(packet_length), | |
| 47 offset_ms((base::TimeTicks::Now() - start).InMilliseconds()) {} | |
| 48 | |
| 49 // Length of the packet dump including this header. | |
| 50 uint16 packet_dump_length; | |
| 51 | |
| 52 // Length of header + payload of the RTP packet. | |
| 53 uint16 packet_length; | |
| 54 | |
| 55 // Milliseconds since the start of recording. | |
| 56 uint32 offset_ms; | |
| 57 }; | |
| 58 | |
| 59 bool AppendToBuffer( | |
| 60 const uint8* src, size_t src_len, std::vector<uint8>* dest) { | |
| 61 if (dest->capacity() < dest->size() + src_len) | |
| 62 return false; | |
| 63 | |
| 64 for (size_t i = 0; i < src_len; ++i) { | |
| 65 dest->push_back(src[i]); | |
| 66 } | |
| 67 return true; | |
| 68 } | |
| 69 | |
| 70 uint16 GetBigEndian16(const uint8* memory) { | |
| 71 return static_cast<uint16>((memory[0] << 8) | (memory[1] << 0)); | |
| 72 } | |
| 73 | |
| 74 void GetRtpHeaderLen(const uint8* packet, size_t length, size_t* output) { | |
| 75 static const size_t kMinRtpPacketLen = 12; | |
| 76 | |
| 77 DCHECK(packet && length >= kMinRtpPacketLen && output); | |
| 78 | |
| 79 // Get base header size + length of CSRCs (not counting extension yet). | |
| 80 size_t header_size = kMinRtpPacketLen + (packet[0] & 0xF) * sizeof(uint32); | |
| 81 DCHECK(length >= header_size); | |
| 82 | |
| 83 // If there's an extension, read and add the extension size. | |
| 84 if (packet[0] & 0x10) { | |
| 85 DCHECK(length >= header_size + sizeof(uint32)); | |
| 86 | |
| 87 header_size += ((GetBigEndian16(packet + header_size + 2) + 1) * | |
| 88 sizeof(uint32)); | |
| 89 DCHECK(length >= header_size); | |
| 90 } | |
| 91 *output = header_size; | |
| 92 } | |
| 93 | |
| 94 } // namespace | |
| 95 | |
| 96 WebRtcRtpDumpWriter::WebRtcRtpDumpWriter( | |
| 97 const base::FilePath& dump_path, | |
| 98 size_t max_dump_size, | |
| 99 const base::Closure& max_dump_size_reached_callback) | |
| 100 : dump_path_(dump_path), | |
| 101 max_dump_size_(max_dump_size), | |
| 102 max_dump_size_reached_callback_(max_dump_size_reached_callback), | |
| 103 weak_ptr_factory_(this) {} | |
| 104 | |
| 105 WebRtcRtpDumpWriter::~WebRtcRtpDumpWriter() { | |
| 106 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 107 } | |
| 108 | |
| 109 void WebRtcRtpDumpWriter::WriteRtpPacket(const uint8* packet, size_t length) { | |
| 110 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 111 | |
| 112 static const size_t kMaxInMemoryBufferSize = 65536; // 64KB | |
| 113 | |
| 114 bool succeeded = true; | |
| 115 if (!buffer_.capacity()) { | |
| 116 buffer_.reserve(std::min(kMaxInMemoryBufferSize, max_dump_size_)); | |
| 117 | |
| 118 start_time_ = base::TimeTicks::Now(); | |
| 119 | |
| 120 // Writes the dump file header. | |
| 121 succeeded = AppendToBuffer(RtpDumpFileHeader::kFirstLine, | |
| 122 arraysize(RtpDumpFileHeader::kFirstLine) - 1, | |
| 123 &buffer_); | |
| 124 DCHECK(succeeded); | |
| 125 | |
| 126 RtpDumpFileHeader header(start_time_); | |
| 127 succeeded = AppendToBuffer(reinterpret_cast<uint8*>(&header), | |
| 128 sizeof(header), | |
| 129 &buffer_); | |
| 130 DCHECK(succeeded); | |
| 131 } | |
| 132 | |
| 133 size_t rtp_header_length = 0; | |
| 134 GetRtpHeaderLen(packet, length, &rtp_header_length); | |
| 135 if (!rtp_header_length) | |
| 136 return; | |
| 137 | |
| 138 size_t packet_dump_length = sizeof(PacketDumpHeader) + rtp_header_length; | |
| 139 | |
| 140 // Flushes the buffer to disk if the buffer is full. | |
| 141 if (buffer_.capacity() < buffer_.size() + packet_dump_length) | |
| 142 Flush(base::Callback<void(bool)>()); | |
| 143 | |
| 144 // Writes the packet dump header. | |
| 145 PacketDumpHeader packet_header(start_time_, packet_dump_length, length); | |
| 146 | |
| 147 succeeded = AppendToBuffer(reinterpret_cast<uint8*>(&packet_header), | |
| 148 sizeof(packet_header), | |
| 149 &buffer_); | |
| 150 DCHECK(succeeded); | |
| 151 | |
| 152 // Writes the actual RTP packet header. | |
| 153 succeeded = AppendToBuffer(packet, rtp_header_length, &buffer_); | |
| 154 DCHECK(succeeded); | |
| 155 } | |
| 156 | |
| 157 void WebRtcRtpDumpWriter::Flush( | |
| 158 const base::Callback<void(bool)>& finished_callback) { | |
| 159 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 160 | |
| 161 scoped_ptr<std::vector<uint8>> new_buffer(new std::vector<uint8>()); | |
| 162 new_buffer->reserve(buffer_.capacity()); | |
| 163 | |
| 164 new_buffer->swap(buffer_); | |
| 165 | |
| 166 scoped_ptr<bool> result(new bool(false)); | |
| 167 | |
| 168 // OnFlushDone is necessary to avoid running the callback after this object is | |
|
Henrik Grunell
2014/05/07 09:38:19
Will this object delete the file if this happens?
| |
| 169 // gone. | |
| 170 BrowserThread::PostTaskAndReply( | |
| 171 BrowserThread::FILE, | |
| 172 FROM_HERE, | |
| 173 base::Bind(&WebRtcRtpDumpWriter::CompressAndWriteToFileOnFileThread, | |
| 174 Passed(&new_buffer), | |
| 175 dump_path_, | |
| 176 result.get()), | |
| 177 base::Bind(&WebRtcRtpDumpWriter::OnFlushDone, | |
| 178 weak_ptr_factory_.GetWeakPtr(), | |
| 179 finished_callback, | |
| 180 Passed(&result))); | |
| 181 } | |
| 182 | |
| 183 // static | |
| 184 void WebRtcRtpDumpWriter::CompressAndWriteToFileOnFileThread( | |
| 185 const scoped_ptr<std::vector<uint8>>& buffer, | |
| 186 const base::FilePath& dump_path, | |
| 187 bool* result) { | |
| 188 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
|
Henrik Grunell
2014/05/07 09:38:19
OK, I see you're doing it on the FILE thread.
| |
| 189 | |
| 190 // TODO: implement compressing file and writing file. | |
| 191 *result = true; | |
| 192 } | |
| 193 | |
| 194 void WebRtcRtpDumpWriter::OnFlushDone( | |
| 195 const base::Callback<void(bool)>& callback, | |
| 196 const scoped_ptr<bool>& result) { | |
| 197 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 198 | |
| 199 if(!callback.is_null()) | |
| 200 callback.Run(*result); | |
| 201 } | |
| OLD | NEW |