com.github.mrstampy.pprspray.core.streamer.util.MediaStreamerUtils.java Source code

Java tutorial

Introduction

Here is the source code for com.github.mrstampy.pprspray.core.streamer.util.MediaStreamerUtils.java

Source

/*
 * PepperSpray-core, Encrypted Secure Communications Library
 * 
 * Copyright (C) 2014 Burton Alexander
 * 
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation; either version 2 of the License, or (at your option) any later
 * version.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 * 
 * You should have received a copy of the GNU General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc., 51
 * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 * 
 */
package com.github.mrstampy.pprspray.core.streamer.util;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;

import java.net.InetSocketAddress;
import java.security.SecureRandom;
import java.util.Arrays;

import com.github.mrstampy.kitchensync.netty.channel.DefaultChannelRegistry;
import com.github.mrstampy.kitchensync.netty.channel.KiSyChannel;
import com.github.mrstampy.pprspray.core.streamer.MediaStreamType;
import com.github.mrstampy.pprspray.core.streamer.footer.MediaFooter;
import com.github.mrstampy.pprspray.core.streamer.text.AbstractMetaTextChunk;
import com.github.mrstampy.pprspray.core.streamer.text.DefaultJsonChunk;
import com.github.mrstampy.pprspray.core.streamer.text.DefaultJsonChunkProcessor;
import com.github.mrstampy.pprspray.core.streamer.text.DefaultXmlChunkProcessor;

/**
 * The Class MediaStreamerUtils.
 */
public class MediaStreamerUtils {

    /** The Constant DEFAULT_HEADER_LENGTH. */
    public static final int DEFAULT_HEADER_LENGTH = 23;

    /** The Constant FOOTER_LENGTH. */
    public static final int FOOTER_LENGTH = 12;

    /** The Constant MEDIA_TYPE_CHUNK. */
    protected static final Chunk MEDIA_TYPE_CHUNK = new Chunk(0, 4);

    /** The Constant HEADER_LENGTH_CHUNK. */
    protected static final Chunk HEADER_LENGTH_CHUNK = new Chunk(4, 6);

    /** The Constant MEDIA_HASH_CHUNK. */
    protected static final Chunk MESSAGE_HASH_CHUNK = new Chunk(6, 10);

    /** The Constant MEDIA_HASH_CHUNK. */
    protected static final Chunk MEDIA_HASH_CHUNK = new Chunk(10, 14);

    /** The Constant SEQUENCE_CHUNK. */
    protected static final Chunk SEQUENCE_CHUNK = new Chunk(14, 22);

    /** The Constant ACK_REQ_CHUNK. */
    protected static final Chunk ACK_REQ_CHUNK = new Chunk(22, DEFAULT_HEADER_LENGTH);

    private static SecureRandom rand = new SecureRandom();

    /**
     * Creates the marshalling class name hash.
     *
     * @param clazz
     *          the clazz
     * @return the int
     */
    public static int createMarshallingClassNameHash(Class<?> clazz) {
        return clazz == null ? AbstractMetaTextChunk.NO_MARSHALLING_CLASS : clazz.getName().hashCode();
    }

    /**
     * Send termination event.
     *
     * @param mediaHash
     *          the media hash
     * @param local
     *          the local
     * @param remote
     *          the remote
     */
    public static void sendTerminationEvent(int mediaHash, InetSocketAddress local, InetSocketAddress remote) {
        KiSyChannel channel = getChannel(local);

        sendTerminationEvent(mediaHash, channel, remote);
    }

    /**
     * Send termination event.
     *
     * @param mediaHash
     *          the media hash
     * @param channel
     *          the channel
     * @param remote
     *          the remote
     */
    public static void sendTerminationEvent(int mediaHash, KiSyChannel channel, InetSocketAddress remote) {
        if (channel == null)
            return;

        MediaFooter footer = new MediaFooter(MediaStreamType.NEGOTIATION, mediaHash);

        byte[] terminate = footer.createFooter();
        ChannelFuture cf = channel.send(terminate, remote);
        cf.awaitUninterruptibly();
    }

    /**
     * Gets the channel.
     *
     * @param local
     *          the local
     * @return the channel
     */
    public static KiSyChannel getChannel(InetSocketAddress local) {
        KiSyChannel channel = DefaultChannelRegistry.INSTANCE.getChannel(local.getPort());
        if (channel == null)
            channel = DefaultChannelRegistry.INSTANCE.getMulticastChannel(local);

        return channel;
    }

    /**
     * Checks if is binary chunk.
     *
     * @param message
     *          the message
     * @return true, if checks if is binary chunk
     */
    public static boolean isBinaryChunk(byte[] message) {
        return isMediaType(message, MediaStreamType.BINARY);
    }

    /**
     * Checks if is file chunk.
     *
     * @param message
     *          the message
     * @return true, if checks if is file chunk
     */
    public static boolean isFileChunk(byte[] message) {
        return isMediaType(message, MediaStreamType.FILE);
    }

