| Index: media/cast/video_receiver/video_receiver.cc
|
| diff --git a/media/cast/video_receiver/video_receiver.cc b/media/cast/video_receiver/video_receiver.cc
|
| index 95b1d5127a8e96f75915b75de9fb7c02b9aa2587..2aaa5d2b8e7a5858426ff9c6bc8b0ee11541cc03 100644
|
| --- a/media/cast/video_receiver/video_receiver.cc
|
| +++ b/media/cast/video_receiver/video_receiver.cc
|
| @@ -10,45 +10,23 @@
|
| #include "base/debug/trace_event.h"
|
| #include "base/logging.h"
|
| #include "base/message_loop/message_loop.h"
|
| -#include "media/cast/cast_defines.h"
|
| -#include "media/cast/framer/framer.h"
|
| -#include "media/cast/rtcp/receiver_rtcp_event_subscriber.h"
|
| -#include "media/cast/rtcp/rtcp_sender.h"
|
| +#include "media/base/video_frame.h"
|
| +#include "media/cast/logging/logging_defines.h"
|
| +#include "media/cast/transport/cast_transport_defines.h"
|
| #include "media/cast/video_receiver/video_decoder.h"
|
|
|
| namespace {
|
| -
|
| -static const int64 kMinSchedulingDelayMs = 1;
|
| -static const int64 kMinTimeBetweenOffsetUpdatesMs = 1000;
|
| -static const int kTimeOffsetMaxCounter = 10;
|
| -
|
| +const int kMinSchedulingDelayMs = 1;
|
| +const int kMinTimeBetweenOffsetUpdatesMs = 1000;
|
| +const int kTimeOffsetMaxCounter = 10;
|
| } // namespace
|
|
|
| namespace media {
|
| namespace cast {
|
|
|
| -// Local implementation of RtpPayloadFeedback (defined in rtp_defines.h)
|
| -// Used to convey cast-specific feedback from receiver to sender.
|
| -// Callback triggered by the Framer (cast message builder).
|
| -class LocalRtpVideoFeedback : public RtpPayloadFeedback {
|
| - public:
|
| - explicit LocalRtpVideoFeedback(VideoReceiver* video_receiver)
|
| - : video_receiver_(video_receiver) {}
|
| -
|
| - virtual void CastFeedback(const RtcpCastMessage& cast_message) OVERRIDE {
|
| - video_receiver_->CastFeedback(cast_message);
|
| - }
|
| -
|
| - private:
|
| - VideoReceiver* video_receiver_;
|
| -
|
| - DISALLOW_IMPLICIT_CONSTRUCTORS(LocalRtpVideoFeedback);
|
| -};
|
| -
|
| VideoReceiver::VideoReceiver(scoped_refptr<CastEnvironment> cast_environment,
|
| const VideoReceiverConfig& video_config,
|
| - transport::PacedPacketSender* const packet_sender,
|
| - const SetTargetDelayCallback& target_delay_cb)
|
| + transport::PacedPacketSender* const packet_sender)
|
| : RtpReceiver(cast_environment->Clock(), NULL, &video_config),
|
| cast_environment_(cast_environment),
|
| event_subscriber_(kReceiverRtcpEventHistorySize,
|
| @@ -56,48 +34,52 @@ VideoReceiver::VideoReceiver(scoped_refptr<CastEnvironment> cast_environment,
|
| codec_(video_config.codec),
|
| target_delay_delta_(
|
| base::TimeDelta::FromMilliseconds(video_config.rtp_max_delay_ms)),
|
| - frame_delay_(base::TimeDelta::FromMilliseconds(
|
| - 1000 / video_config.max_frame_rate)),
|
| - incoming_payload_feedback_(new LocalRtpVideoFeedback(this)),
|
| + expected_frame_duration_(
|
| + base::TimeDelta::FromSeconds(1) / video_config.max_frame_rate),
|
| + framer_(cast_environment->Clock(),
|
| + this,
|
| + video_config.incoming_ssrc,
|
| + video_config.decoder_faster_than_max_frame_rate,
|
| + video_config.rtp_max_delay_ms * video_config.max_frame_rate /
|
| + 1000),
|
| + rtcp_(cast_environment_,
|
| + NULL,
|
| + NULL,
|
| + packet_sender,
|
| + GetStatistics(),
|
| + video_config.rtcp_mode,
|
| + base::TimeDelta::FromMilliseconds(video_config.rtcp_interval),
|
| + video_config.feedback_ssrc,
|
| + video_config.incoming_ssrc,
|
| + video_config.rtcp_c_name),
|
| time_offset_counter_(0),
|
| - decryptor_(),
|
| time_incoming_packet_updated_(false),
|
| incoming_rtp_timestamp_(0),
|
| - target_delay_cb_(target_delay_cb),
|
| + is_waiting_for_consecutive_frame_(false),
|
| weak_factory_(this) {
|
| - int max_unacked_frames =
|
| - video_config.rtp_max_delay_ms * video_config.max_frame_rate / 1000;
|
| - DCHECK(max_unacked_frames) << "Invalid argument";
|
| -
|
| - decryptor_.Initialize(video_config.aes_key, video_config.aes_iv_mask);
|
| - framer_.reset(new Framer(cast_environment->Clock(),
|
| - incoming_payload_feedback_.get(),
|
| - video_config.incoming_ssrc,
|
| - video_config.decoder_faster_than_max_frame_rate,
|
| - max_unacked_frames));
|
| -
|
| + DCHECK_GT(video_config.rtp_max_delay_ms, 0);
|
| + DCHECK_GT(video_config.max_frame_rate, 0);
|
| if (!video_config.use_external_decoder) {
|
| - video_decoder_.reset(new VideoDecoder(video_config, cast_environment));
|
| + video_decoder_.reset(new VideoDecoder(cast_environment, video_config));
|
| }
|
| -
|
| - rtcp_.reset(
|
| - new Rtcp(cast_environment_,
|
| - NULL,
|
| - NULL,
|
| - packet_sender,
|
| - GetStatistics(),
|
| - video_config.rtcp_mode,
|
| - base::TimeDelta::FromMilliseconds(video_config.rtcp_interval),
|
| - video_config.feedback_ssrc,
|
| - video_config.incoming_ssrc,
|
| - video_config.rtcp_c_name));
|
| - // Set the target delay that will be conveyed to the sender.
|
| - rtcp_->SetTargetDelay(target_delay_delta_);
|
| + decryptor_.Initialize(video_config.aes_key, video_config.aes_iv_mask);
|
| + rtcp_.SetTargetDelay(target_delay_delta_);
|
| cast_environment_->Logging()->AddRawEventSubscriber(&event_subscriber_);
|
| memset(frame_id_to_rtp_timestamp_, 0, sizeof(frame_id_to_rtp_timestamp_));
|
| }
|
|
|
| VideoReceiver::~VideoReceiver() {
|
| + DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
|
| +
|
| + // If any callbacks for encoded video frames are queued, flush them out now.
|
| + // This is critical because some Closures in |frame_request_queue_| may have
|
| + // Unretained references to |this|.
|
| + while (!frame_request_queue_.empty()) {
|
| + frame_request_queue_.front().Run(
|
| + make_scoped_ptr<transport::EncodedVideoFrame>(NULL), base::TimeTicks());
|
| + frame_request_queue_.pop_front();
|
| + }
|
| +
|
| cast_environment_->Logging()->RemoveRawEventSubscriber(&event_subscriber_);
|
| }
|
|
|
| @@ -110,209 +92,155 @@ void VideoReceiver::InitializeTimers() {
|
| void VideoReceiver::GetRawVideoFrame(
|
| const VideoFrameDecodedCallback& callback) {
|
| DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
|
| + DCHECK(!callback.is_null());
|
| + DCHECK(video_decoder_.get());
|
| GetEncodedVideoFrame(base::Bind(
|
| - &VideoReceiver::DecodeVideoFrame, base::Unretained(this), callback));
|
| + &VideoReceiver::DecodeEncodedVideoFrame,
|
| + // Note: Use of Unretained is safe since this Closure is guaranteed to be
|
| + // invoked before destruction of |this|.
|
| + base::Unretained(this),
|
| + callback));
|
| }
|
|
|
| -// Called when we have a frame to decode.
|
| -void VideoReceiver::DecodeVideoFrame(
|
| +void VideoReceiver::DecodeEncodedVideoFrame(
|
| const VideoFrameDecodedCallback& callback,
|
| scoped_ptr<transport::EncodedVideoFrame> encoded_frame,
|
| - const base::TimeTicks& render_time) {
|
| + const base::TimeTicks& playout_time) {
|
| DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
|
| -
|
| - // Hand the ownership of the encoded frame to the decode thread.
|
| - cast_environment_->PostTask(CastEnvironment::VIDEO,
|
| - FROM_HERE,
|
| - base::Bind(&VideoReceiver::DecodeVideoFrameThread,
|
| - base::Unretained(this),
|
| - base::Passed(&encoded_frame),
|
| - render_time,
|
| - callback));
|
| -}
|
| -
|
| -// Utility function to run the decoder on a designated decoding thread.
|
| -void VideoReceiver::DecodeVideoFrameThread(
|
| - scoped_ptr<transport::EncodedVideoFrame> encoded_frame,
|
| - const base::TimeTicks render_time,
|
| - const VideoFrameDecodedCallback& frame_decoded_callback) {
|
| - DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::VIDEO));
|
| - DCHECK(video_decoder_);
|
| -
|
| - if (!(video_decoder_->DecodeVideoFrame(
|
| - encoded_frame.get(), render_time, frame_decoded_callback))) {
|
| - // This will happen if we decide to decode but not show a frame.
|
| - cast_environment_->PostTask(CastEnvironment::MAIN,
|
| - FROM_HERE,
|
| - base::Bind(&VideoReceiver::GetRawVideoFrame,
|
| - base::Unretained(this),
|
| - frame_decoded_callback));
|
| + if (!encoded_frame) {
|
| + callback.Run(make_scoped_refptr<VideoFrame>(NULL), playout_time, false);
|
| + return;
|
| }
|
| + const uint32 frame_id = encoded_frame->frame_id;
|
| + const uint32 rtp_timestamp = encoded_frame->rtp_timestamp;
|
| + video_decoder_->DecodeFrame(encoded_frame.Pass(),
|
| + base::Bind(&VideoReceiver::EmitRawVideoFrame,
|
| + cast_environment_,
|
| + callback,
|
| + frame_id,
|
| + rtp_timestamp,
|
| + playout_time));
|
| }
|
|
|
| -bool VideoReceiver::DecryptVideoFrame(
|
| - scoped_ptr<transport::EncodedVideoFrame>* video_frame) {
|
| - DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
|
| -
|
| - if (!decryptor_.initialized())
|
| - return false;
|
| -
|
| - std::string decrypted_video_data;
|
| - if (!decryptor_.Decrypt((*video_frame)->frame_id,
|
| - (*video_frame)->data,
|
| - &decrypted_video_data)) {
|
| - // Give up on this frame, release it from jitter buffer.
|
| - framer_->ReleaseFrame((*video_frame)->frame_id);
|
| - return false;
|
| +// static
|
| +void VideoReceiver::EmitRawVideoFrame(
|
| + const scoped_refptr<CastEnvironment>& cast_environment,
|
| + const VideoFrameDecodedCallback& callback,
|
| + uint32 frame_id,
|
| + uint32 rtp_timestamp,
|
| + const base::TimeTicks& playout_time,
|
| + const scoped_refptr<VideoFrame>& video_frame,
|
| + bool is_continuous) {
|
| + DCHECK(cast_environment->CurrentlyOn(CastEnvironment::MAIN));
|
| + if (video_frame) {
|
| + const base::TimeTicks now = cast_environment->Clock()->NowTicks();
|
| + cast_environment->Logging()->InsertFrameEvent(
|
| + now, kVideoFrameDecoded, rtp_timestamp, frame_id);
|
| + cast_environment->Logging()->InsertFrameEventWithDelay(
|
| + now, kVideoRenderDelay, rtp_timestamp, frame_id,
|
| + playout_time - now);
|
| + // Used by chrome/browser/extension/api/cast_streaming/performance_test.cc
|
| + TRACE_EVENT_INSTANT1(
|
| + "cast_perf_test", "FrameDecoded",
|
| + TRACE_EVENT_SCOPE_THREAD,
|
| + "rtp_timestamp", rtp_timestamp);
|
| }
|
| - (*video_frame)->data.swap(decrypted_video_data);
|
| - return true;
|
| + callback.Run(video_frame, playout_time, is_continuous);
|
| }
|
|
|
| -// Called from the main cast thread.
|
| void VideoReceiver::GetEncodedVideoFrame(
|
| const VideoFrameEncodedCallback& callback) {
|
| DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
|
| - scoped_ptr<transport::EncodedVideoFrame> encoded_frame(
|
| - new transport::EncodedVideoFrame());
|
| - bool next_frame = false;
|
| -
|
| - if (!framer_->GetEncodedVideoFrame(encoded_frame.get(), &next_frame)) {
|
| - // We have no video frames. Wait for new packet(s).
|
| - queued_encoded_callbacks_.push_back(callback);
|
| - return;
|
| - }
|
| -
|
| - if (decryptor_.initialized() && !DecryptVideoFrame(&encoded_frame)) {
|
| - // Logging already done.
|
| - queued_encoded_callbacks_.push_back(callback);
|
| - return;
|
| - }
|
| -
|
| - base::TimeTicks render_time;
|
| - if (PullEncodedVideoFrame(next_frame, &encoded_frame, &render_time)) {
|
| - cast_environment_->PostTask(
|
| - CastEnvironment::MAIN,
|
| - FROM_HERE,
|
| - base::Bind(callback, base::Passed(&encoded_frame), render_time));
|
| - } else {
|
| - // We have a video frame; however we are missing packets and we have time
|
| - // to wait for new packet(s).
|
| - queued_encoded_callbacks_.push_back(callback);
|
| - }
|
| + frame_request_queue_.push_back(callback);
|
| + EmitAvailableEncodedFrames();
|
| }
|
|
|
| -// Should we pull the encoded video frame from the framer? decided by if this is
|
| -// the next frame or we are running out of time and have to pull the following
|
| -// frame.
|
| -// If the frame is too old to be rendered we set the don't show flag in the
|
| -// video bitstream where possible.
|
| -bool VideoReceiver::PullEncodedVideoFrame(
|
| - bool next_frame,
|
| - scoped_ptr<transport::EncodedVideoFrame>* encoded_frame,
|
| - base::TimeTicks* render_time) {
|
| +void VideoReceiver::EmitAvailableEncodedFrames() {
|
| DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
|
| - base::TimeTicks now = cast_environment_->Clock()->NowTicks();
|
| - *render_time = GetRenderTime(now, (*encoded_frame)->rtp_timestamp);
|
|
|
| - // TODO(mikhal): Store actual render time and not diff.
|
| - cast_environment_->Logging()->InsertFrameEventWithDelay(
|
| - now,
|
| - kVideoRenderDelay,
|
| - (*encoded_frame)->rtp_timestamp,
|
| - (*encoded_frame)->frame_id,
|
| - now - *render_time);
|
| -
|
| - // Minimum time before a frame is due to be rendered before we pull it for
|
| - // decode.
|
| - base::TimeDelta min_wait_delta = frame_delay_;
|
| - base::TimeDelta time_until_render = *render_time - now;
|
| - if (!next_frame && (time_until_render > min_wait_delta)) {
|
| - // Example:
|
| - // We have decoded frame 1 and we have received the complete frame 3, but
|
| - // not frame 2. If we still have time before frame 3 should be rendered we
|
| - // will wait for 2 to arrive, however if 2 never show up this timer will hit
|
| - // and we will pull out frame 3 for decoding and rendering.
|
| - base::TimeDelta time_until_release = time_until_render - min_wait_delta;
|
| - cast_environment_->PostDelayedTask(
|
| - CastEnvironment::MAIN,
|
| - FROM_HERE,
|
| - base::Bind(&VideoReceiver::PlayoutTimeout, weak_factory_.GetWeakPtr()),
|
| - time_until_release);
|
| - VLOG(1) << "Wait before releasing frame "
|
| - << static_cast<int>((*encoded_frame)->frame_id) << " time "
|
| - << time_until_release.InMilliseconds();
|
| - return false;
|
| - }
|
| -
|
| - base::TimeDelta dont_show_timeout_delta =
|
| - base::TimeDelta::FromMilliseconds(-kDontShowTimeoutMs);
|
| - if (codec_ == transport::kVp8 &&
|
| - time_until_render < dont_show_timeout_delta) {
|
| - (*encoded_frame)->data[0] &= 0xef;
|
| - VLOG(1) << "Don't show frame "
|
| - << static_cast<int>((*encoded_frame)->frame_id)
|
| - << " time_until_render:" << time_until_render.InMilliseconds();
|
| - } else {
|
| - VLOG(2) << "Show frame " << static_cast<int>((*encoded_frame)->frame_id)
|
| - << " time_until_render:" << time_until_render.InMilliseconds();
|
| - }
|
| - // We have a copy of the frame, release this one.
|
| - framer_->ReleaseFrame((*encoded_frame)->frame_id);
|
| - (*encoded_frame)->codec = codec_;
|
| -
|
| - // Used by chrome/browser/extension/api/cast_streaming/performance_test.cc
|
| - TRACE_EVENT_INSTANT2(
|
| - "cast_perf_test", "PullEncodedVideoFrame",
|
| - TRACE_EVENT_SCOPE_THREAD,
|
| - "rtp_timestamp", (*encoded_frame)->rtp_timestamp,
|
| - "render_time", render_time->ToInternalValue());
|
| -
|
| - return true;
|
| -}
|
| -
|
| -void VideoReceiver::PlayoutTimeout() {
|
| - DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
|
| - if (queued_encoded_callbacks_.empty())
|
| - return;
|
| + while (!frame_request_queue_.empty()) {
|
| + // Attempt to peek at the next completed frame from the |framer_|.
|
| + // TODO(miu): We should only be peeking at the metadata, and not copying the
|
| + // payload yet! Or, at least, peek using a StringPiece instead of a copy.
|
| + scoped_ptr<transport::EncodedVideoFrame> encoded_frame(
|
| + new transport::EncodedVideoFrame());
|
| + bool is_consecutively_next_frame = false;
|
| + if (!framer_.GetEncodedVideoFrame(encoded_frame.get(),
|
| + &is_consecutively_next_frame)) {
|
| + VLOG(1) << "Wait for more video packets to produce a completed frame.";
|
| + return; // OnReceivedPayloadData() will invoke this method in the future.
|
| + }
|
|
|
| - bool next_frame = false;
|
| - scoped_ptr<transport::EncodedVideoFrame> encoded_frame(
|
| - new transport::EncodedVideoFrame());
|
| + // If |framer_| has a frame ready that is out of sequence, examine the
|
| + // playout time to determine whether it's acceptable to continue, thereby
|
| + // skipping one or more frames. Skip if the missing frame wouldn't complete
|
| + // playing before the start of playback of the available frame.
|
| + const base::TimeTicks now = cast_environment_->Clock()->NowTicks();
|
| + const base::TimeTicks playout_time =
|
| + GetPlayoutTime(now, encoded_frame->rtp_timestamp);
|
| + if (!is_consecutively_next_frame) {
|
| + // TODO(miu): Also account for expected decode time here?
|
| + const base::TimeTicks earliest_possible_end_time_of_missing_frame =
|
| + now + expected_frame_duration_;
|
| + if (earliest_possible_end_time_of_missing_frame < playout_time) {
|
| + VLOG(1) << "Wait for next consecutive frame instead of skipping.";
|
| + if (!is_waiting_for_consecutive_frame_) {
|
| + is_waiting_for_consecutive_frame_ = true;
|
| + cast_environment_->PostDelayedTask(
|
| + CastEnvironment::MAIN,
|
| + FROM_HERE,
|
| + base::Bind(&VideoReceiver::EmitAvailableEncodedFramesAfterWaiting,
|
| + weak_factory_.GetWeakPtr()),
|
| + playout_time - now);
|
| + }
|
| + return;
|
| + }
|
| + }
|
|
|
| - if (!framer_->GetEncodedVideoFrame(encoded_frame.get(), &next_frame)) {
|
| - // We have no video frames. Wait for new packet(s).
|
| - // Since the application can post multiple VideoFrameEncodedCallback and
|
| - // we only check the next frame to play out we might have multiple timeout
|
| - // events firing after each other; however this should be a rare event.
|
| - VLOG(1) << "Failed to retrieved a complete frame at this point in time";
|
| - return;
|
| - }
|
| - VLOG(2) << "PlayoutTimeout retrieved frame "
|
| - << static_cast<int>(encoded_frame->frame_id);
|
| + // Decrypt the payload data in the frame, if crypto is being used.
|
| + if (decryptor_.initialized()) {
|
| + std::string decrypted_video_data;
|
| + if (!decryptor_.Decrypt(encoded_frame->frame_id,
|
| + encoded_frame->data,
|
| + &decrypted_video_data)) {
|
| + // Decryption failed. Give up on this frame, releasing it from the
|
| + // jitter buffer.
|
| + framer_.ReleaseFrame(encoded_frame->frame_id);
|
| + continue;
|
| + }
|
| + encoded_frame->data.swap(decrypted_video_data);
|
| + }
|
|
|
| - if (decryptor_.initialized() && !DecryptVideoFrame(&encoded_frame)) {
|
| - // Logging already done.
|
| - return;
|
| + // At this point, we have a decrypted EncodedVideoFrame ready to be emitted.
|
| + encoded_frame->codec = codec_;
|
| + framer_.ReleaseFrame(encoded_frame->frame_id);
|
| + // Used by chrome/browser/extension/api/cast_streaming/performance_test.cc
|
| + TRACE_EVENT_INSTANT2(
|
| + "cast_perf_test", "PullEncodedVideoFrame",
|
| + TRACE_EVENT_SCOPE_THREAD,
|
| + "rtp_timestamp", encoded_frame->rtp_timestamp,
|
| + // TODO(miu): Need to find an alternative to using ToInternalValue():
|
| + "render_time", playout_time.ToInternalValue());
|
| + cast_environment_->PostTask(CastEnvironment::MAIN,
|
| + FROM_HERE,
|
| + base::Bind(frame_request_queue_.front(),
|
| + base::Passed(&encoded_frame),
|
| + playout_time));
|
| + frame_request_queue_.pop_front();
|
| }
|
| +}
|
|
|
| - base::TimeTicks render_time;
|
| - if (PullEncodedVideoFrame(next_frame, &encoded_frame, &render_time)) {
|
| - if (!queued_encoded_callbacks_.empty()) {
|
| - VideoFrameEncodedCallback callback = queued_encoded_callbacks_.front();
|
| - queued_encoded_callbacks_.pop_front();
|
| - cast_environment_->PostTask(
|
| - CastEnvironment::MAIN,
|
| - FROM_HERE,
|
| - base::Bind(callback, base::Passed(&encoded_frame), render_time));
|
| - }
|
| - }
|
| - // Else we have a video frame; however we are missing packets and we have time
|
| - // to wait for new packet(s).
|
| +void VideoReceiver::EmitAvailableEncodedFramesAfterWaiting() {
|
| + DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
|
| + DCHECK(is_waiting_for_consecutive_frame_);
|
| + is_waiting_for_consecutive_frame_ = false;
|
| + EmitAvailableEncodedFrames();
|
| }
|
|
|
| -base::TimeTicks VideoReceiver::GetRenderTime(base::TimeTicks now,
|
| - uint32 rtp_timestamp) {
|
| +base::TimeTicks VideoReceiver::GetPlayoutTime(base::TimeTicks now,
|
| + uint32 rtp_timestamp) {
|
| + // TODO(miu): This and AudioReceiver::GetPlayoutTime() need to be reconciled!
|
| +
|
| DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
|
| // Senders time in ms when this frame was captured.
|
| // Note: the senders clock and our local clock might not be synced.
|
| @@ -321,17 +249,15 @@ base::TimeTicks VideoReceiver::GetRenderTime(base::TimeTicks now,
|
| // Compute the time offset_in_ticks based on the incoming_rtp_timestamp_.
|
| if (time_offset_counter_ == 0) {
|
| // Check for received RTCP to sync the stream play it out asap.
|
| - if (rtcp_->RtpTimestampInSenderTime(kVideoFrequency,
|
| - incoming_rtp_timestamp_,
|
| - &rtp_timestamp_in_ticks)) {
|
| -
|
| + if (rtcp_.RtpTimestampInSenderTime(kVideoFrequency,
|
| + incoming_rtp_timestamp_,
|
| + &rtp_timestamp_in_ticks)) {
|
| ++time_offset_counter_;
|
| }
|
| - return now;
|
| } else if (time_incoming_packet_updated_) {
|
| - if (rtcp_->RtpTimestampInSenderTime(kVideoFrequency,
|
| - incoming_rtp_timestamp_,
|
| - &rtp_timestamp_in_ticks)) {
|
| + if (rtcp_.RtpTimestampInSenderTime(kVideoFrequency,
|
| + incoming_rtp_timestamp_,
|
| + &rtp_timestamp_in_ticks)) {
|
| // Time to update the time_offset.
|
| base::TimeDelta time_offset =
|
| time_incoming_packet_ - rtp_timestamp_in_ticks;
|
| @@ -342,33 +268,47 @@ base::TimeTicks VideoReceiver::GetRenderTime(base::TimeTicks now,
|
| // very slow, and negligible for this use case.
|
| if (time_offset_counter_ == 1)
|
| time_offset_ = time_offset;
|
| - else if (time_offset_counter_ < kTimeOffsetMaxCounter) {
|
| + else if (time_offset_counter_ < kTimeOffsetMaxCounter) {
|
| time_offset_ = std::min(time_offset_, time_offset);
|
| }
|
| - ++time_offset_counter_;
|
| + if (time_offset_counter_ < kTimeOffsetMaxCounter)
|
| + ++time_offset_counter_;
|
| }
|
| }
|
| // Reset |time_incoming_packet_updated_| to enable a future measurement.
|
| time_incoming_packet_updated_ = false;
|
| // Compute the actual rtp_timestamp_in_ticks based on the current timestamp.
|
| - if (!rtcp_->RtpTimestampInSenderTime(
|
| + if (!rtcp_.RtpTimestampInSenderTime(
|
| kVideoFrequency, rtp_timestamp, &rtp_timestamp_in_ticks)) {
|
| // This can fail if we have not received any RTCP packets in a long time.
|
| - return now;
|
| + // BUG: These calculations are a placeholder, and to be revisited in a
|
| + // soon-upcoming change. http://crbug.com/356942
|
| + const int frequency_khz = kVideoFrequency / 1000;
|
| + const base::TimeDelta delta_based_on_rtp_timestamps =
|
| + base::TimeDelta::FromMilliseconds(
|
| + static_cast<int32>(rtp_timestamp - incoming_rtp_timestamp_) /
|
| + frequency_khz);
|
| + return time_incoming_packet_ + delta_based_on_rtp_timestamps;
|
| }
|
|
|
| base::TimeTicks render_time =
|
| rtp_timestamp_in_ticks + time_offset_ + target_delay_delta_;
|
| + // TODO(miu): This is broken since this "getter" method may be called on
|
| + // frames received out-of-order, which means the playout times for earlier
|
| + // frames will be computed incorrectly.
|
| +#if 0
|
| if (last_render_time_ > render_time)
|
| render_time = last_render_time_;
|
| last_render_time_ = render_time;
|
| +#endif
|
| +
|
| return render_time;
|
| }
|
|
|
| void VideoReceiver::IncomingPacket(scoped_ptr<Packet> packet) {
|
| DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
|
| if (Rtcp::IsRtcpPacket(&packet->front(), packet->size())) {
|
| - rtcp_->IncomingRtcpPacket(&packet->front(), packet->size());
|
| + rtcp_.IncomingRtcpPacket(&packet->front(), packet->size());
|
| } else {
|
| ReceivedPacket(&packet->front(), packet->size());
|
| }
|
| @@ -407,9 +347,8 @@ void VideoReceiver::OnReceivedPayloadData(const uint8* payload_data,
|
| payload_size);
|
|
|
| bool duplicate = false;
|
| - bool complete =
|
| - framer_->InsertPacket(payload_data, payload_size, rtp_header, &duplicate);
|
| -
|
| + const bool complete =
|
| + framer_.InsertPacket(payload_data, payload_size, rtp_header, &duplicate);
|
| if (duplicate) {
|
| cast_environment_->Logging()->InsertPacketEvent(
|
| now,
|
| @@ -424,16 +363,8 @@ void VideoReceiver::OnReceivedPayloadData(const uint8* payload_data,
|
| }
|
| if (!complete)
|
| return; // Video frame not complete; wait for more packets.
|
| - if (queued_encoded_callbacks_.empty())
|
| - return; // No pending callback.
|
| -
|
| - VideoFrameEncodedCallback callback = queued_encoded_callbacks_.front();
|
| - queued_encoded_callbacks_.pop_front();
|
| - cast_environment_->PostTask(CastEnvironment::MAIN,
|
| - FROM_HERE,
|
| - base::Bind(&VideoReceiver::GetEncodedVideoFrame,
|
| - weak_factory_.GetWeakPtr(),
|
| - callback));
|
| +
|
| + EmitAvailableEncodedFrames();
|
| }
|
|
|
| // Send a cast feedback message. Actual message created in the framer (cast
|
| @@ -447,7 +378,7 @@ void VideoReceiver::CastFeedback(const RtcpCastMessage& cast_message) {
|
| cast_environment_->Logging()->InsertFrameEvent(
|
| now, kVideoAckSent, rtp_timestamp, cast_message.ack_frame_id_);
|
|
|
| - rtcp_->SendRtcpFromRtpReceiver(&cast_message, &event_subscriber_);
|
| + rtcp_.SendRtcpFromRtpReceiver(&cast_message, &event_subscriber_);
|
| }
|
|
|
| // Cast messages should be sent within a maximum interval. Schedule a call
|
| @@ -455,8 +386,7 @@ void VideoReceiver::CastFeedback(const RtcpCastMessage& cast_message) {
|
| void VideoReceiver::ScheduleNextCastMessage() {
|
| DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
|
| base::TimeTicks send_time;
|
| - framer_->TimeToSendNextCastMessage(&send_time);
|
| -
|
| + framer_.TimeToSendNextCastMessage(&send_time);
|
| base::TimeDelta time_to_send =
|
| send_time - cast_environment_->Clock()->NowTicks();
|
| time_to_send = std::max(
|
| @@ -471,14 +401,13 @@ void VideoReceiver::ScheduleNextCastMessage() {
|
|
|
| void VideoReceiver::SendNextCastMessage() {
|
| DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
|
| - framer_->SendCastMessage(); // Will only send a message if it is time.
|
| + framer_.SendCastMessage(); // Will only send a message if it is time.
|
| ScheduleNextCastMessage();
|
| }
|
|
|
| -// Schedule the next RTCP report to be sent back to the sender.
|
| void VideoReceiver::ScheduleNextRtcpReport() {
|
| DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
|
| - base::TimeDelta time_to_next = rtcp_->TimeToSendNextRtcpReport() -
|
| + base::TimeDelta time_to_next = rtcp_.TimeToSendNextRtcpReport() -
|
| cast_environment_->Clock()->NowTicks();
|
|
|
| time_to_next = std::max(
|
| @@ -494,15 +423,9 @@ void VideoReceiver::ScheduleNextRtcpReport() {
|
|
|
| void VideoReceiver::SendNextRtcpReport() {
|
| DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
|
| - rtcp_->SendRtcpFromRtpReceiver(NULL, NULL);
|
| + rtcp_.SendRtcpFromRtpReceiver(NULL, NULL);
|
| ScheduleNextRtcpReport();
|
| }
|
|
|
| -void VideoReceiver::UpdateTargetDelay() {
|
| - NOTIMPLEMENTED();
|
| - rtcp_->SetTargetDelay(target_delay_delta_);
|
| - target_delay_cb_.Run(target_delay_delta_);
|
| -}
|
| -
|
| } // namespace cast
|
| } // namespace media
|
|
|