henrike@webrtc.org | 28e2075 | 2013-07-10 00:45:36 +0000 | [diff] [blame] | 1 | /* |
| 2 | * libjingle |
| 3 | * Copyright 2010 Google Inc. |
| 4 | * |
| 5 | * Redistribution and use in source and binary forms, with or without |
| 6 | * modification, are permitted provided that the following conditions are met: |
| 7 | * |
| 8 | * 1. Redistributions of source code must retain the above copyright notice, |
| 9 | * this list of conditions and the following disclaimer. |
| 10 | * 2. Redistributions in binary form must reproduce the above copyright notice, |
| 11 | * this list of conditions and the following disclaimer in the documentation |
| 12 | * and/or other materials provided with the distribution. |
| 13 | * 3. The name of the author may not be used to endorse or promote products |
| 14 | * derived from this software without specific prior written permission. |
| 15 | * |
| 16 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED |
| 17 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
| 18 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO |
| 19 | * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| 20 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| 21 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; |
| 22 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
| 23 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR |
| 24 | * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF |
| 25 | * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 26 | */ |
| 27 | |
| 28 | #ifndef TALK_MEDIA_BASE_RTPDUMP_H_ |
| 29 | #define TALK_MEDIA_BASE_RTPDUMP_H_ |
| 30 | |
pbos@webrtc.org | 371243d | 2014-03-07 15:22:04 +0000 | [diff] [blame] | 31 | #include <string.h> |
| 32 | |
henrike@webrtc.org | 28e2075 | 2013-07-10 00:45:36 +0000 | [diff] [blame] | 33 | #include <string> |
| 34 | #include <vector> |
| 35 | |
buildbot@webrtc.org | d4e598d | 2014-07-29 17:36:52 +0000 | [diff] [blame] | 36 | #include "webrtc/base/basictypes.h" |
| 37 | #include "webrtc/base/bytebuffer.h" |
| 38 | #include "webrtc/base/stream.h" |
henrike@webrtc.org | 28e2075 | 2013-07-10 00:45:36 +0000 | [diff] [blame] | 39 | |
| 40 | namespace cricket { |
| 41 | |
| 42 | // We use the RTP dump file format compatible to the format used by rtptools |
| 43 | // (http://www.cs.columbia.edu/irt/software/rtptools/) and Wireshark |
| 44 | // (http://wiki.wireshark.org/rtpdump). In particular, the file starts with the |
| 45 | // first line "#!rtpplay1.0 address/port\n", followed by a 16 byte file header. |
| 46 | // For each packet, the file contains a 8 byte dump packet header, followed by |
| 47 | // the actual RTP or RTCP packet. |
| 48 | |
| 49 | enum RtpDumpPacketFilter { |
| 50 | PF_NONE = 0x0, |
| 51 | PF_RTPHEADER = 0x1, |
| 52 | PF_RTPPACKET = 0x3, // includes header |
| 53 | // PF_RTCPHEADER = 0x4, // TODO(juberti) |
| 54 | PF_RTCPPACKET = 0xC, // includes header |
| 55 | PF_ALL = 0xF |
| 56 | }; |
| 57 | |
| 58 | struct RtpDumpFileHeader { |
| 59 | RtpDumpFileHeader(uint32 start_ms, uint32 s, uint16 p); |
buildbot@webrtc.org | d4e598d | 2014-07-29 17:36:52 +0000 | [diff] [blame] | 60 | void WriteToByteBuffer(rtc::ByteBuffer* buf); |
henrike@webrtc.org | 28e2075 | 2013-07-10 00:45:36 +0000 | [diff] [blame] | 61 | |
| 62 | static const char kFirstLine[]; |
| 63 | static const size_t kHeaderLength = 16; |
| 64 | uint32 start_sec; // start of recording, the seconds part. |
| 65 | uint32 start_usec; // start of recording, the microseconds part. |
| 66 | uint32 source; // network source (multicast address). |
| 67 | uint16 port; // UDP port. |
| 68 | uint16 padding; // 2 bytes padding. |
| 69 | }; |
| 70 | |
| 71 | struct RtpDumpPacket { |
| 72 | RtpDumpPacket() {} |
| 73 | |
| 74 | RtpDumpPacket(const void* d, size_t s, uint32 elapsed, bool rtcp) |
| 75 | : elapsed_time(elapsed), |
| 76 | original_data_len((rtcp) ? 0 : s) { |
| 77 | data.resize(s); |
| 78 | memcpy(&data[0], d, s); |
| 79 | } |
| 80 | |
| 81 | // In the rtpdump file format, RTCP packets have their data len set to zero, |
| 82 | // since RTCP has an internal length field. |
| 83 | bool is_rtcp() const { return original_data_len == 0; } |
| 84 | bool IsValidRtpPacket() const; |
| 85 | bool IsValidRtcpPacket() const; |
| 86 | // Get the payload type, sequence number, timestampe, and SSRC of the RTP |
| 87 | // packet. Return true and set the output parameter if successful. |
| 88 | bool GetRtpPayloadType(int* pt) const; |
| 89 | bool GetRtpSeqNum(int* seq_num) const; |
| 90 | bool GetRtpTimestamp(uint32* ts) const; |
| 91 | bool GetRtpSsrc(uint32* ssrc) const; |
| 92 | bool GetRtpHeaderLen(size_t* len) const; |
| 93 | // Get the type of the RTCP packet. Return true and set the output parameter |
| 94 | // if successful. |
| 95 | bool GetRtcpType(int* type) const; |
| 96 | |
| 97 | static const size_t kHeaderLength = 8; |
| 98 | uint32 elapsed_time; // Milliseconds since the start of recording. |
| 99 | std::vector<uint8> data; // The actual RTP or RTCP packet. |
| 100 | size_t original_data_len; // The original length of the packet; may be |
| 101 | // greater than data.size() if only part of the |
| 102 | // packet was recorded. |
| 103 | }; |
| 104 | |
| 105 | class RtpDumpReader { |
| 106 | public: |
buildbot@webrtc.org | d4e598d | 2014-07-29 17:36:52 +0000 | [diff] [blame] | 107 | explicit RtpDumpReader(rtc::StreamInterface* stream) |
henrike@webrtc.org | 28e2075 | 2013-07-10 00:45:36 +0000 | [diff] [blame] | 108 | : stream_(stream), |
| 109 | file_header_read_(false), |
| 110 | first_line_and_file_header_len_(0), |
| 111 | start_time_ms_(0), |
| 112 | ssrc_override_(0) { |
| 113 | } |
| 114 | virtual ~RtpDumpReader() {} |
| 115 | |
| 116 | // Use the specified ssrc, rather than the ssrc from dump, for RTP packets. |
| 117 | void SetSsrc(uint32 ssrc); |
buildbot@webrtc.org | d4e598d | 2014-07-29 17:36:52 +0000 | [diff] [blame] | 118 | virtual rtc::StreamResult ReadPacket(RtpDumpPacket* packet); |
henrike@webrtc.org | 28e2075 | 2013-07-10 00:45:36 +0000 | [diff] [blame] | 119 | |
| 120 | protected: |
buildbot@webrtc.org | d4e598d | 2014-07-29 17:36:52 +0000 | [diff] [blame] | 121 | rtc::StreamResult ReadFileHeader(); |
henrike@webrtc.org | 28e2075 | 2013-07-10 00:45:36 +0000 | [diff] [blame] | 122 | bool RewindToFirstDumpPacket() { |
| 123 | return stream_->SetPosition(first_line_and_file_header_len_); |
| 124 | } |
| 125 | |
| 126 | private: |
| 127 | // Check if its matches "#!rtpplay1.0 address/port\n". |
| 128 | bool CheckFirstLine(const std::string& first_line); |
| 129 | |
buildbot@webrtc.org | d4e598d | 2014-07-29 17:36:52 +0000 | [diff] [blame] | 130 | rtc::StreamInterface* stream_; |
henrike@webrtc.org | 28e2075 | 2013-07-10 00:45:36 +0000 | [diff] [blame] | 131 | bool file_header_read_; |
| 132 | size_t first_line_and_file_header_len_; |
| 133 | uint32 start_time_ms_; |
| 134 | uint32 ssrc_override_; |
| 135 | |
henrikg | 3c089d7 | 2015-09-16 05:37:44 -0700 | [diff] [blame^] | 136 | RTC_DISALLOW_COPY_AND_ASSIGN(RtpDumpReader); |
henrike@webrtc.org | 28e2075 | 2013-07-10 00:45:36 +0000 | [diff] [blame] | 137 | }; |
| 138 | |
| 139 | // RtpDumpLoopReader reads RTP dump packets from the input stream and rewinds |
| 140 | // the stream when it ends. RtpDumpLoopReader maintains the elapsed time, the |
| 141 | // RTP sequence number and the RTP timestamp properly. RtpDumpLoopReader can |
| 142 | // handle both RTP dump and RTCP dump. We assume that the dump does not mix |
| 143 | // RTP packets and RTCP packets. |
| 144 | class RtpDumpLoopReader : public RtpDumpReader { |
| 145 | public: |
buildbot@webrtc.org | d4e598d | 2014-07-29 17:36:52 +0000 | [diff] [blame] | 146 | explicit RtpDumpLoopReader(rtc::StreamInterface* stream); |
| 147 | virtual rtc::StreamResult ReadPacket(RtpDumpPacket* packet); |
henrike@webrtc.org | 28e2075 | 2013-07-10 00:45:36 +0000 | [diff] [blame] | 148 | |
| 149 | private: |
| 150 | // During the first loop, update the statistics, including packet count, frame |
| 151 | // count, timestamps, and sequence number, of the input stream. |
| 152 | void UpdateStreamStatistics(const RtpDumpPacket& packet); |
| 153 | |
| 154 | // At the end of first loop, calculate elapsed_time_increases_, |
| 155 | // rtp_seq_num_increase_, and rtp_timestamp_increase_. |
| 156 | void CalculateIncreases(); |
| 157 | |
| 158 | // During the second and later loops, update the elapsed time of the dump |
| 159 | // packet. If the dumped packet is a RTP packet, update its RTP sequence |
| 160 | // number and timestamp as well. |
| 161 | void UpdateDumpPacket(RtpDumpPacket* packet); |
| 162 | |
| 163 | int loop_count_; |
| 164 | // How much to increase the elapsed time, RTP sequence number, RTP timestampe |
| 165 | // for each loop. They are calcualted with the variables below during the |
| 166 | // first loop. |
| 167 | uint32 elapsed_time_increases_; |
| 168 | int rtp_seq_num_increase_; |
| 169 | uint32 rtp_timestamp_increase_; |
| 170 | // How many RTP packets and how many payload frames in the input stream. RTP |
| 171 | // packets belong to the same frame have the same RTP timestamp, different |
| 172 | // dump timestamp, and different RTP sequence number. |
| 173 | uint32 packet_count_; |
| 174 | uint32 frame_count_; |
| 175 | // The elapsed time, RTP sequence number, and RTP timestamp of the first and |
| 176 | // the previous dump packets in the input stream. |
| 177 | uint32 first_elapsed_time_; |
| 178 | int first_rtp_seq_num_; |
| 179 | uint32 first_rtp_timestamp_; |
| 180 | uint32 prev_elapsed_time_; |
| 181 | int prev_rtp_seq_num_; |
| 182 | uint32 prev_rtp_timestamp_; |
| 183 | |
henrikg | 3c089d7 | 2015-09-16 05:37:44 -0700 | [diff] [blame^] | 184 | RTC_DISALLOW_COPY_AND_ASSIGN(RtpDumpLoopReader); |
henrike@webrtc.org | 28e2075 | 2013-07-10 00:45:36 +0000 | [diff] [blame] | 185 | }; |
| 186 | |
| 187 | class RtpDumpWriter { |
| 188 | public: |
buildbot@webrtc.org | d4e598d | 2014-07-29 17:36:52 +0000 | [diff] [blame] | 189 | explicit RtpDumpWriter(rtc::StreamInterface* stream); |
henrike@webrtc.org | 28e2075 | 2013-07-10 00:45:36 +0000 | [diff] [blame] | 190 | |
| 191 | // Filter to control what packets we actually record. |
| 192 | void set_packet_filter(int filter); |
| 193 | // Write a RTP or RTCP packet. The parameters data points to the packet and |
| 194 | // data_len is its length. |
buildbot@webrtc.org | d4e598d | 2014-07-29 17:36:52 +0000 | [diff] [blame] | 195 | rtc::StreamResult WriteRtpPacket(const void* data, size_t data_len) { |
henrike@webrtc.org | 28e2075 | 2013-07-10 00:45:36 +0000 | [diff] [blame] | 196 | return WritePacket(data, data_len, GetElapsedTime(), false); |
| 197 | } |
buildbot@webrtc.org | d4e598d | 2014-07-29 17:36:52 +0000 | [diff] [blame] | 198 | rtc::StreamResult WriteRtcpPacket(const void* data, size_t data_len) { |
henrike@webrtc.org | 28e2075 | 2013-07-10 00:45:36 +0000 | [diff] [blame] | 199 | return WritePacket(data, data_len, GetElapsedTime(), true); |
| 200 | } |
buildbot@webrtc.org | d4e598d | 2014-07-29 17:36:52 +0000 | [diff] [blame] | 201 | rtc::StreamResult WritePacket(const RtpDumpPacket& packet) { |
henrike@webrtc.org | 28e2075 | 2013-07-10 00:45:36 +0000 | [diff] [blame] | 202 | return WritePacket(&packet.data[0], packet.data.size(), packet.elapsed_time, |
| 203 | packet.is_rtcp()); |
| 204 | } |
| 205 | uint32 GetElapsedTime() const; |
| 206 | |
| 207 | bool GetDumpSize(size_t* size) { |
| 208 | // Note that we use GetPosition(), rather than GetSize(), to avoid flush the |
| 209 | // stream per write. |
| 210 | return stream_ && size && stream_->GetPosition(size); |
| 211 | } |
| 212 | |
| 213 | protected: |
buildbot@webrtc.org | d4e598d | 2014-07-29 17:36:52 +0000 | [diff] [blame] | 214 | rtc::StreamResult WriteFileHeader(); |
henrike@webrtc.org | 28e2075 | 2013-07-10 00:45:36 +0000 | [diff] [blame] | 215 | |
| 216 | private: |
buildbot@webrtc.org | d4e598d | 2014-07-29 17:36:52 +0000 | [diff] [blame] | 217 | rtc::StreamResult WritePacket(const void* data, size_t data_len, |
henrike@webrtc.org | 28e2075 | 2013-07-10 00:45:36 +0000 | [diff] [blame] | 218 | uint32 elapsed, bool rtcp); |
| 219 | size_t FilterPacket(const void* data, size_t data_len, bool rtcp); |
buildbot@webrtc.org | d4e598d | 2014-07-29 17:36:52 +0000 | [diff] [blame] | 220 | rtc::StreamResult WriteToStream(const void* data, size_t data_len); |
henrike@webrtc.org | 28e2075 | 2013-07-10 00:45:36 +0000 | [diff] [blame] | 221 | |
buildbot@webrtc.org | d4e598d | 2014-07-29 17:36:52 +0000 | [diff] [blame] | 222 | rtc::StreamInterface* stream_; |
henrike@webrtc.org | 28e2075 | 2013-07-10 00:45:36 +0000 | [diff] [blame] | 223 | int packet_filter_; |
| 224 | bool file_header_written_; |
| 225 | uint32 start_time_ms_; // Time when the record starts. |
| 226 | // If writing to the stream takes longer than this many ms, log a warning. |
| 227 | uint32 warn_slow_writes_delay_; |
henrikg | 3c089d7 | 2015-09-16 05:37:44 -0700 | [diff] [blame^] | 228 | RTC_DISALLOW_COPY_AND_ASSIGN(RtpDumpWriter); |
henrike@webrtc.org | 28e2075 | 2013-07-10 00:45:36 +0000 | [diff] [blame] | 229 | }; |
| 230 | |
| 231 | } // namespace cricket |
| 232 | |
| 233 | #endif // TALK_MEDIA_BASE_RTPDUMP_H_ |