DASH Media Packaging SDK
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator
udp_file.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 #if defined(OS_WIN)
10 #include <windows.h>
11 #include <ws2tcpip.h>
12 #define close closesocket
13 #else
14 #include <arpa/inet.h>
15 #include <errno.h>
16 #include <strings.h>
17 #include <sys/socket.h>
18 #include <unistd.h>
19 #define INVALID_SOCKET -1
20 #endif // defined(OS_WIN)
21 
22 #include <limits>
23 
24 #include "packager/base/logging.h"
25 #include "packager/media/file/udp_options.h"
26 
27 namespace shaka {
28 namespace media {
29 
30 namespace {
31 
32 bool IsIpv4MulticastAddress(const struct in_addr& addr) {
33  return (ntohl(addr.s_addr) & 0xf0000000) == 0xe0000000;
34 }
35 
36 } // anonymous namespace
37 
38 UdpFile::UdpFile(const char* file_name) :
39  File(file_name),
40  socket_(INVALID_SOCKET) {}
41 
42 UdpFile::~UdpFile() {}
43 
45  if (socket_ != INVALID_SOCKET) {
46  close(socket_);
47  socket_ = INVALID_SOCKET;
48  }
49  delete this;
50  return true;
51 }
52 
53 int64_t UdpFile::Read(void* buffer, uint64_t length) {
54  DCHECK(buffer);
55  DCHECK_GE(length, 65535u)
56  << "Buffer may be too small to read entire datagram.";
57 
58  if (socket_ == INVALID_SOCKET)
59  return -1;
60 
61  int64_t result;
62  do {
63  result = recvfrom(socket_, reinterpret_cast<char *>(buffer),
64  length, 0, NULL, 0);
65  } while ((result == -1) && (errno == EINTR));
66 
67  return result;
68 }
69 
70 int64_t UdpFile::Write(const void* buffer, uint64_t length) {
71  NOTIMPLEMENTED();
72  return -1;
73 }
74 
75 int64_t UdpFile::Size() {
76  if (socket_ == INVALID_SOCKET)
77  return -1;
78 
79  return std::numeric_limits<int64_t>::max();
80 }
81 
83  NOTIMPLEMENTED();
84  return false;
85 }
86 
87 bool UdpFile::Seek(uint64_t position) {
88  NOTIMPLEMENTED();
89  return false;
90 }
91 
92 bool UdpFile::Tell(uint64_t* position) {
93  NOTIMPLEMENTED();
94  return false;
95 }
96 
97 #if defined(OS_WIN)
98 class LibWinsockInitializer {
99  public:
100  LibWinsockInitializer() {
101  WSADATA wsa_data;
102  error_ = WSAStartup(MAKEWORD(2, 2), &wsa_data);
103  }
104 
105  ~LibWinsockInitializer() {
106  if (error_ == 0) WSACleanup();
107  }
108 
109  int error() const { return error_; }
110 
111  private:
112  int error_;
113 };
114 #endif // defined(OS_WIN)
115 
116 class ScopedSocket {
117  public:
118  explicit ScopedSocket(SOCKET sock_fd)
119  : sock_fd_(sock_fd) {}
120 
121  ~ScopedSocket() {
122  if (sock_fd_ != INVALID_SOCKET)
123  close(sock_fd_);
124  }
125 
126  SOCKET get() { return sock_fd_; }
127 
128  SOCKET release() {
129  SOCKET socket = sock_fd_;
130  sock_fd_ = INVALID_SOCKET;
131  return socket;
132  }
133 
134  private:
135  SOCKET sock_fd_;
136 
137  DISALLOW_COPY_AND_ASSIGN(ScopedSocket);
138 };
139 
141 #if defined(OS_WIN)
142  static LibWinsockInitializer lib_winsock_initializer;
143  if (lib_winsock_initializer.error() != 0) {
144  LOG(ERROR) << "Winsock start up failed with error "
145  << lib_winsock_initializer.error();
146  return false;
147  }
148 #endif // defined(OS_WIN)
149 
150  DCHECK_EQ(INVALID_SOCKET, socket_);
151 
152  std::unique_ptr<UdpOptions> options =
154  if (!options)
155  return false;
156 
157  ScopedSocket new_socket(socket(AF_INET, SOCK_DGRAM, 0));
158  if (new_socket.get() == INVALID_SOCKET) {
159  LOG(ERROR) << "Could not allocate socket.";
160  return false;
161  }
162 
163  struct in_addr local_in_addr = {0};
164  if (inet_pton(AF_INET, options->address().c_str(),
165  &local_in_addr) != 1) {
166  LOG(ERROR) << "Malformed IPv4 address " << options->address();
167  return false;
168  }
169 
170  struct sockaddr_in local_sock_addr = {0};
171  // TODO(kqyang): Support IPv6.
172  local_sock_addr.sin_family = AF_INET;
173  local_sock_addr.sin_port = htons(options->port());
174  const bool is_multicast = IsIpv4MulticastAddress(local_in_addr);
175  if (is_multicast) {
176  local_sock_addr.sin_addr.s_addr = htonl(INADDR_ANY);
177  } else {
178  local_sock_addr.sin_addr = local_in_addr;
179  }
180 
181  if (options->reuse()) {
182  const int optval = 1;
183  if (setsockopt(new_socket.get(), SOL_SOCKET, SO_REUSEADDR,
184  reinterpret_cast<const char *>(&optval),
185  sizeof(optval)) < 0) {
186  LOG(ERROR)
187  << "Could not apply the SO_REUSEADDR property to the UDP socket";
188  return false;
189  }
190  }
191 
192  if (bind(new_socket.get(),
193  reinterpret_cast<struct sockaddr*>(&local_sock_addr),
194  sizeof(local_sock_addr))) {
195  LOG(ERROR) << "Could not bind UDP socket";
196  return false;
197  }
198 
199  if (is_multicast) {
200  struct ip_mreq multicast_group;
201  multicast_group.imr_multiaddr = local_in_addr;
202 
203  if (options->interface_address().empty()) {
204  LOG(ERROR) << "Interface address is required for multicast, which can be "
205  "specified in udp url, e.g. "
206  "udp://ip:port?interface=interface_ip.";
207  return false;
208  }
209  if (inet_pton(AF_INET, options->interface_address().c_str(),
210  &multicast_group.imr_interface) != 1) {
211  LOG(ERROR) << "Malformed IPv4 interface address "
212  << options->interface_address();
213  return false;
214  }
215 
216  if (setsockopt(new_socket.get(), IPPROTO_IP, IP_ADD_MEMBERSHIP,
217  reinterpret_cast<const char *>(&multicast_group),
218  sizeof(multicast_group)) < 0) {
219  LOG(ERROR) << "Failed to join multicast group.";
220  return false;
221  }
222  }
223 
224  // Set timeout if needed.
225  if (options->timeout_us() != 0) {
226  struct timeval tv;
227  tv.tv_sec = options->timeout_us() / 1000000;
228  tv.tv_usec = options->timeout_us() % 1000000;
229  if (setsockopt(new_socket.get(), SOL_SOCKET, SO_RCVTIMEO,
230  reinterpret_cast<char*>(&tv), sizeof(tv)) < 0) {
231  LOG(ERROR) << "Failed to set socket timeout.";
232  return false;
233  }
234  }
235 
236  socket_ = new_socket.release();
237  return true;
238 }
239 
240 } // namespace media
241 } // namespace shaka
bool Open() override
Internal open. Should not be used directly.
Definition: udp_file.cc:140
int64_t Write(const void *buffer, uint64_t length) override
Definition: udp_file.cc:70
Define an abstract file interface.
Definition: file.h:24
int64_t Read(void *buffer, uint64_t length) override
Definition: udp_file.cc:53
static std::unique_ptr< UdpOptions > ParseFromString(base::StringPiece udp_url)
Definition: udp_options.cc:73
bool Tell(uint64_t *position) override
Definition: udp_file.cc:92
bool Flush() override
Definition: udp_file.cc:82
UdpFile(const char *address_and_port)
Definition: udp_file.cc:38
const std::string & file_name() const
Definition: file.h:91
bool Seek(uint64_t position) override
Definition: udp_file.cc:87
bool Close() override
Definition: udp_file.cc:44
int64_t Size() override
Definition: udp_file.cc:75