Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / media / cast / transport / transport / udp_transport.cc
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/transport/transport/udp_transport.h"
6
7 #include <algorithm>
8 #include <string>
9
10 #include "base/bind.h"
11 #include "base/logging.h"
12 #include "base/memory/ref_counted.h"
13 #include "base/memory/scoped_ptr.h"
14 #include "base/message_loop/message_loop.h"
15 #include "base/rand_util.h"
16 #include "net/base/io_buffer.h"
17 #include "net/base/net_errors.h"
18 #include "net/base/rand_callback.h"
19
20 namespace media {
21 namespace cast {
22 namespace transport {
23
24 namespace {
25 const int kMaxPacketSize = 1500;
26
27 bool IsEmpty(const net::IPEndPoint& addr) {
28   net::IPAddressNumber empty_addr(addr.address().size());
29   return std::equal(
30              empty_addr.begin(), empty_addr.end(), addr.address().begin()) &&
31          !addr.port();
32 }
33
34 bool IsEqual(const net::IPEndPoint& addr1, const net::IPEndPoint& addr2) {
35   return addr1.port() == addr2.port() && std::equal(addr1.address().begin(),
36                                                     addr1.address().end(),
37                                                     addr2.address().begin());
38 }
39 }  // namespace
40
41 UdpTransport::UdpTransport(
42     const scoped_refptr<base::SingleThreadTaskRunner>& io_thread_proxy,
43     const net::IPEndPoint& local_end_point,
44     const net::IPEndPoint& remote_end_point,
45     const CastTransportStatusCallback& status_callback)
46     : io_thread_proxy_(io_thread_proxy),
47       local_addr_(local_end_point),
48       remote_addr_(remote_end_point),
49       udp_socket_(new net::UDPSocket(net::DatagramSocket::DEFAULT_BIND,
50                                      net::RandIntCallback(),
51                                      NULL,
52                                      net::NetLog::Source())),
53       send_pending_(false),
54       recv_buf_(new net::IOBuffer(kMaxPacketSize)),
55       status_callback_(status_callback),
56       weak_factory_(this) {
57   DCHECK(!IsEmpty(local_end_point) || !IsEmpty(remote_end_point));
58 }
59
60 UdpTransport::~UdpTransport() {}
61
62 void UdpTransport::StartReceiving(
63     const PacketReceiverCallback& packet_receiver) {
64   DCHECK(io_thread_proxy_->RunsTasksOnCurrentThread());
65
66   packet_receiver_ = packet_receiver;
67   udp_socket_->AllowAddressReuse();
68   udp_socket_->SetMulticastLoopbackMode(true);
69   if (!IsEmpty(local_addr_)) {
70     if (udp_socket_->Bind(local_addr_) < 0) {
71       status_callback_.Run(TRANSPORT_SOCKET_ERROR);
72       LOG(ERROR) << "Failed to bind local address.";
73       return;
74     }
75   } else if (!IsEmpty(remote_addr_)) {
76     if (udp_socket_->Connect(remote_addr_) < 0) {
77       status_callback_.Run(TRANSPORT_SOCKET_ERROR);
78       LOG(ERROR) << "Failed to connect to remote address.";
79       return;
80     }
81   } else {
82     NOTREACHED() << "Either local or remote address has to be defined.";
83   }
84   ReceiveOnePacket();
85 }
86
87 void UdpTransport::ReceiveOnePacket() {
88   DCHECK(io_thread_proxy_->RunsTasksOnCurrentThread());
89
90   int result = udp_socket_->RecvFrom(
91       recv_buf_,
92       kMaxPacketSize,
93       &recv_addr_,
94       base::Bind(&UdpTransport::OnReceived, weak_factory_.GetWeakPtr()));
95   if (result > 0) {
96     OnReceived(result);
97   } else if (result != net::ERR_IO_PENDING) {
98     LOG(ERROR) << "Failed to receive packet: " << result << "."
99                << " Stop receiving packets.";
100     status_callback_.Run(TRANSPORT_SOCKET_ERROR);
101   }
102 }
103
104 void UdpTransport::OnReceived(int result) {
105   DCHECK(io_thread_proxy_->RunsTasksOnCurrentThread());
106   if (result < 0) {
107     LOG(ERROR) << "Failed to receive packet: " << result << "."
108                << " Stop receiving packets.";
109     status_callback_.Run(TRANSPORT_SOCKET_ERROR);
110     return;
111   }
112
113   if (IsEmpty(remote_addr_)) {
114     remote_addr_ = recv_addr_;
115     VLOG(1) << "First packet received from: " << remote_addr_.ToString() << ".";
116   } else if (!IsEqual(remote_addr_, recv_addr_)) {
117     LOG(ERROR) << "Received from an unrecognized address: "
118                << recv_addr_.ToString() << ".";
119     return;
120   }
121   // TODO(hclam): The interfaces should use net::IOBuffer to eliminate memcpy.
122   scoped_ptr<Packet> packet(
123       new Packet(recv_buf_->data(), recv_buf_->data() + result));
124   packet_receiver_.Run(packet.Pass());
125   ReceiveOnePacket();
126 }
127
128 bool UdpTransport::SendPacket(const Packet& packet) {
129   DCHECK(io_thread_proxy_->RunsTasksOnCurrentThread());
130
131   if (send_pending_) {
132     VLOG(1) << "Cannot send because of pending IO.";
133     return false;
134   }
135
136   // TODO(hclam): This interface should take a net::IOBuffer to minimize
137   // memcpy.
138   scoped_refptr<net::IOBuffer> buf =
139       new net::IOBuffer(static_cast<int>(packet.size()));
140   memcpy(buf->data(), &packet[0], packet.size());
141   int ret = udp_socket_->SendTo(
142       buf,
143       static_cast<int>(packet.size()),
144       remote_addr_,
145       base::Bind(&UdpTransport::OnSent, weak_factory_.GetWeakPtr(), buf));
146   if (ret == net::ERR_IO_PENDING)
147     send_pending_ = true;
148   // When ok, will return a positive value equal the number of bytes sent.
149   return ret >= net::OK;
150 }
151
152 void UdpTransport::OnSent(const scoped_refptr<net::IOBuffer>& buf, int result) {
153   DCHECK(io_thread_proxy_->RunsTasksOnCurrentThread());
154
155   send_pending_ = false;
156   if (result < 0) {
157     LOG(ERROR) << "Failed to send packet: " << result << ".";
158     status_callback_.Run(TRANSPORT_SOCKET_ERROR);
159   }
160 }
161
162 }  // namespace transport
163 }  // namespace cast
164 }  // namespace media