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 <strings.h>
12 #include <sys/socket.h>
13 #include <unistd.h>
14 
15 #include <limits>
16 
17 #include "packager/base/logging.h"
18 #include "packager/media/file/udp_options.h"
19 
20 // TODO(tinskip): Adapt to work with winsock.
21 
22 namespace shaka {
23 namespace media {
24 
25 namespace {
26 
27 const int kInvalidSocket(-1);
28 
29 bool IsIpv4MulticastAddress(const struct in_addr& addr) {
30  return (ntohl(addr.s_addr) & 0xf0000000) == 0xe0000000;
31 }
32 
33 } // anonymous namespace
34 
35 UdpFile::UdpFile(const char* file_name) :
36  File(file_name),
37  socket_(kInvalidSocket) {}
38 
39 UdpFile::~UdpFile() {}
40 
42  if (socket_ != kInvalidSocket) {
43  close(socket_);
44  socket_ = kInvalidSocket;
45  }
46  delete this;
47  return true;
48 }
49 
50 int64_t UdpFile::Read(void* buffer, uint64_t length) {
51  DCHECK(buffer);
52  DCHECK_GE(length, 65535u)
53  << "Buffer may be too small to read entire datagram.";
54 
55  if (socket_ == kInvalidSocket)
56  return -1;
57 
58  int64_t result;
59  do {
60  result = recvfrom(socket_, buffer, length, 0, NULL, 0);
61  } while ((result == -1) && (errno == EINTR));
62 
63  return result;
64 }
65 
66 int64_t UdpFile::Write(const void* buffer, uint64_t length) {
67  NOTIMPLEMENTED();
68  return -1;
69 }
70 
71 int64_t UdpFile::Size() {
72  if (socket_ == kInvalidSocket)
73  return -1;
74 
75  return std::numeric_limits<int64_t>::max();
76 }
77 
79  NOTIMPLEMENTED();
80  return false;
81 }
82 
83 bool UdpFile::Seek(uint64_t position) {
84  NOTIMPLEMENTED();
85  return false;
86 }
87 
88 bool UdpFile::Tell(uint64_t* position) {
89  NOTIMPLEMENTED();
90  return false;
91 }
92 
93 class ScopedSocket {
94  public:
95  explicit ScopedSocket(int sock_fd)
96  : sock_fd_(sock_fd) {}
97 
98  ~ScopedSocket() {
99  if (sock_fd_ != kInvalidSocket)
100  close(sock_fd_);
101  }
102 
103  int get() { return sock_fd_; }
104 
105  int release() {
106  int socket = sock_fd_;
107  sock_fd_ = kInvalidSocket;
108  return socket;
109  }
110 
111  private:
112  int sock_fd_;
113 
114  DISALLOW_COPY_AND_ASSIGN(ScopedSocket);
115 };
116 
118  DCHECK_EQ(kInvalidSocket, socket_);
119 
120  std::unique_ptr<UdpOptions> options =
122  if (!options)
123  return false;
124 
125  ScopedSocket new_socket(socket(AF_INET, SOCK_DGRAM, 0));
126  if (new_socket.get() == kInvalidSocket) {
127  LOG(ERROR) << "Could not allocate socket.";
128  return false;
129  }
130 
131  struct sockaddr_in local_sock_addr;
132  bzero(&local_sock_addr, sizeof(local_sock_addr));
133  // TODO(kqyang): Support IPv6.
134  local_sock_addr.sin_family = AF_INET;
135  local_sock_addr.sin_port = htons(options->port());
136  if (inet_pton(AF_INET, options->address().c_str(),
137  &local_sock_addr.sin_addr) != 1) {
138  LOG(ERROR) << "Malformed IPv4 address " << options->address();
139  return false;
140  }
141 
142  if (options->reuse()) {
143  const int optval = 1;
144  if (setsockopt(new_socket.get(), SOL_SOCKET, SO_REUSEADDR, &optval,
145  sizeof(optval)) < 0) {
146  LOG(ERROR)
147  << "Could not apply the SO_REUSEADDR property to the UDP socket";
148  return false;
149  }
150  }
151 
152  if (bind(new_socket.get(),
153  reinterpret_cast<struct sockaddr*>(&local_sock_addr),
154  sizeof(local_sock_addr))) {
155  LOG(ERROR) << "Could not bind UDP socket";
156  return false;
157  }
158 
159  if (IsIpv4MulticastAddress(local_sock_addr.sin_addr)) {
160  struct ip_mreq multicast_group;
161  multicast_group.imr_multiaddr = local_sock_addr.sin_addr;
162 
163  if (options->interface_address().empty()) {
164  LOG(ERROR) << "Interface address is required for multicast, which can be "
165  "specified in udp url, e.g. "
166  "udp://ip:port?interface=interface_ip.";
167  return false;
168  }
169  if (inet_pton(AF_INET, options->interface_address().c_str(),
170  &multicast_group.imr_interface) != 1) {
171  LOG(ERROR) << "Malformed IPv4 interface address "
172  << options->interface_address();
173  return false;
174  }
175 
176  if (setsockopt(new_socket.get(), IPPROTO_IP, IP_ADD_MEMBERSHIP,
177  &multicast_group, sizeof(multicast_group)) < 0) {
178  LOG(ERROR) << "Failed to join multicast group.";
179  return false;
180  }
181  }
182 
183  // Set timeout if needed.
184  if (options->timeout_us() != 0) {
185  struct timeval tv;
186  tv.tv_sec = options->timeout_us() / 1000000;
187  tv.tv_usec = options->timeout_us() % 1000000;
188  if (setsockopt(new_socket.get(), SOL_SOCKET, SO_RCVTIMEO,
189  reinterpret_cast<char*>(&tv), sizeof(tv)) < 0) {
190  LOG(ERROR) << "Failed to set socket timeout.";
191  return false;
192  }
193  }
194 
195  socket_ = new_socket.release();
196  return true;
197 }
198 
199 } // namespace media
200 } // 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
static std::unique_ptr< UdpOptions > ParseFromString(base::StringPiece udp_url)
Definition: udp_options.cc:73
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