| OLD | NEW | 
|    1 /* |    1 /* | 
|    2  *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. |    2  *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. | 
|    3  * |    3  * | 
|    4  *  Use of this source code is governed by a BSD-style license |    4  *  Use of this source code is governed by a BSD-style license | 
|    5  *  that can be found in the LICENSE file in the root of the source |    5  *  that can be found in the LICENSE file in the root of the source | 
|    6  *  tree. An additional intellectual property rights grant can be found |    6  *  tree. An additional intellectual property rights grant can be found | 
|    7  *  in the file PATENTS.  All contributing project authors may |    7  *  in the file PATENTS.  All contributing project authors may | 
|    8  *  be found in the AUTHORS file in the root of the source tree. |    8  *  be found in the AUTHORS file in the root of the source tree. | 
|    9  */ |    9  */ | 
|   10  |   10  | 
|   11 // This is the implementation of the PacketBuffer class. It is mostly based on |   11 // This is the implementation of the PacketBuffer class. It is mostly based on | 
|   12 // an STL list. The list is kept sorted at all times so that the next packet to |   12 // an STL list. The list is kept sorted at all times so that the next packet to | 
|   13 // decode is at the beginning of the list. |   13 // decode is at the beginning of the list. | 
|   14  |   14  | 
|   15 #include "webrtc/modules/audio_coding/neteq/packet_buffer.h" |   15 #include "webrtc/modules/audio_coding/neteq/packet_buffer.h" | 
|   16  |   16  | 
|   17 #include <algorithm>  // find_if() |   17 #include <algorithm>  // find_if() | 
|   18  |   18  | 
|   19 #include "webrtc/base/logging.h" |   19 #include "webrtc/base/logging.h" | 
|   20 #include "webrtc/modules/audio_coding/codecs/audio_decoder.h" |   20 #include "webrtc/modules/audio_coding/codecs/audio_decoder.h" | 
|   21 #include "webrtc/modules/audio_coding/neteq/decoder_database.h" |   21 #include "webrtc/modules/audio_coding/neteq/decoder_database.h" | 
|   22 #include "webrtc/modules/audio_coding/neteq/tick_timer.h" |   22 #include "webrtc/modules/audio_coding/neteq/tick_timer.h" | 
|   23  |   23  | 
|   24 namespace webrtc { |   24 namespace webrtc { | 
|   25 namespace { |   25 namespace { | 
|   26 // Predicate used when inserting packets in the buffer list. |   26 // Predicate used when inserting packets in the buffer list. | 
|   27 // Operator() returns true when |packet| goes before |new_packet|. |   27 // Operator() returns true when |packet| goes before |new_packet|. | 
|   28 class NewTimestampIsLarger { |   28 class NewTimestampIsLarger { | 
|   29  public: |   29  public: | 
|   30   explicit NewTimestampIsLarger(const Packet* new_packet) |   30   explicit NewTimestampIsLarger(const Packet& new_packet) | 
|   31       : new_packet_(new_packet) { |   31       : new_packet_(new_packet) { | 
|   32   } |   32   } | 
|   33   bool operator()(Packet* packet) { |   33   bool operator()(const Packet& packet) { | 
|   34     return (*new_packet_ >= *packet); |   34     return (new_packet_ >= packet); | 
|   35   } |   35   } | 
|   36  |   36  | 
|   37  private: |   37  private: | 
|   38   const Packet* new_packet_; |   38   const Packet& new_packet_; | 
|   39 }; |   39 }; | 
|   40  |   40  | 
|   41 // Returns true if both payload types are known to the decoder database, and |   41 // Returns true if both payload types are known to the decoder database, and | 
|   42 // have the same sample rate. |   42 // have the same sample rate. | 
|   43 bool EqualSampleRates(uint8_t pt1, |   43 bool EqualSampleRates(uint8_t pt1, | 
|   44                       uint8_t pt2, |   44                       uint8_t pt2, | 
|   45                       const DecoderDatabase& decoder_database) { |   45                       const DecoderDatabase& decoder_database) { | 
|   46   auto di1 = decoder_database.GetDecoderInfo(pt1); |   46   auto di1 = decoder_database.GetDecoderInfo(pt1); | 
|   47   auto di2 = decoder_database.GetDecoderInfo(pt2); |   47   auto di2 = decoder_database.GetDecoderInfo(pt2); | 
|   48   return di1 && di2 && di1->SampleRateHz() == di2->SampleRateHz(); |   48   return di1 && di2 && di1->SampleRateHz() == di2->SampleRateHz(); | 
|   49 } |   49 } | 
|   50 }  // namespace |   50 }  // namespace | 
|   51  |   51  | 
|   52 PacketBuffer::PacketBuffer(size_t max_number_of_packets, |   52 PacketBuffer::PacketBuffer(size_t max_number_of_packets, | 
|   53                            const TickTimer* tick_timer) |   53                            const TickTimer* tick_timer) | 
|   54     : max_number_of_packets_(max_number_of_packets), tick_timer_(tick_timer) {} |   54     : max_number_of_packets_(max_number_of_packets), tick_timer_(tick_timer) {} | 
|   55  |   55  | 
|   56 // Destructor. All packets in the buffer will be destroyed. |   56 // Destructor. All packets in the buffer will be destroyed. | 
|   57 PacketBuffer::~PacketBuffer() { |   57 PacketBuffer::~PacketBuffer() { | 
|   58   Flush(); |   58   Flush(); | 
|   59 } |   59 } | 
|   60  |   60  | 
|   61 // Flush the buffer. All packets in the buffer will be destroyed. |   61 // Flush the buffer. All packets in the buffer will be destroyed. | 
|   62 void PacketBuffer::Flush() { |   62 void PacketBuffer::Flush() { | 
|   63   DeleteAllPackets(&buffer_); |   63   buffer_.clear(); | 
|   64 } |   64 } | 
|   65  |   65  | 
|   66 bool PacketBuffer::Empty() const { |   66 bool PacketBuffer::Empty() const { | 
|   67   return buffer_.empty(); |   67   return buffer_.empty(); | 
|   68 } |   68 } | 
|   69  |   69  | 
|   70 int PacketBuffer::InsertPacket(Packet* packet) { |   70 int PacketBuffer::InsertPacket(Packet&& packet) { | 
|   71   if (!packet || packet->empty()) { |   71   if (packet.empty()) { | 
|   72     if (packet) { |  | 
|   73       delete packet; |  | 
|   74     } |  | 
|   75     LOG(LS_WARNING) << "InsertPacket invalid packet"; |   72     LOG(LS_WARNING) << "InsertPacket invalid packet"; | 
|   76     return kInvalidPacket; |   73     return kInvalidPacket; | 
|   77   } |   74   } | 
|   78  |   75  | 
|   79   RTC_DCHECK_GE(packet->priority.codec_level, 0); |   76   RTC_DCHECK_GE(packet.priority.codec_level, 0); | 
|   80   RTC_DCHECK_GE(packet->priority.red_level, 0); |   77   RTC_DCHECK_GE(packet.priority.red_level, 0); | 
|   81  |   78  | 
|   82   int return_val = kOK; |   79   int return_val = kOK; | 
|   83  |   80  | 
|   84   packet->waiting_time = tick_timer_->GetNewStopwatch(); |   81   packet.waiting_time = tick_timer_->GetNewStopwatch(); | 
|   85  |   82  | 
|   86   if (buffer_.size() >= max_number_of_packets_) { |   83   if (buffer_.size() >= max_number_of_packets_) { | 
|   87     // Buffer is full. Flush it. |   84     // Buffer is full. Flush it. | 
|   88     Flush(); |   85     Flush(); | 
|   89     LOG(LS_WARNING) << "Packet buffer flushed"; |   86     LOG(LS_WARNING) << "Packet buffer flushed"; | 
|   90     return_val = kFlushed; |   87     return_val = kFlushed; | 
|   91   } |   88   } | 
|   92  |   89  | 
|   93   // Get an iterator pointing to the place in the buffer where the new packet |   90   // Get an iterator pointing to the place in the buffer where the new packet | 
|   94   // should be inserted. The list is searched from the back, since the most |   91   // should be inserted. The list is searched from the back, since the most | 
|   95   // likely case is that the new packet should be near the end of the list. |   92   // likely case is that the new packet should be near the end of the list. | 
|   96   PacketList::reverse_iterator rit = std::find_if( |   93   PacketList::reverse_iterator rit = std::find_if( | 
|   97       buffer_.rbegin(), buffer_.rend(), |   94       buffer_.rbegin(), buffer_.rend(), | 
|   98       NewTimestampIsLarger(packet)); |   95       NewTimestampIsLarger(packet)); | 
|   99  |   96  | 
|  100   // The new packet is to be inserted to the right of |rit|. If it has the same |   97   // The new packet is to be inserted to the right of |rit|. If it has the same | 
|  101   // timestamp as |rit|, which has a higher priority, do not insert the new |   98   // timestamp as |rit|, which has a higher priority, do not insert the new | 
|  102   // packet to list. |   99   // packet to list. | 
|  103   if (rit != buffer_.rend() && packet->timestamp == (*rit)->timestamp) { |  100   if (rit != buffer_.rend() && packet.timestamp == rit->timestamp) { | 
|  104     delete packet; |  | 
|  105     return return_val; |  101     return return_val; | 
|  106   } |  102   } | 
|  107  |  103  | 
|  108   // The new packet is to be inserted to the left of |it|. If it has the same |  104   // The new packet is to be inserted to the left of |it|. If it has the same | 
|  109   // timestamp as |it|, which has a lower priority, replace |it| with the new |  105   // timestamp as |it|, which has a lower priority, replace |it| with the new | 
|  110   // packet. |  106   // packet. | 
|  111   PacketList::iterator it = rit.base(); |  107   PacketList::iterator it = rit.base(); | 
|  112   if (it != buffer_.end() && packet->timestamp == (*it)->timestamp) { |  108   if (it != buffer_.end() && packet.timestamp == it->timestamp) { | 
|  113     delete *it; |  | 
|  114     it = buffer_.erase(it); |  109     it = buffer_.erase(it); | 
|  115   } |  110   } | 
|  116   buffer_.insert(it, packet);  // Insert the packet at that position. |  111   buffer_.insert(it, std::move(packet));  // Insert the packet at that position. | 
|  117  |  112  | 
|  118   return return_val; |  113   return return_val; | 
|  119 } |  114 } | 
|  120  |  115  | 
|  121 int PacketBuffer::InsertPacketList( |  116 int PacketBuffer::InsertPacketList( | 
|  122     PacketList* packet_list, |  117     PacketList* packet_list, | 
|  123     const DecoderDatabase& decoder_database, |  118     const DecoderDatabase& decoder_database, | 
|  124     rtc::Optional<uint8_t>* current_rtp_payload_type, |  119     rtc::Optional<uint8_t>* current_rtp_payload_type, | 
|  125     rtc::Optional<uint8_t>* current_cng_rtp_payload_type) { |  120     rtc::Optional<uint8_t>* current_cng_rtp_payload_type) { | 
|  126   bool flushed = false; |  121   bool flushed = false; | 
|  127   while (!packet_list->empty()) { |  122   for (auto& packet : *packet_list) { | 
|  128     Packet* packet = packet_list->front(); |  123     if (decoder_database.IsComfortNoise(packet.payload_type)) { | 
|  129     if (decoder_database.IsComfortNoise(packet->payload_type)) { |  | 
|  130       if (*current_cng_rtp_payload_type && |  124       if (*current_cng_rtp_payload_type && | 
|  131           **current_cng_rtp_payload_type != packet->payload_type) { |  125           **current_cng_rtp_payload_type != packet.payload_type) { | 
|  132         // New CNG payload type implies new codec type. |  126         // New CNG payload type implies new codec type. | 
|  133         *current_rtp_payload_type = rtc::Optional<uint8_t>(); |  127         *current_rtp_payload_type = rtc::Optional<uint8_t>(); | 
|  134         Flush(); |  128         Flush(); | 
|  135         flushed = true; |  129         flushed = true; | 
|  136       } |  130       } | 
|  137       *current_cng_rtp_payload_type = |  131       *current_cng_rtp_payload_type = | 
|  138           rtc::Optional<uint8_t>(packet->payload_type); |  132           rtc::Optional<uint8_t>(packet.payload_type); | 
|  139     } else if (!decoder_database.IsDtmf(packet->payload_type)) { |  133     } else if (!decoder_database.IsDtmf(packet.payload_type)) { | 
|  140       // This must be speech. |  134       // This must be speech. | 
|  141       if ((*current_rtp_payload_type && |  135       if ((*current_rtp_payload_type && | 
|  142            **current_rtp_payload_type != packet->payload_type) || |  136            **current_rtp_payload_type != packet.payload_type) || | 
|  143           (*current_cng_rtp_payload_type && |  137           (*current_cng_rtp_payload_type && | 
|  144            !EqualSampleRates(packet->payload_type, |  138            !EqualSampleRates(packet.payload_type, | 
|  145                              **current_cng_rtp_payload_type, |  139                              **current_cng_rtp_payload_type, | 
|  146                              decoder_database))) { |  140                              decoder_database))) { | 
|  147         *current_cng_rtp_payload_type = rtc::Optional<uint8_t>(); |  141         *current_cng_rtp_payload_type = rtc::Optional<uint8_t>(); | 
|  148         Flush(); |  142         Flush(); | 
|  149         flushed = true; |  143         flushed = true; | 
|  150       } |  144       } | 
|  151       *current_rtp_payload_type = rtc::Optional<uint8_t>(packet->payload_type); |  145       *current_rtp_payload_type = rtc::Optional<uint8_t>(packet.payload_type); | 
|  152     } |  146     } | 
|  153     int return_val = InsertPacket(packet); |  147     int return_val = InsertPacket(std::move(packet)); | 
|  154     packet_list->pop_front(); |  | 
|  155     if (return_val == kFlushed) { |  148     if (return_val == kFlushed) { | 
|  156       // The buffer flushed, but this is not an error. We can still continue. |  149       // The buffer flushed, but this is not an error. We can still continue. | 
|  157       flushed = true; |  150       flushed = true; | 
|  158     } else if (return_val != kOK) { |  151     } else if (return_val != kOK) { | 
|  159       // An error occurred. Delete remaining packets in list and return. |  152       // An error occurred. Delete remaining packets in list and return. | 
|  160       DeleteAllPackets(packet_list); |  153       packet_list->clear(); | 
|  161       return return_val; |  154       return return_val; | 
|  162     } |  155     } | 
|  163   } |  156   } | 
 |  157   packet_list->clear(); | 
|  164   return flushed ? kFlushed : kOK; |  158   return flushed ? kFlushed : kOK; | 
|  165 } |  159 } | 
|  166  |  160  | 
|  167 int PacketBuffer::NextTimestamp(uint32_t* next_timestamp) const { |  161 int PacketBuffer::NextTimestamp(uint32_t* next_timestamp) const { | 
|  168   if (Empty()) { |  162   if (Empty()) { | 
|  169     return kBufferEmpty; |  163     return kBufferEmpty; | 
|  170   } |  164   } | 
|  171   if (!next_timestamp) { |  165   if (!next_timestamp) { | 
|  172     return kInvalidPointer; |  166     return kInvalidPointer; | 
|  173   } |  167   } | 
|  174   *next_timestamp = buffer_.front()->timestamp; |  168   *next_timestamp = buffer_.front().timestamp; | 
|  175   return kOK; |  169   return kOK; | 
|  176 } |  170 } | 
|  177  |  171  | 
|  178 int PacketBuffer::NextHigherTimestamp(uint32_t timestamp, |  172 int PacketBuffer::NextHigherTimestamp(uint32_t timestamp, | 
|  179                                       uint32_t* next_timestamp) const { |  173                                       uint32_t* next_timestamp) const { | 
|  180   if (Empty()) { |  174   if (Empty()) { | 
|  181     return kBufferEmpty; |  175     return kBufferEmpty; | 
|  182   } |  176   } | 
|  183   if (!next_timestamp) { |  177   if (!next_timestamp) { | 
|  184     return kInvalidPointer; |  178     return kInvalidPointer; | 
|  185   } |  179   } | 
|  186   PacketList::const_iterator it; |  180   PacketList::const_iterator it; | 
|  187   for (it = buffer_.begin(); it != buffer_.end(); ++it) { |  181   for (it = buffer_.begin(); it != buffer_.end(); ++it) { | 
|  188     if ((*it)->timestamp >= timestamp) { |  182     if (it->timestamp >= timestamp) { | 
|  189       // Found a packet matching the search. |  183       // Found a packet matching the search. | 
|  190       *next_timestamp = (*it)->timestamp; |  184       *next_timestamp = it->timestamp; | 
|  191       return kOK; |  185       return kOK; | 
|  192     } |  186     } | 
|  193   } |  187   } | 
|  194   return kNotFound; |  188   return kNotFound; | 
|  195 } |  189 } | 
|  196  |  190  | 
|  197 const Packet* PacketBuffer::PeekNextPacket() const { |  191 const Packet* PacketBuffer::PeekNextPacket() const { | 
|  198   return buffer_.empty() ? nullptr : buffer_.front(); |  192   return buffer_.empty() ? nullptr : &buffer_.front(); | 
|  199 } |  193 } | 
|  200  |  194  | 
|  201 Packet* PacketBuffer::GetNextPacket(size_t* discard_count) { |  195 rtc::Optional<Packet> PacketBuffer::GetNextPacket() { | 
|  202   if (Empty()) { |  196   if (Empty()) { | 
|  203     // Buffer is empty. |  197     // Buffer is empty. | 
|  204     return NULL; |  198     return rtc::Optional<Packet>(); | 
|  205   } |  199   } | 
|  206  |  200  | 
|  207   Packet* packet = buffer_.front(); |  201   rtc::Optional<Packet> packet(std::move(buffer_.front())); | 
|  208   // Assert that the packet sanity checks in InsertPacket method works. |  202   // Assert that the packet sanity checks in InsertPacket method works. | 
|  209   RTC_DCHECK(packet && !packet->empty()); |  203   RTC_DCHECK(!packet->empty()); | 
|  210   buffer_.pop_front(); |  204   buffer_.pop_front(); | 
|  211  |  205  | 
|  212   // Discard other packets with the same timestamp. These are duplicates or |  | 
|  213   // redundant payloads that should not be used. |  | 
|  214   size_t discards = 0; |  | 
|  215  |  | 
|  216   while (!Empty() && buffer_.front()->timestamp == packet->timestamp) { |  | 
|  217     if (DiscardNextPacket() != kOK) { |  | 
|  218       assert(false);  // Must be ok by design. |  | 
|  219     } |  | 
|  220     ++discards; |  | 
|  221   } |  | 
|  222   // The way of inserting packet should not cause any packet discarding here. |  | 
|  223   // TODO(minyue): remove |discard_count|. |  | 
|  224   assert(discards == 0); |  | 
|  225   if (discard_count) |  | 
|  226     *discard_count = discards; |  | 
|  227  |  | 
|  228   return packet; |  206   return packet; | 
|  229 } |  207 } | 
|  230  |  208  | 
|  231 int PacketBuffer::DiscardNextPacket() { |  209 int PacketBuffer::DiscardNextPacket() { | 
|  232   if (Empty()) { |  210   if (Empty()) { | 
|  233     return kBufferEmpty; |  211     return kBufferEmpty; | 
|  234   } |  212   } | 
|  235   // Assert that the packet sanity checks in InsertPacket method works. |  213   // Assert that the packet sanity checks in InsertPacket method works. | 
|  236   RTC_DCHECK(buffer_.front()); |  214   RTC_DCHECK(!buffer_.front().empty()); | 
|  237   RTC_DCHECK(!buffer_.front()->empty()); |  215   buffer_.pop_front(); | 
|  238   DeleteFirstPacket(&buffer_); |  | 
|  239   return kOK; |  216   return kOK; | 
|  240 } |  217 } | 
|  241  |  218  | 
|  242 int PacketBuffer::DiscardOldPackets(uint32_t timestamp_limit, |  219 int PacketBuffer::DiscardOldPackets(uint32_t timestamp_limit, | 
|  243                                     uint32_t horizon_samples) { |  220                                     uint32_t horizon_samples) { | 
|  244   while (!Empty() && timestamp_limit != buffer_.front()->timestamp && |  221   while (!Empty() && timestamp_limit != buffer_.front().timestamp && | 
|  245          IsObsoleteTimestamp(buffer_.front()->timestamp, timestamp_limit, |  222          IsObsoleteTimestamp(buffer_.front().timestamp, timestamp_limit, | 
|  246                              horizon_samples)) { |  223                              horizon_samples)) { | 
|  247     if (DiscardNextPacket() != kOK) { |  224     if (DiscardNextPacket() != kOK) { | 
|  248       assert(false);  // Must be ok by design. |  225       assert(false);  // Must be ok by design. | 
|  249     } |  226     } | 
|  250   } |  227   } | 
|  251   return 0; |  228   return 0; | 
|  252 } |  229 } | 
|  253  |  230  | 
|  254 int PacketBuffer::DiscardAllOldPackets(uint32_t timestamp_limit) { |  231 int PacketBuffer::DiscardAllOldPackets(uint32_t timestamp_limit) { | 
|  255   return DiscardOldPackets(timestamp_limit, 0); |  232   return DiscardOldPackets(timestamp_limit, 0); | 
|  256 } |  233 } | 
|  257  |  234  | 
|  258 void PacketBuffer::DiscardPacketsWithPayloadType(uint8_t payload_type) { |  235 void PacketBuffer::DiscardPacketsWithPayloadType(uint8_t payload_type) { | 
|  259   for (auto it = buffer_.begin(); it != buffer_.end(); /* */) { |  236   for (auto it = buffer_.begin(); it != buffer_.end(); /* */) { | 
|  260     Packet* packet = *it; |  237     const Packet& packet = *it; | 
|  261     if (packet->payload_type == payload_type) { |  238     if (packet.payload_type == payload_type) { | 
|  262       delete packet; |  | 
|  263       it = buffer_.erase(it); |  239       it = buffer_.erase(it); | 
|  264     } else { |  240     } else { | 
|  265       ++it; |  241       ++it; | 
|  266     } |  242     } | 
|  267   } |  243   } | 
|  268 } |  244 } | 
|  269  |  245  | 
|  270 size_t PacketBuffer::NumPacketsInBuffer() const { |  246 size_t PacketBuffer::NumPacketsInBuffer() const { | 
|  271   return buffer_.size(); |  247   return buffer_.size(); | 
|  272 } |  248 } | 
|  273  |  249  | 
|  274 size_t PacketBuffer::NumSamplesInBuffer(size_t last_decoded_length) const { |  250 size_t PacketBuffer::NumSamplesInBuffer(size_t last_decoded_length) const { | 
|  275   size_t num_samples = 0; |  251   size_t num_samples = 0; | 
|  276   size_t last_duration = last_decoded_length; |  252   size_t last_duration = last_decoded_length; | 
|  277   for (Packet* packet : buffer_) { |  253   for (const Packet& packet : buffer_) { | 
|  278     if (packet->frame) { |  254     if (packet.frame) { | 
|  279       // TODO(hlundin): Verify that it's fine to count all packets and remove |  255       // TODO(hlundin): Verify that it's fine to count all packets and remove | 
|  280       // this check. |  256       // this check. | 
|  281       if (packet->priority != Packet::Priority(0, 0)) { |  257       if (packet.priority != Packet::Priority(0, 0)) { | 
|  282         continue; |  258         continue; | 
|  283       } |  259       } | 
|  284       size_t duration = packet->frame->Duration(); |  260       size_t duration = packet.frame->Duration(); | 
|  285       if (duration > 0) { |  261       if (duration > 0) { | 
|  286         last_duration = duration;  // Save the most up-to-date (valid) duration. |  262         last_duration = duration;  // Save the most up-to-date (valid) duration. | 
|  287       } |  263       } | 
|  288     } |  264     } | 
|  289     num_samples += last_duration; |  265     num_samples += last_duration; | 
|  290   } |  266   } | 
|  291   return num_samples; |  267   return num_samples; | 
|  292 } |  268 } | 
|  293  |  269  | 
|  294 bool PacketBuffer::DeleteFirstPacket(PacketList* packet_list) { |  | 
|  295   if (packet_list->empty()) { |  | 
|  296     return false; |  | 
|  297   } |  | 
|  298   Packet* first_packet = packet_list->front(); |  | 
|  299   delete first_packet; |  | 
|  300   packet_list->pop_front(); |  | 
|  301   return true; |  | 
|  302 } |  | 
|  303  |  | 
|  304 void PacketBuffer::DeleteAllPackets(PacketList* packet_list) { |  | 
|  305   while (DeleteFirstPacket(packet_list)) { |  | 
|  306     // Continue while the list is not empty. |  | 
|  307   } |  | 
|  308 } |  | 
|  309  |  | 
|  310 void PacketBuffer::BufferStat(int* num_packets, int* max_num_packets) const { |  270 void PacketBuffer::BufferStat(int* num_packets, int* max_num_packets) const { | 
|  311   *num_packets = static_cast<int>(buffer_.size()); |  271   *num_packets = static_cast<int>(buffer_.size()); | 
|  312   *max_num_packets = static_cast<int>(max_number_of_packets_); |  272   *max_num_packets = static_cast<int>(max_number_of_packets_); | 
|  313 } |  273 } | 
|  314  |  274  | 
|  315 }  // namespace webrtc |  275 }  // namespace webrtc | 
| OLD | NEW |