/* * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.android.exoplayer.upstream; import com.google.android.exoplayer.C; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.MulticastSocket; /** * A UDP {@link DataSource}. */ public final class UdpDataSource implements UriDataSource { /** * Thrown when an error is encountered when trying to read from a {@link UdpDataSource}. */ public static final class UdpDataSourceException extends IOException { public UdpDataSourceException(String message) { super(message); } public UdpDataSourceException(IOException cause) { super(cause); } } /** * The default maximum datagram packet size, in bytes. */ public static final int DEFAULT_MAX_PACKET_SIZE = 2000; private final TransferListener listener; private final DatagramPacket packet; private DataSpec dataSpec; private DatagramSocket socket; private MulticastSocket multicastSocket; private InetAddress address; private InetSocketAddress socketAddress; private boolean opened; private byte[] packetBuffer; private int packetRemaining; /** * @param listener An optional listener. */ public UdpDataSource(TransferListener listener) { this(listener, DEFAULT_MAX_PACKET_SIZE); } /** * @param listener An optional listener. * @param maxPacketSize The maximum datagram packet size, in bytes. */ public UdpDataSource(TransferListener listener, int maxPacketSize) { this.listener = listener; packetBuffer = new byte[maxPacketSize]; packet = new DatagramPacket(packetBuffer, 0, maxPacketSize); } @Override public long open(DataSpec dataSpec) throws UdpDataSourceException { this.dataSpec = dataSpec; String uri = dataSpec.uri.toString(); String host = uri.substring(0, uri.indexOf(':')); int port = Integer.parseInt(uri.substring(uri.indexOf(':') + 1)); try { address = InetAddress.getByName(host); socketAddress = new InetSocketAddress(address, port); if (address.isMulticastAddress()) { multicastSocket = new MulticastSocket(socketAddress); multicastSocket.joinGroup(address); socket = multicastSocket; } else { socket = new DatagramSocket(socketAddress); } } catch (IOException e) { throw new UdpDataSourceException(e); } opened = true; if (listener != null) { listener.onTransferStart(); } return C.LENGTH_UNBOUNDED; } @Override public int read(byte[] buffer, int offset, int readLength) throws UdpDataSourceException { if (packetRemaining == 0) { // We've read all of the data from the current packet. Get another. try { socket.receive(packet); } catch (IOException e) { throw new UdpDataSourceException(e); } packetRemaining = packet.getLength(); if (listener != null) { listener.onBytesTransferred(packetRemaining); } } int packetOffset = packet.getLength() - packetRemaining; int bytesToRead = Math.min(packetRemaining, readLength); System.arraycopy(packetBuffer, packetOffset, buffer, offset, bytesToRead); packetRemaining -= bytesToRead; return bytesToRead; } @Override public void close() { if (multicastSocket != null) { try { multicastSocket.leaveGroup(address); } catch (IOException e) { // Do nothing. } multicastSocket = null; } if (socket != null) { socket.close(); socket = null; } address = null; socketAddress = null; packetRemaining = 0; if (opened) { opened = false; if (listener != null) { listener.onTransferEnd(); } } } @Override public String getUri() { return dataSpec == null ? null : dataSpec.uri.toString(); } }