DASH Media Packaging SDK
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator
udp_file_posix.cc
1 // Copyright 2014 Google Inc. All rights reserved.
2 //
3 // Use of this source code is governed by a BSD-style
4 // license that can be found in the LICENSE file or at
5 // https://developers.google.com/open-source/licenses/bsd
6 
7 #include "packager/media/file/udp_file.h"
8 
9 #include <arpa/inet.h>
10 #include <errno.h>
11 #include <gflags/gflags.h>
12 #include <strings.h>
13 #include <sys/socket.h>
14 #include <unistd.h>
15 
16 #include <limits>
17 
18 #include "packager/base/logging.h"
19 #include "packager/base/strings/string_number_conversions.h"
20 
21 // TODO(tinskip): Adapt to work with winsock.
22 
23 DEFINE_string(udp_interface_address,
24  "0.0.0.0",
25  "IP address of the interface over which to receive UDP unicast"
26  " or multicast streams");
27 
28 namespace shaka {
29 namespace media {
30 
31 namespace {
32 
33 const int kInvalidSocket(-1);
34 
35 bool StringToIpv4Address(const std::string& addr_in, uint32_t* addr_out) {
36  DCHECK(addr_out);
37 
38  *addr_out = 0;
39  size_t start_pos(0);
40  size_t end_pos(0);
41  for (int i = 0; i < 4; ++i) {
42  end_pos = addr_in.find('.', start_pos);
43  if ((end_pos == std::string::npos) != (i == 3))
44  return false;
45  unsigned addr_byte;
46  if (!base::StringToUint(addr_in.substr(start_pos, end_pos - start_pos),
47  &addr_byte)
48  || (addr_byte > 255))
49  return false;
50  *addr_out <<= 8;
51  *addr_out |= addr_byte;
52  start_pos = end_pos + 1;
53  }
54  return true;
55 }
56 
57 bool StringToIpv4AddressAndPort(const std::string& addr_and_port,
58  uint32_t* addr,
59  uint16_t* port) {
60  DCHECK(addr);
61  DCHECK(port);
62 
63  size_t colon_pos = addr_and_port.find(':');
64  if (colon_pos == std::string::npos) {
65  return false;
66  }
67  if (!StringToIpv4Address(addr_and_port.substr(0, colon_pos), addr))
68  return false;
69  unsigned port_value;
70  if (!base::StringToUint(addr_and_port.substr(colon_pos + 1),
71  &port_value) ||
72  (port_value > 65535))
73  return false;
74  *port = port_value;
75  return true;
76 }
77 
78 bool IsIpv4MulticastAddress(uint32_t addr) {
79  return (addr & 0xf0000000) == 0xe0000000;
80 }
81 
82 } // anonymous namespace
83 
84 UdpFile::UdpFile(const char* file_name) :
85  File(file_name),
86  socket_(kInvalidSocket) {}
87 
88 UdpFile::~UdpFile() {}
89 
91  if (socket_ != kInvalidSocket) {
92  close(socket_);
93  socket_ = kInvalidSocket;
94  }
95  delete this;
96  return true;
97 }
98 
99 int64_t UdpFile::Read(void* buffer, uint64_t length) {
100  DCHECK(buffer);
101  DCHECK_GE(length, 65535u)
102  << "Buffer may be too small to read entire datagram.";
103 
104  if (socket_ == kInvalidSocket)
105  return -1;
106 
107  int64_t result;
108  do {
109  result = recvfrom(socket_, buffer, length, 0, NULL, 0);
110  } while ((result == -1) && (errno == EINTR));
111 
112  return result;
113 }
114 
115 int64_t UdpFile::Write(const void* buffer, uint64_t length) {
116  NOTIMPLEMENTED();
117  return -1;
118 }
119 
120 int64_t UdpFile::Size() {
121  if (socket_ == kInvalidSocket)
122  return -1;
123 
124  return std::numeric_limits<int64_t>::max();
125 }
126 
128  NOTIMPLEMENTED();
129  return false;
130 }
131 
132 bool UdpFile::Seek(uint64_t position) {
133  NOTIMPLEMENTED();
134  return false;
135 }
136 
137 bool UdpFile::Tell(uint64_t* position) {
138  NOTIMPLEMENTED();
139  return false;
140 }
141 
142 class ScopedSocket {
143  public:
144  explicit ScopedSocket(int sock_fd)
145  : sock_fd_(sock_fd) {}
146 
147  ~ScopedSocket() {
148  if (sock_fd_ != kInvalidSocket)
149  close(sock_fd_);
150  }
151 
152  int get() { return sock_fd_; }
153 
154  int release() {
155  int socket = sock_fd_;
156  sock_fd_ = kInvalidSocket;
157  return socket;
158  }
159 
160  private:
161  int sock_fd_;
162 
163  DISALLOW_COPY_AND_ASSIGN(ScopedSocket);
164 };
165 
167  DCHECK_EQ(kInvalidSocket, socket_);
168 
169  // TODO(tinskip): Support IPv6 addresses.
170  uint32_t dest_addr;
171  uint16_t dest_port;
172  if (!StringToIpv4AddressAndPort(file_name(),
173  &dest_addr,
174  &dest_port)) {
175  LOG(ERROR) << "Malformed IPv4 address:port UDP stream specifier.";
176  return false;
177  }
178 
179  ScopedSocket new_socket(socket(AF_INET, SOCK_DGRAM, 0));
180  if (new_socket.get() == kInvalidSocket) {
181  LOG(ERROR) << "Could not allocate socket.";
182  return false;
183  }
184 
185  struct sockaddr_in local_sock_addr;
186  bzero(&local_sock_addr, sizeof(local_sock_addr));
187  local_sock_addr.sin_family = AF_INET;
188  local_sock_addr.sin_port = htons(dest_port);
189  local_sock_addr.sin_addr.s_addr = htonl(dest_addr);
190  if (bind(new_socket.get(),
191  reinterpret_cast<struct sockaddr*>(&local_sock_addr),
192  sizeof(local_sock_addr))) {
193  LOG(ERROR) << "Could not bind UDP socket";
194  return false;
195  }
196 
197  if (IsIpv4MulticastAddress(dest_addr)) {
198  uint32_t if_addr;
199  if (!StringToIpv4Address(FLAGS_udp_interface_address, &if_addr)) {
200  LOG(ERROR) << "Malformed IPv4 address for interface.";
201  return false;
202  }
203  struct ip_mreq multicast_group;
204  multicast_group.imr_multiaddr.s_addr = htonl(dest_addr);
205  multicast_group.imr_interface.s_addr = htonl(if_addr);
206  if (setsockopt(new_socket.get(),
207  IPPROTO_IP,
208  IP_ADD_MEMBERSHIP,
209  &multicast_group,
210  sizeof(multicast_group)) < 0) {
211  LOG(ERROR) << "Failed to join multicast group.";
212  return false;
213  }
214  }
215 
216  socket_ = new_socket.release();
217  return true;
218 }
219 
220 } // namespace media
221 } // namespace shaka
bool Open() override
Internal open. Should not be used directly.
int64_t Write(const void *buffer, uint64_t length) override
Define an abstract file interface.
Definition: file.h:24
int64_t Read(void *buffer, uint64_t length) override
bool Tell(uint64_t *position) override
bool Flush() override
UdpFile(const char *address_and_port)
const std::string & file_name() const
Definition: file.h:91
bool Seek(uint64_t position) override
bool Close() override
int64_t Size() override