Added implementation of UdpFile for UDP unicast/multicast capture.

Change-Id: Ie11b86821d34f6b93cd8d00c1709572d1a46e72b
This commit is contained in:
Thomas Inskip 2014-04-30 16:22:15 -07:00
parent 1773d08b8d
commit 2eb40fef65
4 changed files with 273 additions and 0 deletions

View File

@ -9,10 +9,13 @@
#include "base/logging.h" #include "base/logging.h"
#include "base/memory/scoped_ptr.h" #include "base/memory/scoped_ptr.h"
#include "media/file/local_file.h" #include "media/file/local_file.h"
#include "media/file/udp_file.h"
#include "base/strings/string_util.h"
namespace media { namespace media {
const char* kLocalFilePrefix = "file://"; const char* kLocalFilePrefix = "file://";
const char* kUdpFilePrefix = "udp://";
typedef File* (*FileFactoryFunction)(const char* file_name, const char* mode); typedef File* (*FileFactoryFunction)(const char* file_name, const char* mode);
@ -26,8 +29,17 @@ static File* CreateLocalFile(const char* file_name, const char* mode) {
return new LocalFile(file_name, mode); return new LocalFile(file_name, mode);
} }
static File* CreateUdpFile(const char* file_name, const char* mode) {
if (base::strcasecmp(mode, "r")) {
NOTIMPLEMENTED() << "UdpFile only supports read (receive) mode.";
return NULL;
}
return new UdpFile(file_name);
}
static const SupportedTypeInfo kSupportedTypeInfo[] = { static const SupportedTypeInfo kSupportedTypeInfo[] = {
{ kLocalFilePrefix, strlen(kLocalFilePrefix), &CreateLocalFile }, { kLocalFilePrefix, strlen(kLocalFilePrefix), &CreateLocalFile },
{ kUdpFilePrefix, strlen(kUdpFilePrefix), &CreateUdpFile },
}; };
File* File::Create(const char* file_name, const char* mode) { File* File::Create(const char* file_name, const char* mode) {

View File

@ -24,9 +24,12 @@
'file_closer.h', 'file_closer.h',
'local_file.cc', 'local_file.cc',
'local_file.h', 'local_file.h',
'udp_file.cc',
'udp_file.h',
], ],
'dependencies': [ 'dependencies': [
'../../base/base.gyp:base', '../../base/base.gyp:base',
'../../third_party/gflags/gflags.gyp:gflags',
], ],
}, },
{ {

210
media/file/udp_file.cc Normal file
View File

@ -0,0 +1,210 @@
// Copyright 2014 Google Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
#include "media/file/udp_file.h"
#include "base/logging.h"
#include "base/strings/string_number_conversions.h"
#include "gflags/gflags.h"
#include <arpa/inet.h>
#include <errno.h>
#include <strings.h>
#include <sys/socket.h>
// TODO(tinskip): Adapt to work with winsock.
DEFINE_string(udp_interface_address,
"0.0.0.0",
"IP address of the interface over which to receive UDP unicast"
" or multicast streams");
namespace media {
namespace {
const int kInvalidSocket(-1);
bool StringToIpv4Address(const std::string& addr_in, uint32* addr_out) {
DCHECK(addr_out);
*addr_out = 0;
size_t start_pos(0);
size_t end_pos(0);
for (int i = 0; i < 4; ++i) {
end_pos = addr_in.find('.', start_pos);
if ((end_pos == std::string::npos) != (i == 3))
return false;
unsigned addr_byte;
if (!base::StringToUint(addr_in.substr(start_pos, end_pos - start_pos),
&addr_byte)
|| (addr_byte > 255))
return false;
*addr_out <<= 8;
*addr_out |= addr_byte;
start_pos = end_pos + 1;
}
return true;
}
bool StringToIpv4AddressAndPort(const std::string& addr_and_port,
uint32* addr,
uint16* port) {
DCHECK(addr);
DCHECK(port);
size_t colon_pos = addr_and_port.find(':');
if (colon_pos == std::string::npos) {
return false;
}
if (!StringToIpv4Address(addr_and_port.substr(0, colon_pos), addr))
return false;
unsigned port_value;
if (!base::StringToUint(addr_and_port.substr(colon_pos + 1),
&port_value) ||
(port_value > 65535))
return false;
*port = port_value;
return true;
}
bool IsIpv4MulticastAddress(uint32 addr) {
return (addr & 0xf0000000) == 0xe0000000;
}
} // anonymous namespace
UdpFile::UdpFile(const char* file_name) :
File(file_name),
socket_(kInvalidSocket) {}
UdpFile::~UdpFile() {}
bool UdpFile::Close() {
if (socket_ != kInvalidSocket) {
close(socket_);
socket_ = kInvalidSocket;
}
delete this;
return true;
}
int64 UdpFile::Read(void* buffer, uint64 length) {
DCHECK(buffer);
DCHECK_GE(length, 65535u)
<< "Buffer may be too small to read entire datagram.";
if (socket_ == kInvalidSocket)
return -1;
int64 result;
do {
result = recvfrom(socket_, buffer, length, 0, NULL, 0);
} while ((result == -1) && (errno == EINTR));
return result;
}
int64 UdpFile::Write(const void* buffer, uint64 length) {
NOTIMPLEMENTED();
return -1;
}
int64 UdpFile::Size() {
if (socket_ == kInvalidSocket)
return -1;
return kint64max;
}
bool UdpFile::Flush() {
NOTIMPLEMENTED();
return false;
}
bool UdpFile::Eof() {
return socket_ == kInvalidSocket;
}
class ScopedSocket {
public:
explicit ScopedSocket(int sock_fd)
: sock_fd_(sock_fd) {}
~ScopedSocket() {
if (sock_fd_ != kInvalidSocket)
close(sock_fd_);
}
int get() { return sock_fd_; }
int release() {
int socket = sock_fd_;
sock_fd_ = kInvalidSocket;
return socket;
}
private:
int sock_fd_;
DISALLOW_COPY_AND_ASSIGN(ScopedSocket);
};
bool UdpFile::Open() {
DCHECK_EQ(kInvalidSocket, socket_);
// TODO(tinskip): Support IPv6 addresses.
uint32 dest_addr;
uint16 dest_port;
if (!StringToIpv4AddressAndPort(file_name(),
&dest_addr,
&dest_port)) {
LOG(ERROR) << "Malformed IPv4 address:port UDP stream specifier.";
return false;
}
ScopedSocket new_socket(socket(AF_INET, SOCK_DGRAM, 0));
if (new_socket.get() == kInvalidSocket) {
LOG(ERROR) << "Could not allocate socket.";
return false;
}
struct sockaddr_in local_sock_addr;
bzero(&local_sock_addr, sizeof(local_sock_addr));
local_sock_addr.sin_family = AF_INET;
local_sock_addr.sin_port = htons(dest_port);
local_sock_addr.sin_addr.s_addr = htonl(dest_addr);
if (bind(new_socket.get(),
reinterpret_cast<struct sockaddr*>(&local_sock_addr),
sizeof(local_sock_addr))) {
LOG(ERROR) << "Could not bind UDP socket";
return false;
}
if (IsIpv4MulticastAddress(dest_addr)) {
uint32 if_addr;
if (!StringToIpv4Address(FLAGS_udp_interface_address, &if_addr)) {
LOG(ERROR) << "Malformed IPv4 address for interface.";
return false;
}
struct ip_mreq multicast_group;
multicast_group.imr_multiaddr.s_addr = htonl(dest_addr);
multicast_group.imr_interface.s_addr = htonl(if_addr);
if (setsockopt(new_socket.get(),
IPPROTO_IP,
IP_ADD_MEMBERSHIP,
&multicast_group,
sizeof(multicast_group)) < 0) {
LOG(ERROR) << "Failed to join multicast group.";
return false;
}
}
socket_ = new_socket.release();
return true;
}
} // namespace media

48
media/file/udp_file.h Normal file
View File

@ -0,0 +1,48 @@
// Copyright 2014 Google Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
#ifndef MEDIA_FILE_UDP_FILE_H_
#define MEDIA_FILE_UDP_FILE_H_
#include <string>
#include "base/basictypes.h"
#include "base/compiler_specific.h"
#include "media/file/file.h"
namespace media {
/// Implements UdpFile, which receives UDP unicast and multicast streams.
class UdpFile : public File {
public:
/// @param file_name C string containing the address of the stream to receive.
/// It should be of the form "<ip_address>:<port>".
explicit UdpFile(const char* address_and_port);
/// @name File implementation overrides.
/// @{
virtual bool Close() OVERRIDE;
virtual int64 Read(void* buffer, uint64 length) OVERRIDE;
virtual int64 Write(const void* buffer, uint64 length) OVERRIDE;
virtual int64 Size() OVERRIDE;
virtual bool Flush() OVERRIDE;
virtual bool Eof() OVERRIDE;
/// @}
protected:
virtual ~UdpFile();
virtual bool Open() OVERRIDE;
private:
int socket_;
DISALLOW_COPY_AND_ASSIGN(UdpFile);
};
} // namespace media
#endif // MEDIA_FILE_UDP_FILE_H_