Home | History | Annotate | Download | only in phone
      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 #include "talk/session/phone/rtpdump.h"
     29 
     30 #include <string>
     31 
     32 #include "talk/base/bytebuffer.h"
     33 #include "talk/base/byteorder.h"
     34 #include "talk/base/logging.h"
     35 #include "talk/base/time.h"
     36 
     37 namespace cricket {
     38 
     39 const std::string RtpDumpFileHeader::kFirstLine =
     40     "#!rtpplay1.0 0.0.0.0/0\n";
     41 
     42 RtpDumpFileHeader::RtpDumpFileHeader(uint32 start_ms, uint32 s, uint16 p)
     43     : start_sec(start_ms / 1000),
     44       start_usec(start_ms % 1000 * 1000),
     45       source(s),
     46       port(p),
     47       padding(0) {
     48 }
     49 
     50 void RtpDumpFileHeader::WriteToByteBuffer(talk_base::ByteBuffer* buf) {
     51   buf->WriteUInt32(start_sec);
     52   buf->WriteUInt32(start_usec);
     53   buf->WriteUInt32(source);
     54   buf->WriteUInt16(port);
     55   buf->WriteUInt16(padding);
     56 }
     57 
     58 // RTP packet format (http://www.networksorcery.com/enp/protocol/rtp.htm).
     59 static const size_t kMinimumRtpHeaderSize = 12;
     60 static const uint32 kDefaultTimeIncrease = 30;
     61 
     62 bool RtpDumpPacket::IsValidRtpPacket() const {
     63   return !is_rtcp && data.size() >= kMinimumRtpHeaderSize;
     64 }
     65 
     66 bool RtpDumpPacket::GetRtpSeqNum(uint16* seq_num) const {
     67   if (!seq_num || !IsValidRtpPacket()) {
     68     return false;
     69   }
     70   *seq_num = talk_base::GetBE16(&data[2]);
     71   return true;
     72 }
     73 
     74 bool RtpDumpPacket::GetRtpTimestamp(uint32* ts) const {
     75   if (!ts || !IsValidRtpPacket()) {
     76     return false;
     77   }
     78   *ts = talk_base::GetBE32(&data[4]);
     79   return true;
     80 }
     81 
     82 bool RtpDumpPacket::GetRtpSsrc(uint32* ssrc) const {
     83   if (!ssrc || !IsValidRtpPacket()) {
     84     return false;
     85   }
     86   *ssrc = talk_base::GetBE32(&data[8]);
     87   return true;
     88 }
     89 
     90 ///////////////////////////////////////////////////////////////////////////
     91 // Implementation of RtpDumpReader.
     92 ///////////////////////////////////////////////////////////////////////////
     93 talk_base::StreamResult RtpDumpReader::ReadPacket(RtpDumpPacket* packet) {
     94   if (!packet) return talk_base::SR_ERROR;
     95 
     96   talk_base::StreamResult res = talk_base::SR_SUCCESS;
     97   // Read the file header if it has not been read yet.
     98   if (!file_header_read_) {
     99     res = ReadFileHeader();
    100     if (res != talk_base::SR_SUCCESS) {
    101       return res;
    102     }
    103     file_header_read_ = true;
    104   }
    105 
    106   // Read the RTP dump packet header.
    107   char header[RtpDumpPacket::kHeaderLength];
    108   res = stream_->ReadAll(header, sizeof(header), NULL, NULL);
    109   if (res != talk_base::SR_SUCCESS) {
    110     return res;
    111   }
    112   talk_base::ByteBuffer buf(header, sizeof(header));
    113   uint16 dump_packet_len;
    114   uint16 data_len;
    115   buf.ReadUInt16(&dump_packet_len);
    116   buf.ReadUInt16(&data_len);  // data.size() for RTP, 0 for RTCP.
    117   packet->is_rtcp = (0 == data_len);
    118   buf.ReadUInt32(&packet->elapsed_time);
    119   packet->data.resize(dump_packet_len - sizeof(header));
    120 
    121   // Read the actual RTP or RTCP packet.
    122   return stream_->ReadAll(&packet->data[0], packet->data.size(), NULL, NULL);
    123 }
    124 
    125 talk_base::StreamResult RtpDumpReader::ReadFileHeader() {
    126   // Read the first line.
    127   std::string first_line;
    128   talk_base::StreamResult res = stream_->ReadLine(&first_line);
    129   if (res != talk_base::SR_SUCCESS) {
    130     return res;
    131   }
    132   if (!CheckFirstLine(first_line)) {
    133     return talk_base::SR_ERROR;
    134   }
    135 
    136   // Read the 16 byte file header.
    137   char header[RtpDumpFileHeader::kHeaderLength];
    138   res = stream_->ReadAll(header, sizeof(header), NULL, NULL);
    139   if (res == talk_base::SR_SUCCESS) {
    140     talk_base::ByteBuffer buf(header, sizeof(header));
    141     uint32 start_sec;
    142     uint32 start_usec;
    143     buf.ReadUInt32(&start_sec);
    144     buf.ReadUInt32(&start_usec);
    145     start_time_ms_ = start_sec * 1000 + start_usec / 1000;
    146     // Increase the length by 1 since first_line does not contain the ending \n.
    147     first_line_and_file_header_len_ = first_line.size() + 1 + sizeof(header);
    148   }
    149   return res;
    150 }
    151 
    152 bool RtpDumpReader::CheckFirstLine(const std::string& first_line) {
    153   // The first line is like "#!rtpplay1.0 address/port"
    154   bool matched = (0 == first_line.find("#!rtpplay1.0 "));
    155 
    156   // The address could be IP or hostname. We do not check it here. Instead, we
    157   // check the port at the end.
    158   size_t pos = first_line.find('/');
    159   matched &= (pos != std::string::npos && pos < first_line.size() - 1);
    160   for (++pos; pos < first_line.size() && matched; ++pos) {
    161     matched &= (0 != isdigit(first_line[pos]));
    162   }
    163 
    164   return matched;
    165 }
    166 
    167 ///////////////////////////////////////////////////////////////////////////
    168 // Implementation of RtpDumpLoopReader.
    169 ///////////////////////////////////////////////////////////////////////////
    170 RtpDumpLoopReader::RtpDumpLoopReader(talk_base::StreamInterface* stream)
    171     : RtpDumpReader(stream),
    172       loop_count_(0),
    173       elapsed_time_increases_(0),
    174       rtp_seq_num_increase_(0),
    175       rtp_timestamp_increase_(0),
    176       packet_count_(0),
    177       frame_count_(0),
    178       first_elapsed_time_(0),
    179       first_rtp_seq_num_(0),
    180       first_rtp_timestamp_(0),
    181       prev_elapsed_time_(0),
    182       prev_rtp_seq_num_(0),
    183       prev_rtp_timestamp_(0) {
    184 }
    185 
    186 talk_base::StreamResult RtpDumpLoopReader::ReadPacket(RtpDumpPacket* packet) {
    187   if (!packet) return talk_base::SR_ERROR;
    188 
    189   talk_base::StreamResult res = RtpDumpReader::ReadPacket(packet);
    190   if (talk_base::SR_SUCCESS == res) {
    191     if (0 == loop_count_) {
    192       // During the first loop, we update the statistics of the input stream.
    193       UpdateStreamStatistics(*packet);
    194     }
    195   } else if (talk_base::SR_EOS == res) {
    196     if (0 == loop_count_) {
    197       // At the end of the first loop, calculate elapsed_time_increases_,
    198       // rtp_seq_num_increase_, and rtp_timestamp_increase_, which will be
    199       // used during the second and later loops.
    200       CalculateIncreases();
    201     }
    202 
    203     // Rewind the input stream to the first dump packet and read again.
    204     ++loop_count_;
    205     if (RewindToFirstDumpPacket()) {
    206       res = RtpDumpReader::ReadPacket(packet);
    207     }
    208   }
    209 
    210   if (talk_base::SR_SUCCESS == res && loop_count_ > 0) {
    211     // During the second and later loops, we update the elapsed time of the dump
    212     // packet. If the dumped packet is a RTP packet, we also update its RTP
    213     // sequence number and timestamp.
    214     UpdateDumpPacket(packet);
    215   }
    216 
    217   return res;
    218 }
    219 
    220 void RtpDumpLoopReader::UpdateStreamStatistics(const RtpDumpPacket& packet) {
    221   // Get the RTP sequence number and timestamp of the dump packet.
    222   uint16 rtp_seq_num = 0;
    223   packet.GetRtpSeqNum(&rtp_seq_num);
    224   uint32 rtp_timestamp = 0;
    225   packet.GetRtpTimestamp(&rtp_timestamp);
    226 
    227   // Set the timestamps and sequence number for the first dump packet.
    228   if (0 == packet_count_++) {
    229     first_elapsed_time_ = packet.elapsed_time;
    230     first_rtp_seq_num_ = rtp_seq_num;
    231     first_rtp_timestamp_ = rtp_timestamp;
    232     // The first packet belongs to a new payload frame.
    233     ++frame_count_;
    234   } else if (rtp_timestamp != prev_rtp_timestamp_) {
    235     // The current and previous packets belong to different payload frames.
    236     ++frame_count_;
    237   }
    238 
    239   prev_elapsed_time_ = packet.elapsed_time;
    240   prev_rtp_timestamp_ = rtp_timestamp;
    241   prev_rtp_seq_num_ = rtp_seq_num;
    242 }
    243 
    244 void RtpDumpLoopReader::CalculateIncreases() {
    245   // At this time, prev_elapsed_time_, prev_rtp_seq_num_, and
    246   // prev_rtp_timestamp_ are values of the last dump packet in the input stream.
    247   rtp_seq_num_increase_ = prev_rtp_seq_num_ - first_rtp_seq_num_ + 1;
    248   // If we have only one packet or frame, we use the default timestamp
    249   // increase. Otherwise, we use the difference between the first and the last
    250   // packets or frames.
    251   elapsed_time_increases_ = packet_count_ <= 1 ? kDefaultTimeIncrease :
    252       (prev_elapsed_time_ - first_elapsed_time_) * packet_count_ /
    253       (packet_count_ - 1);
    254   rtp_timestamp_increase_ = frame_count_ <= 1 ? kDefaultTimeIncrease :
    255       (prev_rtp_timestamp_ - first_rtp_timestamp_) * frame_count_ /
    256       (frame_count_ - 1);
    257 }
    258 
    259 void RtpDumpLoopReader::UpdateDumpPacket(RtpDumpPacket* packet) {
    260   // Increase the elapsed time of the dump packet.
    261   packet->elapsed_time += loop_count_ * elapsed_time_increases_;
    262 
    263   if (packet->IsValidRtpPacket()) {
    264     // Get the old RTP sequence number and timestamp.
    265     uint16 sequence = 0;
    266     packet->GetRtpSeqNum(&sequence);
    267     uint32 timestamp = 0;
    268     packet->GetRtpTimestamp(&timestamp);
    269     // Increase the RTP sequence number and timestamp.
    270     sequence += loop_count_ * rtp_seq_num_increase_;
    271     timestamp += loop_count_ * rtp_timestamp_increase_;
    272     // Write the updated sequence number and timestamp back to the RTP packet.
    273     talk_base::ByteBuffer buffer;
    274     buffer.WriteUInt16(sequence);
    275     buffer.WriteUInt32(timestamp);
    276     memcpy(&packet->data[2], buffer.Data(), buffer.Length());
    277   }
    278 }
    279 
    280 ///////////////////////////////////////////////////////////////////////////
    281 // Implementation of RtpDumpWriter.
    282 ///////////////////////////////////////////////////////////////////////////
    283 
    284 RtpDumpWriter::RtpDumpWriter(talk_base::StreamInterface* stream)
    285     : stream_(stream),
    286       file_header_written_(false),
    287       start_time_ms_(talk_base::Time()) {
    288   }
    289 
    290 uint32 RtpDumpWriter::GetElapsedTime() const {
    291   return talk_base::TimeSince(start_time_ms_);
    292 }
    293 
    294 talk_base::StreamResult RtpDumpWriter::WritePacket(
    295     const void* data, size_t data_len, uint32 elapsed, bool rtcp) {
    296   if (!stream_ || !data || 0 == data_len) return talk_base::SR_ERROR;
    297 
    298   talk_base::StreamResult res = talk_base::SR_SUCCESS;
    299   // Write the file header if it has not been written yet.
    300   if (!file_header_written_) {
    301     res = WriteFileHeader();
    302     if (res != talk_base::SR_SUCCESS) {
    303       return res;
    304     }
    305     file_header_written_ = true;
    306   }
    307 
    308   // Write the dump packet header.
    309   talk_base::ByteBuffer buf;
    310   buf.WriteUInt16(static_cast<uint16>(RtpDumpPacket::kHeaderLength + data_len));
    311   buf.WriteUInt16(static_cast<uint16>(rtcp ? 0 : data_len));
    312   buf.WriteUInt32(elapsed);
    313   res = stream_->WriteAll(buf.Data(), buf.Length(), NULL, NULL);
    314   if (res != talk_base::SR_SUCCESS) {
    315     return res;
    316   }
    317 
    318   // Write the actual RTP or RTCP packet.
    319   return stream_->WriteAll(data, data_len, NULL, NULL);
    320 }
    321 
    322 talk_base::StreamResult RtpDumpWriter::WriteFileHeader() {
    323   talk_base::StreamResult res = stream_->WriteAll(
    324       RtpDumpFileHeader::kFirstLine.c_str(),
    325       RtpDumpFileHeader::kFirstLine.size(), NULL, NULL);
    326   if (res != talk_base::SR_SUCCESS) {
    327     return res;
    328   }
    329 
    330   talk_base::ByteBuffer buf;
    331   RtpDumpFileHeader file_header(talk_base::Time(), 0, 0);
    332   file_header.WriteToByteBuffer(&buf);
    333   return stream_->WriteAll(buf.Data(), buf.Length(), NULL, NULL);
    334 }
    335 
    336 }  // namespace cricket
    337