Index: media/cast/transport/transport/udp_transport.cc |
diff --git a/media/cast/transport/transport/udp_transport.cc b/media/cast/transport/transport/udp_transport.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..80fe55b65153a73e7873f830845bf382e42e99c4 |
--- /dev/null |
+++ b/media/cast/transport/transport/udp_transport.cc |
@@ -0,0 +1,153 @@ |
+// Copyright 2013 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "media/cast/transport/transport/udp_transport.h" |
+ |
+#include <algorithm> |
+#include <string> |
+ |
+#include "base/bind.h" |
+#include "base/logging.h" |
+#include "base/memory/ref_counted.h" |
+#include "base/memory/scoped_ptr.h" |
+#include "base/message_loop/message_loop.h" |
+#include "base/rand_util.h" |
+#include "net/base/io_buffer.h" |
+#include "net/base/net_errors.h" |
+#include "net/base/rand_callback.h" |
+ |
+namespace media { |
+namespace cast { |
+namespace transport { |
+ |
+namespace { |
+const int kMaxPacketSize = 1500; |
+ |
+bool IsEmpty(const net::IPEndPoint& addr) { |
+ net::IPAddressNumber empty_addr(addr.address().size()); |
+ return std::equal(empty_addr.begin(), |
+ empty_addr.end(), |
+ addr.address().begin()); |
+} |
+ |
+bool IsEqual(const net::IPEndPoint& addr1, const net::IPEndPoint& addr2) { |
+ return addr1.port() == addr2.port() && |
+ std::equal(addr1.address().begin(), |
+ addr1.address().end(), |
+ addr2.address().begin()); |
+} |
+ |
+} // namespace |
+ |
+UdpTransport::UdpTransport( |
+ const scoped_refptr<base::TaskRunner>& io_thread_proxy, |
+ const net::IPEndPoint& local_end_point, |
+ const net::IPEndPoint& remote_end_point) |
+ : io_thread_proxy_(io_thread_proxy), |
+ local_addr_(local_end_point), |
+ remote_addr_(remote_end_point), |
+ udp_socket_(new net::UDPServerSocket(NULL, net::NetLog::Source())), |
+ recv_buf_(new net::IOBuffer(kMaxPacketSize)), |
+ packet_receiver_(NULL), |
+ weak_factory_(this) { |
+} |
+ |
+UdpTransport::~UdpTransport() { |
+} |
+ |
+void UdpTransport::StartReceiving(PacketReceiver* packet_receiver) { |
+ DCHECK(io_thread_proxy_->RunsTasksOnCurrentThread()); |
+ DCHECK(!packet_receiver_); |
+ |
+ packet_receiver_ = packet_receiver; |
+ udp_socket_->AllowAddressReuse(); |
+ udp_socket_->SetMulticastLoopbackMode(true); |
+ udp_socket_->Listen(local_addr_); |
+ ReceiveOnePacket(); |
+} |
+ |
+void UdpTransport::ReceiveOnePacket() { |
+ DCHECK(io_thread_proxy_->RunsTasksOnCurrentThread()); |
+ |
+ int result = udp_socket_->RecvFrom( |
+ recv_buf_, |
+ kMaxPacketSize, |
+ &recv_addr_, |
+ base::Bind(&UdpTransport::OnReceived, weak_factory_.GetWeakPtr())); |
+ if (result > 0) { |
+ OnReceived(result); |
+ } else if (result != net::ERR_IO_PENDING) { |
+ LOG(ERROR) << "Failed to receive packet: " << result << "." |
+ << " Stop receiving packets."; |
+ } |
+} |
+ |
+void UdpTransport::OnReceived(int result) { |
+ DCHECK(io_thread_proxy_->RunsTasksOnCurrentThread()); |
+ |
+ if (result < 0) { |
+ LOG(ERROR) << "Failed to receive packet: " << result << "." |
+ << " Stop receiving packets."; |
+ return; |
+ } |
+ |
+ if (IsEmpty(remote_addr_)) { |
+ remote_addr_ = recv_addr_; |
+ VLOG(1) << "First packet received from: " |
+ << remote_addr_.ToString() << "."; |
+ } else if (!IsEqual(remote_addr_, recv_addr_)) { |
+ VLOG(1) << "Received from an unrecognized address: " |
+ << recv_addr_.ToString() << "."; |
+ return; |
+ } |
+ |
+ // TODO(hclam): The interfaces should use net::IOBuffer to eliminate memcpy. |
+ uint8* data = new uint8[result]; |
+ memcpy(data, recv_buf_->data(), result); |
+ packet_receiver_->ReceivedPacket( |
+ data, |
+ result, |
+ base::Bind(&PacketReceiver::DeletePacket, data)); |
+ ReceiveOnePacket(); |
+} |
+ |
+bool UdpTransport::SendPackets(const PacketList& packets) { |
+ DCHECK(io_thread_proxy_->RunsTasksOnCurrentThread()); |
+ |
+ bool result = true; |
+ for (size_t i = 0; i < packets.size(); ++i) { |
+ result |= SendPacket(packets[i]); |
+ } |
+ return result; |
+} |
+ |
+bool UdpTransport::SendPacket(const Packet& packet) { |
+ DCHECK(io_thread_proxy_->RunsTasksOnCurrentThread()); |
+ |
+ // TODO(hclam): This interface should take a net::IOBuffer to minimize |
+ // memcpy. |
+ scoped_refptr<net::IOBuffer> buf = new net::IOBuffer( |
+ static_cast<int>(packet.size())); |
+ memcpy(buf->data(), &packet[0], packet.size()); |
+ int ret = udp_socket_->SendTo( |
+ buf, |
+ static_cast<int>(packet.size()), |
+ remote_addr_, |
+ base::Bind(&UdpTransport::OnSent, |
+ weak_factory_.GetWeakPtr(), buf)); |
+ return ret == net::OK; |
+} |
+ |
+void UdpTransport::OnSent(const scoped_refptr<net::IOBuffer>& buf, |
+ int result) { |
+ DCHECK(io_thread_proxy_->RunsTasksOnCurrentThread()); |
+ |
+ if (result < 0) { |
+ VLOG(1) << "Failed to send packet: " << result << "."; |
+ } |
+} |
+ |
+} // namespace transport |
+} // namespace cast |
+} // namespace media |