blob: bc51269b85916e1daf085f17b96a6cfb4bb59734 [file] [log] [blame]
pbos@webrtc.org29d58392013-05-16 12:08:03 +00001/*
2 * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
3 *
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
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
pbos@webrtc.org16e03b72013-10-28 16:32:01 +000011#include "webrtc/video/video_receive_stream.h"
pbos@webrtc.org29d58392013-05-16 12:08:03 +000012
pbos@webrtc.org12dc1a32013-08-05 16:22:53 +000013#include <stdlib.h>
pbos@webrtc.org29d58392013-05-16 12:08:03 +000014
mflodmand1590b22015-12-09 07:07:59 -080015#include <set>
mflodman@webrtc.orgb429e512013-12-18 09:46:22 +000016#include <string>
Erik Språng737336d2016-07-29 12:59:36 +020017#include <utility>
mflodman@webrtc.orgb429e512013-12-18 09:46:22 +000018
pbos@webrtc.org0d852d52015-02-09 15:14:36 +000019#include "webrtc/base/checks.h"
Peter Boström415d2cd2015-10-26 11:35:17 +010020#include "webrtc/base/logging.h"
pbos@webrtc.org29d58392013-05-16 12:08:03 +000021#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
Stefan Holmer80e12072016-02-23 13:30:42 +010022#include "webrtc/modules/congestion_controller/include/congestion_controller.h"
Peter Boström1d04ac62016-02-05 11:25:46 +010023#include "webrtc/modules/utility/include/process_thread.h"
Peter Boströma4c76882016-03-03 16:29:02 +010024#include "webrtc/modules/video_coding/include/video_coding.h"
sprang3911c262016-04-15 01:24:14 -070025#include "webrtc/modules/video_coding/utility/ivf_file_writer.h"
Henrik Kjellander98f53512015-10-28 18:17:40 +010026#include "webrtc/system_wrappers/include/clock.h"
Peter Boström7623ce42015-12-09 12:13:30 +010027#include "webrtc/video/call_stats.h"
sprang@webrtc.org09315702014-02-07 12:06:29 +000028#include "webrtc/video/receive_statistics_proxy.h"
pbos@webrtc.org16e03b72013-10-28 16:32:01 +000029#include "webrtc/video_receive_stream.h"
pbos@webrtc.org29d58392013-05-16 12:08:03 +000030
31namespace webrtc {
mflodmana20de202015-10-18 22:08:19 -070032
sprang3911c262016-04-15 01:24:14 -070033static const bool kEnableFrameRecording = false;
34
Peter Boströmf751bf82016-02-05 14:00:53 +010035static bool UseSendSideBwe(const VideoReceiveStream::Config& config) {
36 if (!config.rtp.transport_cc)
37 return false;
38 for (const auto& extension : config.rtp.extensions) {
isheriff6f8d6862016-05-26 11:24:55 -070039 if (extension.uri == RtpExtension::kTransportSequenceNumberUri)
mflodmana20de202015-10-18 22:08:19 -070040 return true;
41 }
42 return false;
43}
44
pbos@webrtc.org32e85282015-01-15 10:09:39 +000045std::string VideoReceiveStream::Decoder::ToString() const {
46 std::stringstream ss;
Peter Boström74f6e9e2016-04-04 17:56:10 +020047 ss << "{decoder: " << (decoder ? "(VideoDecoder)" : "nullptr");
pbos@webrtc.org32e85282015-01-15 10:09:39 +000048 ss << ", payload_type: " << payload_type;
49 ss << ", payload_name: " << payload_name;
pbos@webrtc.org32e85282015-01-15 10:09:39 +000050 ss << '}';
51
52 return ss.str();
53}
54
55std::string VideoReceiveStream::Config::ToString() const {
56 std::stringstream ss;
57 ss << "{decoders: [";
58 for (size_t i = 0; i < decoders.size(); ++i) {
59 ss << decoders[i].ToString();
60 if (i != decoders.size() - 1)
61 ss << ", ";
62 }
63 ss << ']';
64 ss << ", rtp: " << rtp.ToString();
Peter Boström74f6e9e2016-04-04 17:56:10 +020065 ss << ", renderer: " << (renderer ? "(renderer)" : "nullptr");
pbos@webrtc.org32e85282015-01-15 10:09:39 +000066 ss << ", render_delay_ms: " << render_delay_ms;
pbos8fc7fa72015-07-15 08:02:58 -070067 if (!sync_group.empty())
68 ss << ", sync_group: " << sync_group;
pbos@webrtc.org32e85282015-01-15 10:09:39 +000069 ss << ", pre_decode_callback: "
Peter Boström74f6e9e2016-04-04 17:56:10 +020070 << (pre_decode_callback ? "(EncodedFrameObserver)" : "nullptr");
pbos@webrtc.org32e85282015-01-15 10:09:39 +000071 ss << ", pre_render_callback: "
Peter Boström74f6e9e2016-04-04 17:56:10 +020072 << (pre_render_callback ? "(I420FrameCallback)" : "nullptr");
pbos@webrtc.org32e85282015-01-15 10:09:39 +000073 ss << ", target_delay_ms: " << target_delay_ms;
74 ss << '}';
75
76 return ss.str();
77}
78
79std::string VideoReceiveStream::Config::Rtp::ToString() const {
80 std::stringstream ss;
81 ss << "{remote_ssrc: " << remote_ssrc;
82 ss << ", local_ssrc: " << local_ssrc;
pbosda903ea2015-10-02 02:36:56 -070083 ss << ", rtcp_mode: "
84 << (rtcp_mode == RtcpMode::kCompound ? "RtcpMode::kCompound"
85 : "RtcpMode::kReducedSize");
pbos@webrtc.org32e85282015-01-15 10:09:39 +000086 ss << ", rtcp_xr: ";
87 ss << "{receiver_reference_time_report: "
88 << (rtcp_xr.receiver_reference_time_report ? "on" : "off");
89 ss << '}';
90 ss << ", remb: " << (remb ? "on" : "off");
stefan43edf0f2015-11-20 18:05:48 -080091 ss << ", transport_cc: " << (transport_cc ? "on" : "off");
pbos@webrtc.org32e85282015-01-15 10:09:39 +000092 ss << ", nack: {rtp_history_ms: " << nack.rtp_history_ms << '}';
93 ss << ", fec: " << fec.ToString();
94 ss << ", rtx: {";
95 for (auto& kv : rtx) {
96 ss << kv.first << " -> ";
97 ss << "{ssrc: " << kv.second.ssrc;
98 ss << ", payload_type: " << kv.second.payload_type;
99 ss << '}';
100 }
101 ss << '}';
102 ss << ", extensions: [";
103 for (size_t i = 0; i < extensions.size(); ++i) {
104 ss << extensions[i].ToString();
105 if (i != extensions.size() - 1)
106 ss << ", ";
107 }
108 ss << ']';
109 ss << '}';
110 return ss.str();
111}
112
pbos@webrtc.org776e6f22014-10-29 15:28:39 +0000113namespace {
114VideoCodec CreateDecoderVideoCodec(const VideoReceiveStream::Decoder& decoder) {
115 VideoCodec codec;
116 memset(&codec, 0, sizeof(codec));
117
118 codec.plType = decoder.payload_type;
mflodmand1590b22015-12-09 07:07:59 -0800119 strncpy(codec.plName, decoder.payload_name.c_str(), sizeof(codec.plName));
pbos@webrtc.org776e6f22014-10-29 15:28:39 +0000120 if (decoder.payload_name == "VP8") {
121 codec.codecType = kVideoCodecVP8;
ivica05cfcd32015-09-07 06:04:16 -0700122 } else if (decoder.payload_name == "VP9") {
123 codec.codecType = kVideoCodecVP9;
pbos@webrtc.org776e6f22014-10-29 15:28:39 +0000124 } else if (decoder.payload_name == "H264") {
125 codec.codecType = kVideoCodecH264;
126 } else {
127 codec.codecType = kVideoCodecGeneric;
128 }
129
130 if (codec.codecType == kVideoCodecVP8) {
131 codec.codecSpecific.VP8 = VideoEncoder::GetDefaultVp8Settings();
ivica05cfcd32015-09-07 06:04:16 -0700132 } else if (codec.codecType == kVideoCodecVP9) {
133 codec.codecSpecific.VP9 = VideoEncoder::GetDefaultVp9Settings();
pbos@webrtc.org776e6f22014-10-29 15:28:39 +0000134 } else if (codec.codecType == kVideoCodecH264) {
135 codec.codecSpecific.H264 = VideoEncoder::GetDefaultH264Settings();
136 }
137
138 codec.width = 320;
139 codec.height = 180;
140 codec.startBitrate = codec.minBitrate = codec.maxBitrate =
141 Call::Config::kDefaultStartBitrateBps / 1000;
142
143 return codec;
144}
145} // namespace
pbos@webrtc.org29d58392013-05-16 12:08:03 +0000146
Peter Boströmca835252016-02-11 15:59:46 +0100147namespace internal {
tommi2e82f382016-06-21 00:26:43 -0700148
mflodman0c478b32015-10-21 15:52:16 +0200149VideoReceiveStream::VideoReceiveStream(
150 int num_cpu_cores,
151 CongestionController* congestion_controller,
Tommi733b5472016-06-10 17:58:01 +0200152 VideoReceiveStream::Config config,
mflodman0c478b32015-10-21 15:52:16 +0200153 webrtc::VoiceEngine* voice_engine,
154 ProcessThread* process_thread,
Stefan Holmer58c664c2016-02-08 14:31:30 +0100155 CallStats* call_stats,
156 VieRemb* remb)
solenberg4fbae2b2015-08-28 04:07:10 -0700157 : transport_adapter_(config.rtcp_send_transport),
Tommi733b5472016-06-10 17:58:01 +0200158 config_(std::move(config)),
Peter Boström1d04ac62016-02-05 11:25:46 +0100159 process_thread_(process_thread),
sprang@webrtc.org09315702014-02-07 12:06:29 +0000160 clock_(Clock::GetRealTimeClock()),
Peter Boströmca835252016-02-11 15:59:46 +0100161 decode_thread_(DecodeThreadFunction, this, "DecodingThread"),
mflodman0c478b32015-10-21 15:52:16 +0200162 congestion_controller_(congestion_controller),
Peter Boström1d04ac62016-02-05 11:25:46 +0100163 call_stats_(call_stats),
Peter Boström0b250722016-04-22 18:23:15 +0200164 video_receiver_(clock_, nullptr, this, this, this),
Tommi733b5472016-06-10 17:58:01 +0200165 stats_proxy_(&config_, clock_),
Erik Språng737336d2016-07-29 12:59:36 +0200166 rtp_stream_receiver_(
167 &video_receiver_,
168 congestion_controller_->GetRemoteBitrateEstimator(
169 UseSendSideBwe(config_)),
170 &transport_adapter_,
171 call_stats_->rtcp_rtt_stats(),
172 congestion_controller_->pacer(),
173 congestion_controller_->packet_router(),
174 remb,
175 &config_,
176 &stats_proxy_,
177 process_thread_,
178 congestion_controller_->GetRetransmissionRateLimiter()),
mflodmandc7d0d22016-05-06 05:32:22 -0700179 vie_sync_(&video_receiver_) {
pbosa2f30de2015-10-15 05:22:13 -0700180 LOG(LS_INFO) << "VideoReceiveStream: " << config_.ToString();
pbos@webrtc.org29d58392013-05-16 12:08:03 +0000181
Stefan Holmer58c664c2016-02-08 14:31:30 +0100182 RTC_DCHECK(process_thread_);
183 RTC_DCHECK(congestion_controller_);
184 RTC_DCHECK(call_stats_);
mflodmancfc8e3b2016-05-03 21:22:04 -0700185
henrikg91d6ede2015-09-17 00:24:34 -0700186 RTC_DCHECK(!config_.decoders.empty());
Peter Boström521af4e2015-11-27 16:35:04 +0100187 std::set<int> decoder_payload_types;
Peter Boströmb1ae3a42016-02-15 17:52:40 +0100188 for (const Decoder& decoder : config_.decoders) {
Peter Boström795dbe42015-11-27 14:09:07 +0100189 RTC_CHECK(decoder.decoder);
Peter Boström521af4e2015-11-27 16:35:04 +0100190 RTC_CHECK(decoder_payload_types.find(decoder.payload_type) ==
191 decoder_payload_types.end())
192 << "Duplicate payload type (" << decoder.payload_type
193 << ") for different decoders.";
194 decoder_payload_types.insert(decoder.payload_type);
Peter Boström0b250722016-04-22 18:23:15 +0200195 video_receiver_.RegisterExternalDecoder(decoder.decoder,
196 decoder.payload_type);
pbos@webrtc.org776e6f22014-10-29 15:28:39 +0000197
198 VideoCodec codec = CreateDecoderVideoCodec(decoder);
mflodmanfa666592016-04-28 23:15:33 -0700199 RTC_CHECK(rtp_stream_receiver_.SetReceiveCodec(codec));
Peter Boström0b250722016-04-22 18:23:15 +0200200 RTC_CHECK_EQ(VCM_OK, video_receiver_.RegisterReceiveCodec(
201 &codec, num_cpu_cores, false));
pbos@webrtc.org0181b5f2013-09-09 08:26:30 +0000202 }
203
Peter Boström0b250722016-04-22 18:23:15 +0200204 video_receiver_.SetRenderDelay(config.render_delay_ms);
Peter Boström1d04ac62016-02-05 11:25:46 +0100205
Peter Boström0b250722016-04-22 18:23:15 +0200206 process_thread_->RegisterModule(&video_receiver_);
Peter Boström1794b262016-02-16 14:12:02 +0100207 process_thread_->RegisterModule(&vie_sync_);
pbos@webrtc.org29d58392013-05-16 12:08:03 +0000208}
209
210VideoReceiveStream::~VideoReceiveStream() {
pbosa2f30de2015-10-15 05:22:13 -0700211 LOG(LS_INFO) << "~VideoReceiveStream: " << config_.ToString();
Peter Boströmca835252016-02-11 15:59:46 +0100212 Stop();
213
Peter Boström1794b262016-02-16 14:12:02 +0100214 process_thread_->DeRegisterModule(&vie_sync_);
Peter Boström0b250722016-04-22 18:23:15 +0200215 process_thread_->DeRegisterModule(&video_receiver_);
Peter Boströmb1ae3a42016-02-15 17:52:40 +0100216
mflodmancfc8e3b2016-05-03 21:22:04 -0700217 // Deregister external decoders so they are no longer running during
Peter Boströmb1ae3a42016-02-15 17:52:40 +0100218 // destruction. This effectively stops the VCM since the decoder thread is
219 // stopped, the VCM is deregistered and no asynchronous decoder threads are
220 // running.
221 for (const Decoder& decoder : config_.decoders)
Peter Boström0b250722016-04-22 18:23:15 +0200222 video_receiver_.RegisterExternalDecoder(nullptr, decoder.payload_type);
Peter Boströmb1ae3a42016-02-15 17:52:40 +0100223
Peter Boströmf751bf82016-02-05 14:00:53 +0100224 congestion_controller_->GetRemoteBitrateEstimator(UseSendSideBwe(config_))
mflodmanfa666592016-04-28 23:15:33 -0700225 ->RemoveStream(rtp_stream_receiver_.GetRemoteSsrc());
pbos@webrtc.org29d58392013-05-16 12:08:03 +0000226}
227
pbos1ba8d392016-05-01 20:18:34 -0700228void VideoReceiveStream::SignalNetworkState(NetworkState state) {
mflodmandc7d0d22016-05-06 05:32:22 -0700229 rtp_stream_receiver_.SignalNetworkState(state);
pbos1ba8d392016-05-01 20:18:34 -0700230}
231
232
233bool VideoReceiveStream::DeliverRtcp(const uint8_t* packet, size_t length) {
234 return rtp_stream_receiver_.DeliverRtcp(packet, length);
235}
236
237bool VideoReceiveStream::DeliverRtp(const uint8_t* packet,
238 size_t length,
239 const PacketTime& packet_time) {
240 return rtp_stream_receiver_.DeliverRtp(packet, length, packet_time);
241}
242
pbos@webrtc.orga5c8d2c2014-04-24 11:13:21 +0000243void VideoReceiveStream::Start() {
Peter Boströmca835252016-02-11 15:59:46 +0100244 if (decode_thread_.IsRunning())
245 return;
sprang@webrtc.orgd9b95602014-01-27 13:03:02 +0000246 transport_adapter_.Enable();
tommi2e82f382016-06-21 00:26:43 -0700247 rtc::VideoSinkInterface<VideoFrame>* renderer = nullptr;
248 if (config_.renderer) {
249 if (config_.disable_prerenderer_smoothing) {
250 renderer = this;
251 } else {
252 incoming_video_stream_.reset(
253 new IncomingVideoStream(config_.render_delay_ms, this));
254 renderer = incoming_video_stream_.get();
255 }
256 }
257
258 video_stream_decoder_.reset(new VideoStreamDecoder(
259 &video_receiver_, &rtp_stream_receiver_, &rtp_stream_receiver_,
260 rtp_stream_receiver_.IsRetransmissionsEnabled(),
261 rtp_stream_receiver_.IsFecEnabled(), &stats_proxy_, renderer,
262 config_.pre_render_callback));
263 // Register the channel to receive stats updates.
264 call_stats_->RegisterStatsObserver(video_stream_decoder_.get());
Peter Boströmca835252016-02-11 15:59:46 +0100265 // Start the decode thread
266 decode_thread_.Start();
267 decode_thread_.SetPriority(rtc::kHighestPriority);
mflodmanfa666592016-04-28 23:15:33 -0700268 rtp_stream_receiver_.StartReceive();
pbos@webrtc.org29d58392013-05-16 12:08:03 +0000269}
270
pbos@webrtc.orga5c8d2c2014-04-24 11:13:21 +0000271void VideoReceiveStream::Stop() {
mflodmanfa666592016-04-28 23:15:33 -0700272 rtp_stream_receiver_.StopReceive();
sprang22691e02016-07-13 10:57:07 -0700273 // TriggerDecoderShutdown will release any waiting decoder thread and make it
274 // stop immediately, instead of waiting for a timeout. Needs to be called
275 // before joining the decoder thread thread.
Peter Boström0b250722016-04-22 18:23:15 +0200276 video_receiver_.TriggerDecoderShutdown();
Peter Boströmca835252016-02-11 15:59:46 +0100277 decode_thread_.Stop();
tommi2e82f382016-06-21 00:26:43 -0700278 call_stats_->DeregisterStatsObserver(video_stream_decoder_.get());
279 video_stream_decoder_.reset();
280 incoming_video_stream_.reset();
sprang@webrtc.orgd9b95602014-01-27 13:03:02 +0000281 transport_adapter_.Disable();
pbos@webrtc.org29d58392013-05-16 12:08:03 +0000282}
283
pbos8fc7fa72015-07-15 08:02:58 -0700284void VideoReceiveStream::SetSyncChannel(VoiceEngine* voice_engine,
285 int audio_channel_id) {
Peter Boström74f6e9e2016-04-04 17:56:10 +0200286 if (voice_engine && audio_channel_id != -1) {
pbos8fc7fa72015-07-15 08:02:58 -0700287 VoEVideoSync* voe_sync_interface = VoEVideoSync::GetInterface(voice_engine);
mflodmandc7d0d22016-05-06 05:32:22 -0700288 vie_sync_.ConfigureSync(audio_channel_id, voe_sync_interface,
289 rtp_stream_receiver_.rtp_rtcp(),
mflodmanfa666592016-04-28 23:15:33 -0700290 rtp_stream_receiver_.GetRtpReceiver());
pbos8fc7fa72015-07-15 08:02:58 -0700291 voe_sync_interface->Release();
mflodmandc7d0d22016-05-06 05:32:22 -0700292 } else {
293 vie_sync_.ConfigureSync(-1, nullptr, rtp_stream_receiver_.rtp_rtcp(),
294 rtp_stream_receiver_.GetRtpReceiver());
pbos8fc7fa72015-07-15 08:02:58 -0700295 }
296}
297
sprang@webrtc.org9510e532014-02-07 15:32:45 +0000298VideoReceiveStream::Stats VideoReceiveStream::GetStats() const {
Peter Boströmf751bf82016-02-05 14:00:53 +0100299 return stats_proxy_.GetStats();
sprang@webrtc.org09315702014-02-07 12:06:29 +0000300}
301
tommi2e82f382016-06-21 00:26:43 -0700302// TODO(tommi): This method grabs a lock 6 times.
Tommibd3380f2016-06-10 17:38:17 +0200303void VideoReceiveStream::OnFrame(const VideoFrame& video_frame) {
tommi2e82f382016-06-21 00:26:43 -0700304 // TODO(tommi): OnDecodedFrame grabs a lock, incidentally the same lock
305 // that OnSyncOffsetUpdated() and OnRenderedFrame() below grab.
Peter Boströmf751bf82016-02-05 14:00:53 +0100306 stats_proxy_.OnDecodedFrame();
sprang@webrtc.org09315702014-02-07 12:06:29 +0000307
asaperssonf8cdd182016-03-15 01:00:47 -0700308 int64_t sync_offset_ms;
tommi2e82f382016-06-21 00:26:43 -0700309 // TODO(tommi): GetStreamSyncOffsetInMs grabs three locks. One inside the
310 // function itself, another in GetChannel() and a third in
311 // GetPlayoutTimestamp. Seems excessive. Anyhow, I'm assuming the function
312 // succeeds most of the time, which leads to grabbing a fourth lock.
313 if (vie_sync_.GetStreamSyncOffsetInMs(video_frame, &sync_offset_ms)) {
314 // TODO(tommi): OnSyncOffsetUpdated grabs a lock.
asaperssonf8cdd182016-03-15 01:00:47 -0700315 stats_proxy_.OnSyncOffsetUpdated(sync_offset_ms);
tommi2e82f382016-06-21 00:26:43 -0700316 }
asaperssonf8cdd182016-03-15 01:00:47 -0700317
tommi2e82f382016-06-21 00:26:43 -0700318 // config_.renderer must never be null if we're getting this callback.
319 config_.renderer->OnFrame(video_frame);
pbos@webrtc.org29d58392013-05-16 12:08:03 +0000320
tommi2e82f382016-06-21 00:26:43 -0700321 // TODO(tommi): OnRenderFrame grabs a lock too.
asaperssona1862882016-04-18 00:41:05 -0700322 stats_proxy_.OnRenderedFrame(video_frame.width(), video_frame.height());
pbos@webrtc.org29d58392013-05-16 12:08:03 +0000323}
pbos@webrtc.org26c0c412014-09-03 16:17:12 +0000324
asapersson86b01602015-10-20 23:55:26 -0700325// TODO(asapersson): Consider moving callback from video_encoder.h or
326// creating a different callback.
327int32_t VideoReceiveStream::Encoded(
328 const EncodedImage& encoded_image,
329 const CodecSpecificInfo* codec_specific_info,
330 const RTPFragmentationHeader* fragmentation) {
Peter Boströmf751bf82016-02-05 14:00:53 +0100331 stats_proxy_.OnPreDecode(encoded_image, codec_specific_info);
asapersson86b01602015-10-20 23:55:26 -0700332 if (config_.pre_decode_callback) {
sergeyu37ad3372016-06-14 15:29:37 -0700333 config_.pre_decode_callback->EncodedFrameCallback(
334 EncodedFrame(encoded_image._buffer, encoded_image._length,
335 encoded_image._frameType));
asapersson86b01602015-10-20 23:55:26 -0700336 }
sprang3911c262016-04-15 01:24:14 -0700337 if (kEnableFrameRecording) {
338 if (!ivf_writer_.get()) {
339 RTC_DCHECK(codec_specific_info);
sprang3911c262016-04-15 01:24:14 -0700340 std::ostringstream oss;
341 oss << "receive_bitstream_ssrc_" << config_.rtp.remote_ssrc << ".ivf";
kjellander02b3d272016-04-20 05:05:54 -0700342 ivf_writer_ =
343 IvfFileWriter::Open(oss.str(), codec_specific_info->codecType);
sprang3911c262016-04-15 01:24:14 -0700344 }
345 if (ivf_writer_.get()) {
346 bool ok = ivf_writer_->WriteFrame(encoded_image);
347 RTC_DCHECK(ok);
348 }
349 }
350
asapersson86b01602015-10-20 23:55:26 -0700351 return 0;
352}
353
Peter Boströmca835252016-02-11 15:59:46 +0100354bool VideoReceiveStream::DecodeThreadFunction(void* ptr) {
355 static_cast<VideoReceiveStream*>(ptr)->Decode();
356 return true;
357}
358
359void VideoReceiveStream::Decode() {
360 static const int kMaxDecodeWaitTimeMs = 50;
Peter Boström0b250722016-04-22 18:23:15 +0200361 video_receiver_.Decode(kMaxDecodeWaitTimeMs);
Peter Boströmca835252016-02-11 15:59:46 +0100362}
363
philipel83f831a2016-03-12 03:30:23 -0800364void VideoReceiveStream::SendNack(
365 const std::vector<uint16_t>& sequence_numbers) {
mflodmandc7d0d22016-05-06 05:32:22 -0700366 rtp_stream_receiver_.RequestPacketRetransmit(sequence_numbers);
philipel83f831a2016-03-12 03:30:23 -0800367}
368
369void VideoReceiveStream::RequestKeyFrame() {
mflodmandc7d0d22016-05-06 05:32:22 -0700370 rtp_stream_receiver_.RequestKeyFrame();
philipel83f831a2016-03-12 03:30:23 -0800371}
372
mflodman@webrtc.orgf3973e82013-12-13 09:40:45 +0000373} // namespace internal
374} // namespace webrtc