    /**
     * Checks if is text chunk.
     *
     * @param message
     *          the message
     * @return true, if checks if is text chunk
     */
    public static boolean isTextChunk(byte[] message) {
        return isMediaType(message, MediaStreamType.TEXT);
    }

    /**
     * Checks if is video chunk.
     *
     * @param message
     *          the message
     * @return true, if checks if is video chunk
     */
    public static boolean isVideoChunk(byte[] message) {
        return isMediaType(message, MediaStreamType.VIDEO);
    }

    /**
     * Checks if is default audio chunk.
     *
     * @param message
     *          the message
     * @return true, if checks if is default audio chunk
     */
    public static boolean isAudioChunk(byte[] message) {
        return isMediaType(message, MediaStreamType.AUDIO);
    }

    /**
     * Checks if is media type.
     *
     * @param message
     *          the message
     * @param type
     *          the type
     * @param mediaHash
     *          the media hash
     * @return true, if checks if is media type
     */
    public static boolean isMediaType(byte[] message, MediaStreamType type, int mediaHash) {
        if (message == null || message.length < DEFAULT_HEADER_LENGTH)
            return false;

        return isMediaType(message, type) && isMediaHash(message, mediaHash);
    }

    /**
     * Checks if is media type.
     *
     * @param message
     *          the message
     * @param type
     *          the type
     * @return true, if checks if is media type
     */
    public static boolean isMediaType(byte[] message, MediaStreamType type) {
        if (message == null || message.length < DEFAULT_HEADER_LENGTH)
            return false;

        byte[] b = getChunk(message, MEDIA_TYPE_CHUNK);

        return Arrays.equals(b, type.ordinalBytes());
    }

    /**
     * Checks if is media type footer.
     *
     * @param message
     *          the message
     * @param type
     *          the type
     * @return true, if checks if is media type
     */
    public static boolean isMediaTypeFooter(byte[] message, MediaStreamType type) {
        if (message == null || message.length < FOOTER_LENGTH)
            return false;

        byte[] b = getChunk(message, MEDIA_TYPE_CHUNK);

        return Arrays.equals(b, type.eomBytes());
    }

    /**
     * Checks if is media footer.
     *
     * @param message
     *          the message
     * @param type
     *          the type
     * @param mediaHash
     *          the media hash
     * @return true, if checks if is media footer
     */
    public static boolean isMediaFooter(byte[] message, MediaStreamType type, int mediaHash) {
        if (message == null || message.length != FOOTER_LENGTH)
            return false;

        ByteBuf buf = Unpooled.buffer(8);
        buf.writeBytes(message);

        return Integer.MAX_VALUE - type.ordinal() == buf.getInt(0) && mediaHash == buf.getInt(4);
    }

    /**
     * Checks if is media hash.
     *
     * @param message
     *          the message
     * @param mediaHash
     *          the media hash
     * @return true, if checks if is media hash
     */
    public static boolean isMediaHash(byte[] message, int mediaHash) {
        return getMediaStreamHash(message) == mediaHash;
    }

    /**
     * Gets the media stream type as chunk header.
     *
     * @param message
     *          the message
     * @return the media stream type as chunk header
     */
    public static MediaStreamType getMediaStreamTypeAsChunkHeader(byte[] message) {
        byte[] b = getChunk(message, MEDIA_TYPE_CHUNK);

        return MediaStreamType.getTypeAsHeader(b);
    }

    /**
     * Gets the media stream type as footer.
     *
     * @param message
     *          the message
     * @return the media stream type as footer
     */
    public static MediaStreamType getMediaStreamTypeAsFooter(byte[] message) {
        byte[] b = getChunk(message, MEDIA_TYPE_CHUNK);

        return MediaStreamType.getTypeAsFooter(b);
    }

    /**
     * Gets the media stream hash.
     *
     * @param message
     *          the message
     * @return the media stream hash
     */
    public static int getMediaStreamHash(byte[] message) {
        return getIntegerChunk(getChunk(message, MEDIA_HASH_CHUNK));
    }

    /**
     * Gets the message hash.
     *
     * @param message
     *          the message
     * @return the media stream hash
     */
    public static int getMessageHash(byte[] message) {
        return getIntegerChunk(getChunk(message, MESSAGE_HASH_CHUNK));
    }

    /**
     * Creates the message hash.
     *
     * @return the int
     */
    public static int createMessageHash() {
        return rand.nextInt(Integer.MAX_VALUE);
    }

    /**
     * Gets the media stream header length.
     *
     * @param message
     *          the message
     * @return the media stream hash
     */
    public static int getMediaStreamHeaderLength(byte[] message) {
        return getShortChunk(getChunk(message, HEADER_LENGTH_CHUNK));
    }

    /**
     * Gets the sequence.
     *
     * @param message
     *          the message
     * @return the sequence
     */
    public static long getSequence(byte[] message) {
        return getLongChunk(getChunk(message, SEQUENCE_CHUNK));
    }

