1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "media/cast/test/utility/in_process_receiver.h"
6
7#include "base/bind_helpers.h"
8#include "base/synchronization/waitable_event.h"
9#include "base/time/time.h"
10#include "media/base/video_frame.h"
11#include "media/cast/cast_config.h"
12#include "media/cast/cast_environment.h"
13#include "media/cast/cast_receiver.h"
14#include "media/cast/net/cast_transport_config.h"
15#include "media/cast/net/udp_transport.h"
16
17using media::cast::CastTransportStatus;
18using media::cast::UdpTransport;
19
20namespace media {
21namespace cast {
22
23InProcessReceiver::InProcessReceiver(
24    const scoped_refptr<CastEnvironment>& cast_environment,
25    const net::IPEndPoint& local_end_point,
26    const net::IPEndPoint& remote_end_point,
27    const FrameReceiverConfig& audio_config,
28    const FrameReceiverConfig& video_config)
29    : cast_environment_(cast_environment),
30      local_end_point_(local_end_point),
31      remote_end_point_(remote_end_point),
32      audio_config_(audio_config),
33      video_config_(video_config),
34      weak_factory_(this) {}
35
36InProcessReceiver::~InProcessReceiver() {
37  Stop();
38}
39
40void InProcessReceiver::Start() {
41  cast_environment_->PostTask(CastEnvironment::MAIN,
42                              FROM_HERE,
43                              base::Bind(&InProcessReceiver::StartOnMainThread,
44                                         base::Unretained(this)));
45}
46
47void InProcessReceiver::Stop() {
48  base::WaitableEvent event(false, false);
49  if (cast_environment_->CurrentlyOn(CastEnvironment::MAIN)) {
50    StopOnMainThread(&event);
51  } else {
52    cast_environment_->PostTask(CastEnvironment::MAIN,
53                                FROM_HERE,
54                                base::Bind(&InProcessReceiver::StopOnMainThread,
55                                           base::Unretained(this),
56                                           &event));
57    event.Wait();
58  }
59}
60
61void InProcessReceiver::StopOnMainThread(base::WaitableEvent* event) {
62  DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
63  cast_receiver_.reset(NULL);
64  transport_.reset(NULL);
65  weak_factory_.InvalidateWeakPtrs();
66  event->Signal();
67}
68
69void InProcessReceiver::UpdateCastTransportStatus(CastTransportStatus status) {
70  LOG_IF(ERROR, status == media::cast::TRANSPORT_SOCKET_ERROR)
71      << "Transport socket error occurred.  InProcessReceiver is likely dead.";
72  VLOG(1) << "CastTransportStatus is now " << status;
73}
74
75void InProcessReceiver::StartOnMainThread() {
76  DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
77
78  DCHECK(!transport_ && !cast_receiver_);
79  transport_.reset(
80      new UdpTransport(NULL,
81                       cast_environment_->GetTaskRunner(CastEnvironment::MAIN),
82                       local_end_point_,
83                       remote_end_point_,
84                       base::Bind(&InProcessReceiver::UpdateCastTransportStatus,
85                                  base::Unretained(this))));
86  cast_receiver_ = CastReceiver::Create(
87      cast_environment_, audio_config_, video_config_, transport_.get());
88
89  // TODO(hubbe): Make the cast receiver do this automatically.
90  transport_->StartReceiving(cast_receiver_->packet_receiver());
91
92  PullNextAudioFrame();
93  PullNextVideoFrame();
94}
95
96void InProcessReceiver::GotAudioFrame(scoped_ptr<AudioBus> audio_frame,
97                                      const base::TimeTicks& playout_time,
98                                      bool is_continuous) {
99  DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
100  if (audio_frame.get())
101    OnAudioFrame(audio_frame.Pass(), playout_time, is_continuous);
102  PullNextAudioFrame();
103}
104
105void InProcessReceiver::GotVideoFrame(
106    const scoped_refptr<VideoFrame>& video_frame,
107    const base::TimeTicks& playout_time,
108    bool is_continuous) {
109  DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
110  if (video_frame.get())
111    OnVideoFrame(video_frame, playout_time, is_continuous);
112  PullNextVideoFrame();
113}
114
115void InProcessReceiver::PullNextAudioFrame() {
116  DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
117  cast_receiver_->RequestDecodedAudioFrame(
118      base::Bind(&InProcessReceiver::GotAudioFrame,
119                 weak_factory_.GetWeakPtr()));
120}
121
122void InProcessReceiver::PullNextVideoFrame() {
123  DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
124  cast_receiver_->RequestDecodedVideoFrame(base::Bind(
125      &InProcessReceiver::GotVideoFrame, weak_factory_.GetWeakPtr()));
126}
127
128}  // namespace cast
129}  // namespace media
130