    /**
     * Checks if is ack required.
     *
     * @param message
     *          the message
     * @return true, if checks if is ack required
     */
    public static boolean isAckRequired(byte[] message) {
        return Arrays.copyOfRange(message, ACK_REQ_CHUNK.start, ACK_REQ_CHUNK.end)[0] == 1;
    }

    /**
     * Gets the custom header chunk.
     *
     * @param message
     *          the message
     * @param headerLength
     *          the header length
     * @return the custom header chunk
     */
    public static byte[] getCustomHeaderChunk(byte[] message, int headerLength) {
        //@formatter:off
        return headerLength <= DEFAULT_HEADER_LENGTH ? new byte[0]
                : getChunk(message, new Chunk(DEFAULT_HEADER_LENGTH, headerLength));
        //@formatter:on
    }

    /**
     * Returns true if the message indicates it is a JSON message.
     *
     * @param message
     *          the message
     * @return true, if checks if is json message
     * @see DefaultJsonChunk
     * @see DefaultJsonChunkProcessor
     */
    public static boolean isJsonMessage(byte[] message) {
        return isCustomHeaderMessage(message, DefaultJsonChunkProcessor.JSON_KEY_BYTES);
    }

    /**
     * Checks if is text only message.
     *
     * @param message
     *          the message
     * @return true, if checks if is text only message
     */
    public static boolean isTextOnlyMessage(byte[] message) {
        return isTextChunk(message) && !isJsonMessage(message) && !isXmlMessage(message);
    }

    /**
     * Gets the marshalling class hash.
     *
     * @param message
     *          the message
     * @param keyLength
     *          the key length
     * @return the marshalling class hash
     */
    public static int getMarshallingClassHash(byte[] message, int keyLength) {
        int start = DEFAULT_HEADER_LENGTH + keyLength;
        int end = start + 4;

        return getIntegerChunk(getChunk(message, new Chunk(start, end)));
    }

    /**
     * Checks if is xml message.
     *
     * @param message
     *          the message
     * @return true, if checks if is xml message
     */
    public static boolean isXmlMessage(byte[] message) {
        return isCustomHeaderMessage(message, DefaultXmlChunkProcessor.XML_KEY_BYTES);
    }

    /**
     * Checks if is custom header message.
     *
     * @param message
     *          the message
     * @param headerBytes
     *          the header bytes
     * @return true, if checks if is custom header message
     */
    public static boolean isCustomHeaderMessage(byte[] message, byte[] headerBytes) {
        int minLength = DEFAULT_HEADER_LENGTH + headerBytes.length;

        if (message == null || message.length <= minLength)
            return false;

        byte[] b = getChunk(message, new Chunk(DEFAULT_HEADER_LENGTH, minLength));

        return Arrays.equals(b, headerBytes);
    }

    /**
     * Gets the integer chunk.
     *
     * @param chunk
     *          the chunk
     * @return the integer chunk
     */
    protected static int getIntegerChunk(byte[] chunk) {
        ByteBuf buf = Unpooled.copiedBuffer(chunk);

        return buf.getInt(0);
    }

    /**
     * Gets the short chunk.
     *
     * @param chunk
     *          the chunk
     * @return the short chunk
     */
    protected static int getShortChunk(byte[] chunk) {
        ByteBuf buf = Unpooled.copiedBuffer(chunk);

        return buf.getShort(0);
    }

    /**
     * Gets the long chunk.
     *
     * @param chunk
     *          the chunk
     * @return the long chunk
     */
    protected static long getLongChunk(byte[] chunk) {
        ByteBuf buf = Unpooled.copiedBuffer(chunk);

        return buf.getLong(0);
    }

    /**
     * Gets the chunk.
     *
     * @param message
     *          the message
     * @param chunk
     *          the chunk
     * @return the chunk
     */
    protected static byte[] getChunk(byte[] message, Chunk chunk) {
        return Arrays.copyOfRange(message, chunk.start, chunk.end);
    }

    /**
     * Write header.
     *
     * @param buf
     *          the buf
     * @param type
     *          the type
     * @param headerLength
     *          the header length
     * @param messageHash
     *          the message hash
     * @param mediaHash
     *          the media hash
     * @param sequence
     *          the sequence
     * @param ackRequired
     *          the ack required
     */
    public static void writeHeader(ByteBuf buf, MediaStreamType type, int headerLength, int messageHash,
            int mediaHash, long sequence, boolean ackRequired) {
        buf.writeBytes(type.ordinalBytes());
        buf.writeShort(headerLength);
        buf.writeInt(messageHash);
        buf.writeInt(mediaHash);
        buf.writeLong(sequence);
        buf.writeBoolean(ackRequired);
    }

    /**
     * The Constructor.
     */
    protected MediaStreamerUtils() {
    }

    /**
     * The Class Chunk.
     */
    protected static class Chunk {

        /** The start. */
        int start;

        /** The end. */
        int end;

        /**
         * The Constructor.
         *
         * @param start
         *          the start
         * @param end
         *          the end
         */
        public Chunk(int start, int end) {
            this.start = start;
            this.end = end;
        }
    }

}