/*
 * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
 *
 * See terms of license at gnu.org.
 */
package org.jitsi.impl.neomedia.codec.video.h263p;

import java.awt.*;
import java.util.*;
import java.util.List;
import javax.media.*;
import javax.media.format.*;
import net.sf.fmj.media.*;
import org.jitsi.impl.neomedia.codec.*;
import org.jitsi.service.neomedia.codec.*;

/**
 * Packetizes H.263+ encoded data into RTP packets in accord with RFC 4529
 * "RTP Payload Format for ITU-T Rec. H.263 Video".
 *
 * @author Sebastien Vincent
 * @author Lyubomir Marinov
 */
public class Packetizer extends AbstractPacketizer {

    private static final Format[] DEFAULT_OUTPUT_FORMATS = { new VideoFormat(Constants.H263P_RTP) };

    /**
     * Maximum payload size without the headers.
     */
    public static final int MAX_PAYLOAD_SIZE = 1024;

    /**
     * Name of the plugin.
     */
    private static final String PLUGIN_NAME = "H263+ Packetizer";

    /**
     * Finds the index in <tt>byteStream</tt> at which a Picture Start code
     * begins.
     *
     * @param byteStream the H.263+ encoded byte stream
     * @param beginIndex the inclusive index in <tt>byteStream</tt> at which the
     * search is to begin
     * @param endIndex the exclusive index in <tt>byteStream</tt> at which the
     * search is to end
     * @return the index in <tt>byteStream</tt> at which the Picture Start code
     * begins, otherwise, <tt>endIndex</tt>
     */
    private static int findStartcode(byte[] byteStream, int beginIndex, int endIndex) {
        for (; beginIndex < (endIndex - 3); beginIndex++) {
            if ((byteStream[beginIndex] == 0) && (byteStream[beginIndex + 1] == 0) && ((byteStream[beginIndex + 2] & (byte) 0x80) == -128)) {
                return beginIndex;
            }
        }
        return endIndex;
    }

    /**
     * The sequence number of the next RTP packet to be output by this
     * <tt>Packetizer</tt>.
     */
    private int sequenceNumber = 0;

    /**
     * The timeStamp of the RTP packets in which H263+ packets are to be sent.
     */
    private long timeStamp = 0;

    /**
     * The list of H263+ "Start code" video packets to be sent as payload in RTP
     * packets.
     */
    private final List<byte[]> videoPkts = new LinkedList<byte[]>();

    /**
     * Initializes a new <tt>Packetizer</tt> instance which is to packetize
     * H.263+ encoded data into RTP packets in accord with
     * RFC 4529 "RTP Payload Format for ITU-T Rec. H.263 Video".
     */
    public Packetizer() {
        inputFormats = new Format[] { new VideoFormat(Constants.H263P) };
        inputFormat = null;
        outputFormat = null;
    }

    @Override
    public synchronized void close() {
        if (opened) {
            videoPkts.clear();
            opened = false;
            super.close();
        }
    }

    /**
     * Get the matching output formats for a specific format.
     *
     * @param inputFormat input format
     * @return array for formats matching input format
     */
    private Format[] getMatchingOutputFormats(Format inputFormat) {
        VideoFormat inputVideoFormat = (VideoFormat) inputFormat;
        return new Format[] { new VideoFormat(Constants.H263P_RTP, inputVideoFormat.getSize(), /* maxDataLength */
        Format.NOT_SPECIFIED, Format.byteArray, inputVideoFormat.getFrameRate()) };
    }

    /**
     * Get codec name.
     *
     * @return codec name
     */
    @Override
    public String getName() {
        return PLUGIN_NAME;
    }

    /**
     * Return the list of formats supported at the output.
     * @param inputFormat input <tt>Format</tt> to determine corresponding
     * output <tt>Format/tt>s
     * @return array of formats supported at output
     */
    public Format[] getSupportedOutputFormats(Format inputFormat) {
        if (inputFormat == null)
            return DEFAULT_OUTPUT_FORMATS;
        // mismatch input format
        if (!(inputFormat instanceof VideoFormat) || (AbstractCodec2.matches(inputFormat, inputFormats) == null)) {
            return new Format[0];
        }
        return getMatchingOutputFormats(inputFormat);
    }

    /**
     * Open this <tt>Packetizer</tt>.
     *
     * @throws ResourceUnavailableException if something goes wrong during
     * initialization of the Packetizer.
     */
    @Override
    public synchronized void open() throws ResourceUnavailableException {
        if (!opened) {
            videoPkts.clear();
            sequenceNumber = 0;
            super.open();
            opened = true;
        }
    }

    /**
     * Packetizes H.263+ encoded data so that it becomes ready to be sent as the
     * payload of RTP packets.
     *
     * @param data the bytes which contain the H.263+ encoded data to be
     * packetized
     * @param offset the offset of H.263+ encoded data to be packetized begins
     * @param length the length  of the H.263+ encoded data starting at offset
     * @return <tt>true</tt> if at least one RTP packet payload has been
     * packetized i.e. prepared for sending; otherwise, <tt>false</tt>
     */
    private boolean packetize(byte[] data, int offset, int length) {

        while (length > 0) {
            boolean isPsc = false;
            int pos = 0;
            int maxPayloadLength = MAX_PAYLOAD_SIZE;
            byte[] pkt = null;
            int payloadLength = 0;
            /* is we are at synchronization point (PSC, GSBC, EOS, EOSBS) */
            if (data.length > 3 && data[offset] == 0x00 && data[offset + 1] == 0x00) {
                isPsc = true;
                pos = 2;
            } else {
                maxPayloadLength -= 2;
            }
            if (length > maxPayloadLength) {
                payloadLength = maxPayloadLength;
            } else {
                payloadLength = length;
            }
            pkt = new byte[payloadLength + (isPsc ? 0 : 2)];
            /* add H263+ payload header */
            /* no VRC and no extra picture header */
            pkt[0] = (byte) (isPsc ? 0x04 : 0x00);
            pkt[1] = 0x00;
            System.arraycopy(data, offset + pos, pkt, 2, payloadLength - pos);
            pktAdded = videoPkts.add(pkt) || pktAdded;
            offset += payloadLength;
            length -= payloadLength;
        }
        return pktAdded;
    }

    /**
     * Processes/packetizes a buffer.
     *
     * @param inBuffer input buffer
     * @param outBuffer output buffer
     * @return <tt>BUFFER_PROCESSED_OK</tt> if buffer has been successfully
     * processed
     */
    @Override
    public int process(Buffer inBuffer, Buffer outBuffer) {
        int inLength = inBuffer.getLength();
        byte[] inData = (byte[]) inBuffer.getData();
        int inOffset = inBuffer.getOffset();
        boolean pktAdded = false;
        if (videoPkts.size() > 0) {
            byte[] pktData = videoPkts.remove(0);
            // Send the packet.
            outBuffer.setData(pktData);
            outBuffer.setLength(pktData.length);
            outBuffer.setOffset(0);
            outBuffer.setTimeStamp(timeStamp);
            outBuffer.setSequenceNumber(sequenceNumber++);
            // If there are other packets, send them as well.
            if (videoPkts.size() > 0) {
                return (BUFFER_PROCESSED_OK | INPUT_BUFFER_NOT_CONSUMED);
            } else {
                // It's the last packet of the current frame so mark it.
                outBuffer.setFlags(outBuffer.getFlags() | Buffer.FLAG_RTP_MARKER);
                return BUFFER_PROCESSED_OK;
            }
        }
        if (isEOM(inBuffer)) {
            propagateEOM(outBuffer);
            reset();
            return BUFFER_PROCESSED_OK;
        }
        if (inBuffer.isDiscard()) {
            outBuffer.setDiscard(true);
            reset();
            return BUFFER_PROCESSED_OK;
        }
        Format inFormat = inBuffer.getFormat();
        if ((inFormat != inputFormat) && !inFormat.matches(inputFormat))
            setInputFormat(inFormat);
        int endIndex = inOffset + inLength;
        int beginIndex = findStartcode(inData, inOffset, endIndex);
        if (beginIndex < endIndex) {
            for (int nextBeginIndex; beginIndex < endIndex; beginIndex = nextBeginIndex + 3) {
                nextBeginIndex = findStartcode(inData, beginIndex + 3, endIndex);
                int length = nextBeginIndex - beginIndex;
                if (length > 0) {
                    pktAdded = packetize(inData, beginIndex, length) || pktAdded;
                    beginIndex += length;
                }
            }
        }
        timeStamp = inBuffer.getTimeStamp();
        if (pktAdded) {
            return process(inBuffer, outBuffer);
        } else {
            /* first frame is not a synchronization point, discard ?*/
            return BUFFER_PROCESSED_FAILED;
        }
    }

    /**
     * Sets the input format.
     *
     * @param in format to set
     * @return format
     */
    @Override
    public Format setInputFormat(Format in) {
        /*
         * Return null if the specified input Format is incompatible with this
         * Packetizer.
         */
        if (!(in instanceof VideoFormat) || null == AbstractCodec2.matches(in, inputFormats))
            return null;
        inputFormat = in;
        return in;
    }

    /**
     * Sets the <tt>Format</tt> in which this <tt>Codec</tt> is to output media
     * data.
     *
     * @param format the <tt>Format</tt> in which this <tt>Codec</tt> is to
     * output media data
     * @return the <tt>Format</tt> in which this <tt>Codec</tt> is currently
     * configured to output media data or <tt>null</tt> if <tt>format</tt> was
     * found to be incompatible with this <tt>Codec</tt>
     */
    @Override
    public Format setOutputFormat(Format format) {
        /*
         * Return null if the specified output Format is incompatible with this
         * Packetizer.
         */
        if (!(format instanceof VideoFormat) || (null == AbstractCodec2.matches(format, getMatchingOutputFormats(inputFormat))))
            return null;
        VideoFormat videoFormat = (VideoFormat) format;
        /*
         * A Packetizer translates raw media data in RTP payloads. Consequently,
         * the size of the output is equal to the size of the input.
         */
        Dimension size = null;
        if (inputFormat != null)
            size = ((VideoFormat) inputFormat).getSize();
        if ((size == null) && format.matches(outputFormat))
            size = ((VideoFormat) outputFormat).getSize();
        outputFormat = new VideoFormat(videoFormat.getEncoding(), size, /* maxDataLength */
        Format.NOT_SPECIFIED, Format.byteArray, videoFormat.getFrameRate());
        // Return the outputFormat which is actually set.
        return outputFormat;
    }
}


while (length > 0) {
            boolean isPsc = false;
            int pos = 0;
            int maxPayloadLength = MAX_PAYLOAD_SIZE;
            byte[] pkt = null;
            int payloadLength = 0;
            /* is we are at synchronization point (PSC, GSBC, EOS, EOSBS) */
            if (data.length > 3 && data[offset] == 0x00 && data[offset + 1] == 0x00) {
                isPsc = true;
                pos = 2;
            } else {
                maxPayloadLength -= 2;
            }
            if (length > maxPayloadLength) {
                payloadLength = maxPayloadLength;
            } else {
                payloadLength = length;
            }
            pkt = new byte[payloadLength + (isPsc ? 0 : 2)];
            /* add H263+ payload header */
            /* no VRC and no extra picture header */
            pkt[0] = (byte) (isPsc ? 0x04 : 0x00);
            pkt[1] = 0x00;
            System.arraycopy(data, offset + pos, pkt, 2, payloadLength - pos);
            pktAdded = videoPkts.add(pkt) || pktAdded;
            offset += payloadLength;
            length -= payloadLength;
        }

/*
 * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
 *
 * See terms of license at gnu.org.
 */
package org.jitsi.impl.neomedia.codec.video.h263p;

import java.awt.*;
import java.util.*;
import java.util.List;
import javax.media.*;
import javax.media.format.*;
import net.sf.fmj.media.*;
import org.jitsi.impl.neomedia.codec.*;
import org.jitsi.service.neomedia.codec.*;

/**
 * Packetizes H.263+ encoded data into RTP packets in accord with RFC 4529
 * "RTP Payload Format for ITU-T Rec. H.263 Video".
 *
 * @author Sebastien Vincent
 * @author Lyubomir Marinov
 */
public class Packetizer extends AbstractPacketizer {

    private static final Format[] DEFAULT_OUTPUT_FORMATS = { new VideoFormat(Constants.H263P_RTP) };

    /**
     * Maximum payload size without the headers.
     */
    public static final int MAX_PAYLOAD_SIZE = 1024;

    /**
     * Name of the plugin.
     */
    private static final String PLUGIN_NAME = "H263+ Packetizer";

    /**
     * Finds the index in <tt>byteStream</tt> at which a Picture Start code
     * begins.
     *
     * @param byteStream the H.263+ encoded byte stream
     * @param beginIndex the inclusive index in <tt>byteStream</tt> at which the
     * search is to begin
     * @param endIndex the exclusive index in <tt>byteStream</tt> at which the
     * search is to end
     * @return the index in <tt>byteStream</tt> at which the Picture Start code
     * begins, otherwise, <tt>endIndex</tt>
     */
    private static int findStartcode(byte[] byteStream, int beginIndex, int endIndex) {
        for (; beginIndex < (endIndex - 3); beginIndex++) {
            if ((byteStream[beginIndex] == 0) && (byteStream[beginIndex + 1] == 0) && ((byteStream[beginIndex + 2] & (byte) 0x80) == -128)) {
                return beginIndex;
            }
        }
        return endIndex;
    }

    /**
     * The sequence number of the next RTP packet to be output by this
     * <tt>Packetizer</tt>.
     */
    private int sequenceNumber = 0;

    /**
     * The timeStamp of the RTP packets in which H263+ packets are to be sent.
     */
    private long timeStamp = 0;

    /**
     * The list of H263+ "Start code" video packets to be sent as payload in RTP
     * packets.
     */
    private final List<byte[]> videoPkts = new LinkedList<byte[]>();

    /**
     * Initializes a new <tt>Packetizer</tt> instance which is to packetize
     * H.263+ encoded data into RTP packets in accord with
     * RFC 4529 "RTP Payload Format for ITU-T Rec. H.263 Video".
     */
    public Packetizer() {
        inputFormats = new Format[] { new VideoFormat(Constants.H263P) };
        inputFormat = null;
        outputFormat = null;
    }

    @Override
    public synchronized void close() {
        if (opened) {
            videoPkts.clear();
            opened = false;
            super.close();
        }
    }

    /**
     * Get the matching output formats for a specific format.
     *
     * @param inputFormat input format
     * @return array for formats matching input format
     */
    private Format[] getMatchingOutputFormats(Format inputFormat) {
        VideoFormat inputVideoFormat = (VideoFormat) inputFormat;
        return new Format[] { new VideoFormat(Constants.H263P_RTP, inputVideoFormat.getSize(), /* maxDataLength */
        Format.NOT_SPECIFIED, Format.byteArray, inputVideoFormat.getFrameRate()) };
    }

    /**
     * Get codec name.
     *
     * @return codec name
     */
    @Override
    public String getName() {
        return PLUGIN_NAME;
    }

    /**
     * Return the list of formats supported at the output.
     * @param inputFormat input <tt>Format</tt> to determine corresponding
     * output <tt>Format/tt>s
     * @return array of formats supported at output
     */
    public Format[] getSupportedOutputFormats(Format inputFormat) {
        if (inputFormat == null)
            return DEFAULT_OUTPUT_FORMATS;
        // mismatch input format
        if (!(inputFormat instanceof VideoFormat) || (AbstractCodec2.matches(inputFormat, inputFormats) == null)) {
            return new Format[0];
        }
        return getMatchingOutputFormats(inputFormat);
    }

    /**
     * Open this <tt>Packetizer</tt>.
     *
     * @throws ResourceUnavailableException if something goes wrong during
     * initialization of the Packetizer.
     */
    @Override
    public synchronized void open() throws ResourceUnavailableException {
        if (!opened) {
            videoPkts.clear();
            sequenceNumber = 0;
            super.open();
            opened = true;
        }
    }

    /**
     * Packetizes H.263+ encoded data so that it becomes ready to be sent as the
     * payload of RTP packets.
     *
     * @param data the bytes which contain the H.263+ encoded data to be
     * packetized
     * @param offset the offset of H.263+ encoded data to be packetized begins
     * @param length the length  of the H.263+ encoded data starting at offset
     * @return <tt>true</tt> if at least one RTP packet payload has been
     * packetized i.e. prepared for sending; otherwise, <tt>false</tt>
     */
    private boolean packetize(byte[] data, int offset, int length) {
        boolean pktAdded = false;

        return pktAdded;
    }

    /**
     * Processes/packetizes a buffer.
     *
     * @param inBuffer input buffer
     * @param outBuffer output buffer
     * @return <tt>BUFFER_PROCESSED_OK</tt> if buffer has been successfully
     * processed
     */
    @Override
    public int process(Buffer inBuffer, Buffer outBuffer) {
        int inLength = inBuffer.getLength();
        byte[] inData = (byte[]) inBuffer.getData();
        int inOffset = inBuffer.getOffset();
        boolean pktAdded = false;
        if (videoPkts.size() > 0) {
            byte[] pktData = videoPkts.remove(0);
            // Send the packet.
            outBuffer.setData(pktData);
            outBuffer.setLength(pktData.length);
            outBuffer.setOffset(0);
            outBuffer.setTimeStamp(timeStamp);
            outBuffer.setSequenceNumber(sequenceNumber++);
            // If there are other packets, send them as well.
            if (videoPkts.size() > 0) {
                return (BUFFER_PROCESSED_OK | INPUT_BUFFER_NOT_CONSUMED);
            } else {
                // It's the last packet of the current frame so mark it.
                outBuffer.setFlags(outBuffer.getFlags() | Buffer.FLAG_RTP_MARKER);
                return BUFFER_PROCESSED_OK;
            }
        }
        if (isEOM(inBuffer)) {
            propagateEOM(outBuffer);
            reset();
            return BUFFER_PROCESSED_OK;
        }
        if (inBuffer.isDiscard()) {
            outBuffer.setDiscard(true);
            reset();
            return BUFFER_PROCESSED_OK;
        }
        Format inFormat = inBuffer.getFormat();
        if ((inFormat != inputFormat) && !inFormat.matches(inputFormat))
            setInputFormat(inFormat);
        int endIndex = inOffset + inLength;
        int beginIndex = findStartcode(inData, inOffset, endIndex);
        if (beginIndex < endIndex) {
            for (int nextBeginIndex; beginIndex < endIndex; beginIndex = nextBeginIndex + 3) {
                nextBeginIndex = findStartcode(inData, beginIndex + 3, endIndex);
                int length = nextBeginIndex - beginIndex;
                if (length > 0) {
                    pktAdded = packetize(inData, beginIndex, length) || pktAdded;
                    beginIndex += length;
                }
            }
        }
        timeStamp = inBuffer.getTimeStamp();
        if (pktAdded) {
            return process(inBuffer, outBuffer);
        } else {
            /* first frame is not a synchronization point, discard ?*/
            return BUFFER_PROCESSED_FAILED;
        }
    }

    /**
     * Sets the input format.
     *
     * @param in format to set
     * @return format
     */
    @Override
    public Format setInputFormat(Format in) {
        /*
         * Return null if the specified input Format is incompatible with this
         * Packetizer.
         */
        if (!(in instanceof VideoFormat) || null == AbstractCodec2.matches(in, inputFormats))
            return null;
        inputFormat = in;
        return in;
    }

    /**
     * Sets the <tt>Format</tt> in which this <tt>Codec</tt> is to output media
     * data.
     *
     * @param format the <tt>Format</tt> in which this <tt>Codec</tt> is to
     * output media data
     * @return the <tt>Format</tt> in which this <tt>Codec</tt> is currently
     * configured to output media data or <tt>null</tt> if <tt>format</tt> was
     * found to be incompatible with this <tt>Codec</tt>
     */
    @Override
    public Format setOutputFormat(Format format) {
        /*
         * Return null if the specified output Format is incompatible with this
         * Packetizer.
         */
        if (!(format instanceof VideoFormat) || (null == AbstractCodec2.matches(format, getMatchingOutputFormats(inputFormat))))
            return null;
        VideoFormat videoFormat = (VideoFormat) format;
        /*
         * A Packetizer translates raw media data in RTP payloads. Consequently,
         * the size of the output is equal to the size of the input.
         */
        Dimension size = null;
        if (inputFormat != null)
            size = ((VideoFormat) inputFormat).getSize();
        if ((size == null) && format.matches(outputFormat))
            size = ((VideoFormat) outputFormat).getSize();
        outputFormat = new VideoFormat(videoFormat.getEncoding(), size, /* maxDataLength */
        Format.NOT_SPECIFIED, Format.byteArray, videoFormat.getFrameRate());
        // Return the outputFormat which is actually set.
        return outputFormat;
    }
}


/*
 * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
 *
 * See terms of license at gnu.org.
 */
package org.jitsi.impl.neomedia.codec.video.h263p;

import java.awt.*;
import java.util.*;
import java.util.List;
import javax.media.*;
import javax.media.format.*;
import net.sf.fmj.media.*;
import org.jitsi.impl.neomedia.codec.*;
import org.jitsi.service.neomedia.codec.*;

/**
 * Packetizes H.263+ encoded data into RTP packets in accord with RFC 4529
 * "RTP Payload Format for ITU-T Rec. H.263 Video".
 *
 * @author Sebastien Vincent
 * @author Lyubomir Marinov
 */
public class Packetizer extends AbstractPacketizer {

    private static final Format[] DEFAULT_OUTPUT_FORMATS = { new VideoFormat(Constants.H263P_RTP) };

    /**
     * Maximum payload size without the headers.
     */
    public static final int MAX_PAYLOAD_SIZE = 1024;

    /**
     * Name of the plugin.
     */
    private static final String PLUGIN_NAME = "H263+ Packetizer";

    /**
     * Finds the index in <tt>byteStream</tt> at which a Picture Start code
     * begins.
     *
     * @param byteStream the H.263+ encoded byte stream
     * @param beginIndex the inclusive index in <tt>byteStream</tt> at which the
     * search is to begin
     * @param endIndex the exclusive index in <tt>byteStream</tt> at which the
     * search is to end
     * @return the index in <tt>byteStream</tt> at which the Picture Start code
     * begins, otherwise, <tt>endIndex</tt>
     */
    private static int findStartcode(byte[] byteStream, int beginIndex, int endIndex) {
        for (; beginIndex < (endIndex - 3); beginIndex++) {
            if ((byteStream[beginIndex] == 0) && (byteStream[beginIndex + 1] == 0) && ((byteStream[beginIndex + 2] & (byte) 0x80) == -128)) {
                return beginIndex;
            }
        }
        return endIndex;
    }

    /**
     * The sequence number of the next RTP packet to be output by this
     * <tt>Packetizer</tt>.
     */
    private int sequenceNumber = 0;

    /**
     * The timeStamp of the RTP packets in which H263+ packets are to be sent.
     */
    private long timeStamp = 0;

    /**
     * The list of H263+ "Start code" video packets to be sent as payload in RTP
     * packets.
     */
    private final List<byte[]> videoPkts = new LinkedList<byte[]>();

    /**
     * Initializes a new <tt>Packetizer</tt> instance which is to packetize
     * H.263+ encoded data into RTP packets in accord with
     * RFC 4529 "RTP Payload Format for ITU-T Rec. H.263 Video".
     */
    public Packetizer() {
        inputFormats = new Format[] { new VideoFormat(Constants.H263P) };
        inputFormat = null;
        outputFormat = null;
    }

    @Override
    public synchronized void close() {
        if (opened) {
            videoPkts.clear();
            opened = false;
            super.close();
        }
    }

    /**
     * Get the matching output formats for a specific format.
     *
     * @param inputFormat input format
     * @return array for formats matching input format
     */
    private Format[] getMatchingOutputFormats(Format inputFormat) {
        VideoFormat inputVideoFormat = (VideoFormat) inputFormat;
        return new Format[] { new VideoFormat(Constants.H263P_RTP, inputVideoFormat.getSize(), /* maxDataLength */
        Format.NOT_SPECIFIED, Format.byteArray, inputVideoFormat.getFrameRate()) };
    }

    /**
     * Get codec name.
     *
     * @return codec name
     */
    @Override
    public String getName() {
        return PLUGIN_NAME;
    }

    /**
     * Return the list of formats supported at the output.
     * @param inputFormat input <tt>Format</tt> to determine corresponding
     * output <tt>Format/tt>s
     * @return array of formats supported at output
     */
    public Format[] getSupportedOutputFormats(Format inputFormat) {
        if (inputFormat == null)
            return DEFAULT_OUTPUT_FORMATS;
        // mismatch input format
        if (!(inputFormat instanceof VideoFormat) || (AbstractCodec2.matches(inputFormat, inputFormats) == null)) {
            return new Format[0];
        }
        return getMatchingOutputFormats(inputFormat);
    }

    /**
     * Open this <tt>Packetizer</tt>.
     *
     * @throws ResourceUnavailableException if something goes wrong during
     * initialization of the Packetizer.
     */
    @Override
    public synchronized void open() throws ResourceUnavailableException {
        if (!opened) {
            videoPkts.clear();
            sequenceNumber = 0;
            super.open();
            opened = true;
        }
    }

    /**
     * Packetizes H.263+ encoded data so that it becomes ready to be sent as the
     * payload of RTP packets.
     *
     * @param data the bytes which contain the H.263+ encoded data to be
     * packetized
     * @param offset the offset of H.263+ encoded data to be packetized begins
     * @param length the length  of the H.263+ encoded data starting at offset
     * @return <tt>true</tt> if at least one RTP packet payload has been
     * packetized i.e. prepared for sending; otherwise, <tt>false</tt>
     */
    private boolean packetize(byte[] data, int offset, int length) {
        boolean pktAdded = false;
        while (length > 0) {
            boolean isPsc = false;
            int pos = 0;
            int maxPayloadLength = MAX_PAYLOAD_SIZE;
            byte[] pkt = null;
            int payloadLength = 0;
            /* is we are at synchronization point (PSC, GSBC, EOS, EOSBS) */
            if (data.length > 3 && data[offset] == 0x00 && data[offset + 1] == 0x00) {
                isPsc = true;
                pos = 2;
            } else {
                maxPayloadLength -= 2;
            }
            if (length > maxPayloadLength) {
                payloadLength = maxPayloadLength;
            } else {
                payloadLength = length;
            }
            pkt = new byte[payloadLength + (isPsc ? 0 : 2)];
            /* add H263+ payload header */
            /* no VRC and no extra picture header */
            pkt[0] = (byte) (isPsc ? 0x04 : 0x00);
            pkt[1] = 0x00;
            System.arraycopy(data, offset + pos, pkt, 2, payloadLength - pos);
            pktAdded = videoPkts.add(pkt) || pktAdded;
            offset += payloadLength;
            length -= payloadLength;
        }

    /**
     * Processes/packetizes a buffer.
     *
     * @param inBuffer input buffer
     * @param outBuffer output buffer
     * @return <tt>BUFFER_PROCESSED_OK</tt> if buffer has been successfully
     * processed
     */
    @Override
    public int process(Buffer inBuffer, Buffer outBuffer) {
        int inLength = inBuffer.getLength();
        byte[] inData = (byte[]) inBuffer.getData();
        int inOffset = inBuffer.getOffset();
        boolean pktAdded = false;
        if (videoPkts.size() > 0) {
            byte[] pktData = videoPkts.remove(0);
            // Send the packet.
            outBuffer.setData(pktData);
            outBuffer.setLength(pktData.length);
            outBuffer.setOffset(0);
            outBuffer.setTimeStamp(timeStamp);
            outBuffer.setSequenceNumber(sequenceNumber++);
            // If there are other packets, send them as well.
            if (videoPkts.size() > 0) {
                return (BUFFER_PROCESSED_OK | INPUT_BUFFER_NOT_CONSUMED);
            } else {
                // It's the last packet of the current frame so mark it.
                outBuffer.setFlags(outBuffer.getFlags() | Buffer.FLAG_RTP_MARKER);
                return BUFFER_PROCESSED_OK;
            }
        }
        if (isEOM(inBuffer)) {
            propagateEOM(outBuffer);
            reset();
            return BUFFER_PROCESSED_OK;
        }
        if (inBuffer.isDiscard()) {
            outBuffer.setDiscard(true);
            reset();
            return BUFFER_PROCESSED_OK;
        }
        Format inFormat = inBuffer.getFormat();
        if ((inFormat != inputFormat) && !inFormat.matches(inputFormat))
            setInputFormat(inFormat);
        int endIndex = inOffset + inLength;
        int beginIndex = findStartcode(inData, inOffset, endIndex);
        if (beginIndex < endIndex) {
            for (int nextBeginIndex; beginIndex < endIndex; beginIndex = nextBeginIndex + 3) {
                nextBeginIndex = findStartcode(inData, beginIndex + 3, endIndex);
                int length = nextBeginIndex - beginIndex;
                if (length > 0) {
                    pktAdded = packetize(inData, beginIndex, length) || pktAdded;
                    beginIndex += length;
                }
            }
        }
        timeStamp = inBuffer.getTimeStamp();
        if (pktAdded) {
            return process(inBuffer, outBuffer);
        } else {
            /* first frame is not a synchronization point, discard ?*/
            return BUFFER_PROCESSED_FAILED;
        }
    }

    /**
     * Sets the input format.
     *
     * @param in format to set
     * @return format
     */
    @Override
    public Format setInputFormat(Format in) {
        /*
         * Return null if the specified input Format is incompatible with this
         * Packetizer.
         */
        if (!(in instanceof VideoFormat) || null == AbstractCodec2.matches(in, inputFormats))
            return null;
        inputFormat = in;
        return in;
    }

    /**
     * Sets the <tt>Format</tt> in which this <tt>Codec</tt> is to output media
     * data.
     *
     * @param format the <tt>Format</tt> in which this <tt>Codec</tt> is to
     * output media data
     * @return the <tt>Format</tt> in which this <tt>Codec</tt> is currently
     * configured to output media data or <tt>null</tt> if <tt>format</tt> was
     * found to be incompatible with this <tt>Codec</tt>
     */
    @Override
    public Format setOutputFormat(Format format) {
        /*
         * Return null if the specified output Format is incompatible with this
         * Packetizer.
         */
        if (!(format instanceof VideoFormat) || (null == AbstractCodec2.matches(format, getMatchingOutputFormats(inputFormat))))
            return null;
        VideoFormat videoFormat = (VideoFormat) format;
        /*
         * A Packetizer translates raw media data in RTP payloads. Consequently,
         * the size of the output is equal to the size of the input.
         */
        Dimension size = null;
        if (inputFormat != null)
            size = ((VideoFormat) inputFormat).getSize();
        if ((size == null) && format.matches(outputFormat))
            size = ((VideoFormat) outputFormat).getSize();
        outputFormat = new VideoFormat(videoFormat.getEncoding(), size, /* maxDataLength */
        Format.NOT_SPECIFIED, Format.byteArray, videoFormat.getFrameRate());
        // Return the outputFormat which is actually set.
        return outputFormat;
    }
}


/*
 * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
 *
 * See terms of license at gnu.org.
 */
package org.jitsi.impl.neomedia.codec.video.h263p;

import java.awt.*;
import java.util.*;
import java.util.List;
import javax.media.*;
import javax.media.format.*;
import net.sf.fmj.media.*;
import org.jitsi.impl.neomedia.codec.*;
import org.jitsi.service.neomedia.codec.*;

/**
 * Packetizes H.263+ encoded data into RTP packets in accord with RFC 4529
 * "RTP Payload Format for ITU-T Rec. H.263 Video".
 *
 * @author Sebastien Vincent
 * @author Lyubomir Marinov
 */
public class Packetizer extends AbstractPacketizer {

    private static final Format[] DEFAULT_OUTPUT_FORMATS = { new VideoFormat(Constants.H263P_RTP) };

    /**
     * Maximum payload size without the headers.
     */
    public static final int MAX_PAYLOAD_SIZE = 1024;

    /**
     * Name of the plugin.
     */
    private static final String PLUGIN_NAME = "H263+ Packetizer";

    /**
     * Finds the index in <tt>byteStream</tt> at which a Picture Start code
     * begins.
     *
     * @param byteStream the H.263+ encoded byte stream
     * @param beginIndex the inclusive index in <tt>byteStream</tt> at which the
     * search is to begin
     * @param endIndex the exclusive index in <tt>byteStream</tt> at which the
     * search is to end
     * @return the index in <tt>byteStream</tt> at which the Picture Start code
     * begins, otherwise, <tt>endIndex</tt>
     */
    private static int findStartcode(byte[] byteStream, int beginIndex, int endIndex) {
        for (; beginIndex < (endIndex - 3); beginIndex++) {
            if ((byteStream[beginIndex] == 0) && (byteStream[beginIndex + 1] == 0) && ((byteStream[beginIndex + 2] & (byte) 0x80) == -128)) {
                return beginIndex;
            }
        }
        return endIndex;
    }

    /**
     * The sequence number of the next RTP packet to be output by this
     * <tt>Packetizer</tt>.
     */
    private int sequenceNumber = 0;

    /**
     * The timeStamp of the RTP packets in which H263+ packets are to be sent.
     */
    private long timeStamp = 0;

    /**
     * The list of H263+ "Start code" video packets to be sent as payload in RTP
     * packets.
     */
    private final List<byte[]> videoPkts = new LinkedList<byte[]>();

    /**
     * Initializes a new <tt>Packetizer</tt> instance which is to packetize
     * H.263+ encoded data into RTP packets in accord with
     * RFC 4529 "RTP Payload Format for ITU-T Rec. H.263 Video".
     */
    public Packetizer() {
        inputFormats = new Format[] { new VideoFormat(Constants.H263P) };
        inputFormat = null;
        outputFormat = null;
    }

    @Override
    public synchronized void close() {
        if (opened) {
            videoPkts.clear();
            opened = false;
            super.close();
        }
    }

    /**
     * Get the matching output formats for a specific format.
     *
     * @param inputFormat input format
     * @return array for formats matching input format
     */
    private Format[] getMatchingOutputFormats(Format inputFormat) {
        VideoFormat inputVideoFormat = (VideoFormat) inputFormat;
        return new Format[] { new VideoFormat(Constants.H263P_RTP, inputVideoFormat.getSize(), /* maxDataLength */
        Format.NOT_SPECIFIED, Format.byteArray, inputVideoFormat.getFrameRate()) };
    }

    /**
     * Get codec name.
     *
     * @return codec name
     */
    @Override
    public String getName() {
        return PLUGIN_NAME;
    }

    /**
     * Return the list of formats supported at the output.
     * @param inputFormat input <tt>Format</tt> to determine corresponding
     * output <tt>Format/tt>s
     * @return array of formats supported at output
     */
    public Format[] getSupportedOutputFormats(Format inputFormat) {
        if (inputFormat == null)
            return DEFAULT_OUTPUT_FORMATS;
        // mismatch input format
        if (!(inputFormat instanceof VideoFormat) || (AbstractCodec2.matches(inputFormat, inputFormats) == null)) {
            return new Format[0];
        }
        return getMatchingOutputFormats(inputFormat);
    }

    /**
     * Open this <tt>Packetizer</tt>.
     *
     * @throws ResourceUnavailableException if something goes wrong during
     * initialization of the Packetizer.
     */
    @Override
    public synchronized void open() throws ResourceUnavailableException {
        if (!opened) {
            videoPkts.clear();
            sequenceNumber = 0;
            super.open();
            opened = true;
        }
    }

    /**
     * Packetizes H.263+ encoded data so that it becomes ready to be sent as the
     * payload of RTP packets.
     *
     * @param data the bytes which contain the H.263+ encoded data to be
     * packetized
     * @param offset the offset of H.263+ encoded data to be packetized begins
     * @param length the length  of the H.263+ encoded data starting at offset
     * @return <tt>true</tt> if at least one RTP packet payload has been
     * packetized i.e. prepared for sending; otherwise, <tt>false</tt>
     */
    private boolean packetize(byte[] data, int offset, int length) {
        boolean pktAdded = false;
        while (length > 0) {
            boolean isPsc = false;
            int pos = 0;
            int maxPayloadLength = MAX_PAYLOAD_SIZE;
            byte[] pkt = null;
            int payloadLength = 0;
            /* is we are at synchronization point (PSC, GSBC, EOS, EOSBS) */
            if (data.length > 3 && data[offset] == 0x00 && data[offset + 1] == 0x00) {
                isPsc = true;
                pos = 2;
            } else {
                maxPayloadLength -= 2;
            }
            if (length > maxPayloadLength) {
                payloadLength = maxPayloadLength;
            } else {
                payloadLength = length;
            }
            pkt = new byte[payloadLength + (isPsc ? 0 : 2)];
            /* add H263+ payload header */
            /* no VRC and no extra picture header */
            pkt[0] = (byte) (isPsc ? 0x04 : 0x00);
            pkt[1] = 0x00;
            System.arraycopy(data, offset + pos, pkt, 2, payloadLength - pos);
            pktAdded = videoPkts.add(pkt) || pktAdded;
            offset += payloadLength;
            length -= payloadLength;
        }
        return pktAdded;
    }

    /**
     * Processes/packetizes a buffer.
     *
     * @param inBuffer input buffer
     * @param outBuffer output buffer
     * @return <tt>BUFFER_PROCESSED_OK</tt> if buffer has been successfully
     * processed
     */
    @Override
    public int process(Buffer inBuffer, Buffer outBuffer) {
        int inLength = inBuffer.getLength();
        byte[] inData = (byte[]) inBuffer.getData();
        int inOffset = inBuffer.getOffset();
        boolean pktAdded = false;
        if (videoPkts.size() > 0) {
            byte[] pktData = videoPkts.remove(0);
            // Send the packet.
            outBuffer.setData(pktData);
            outBuffer.setLength(pktData.length);
            outBuffer.setOffset(0);
            outBuffer.setTimeStamp(timeStamp);
            outBuffer.setSequenceNumber(sequenceNumber++);
            // If there are other packets, send them as well.
            if (videoPkts.size() > 0) {
                return (BUFFER_PROCESSED_OK | INPUT_BUFFER_NOT_CONSUMED);
            } else {
                // It's the last packet of the current frame so mark it.
                outBuffer.setFlags(outBuffer.getFlags() | Buffer.FLAG_RTP_MARKER);
                return BUFFER_PROCESSED_OK;
            }
        }
        if (isEOM(inBuffer)) {
            propagateEOM(outBuffer);
            reset();
            return BUFFER_PROCESSED_OK;
        }
        if (inBuffer.isDiscard()) {
            outBuffer.setDiscard(true);
            reset();
            return BUFFER_PROCESSED_OK;
        }
        Format inFormat = inBuffer.getFormat();
        if ((inFormat != inputFormat) && !inFormat.matches(inputFormat))
            setInputFormat(inFormat);
        int endIndex = inOffset + inLength;
        int beginIndex = findStartcode(inData, inOffset, endIndex);
        if (beginIndex < endIndex) {
            for (int nextBeginIndex; beginIndex < endIndex; beginIndex = nextBeginIndex + 3) {
                nextBeginIndex = findStartcode(inData, beginIndex + 3, endIndex);
                int length = nextBeginIndex - beginIndex;
                if (length > 0) {
                    pktAdded = packetize(inData, beginIndex, length) || pktAdded;
                    beginIndex += length;
                }
            }
        }
        timeStamp = inBuffer.getTimeStamp();
        if (pktAdded) {
            return process(inBuffer, outBuffer);
        } else {
            /* first frame is not a synchronization point, discard ?*/
            return BUFFER_PROCESSED_FAILED;
        }
    }

    /**
     * Sets the input format.
     *
     * @param in format to set
     * @return format
     */
    @Override
    public Format setInputFormat(Format in) {
        /*
         * Return null if the specified input Format is incompatible with this
         * Packetizer.
         */
        if (!(in instanceof VideoFormat) || null == AbstractCodec2.matches(in, inputFormats))
            return null;
        inputFormat = in;
        return in;
    }

    /**
     * Sets the <tt>Format</tt> in which this <tt>Codec</tt> is to output media
     * data.
     *
     * @param format the <tt>Format</tt> in which this <tt>Codec</tt> is to
     * output media data
     * @return the <tt>Format</tt> in which this <tt>Codec</tt> is currently
     * configured to output media data or <tt>null</tt> if <tt>format</tt> was
     * found to be incompatible with this <tt>Codec</tt>
     */
    @Override
    public Format setOutputFormat(Format format) {
        /*
         * Return null if the specified output Format is incompatible with this
         * Packetizer.
         */
        if (!(format instanceof VideoFormat) || (null == AbstractCodec2.matches(format, getMatchingOutputFormats(inputFormat))))
            return null;
        VideoFormat videoFormat = (VideoFormat) format;
        /*
         * A Packetizer translates raw media data in RTP payloads. Consequently,
         * the size of the output is equal to the size of the input.
         */
        Dimension size = nul

        if (inputFormat != null)
            size = ((VideoFormat) inputFormat).getSize();
        if ((size == null) && format.matches(outputFormat))
            size = ((VideoFormat) outputFormat).getSize();
        outputFormat = new VideoFormat(videoFormat.getEncoding(), size, /* maxDataLength */
        Format.NOT_SPECIFIED, Format.byteArray, videoFormat.getFrameRate());
        // Return the outputFormat which is actually set.
        return outputFormat;
    }
}


@Override
    public String getName() {
        return PLUGIN_NAME;
    }

/*
 * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
 *
 * See terms of license at gnu.org.
 */
package org.jitsi.impl.neomedia.codec.video.h263p;

import java.awt.*;
import java.util.*;
import java.util.List;
import javax.media.*;
import javax.media.format.*;
import net.sf.fmj.media.*;
import org.jitsi.impl.neomedia.codec.*;
import org.jitsi.service.neomedia.codec.*;

/**
 * Packetizes H.263+ encoded data into RTP packets in accord with RFC 4529
 * "RTP Payload Format for ITU-T Rec. H.263 Video".
 *
 * @author Sebastien Vincent
 * @author Lyubomir Marinov
 */
public class Packetizer extends AbstractPacketizer {

    private static final Format[] DEFAULT_OUTPUT_FORMATS = { new VideoFormat(Constants.H263P_RTP) };

    /**
     * Maximum payload size without the headers.
     */
    public static final int MAX_PAYLOAD_SIZE = 1024;

    /**
     * Name of the plugin.
     */
    private static final String PLUGIN_NAME = "H263+ Packetizer";

    /**
     * Finds the index in <tt>byteStream</tt> at which a Picture Start code
     * begins.
     *
     * @param byteStream the H.263+ encoded byte stream
     * @param beginIndex the inclusive index in <tt>byteStream</tt> at which the
     * search is to begin
     * @param endIndex the exclusive index in <tt>byteStream</tt> at which the
     * search is to end
     * @return the index in <tt>byteStream</tt> at which the Picture Start code
     * begins, otherwise, <tt>endIndex</tt>
     */
    private static int findStartcode(byte[] byteStream, int beginIndex, int endIndex) {
        for (; beginIndex < (endIndex - 3); beginIndex++) {
            if ((byteStream[beginIndex] == 0) && (byteStream[beginIndex + 1] == 0) && ((byteStream[beginIndex + 2] & (byte) 0x80) == -128)) {
                return beginIndex;
            }
        }
        return endIndex;
    }

    /**
     * The sequence number of the next RTP packet to be output by this
     * <tt>Packetizer</tt>.
     */
    private int sequenceNumber = 0;

    /**
     * The timeStamp of the RTP packets in which H263+ packets are to be sent.
     */
    private long timeStamp = 0;

    /**
     * The list of H263+ "Start code" video packets to be sent as payload in RTP
     * packets.
     */
    private final List<byte[]> videoPkts = new LinkedList<byte[]>();

    /**
     * Initializes a new <tt>Packetizer</tt> instance which is to packetize
     * H.263+ encoded data into RTP packets in accord with
     * RFC 4529 "RTP Payload Format for ITU-T Rec. H.263 Video".
     */
    public Packetizer() {
        inputFormats = new Format[] { new VideoFormat(Constants.H263P) };
        inputFormat = null;
        outputFormat = null;
    }

    @Override
    public synchronized void close() {
        if (opened) {
            videoPkts.clear();
            opened = false;
            super.close();
        }
    }

    /**
     * Get the matching output formats for a specific format.
     *
     * @param inputFormat input format
     * @return array for formats matching input format
     */
    private Format[] getMatchingOutputFormats(Format inputFormat) {
        VideoFormat inputVideoFormat = (VideoFormat) inputFormat;
        return new Format[] { new VideoFormat(Constants.H263P_RTP, inputVideoFormat.getSize(), /* maxDataLength */
        Format.NOT_SPECIFIED, Format.byteArray, inputVideoFormat.getFrameRate()) };
    }

    /**
     * Get codec name.
     *
     * @return codec name
     */

    /**
     * Return the list of formats supported at the output.
     * @param inputFormat input <tt>Format</tt> to determine corresponding
     * output <tt>Format/tt>s
     * @return array of formats supported at output
     */
    public Format[] getSupportedOutputFormats(Format inputFormat) {
        if (inputFormat == null)
            return DEFAULT_OUTPUT_FORMATS;
        // mismatch input format
        if (!(inputFormat instanceof VideoFormat) || (AbstractCodec2.matches(inputFormat, inputFormats) == null)) {
            return new Format[0];
        }
        return getMatchingOutputFormats(inputFormat);
    }

    /**
     * Open this <tt>Packetizer</tt>.
     *
     * @throws ResourceUnavailableException if something goes wrong during
     * initialization of the Packetizer.
     */
    @Override
    public synchronized void open() throws ResourceUnavailableException {
        if (!opened) {
            videoPkts.clear();
            sequenceNumber = 0;
            super.open();
            opened = true;
        }
    }

    /**
     * Packetizes H.263+ encoded data so that it becomes ready to be sent as the
     * payload of RTP packets.
     *
     * @param data the bytes which contain the H.263+ encoded data to be
     * packetized
     * @param offset the offset of H.263+ encoded data to be packetized begins
     * @param length the length  of the H.263+ encoded data starting at offset
     * @return <tt>true</tt> if at least one RTP packet payload has been
     * packetized i.e. prepared for sending; otherwise, <tt>false</tt>
     */
    private boolean packetize(byte[] data, int offset, int length) {
        boolean pktAdded = false;
        while (length > 0) {
            boolean isPsc = false;
            int pos = 0;
            int maxPayloadLength = MAX_PAYLOAD_SIZE;
            byte[] pkt = null;
            int payloadLength = 0;
            /* is we are at synchronization point (PSC, GSBC, EOS, EOSBS) */
            if (data.length > 3 && data[offset] == 0x00 && data[offset + 1] == 0x00) {
                isPsc = true;
                pos = 2;
            } else {
                maxPayloadLength -= 2;
            }
            if (length > maxPayloadLength) {
                payloadLength = maxPayloadLength;
            } else {
                payloadLength = length;
            }
            pkt = new byte[payloadLength + (isPsc ? 0 : 2)];
            /* add H263+ payload header */
            /* no VRC and no extra picture header */
            pkt[0] = (byte) (isPsc ? 0x04 : 0x00);
            pkt[1] = 0x00;
            System.arraycopy(data, offset + pos, pkt, 2, payloadLength - pos);
            pktAdded = videoPkts.add(pkt) || pktAdded;
            offset += payloadLength;
            length -= payloadLength;
        }
        return pktAdded;
    }

    /**
     * Processes/packetizes a buffer.
     *
     * @param inBuffer input buffer
     * @param outBuffer output buffer
     * @return <tt>BUFFER_PROCESSED_OK</tt> if buffer has been successfully
     * processed
     */
    @Override
    public int process(Buffer inBuffer, Buffer outBuffer) {
        int inLength = inBuffer.getLength();
        byte[] inData = (byte[]) inBuffer.getData();
        int inOffset = inBuffer.getOffset();
        boolean pktAdded = false;
        if (videoPkts.size() > 0) {
            byte[] pktData = videoPkts.remove(0);
            // Send the packet.
            outBuffer.setData(pktData);
            outBuffer.setLength(pktData.length);
            outBuffer.setOffset(0);
            outBuffer.setTimeStamp(timeStamp);
            outBuffer.setSequenceNumber(sequenceNumber++);
            // If there are other packets, send them as well.
            if (videoPkts.size() > 0) {
                return (BUFFER_PROCESSED_OK | INPUT_BUFFER_NOT_CONSUMED);
            } else {
                // It's the last packet of the current frame so mark it.
                outBuffer.setFlags(outBuffer.getFlags() | Buffer.FLAG_RTP_MARKER);
                return BUFFER_PROCESSED_OK;
            }
        }
        if (isEOM(inBuffer)) {
            propagateEOM(outBuffer);
            reset();
            return BUFFER_PROCESSED_OK;
        }
        if (inBuffer.isDiscard()) {
            outBuffer.setDiscard(true);
            reset();
            return BUFFER_PROCESSED_OK;
        }
        Format inFormat = inBuffer.getFormat();
        if ((inFormat != inputFormat) && !inFormat.matches(inputFormat))
            setInputFormat(inFormat);
        int endIndex = inOffset + inLength;
        int beginIndex = findStartcode(inData, inOffset, endIndex);
        if (beginIndex < endIndex) {
            for (int nextBeginIndex; beginIndex < endIndex; beginIndex = nextBeginIndex + 3) {
                nextBeginIndex = findStartcode(inData, beginIndex + 3, endIndex);
                int length = nextBeginIndex - beginIndex;
                if (length > 0) {
                    pktAdded = packetize(inData, beginIndex, length) || pktAdded;
                    beginIndex += length;
                }
            }
        }
        timeStamp = inBuffer.getTimeStamp();
        if (pktAdded) {
            return process(inBuffer, outBuffer);
        } else {
            /* first frame is not a synchronization point, discard ?*/
            return BUFFER_PROCESSED_FAILED;
        }
    }

    /**
     * Sets the input format.
     *
     * @param in format to set
     * @return format
     */
    @Override
    public Format setInputFormat(Format in) {
        /*
         * Return null if the specified input Format is incompatible with this
         * Packetizer.
         */
        if (!(in instanceof VideoFormat) || null == AbstractCodec2.matches(in, inputFormats))
            return null;
        inputFormat = in;
        return in;
    }

    /**
     * Sets the <tt>Format</tt> in which this <tt>Codec</tt> is to output media
     * data.
     *
     * @param format the <tt>Format</tt> in which this <tt>Codec</tt> is to
     * output media data
     * @return the <tt>Format</tt> in which this <tt>Codec</tt> is currently
     * configured to output media data or <tt>null</tt> if <tt>format</tt> was
     * found to be incompatible with this <tt>Codec</tt>
     */
    @Override
    public Format setOutputFormat(Format format) {
        /*
         * Return null if the specified output Format is incompatible with this
         * Packetizer.
         */
        if (!(format instanceof VideoFormat) || (null == AbstractCodec2.matches(format, getMatchingOutputFormats(inputFormat))))
            return null;
        VideoFormat videoFormat = (VideoFormat) format;
        /*
         * A Packetizer translates raw media data in RTP payloads. Consequently,
         * the size of the output is equal to the size of the input.
         */
        Dimension size = null;
        if (inputFormat != null)
            size = ((VideoFormat) inputFormat).getSize();
        if ((size == null) && format.matches(outputFormat))
            size = ((VideoFormat) outputFormat).getSize();
        outputFormat = new VideoFormat(videoFormat.getEncoding(), size, /* maxDataLength */
        Format.NOT_SPECIFIED, Format.byteArray, videoFormat.getFrameRate());
        // Return the outputFormat which is actually set.
        return outputFormat;
    }
}


if (inBuffer.isDiscard()) {
            outBuffer.setDiscard(true);
            reset();
            return BUFFER_PROCESSED_OK;
        }

/*
 * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
 *
 * See terms of license at gnu.org.
 */
package org.jitsi.impl.neomedia.codec.video.h263p;

import java.awt.*;
import java.util.*;
import java.util.List;
import javax.media.*;
import javax.media.format.*;
import net.sf.fmj.media.*;
import org.jitsi.impl.neomedia.codec.*;
import org.jitsi.service.neomedia.codec.*;

/**
 * Packetizes H.263+ encoded data into RTP packets in accord with RFC 4529
 * "RTP Payload Format for ITU-T Rec. H.263 Video".
 *
 * @author Sebastien Vincent
 * @author Lyubomir Marinov
 */
public class Packetizer extends AbstractPacketizer {

    private static final Format[] DEFAULT_OUTPUT_FORMATS = { new VideoFormat(Constants.H263P_RTP) };

    /**
     * Maximum payload size without the headers.
     */
    public static final int MAX_PAYLOAD_SIZE = 1024;

    /**
     * Name of the plugin.
     */
    private static final String PLUGIN_NAME = "H263+ Packetizer";

    /**
     * Finds the index in <tt>byteStream</tt> at which a Picture Start code
     * begins.
     *
     * @param byteStream the H.263+ encoded byte stream
     * @param beginIndex the inclusive index in <tt>byteStream</tt> at which the
     * search is to begin
     * @param endIndex the exclusive index in <tt>byteStream</tt> at which the
     * search is to end
     * @return the index in <tt>byteStream</tt> at which the Picture Start code
     * begins, otherwise, <tt>endIndex</tt>
     */
    private static int findStartcode(byte[] byteStream, int beginIndex, int endIndex) {
        for (; beginIndex < (endIndex - 3); beginIndex++) {
            if ((byteStream[beginIndex] == 0) && (byteStream[beginIndex + 1] == 0) && ((byteStream[beginIndex + 2] & (byte) 0x80) == -128)) {
                return beginIndex;
            }
        }
        return endIndex;
    }

    /**
     * The sequence number of the next RTP packet to be output by this
     * <tt>Packetizer</tt>.
     */
    private int sequenceNumber = 0;

    /**
     * The timeStamp of the RTP packets in which H263+ packets are to be sent.
     */
    private long timeStamp = 0;

    /**
     * The list of H263+ "Start code" video packets to be sent as payload in RTP
     * packets.
     */
    private final List<byte[]> videoPkts = new LinkedList<byte[]>();

    /**
     * Initializes a new <tt>Packetizer</tt> instance which is to packetize
     * H.263+ encoded data into RTP packets in accord with
     * RFC 4529 "RTP Payload Format for ITU-T Rec. H.263 Video".
     */
    public Packetizer() {
        inputFormats = new Format[] { new VideoFormat(Constants.H263P) };
        inputFormat = null;
        outputFormat = null;
    }

    @Override
    public synchronized void close() {
        if (opened) {
            videoPkts.clear();
            opened = false;
            super.close();
        }
    }

    /**
     * Get the matching output formats for a specific format.
     *
     * @param inputFormat input format
     * @return array for formats matching input format
     */
    private Format[] getMatchingOutputFormats(Format inputFormat) {
        VideoFormat inputVideoFormat = (VideoFormat) inputFormat;
        return new Format[] { new VideoFormat(Constants.H263P_RTP, inputVideoFormat.getSize(), /* maxDataLength */
        Format.NOT_SPECIFIED, Format.byteArray, inputVideoFormat.getFrameRate()) };
    }

    /**
     * Get codec name.
     *
     * @return codec name
     */
    @Override
    public String getName() {
        return PLUGIN_NAME;
    }

    /**
     * Return the list of formats supported at the output.
     * @param inputFormat input <tt>Format</tt> to determine corresponding
     * output <tt>Format/tt>s
     * @return array of formats supported at output
     */
    public Format[] getSupportedOutputFormats(Format inputFormat) {
        if (inputFormat == null)
            return DEFAULT_OUTPUT_FORMATS;
        // mismatch input format
        if (!(inputFormat instanceof VideoFormat) || (AbstractCodec2.matches(inputFormat, inputFormats) == null)) {
            return new Format[0];
        }
        return getMatchingOutputFormats(inputFormat);
    }

    /**
     * Open this <tt>Packetizer</tt>.
     *
     * @throws ResourceUnavailableException if something goes wrong during
     * initialization of the Packetizer.
     */
    @Override
    public synchronized void open() throws ResourceUnavailableException {
        if (!opened) {
            videoPkts.clear();
            sequenceNumber = 0;
            super.open();
            opened = true;
        }
    }

    /**
     * Packetizes H.263+ encoded data so that it becomes ready to be sent as the
     * payload of RTP packets.
     *
     * @param data the bytes which contain the H.263+ encoded data to be
     * packetized
     * @param offset the offset of H.263+ encoded data to be packetized begins
     * @param length the length  of the H.263+ encoded data starting at offset
     * @return <tt>true</tt> if at least one RTP packet payload has been
     * packetized i.e. prepared for sending; otherwise, <tt>false</tt>
     */
    private boolean packetize(byte[] data, int offset, int length) {
        boolean pktAdded = false;
        while (length > 0) {
            boolean isPsc = false;
            int pos = 0;
            int maxPayloadLength = MAX_PAYLOAD_SIZE;
            byte[] pkt = null;
            int payloadLength = 0;
            /* is we are at synchronization point (PSC, GSBC, EOS, EOSBS) */
            if (data.length > 3 && data[offset] == 0x00 && data[offset + 1] == 0x00) {
                isPsc = true;
                pos = 2;
            } else {
                maxPayloadLength -= 2;
            }
            if (length > maxPayloadLength) {
                payloadLength = maxPayloadLength;
            } else {
                payloadLength = length;
            }
            pkt = new byte[payloadLength + (isPsc ? 0 : 2)];
            /* add H263+ payload header */
            /* no VRC and no extra picture header */
            pkt[0] = (byte) (isPsc ? 0x04 : 0x00);
            pkt[1] = 0x00;
            System.arraycopy(data, offset + pos, pkt, 2, payloadLength - pos);
            pktAdded = videoPkts.add(pkt) || pktAdded;
            offset += payloadLength;
            length -= payloadLength;
        }
        return pktAdded;
    }

    /**
     * Processes/packetizes a buffer.
     *
     * @param inBuffer input buffer
     * @param outBuffer output buffer
     * @return <tt>BUFFER_PROCESSED_OK</tt> if buffer has been successfully
     * processed
     */
    @Override
    public int process(Buffer inBuffer, Buffer outBuffer) {
        int inLength = inBuffer.getLength();
        byte[] inData = (byte[]) inBuffer.getData();
        int inOffset = inBuffer.getOffset();
        boolean pktAdded = false;
        if (videoPkts.size() > 0) {
            byte[] pktData = videoPkts.remove(0);
            // Send the packet.
            outBuffer.setData(pktData);
            outBuffer.setLength(pktData.length);
            outBuffer.setOffset(0);
            outBuffer.setTimeStamp(timeStamp);
            outBuffer.setSequenceNumber(sequenceNumber++);
            // If there are other packets, send them as well.
            if (videoPkts.size() > 0) {
                return (BUFFER_PROCESSED_OK | INPUT_BUFFER_NOT_CONSUMED);
            } else {
                // It's the last packet of the current frame so mark it.
                outBuffer.setFlags(outBuffer.getFlags() | Buffer.FLAG_RTP_MARKER);
                return BUFFER_PROCESSED_OK;
            }
        }
        if (isEOM(inBuffer)) {
            propagateEOM(outBuffer);
            reset();
            return BUFFER_PROCESSED_OK;
        }

        Format inFormat = inBuffer.getFormat();
        if ((inFormat != inputFormat) && !inFormat.matches(inputFormat))
            setInputFormat(inFormat);
        int endIndex = inOffset + inLength;
        int beginIndex = findStartcode(inData, inOffset, endIndex);
        if (beginIndex < endIndex) {
            for (int nextBeginIndex; beginIndex < endIndex; beginIndex = nextBeginIndex + 3) {
                nextBeginIndex = findStartcode(inData, beginIndex + 3, endIndex);
                int length = nextBeginIndex - beginIndex;
                if (length > 0) {
                    pktAdded = packetize(inData, beginIndex, length) || pktAdded;
                    beginIndex += length;
                }
            }
        }
        timeStamp = inBuffer.getTimeStamp();
        if (pktAdded) {
            return process(inBuffer, outBuffer);
        } else {
            /* first frame is not a synchronization point, discard ?*/
            return BUFFER_PROCESSED_FAILED;
        }
    }

    /**
     * Sets the input format.
     *
     * @param in format to set
     * @return format
     */
    @Override
    public Format setInputFormat(Format in) {
        /*
         * Return null if the specified input Format is incompatible with this
         * Packetizer.
         */
        if (!(in instanceof VideoFormat) || null == AbstractCodec2.matches(in, inputFormats))
            return null;
        inputFormat = in;
        return in;
    }

    /**
     * Sets the <tt>Format</tt> in which this <tt>Codec</tt> is to output media
     * data.
     *
     * @param format the <tt>Format</tt> in which this <tt>Codec</tt> is to
     * output media data
     * @return the <tt>Format</tt> in which this <tt>Codec</tt> is currently
     * configured to output media data or <tt>null</tt> if <tt>format</tt> was
     * found to be incompatible with this <tt>Codec</tt>
     */
    @Override
    public Format setOutputFormat(Format format) {
        /*
         * Return null if the specified output Format is incompatible with this
         * Packetizer.
         */
        if (!(format instanceof VideoFormat) || (null == AbstractCodec2.matches(format, getMatchingOutputFormats(inputFormat))))
            return null;
        VideoFormat videoFormat = (VideoFormat) format;
        /*
         * A Packetizer translates raw media data in RTP payloads. Consequently,
         * the size of the output is equal to the size of the input.
         */
        Dimension size = null;
        if (inputFormat != null)
            size = ((VideoFormat) inputFormat).getSize();
        if ((size == null) && format.matches(outputFormat))
            size = ((VideoFormat) outputFormat).getSize();
        outputFormat = new VideoFormat(videoFormat.getEncoding(), size, /* maxDataLength */
        Format.NOT_SPECIFIED, Format.byteArray, videoFormat.getFrameRate());
        // Return the outputFormat which is actually set.
        return outputFormat;
    }
}


for (int nextBeginIndex; beginIndex < endIndex; beginIndex = nextBeginIndex + 3) {
                nextBeginIndex = findStartcode(inData, beginIndex + 3, endIndex);
                int length = nextBeginIndex - beginIndex;
                if (length > 0) {
                    pktAdded = packetize(inData, beginIndex, length) || pktAdded;
                    beginIndex += length;
                }
            }

/*
 * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
 *
 * See terms of license at gnu.org.
 */
package org.jitsi.impl.neomedia.codec.video.h263p;

import java.awt.*;
import java.util.*;
import java.util.List;
import javax.media.*;
import javax.media.format.*;
import net.sf.fmj.media.*;
import org.jitsi.impl.neomedia.codec.*;
import org.jitsi.service.neomedia.codec.*;

/**
 * Packetizes H.263+ encoded data into RTP packets in accord with RFC 4529
 * "RTP Payload Format for ITU-T Rec. H.263 Video".
 *
 * @author Sebastien Vincent
 * @author Lyubomir Marinov
 */
public class Packetizer extends AbstractPacketizer {

    private static final Format[] DEFAULT_OUTPUT_FORMATS = { new VideoFormat(Constants.H263P_RTP) };

    /**
     * Maximum payload size without the headers.
     */
    public static final int MAX_PAYLOAD_SIZE = 1024;

    /**
     * Name of the plugin.
     */
    private static final String PLUGIN_NAME = "H263+ Packetizer";

    /**
     * Finds the index in <tt>byteStream</tt> at which a Picture Start code
     * begins.
     *
     * @param byteStream the H.263+ encoded byte stream
     * @param beginIndex the inclusive index in <tt>byteStream</tt> at which the
     * search is to begin
     * @param endIndex the exclusive index in <tt>byteStream</tt> at which the
     * search is to end
     * @return the index in <tt>byteStream</tt> at which the Picture Start code
     * begins, otherwise, <tt>endIndex</tt>
     */
    private static int findStartcode(byte[] byteStream, int beginIndex, int endIndex) {
        for (; beginIndex < (endIndex - 3); beginIndex++) {
            if ((byteStream[beginIndex] == 0) && (byteStream[beginIndex + 1] == 0) && ((byteStream[beginIndex + 2] & (byte) 0x80) == -128)) {
                return beginIndex;
            }
        }
        return endIndex;
    }

    /**
     * The sequence number of the next RTP packet to be output by this
     * <tt>Packetizer</tt>.
     */
    private int sequenceNumber = 0;

    /**
     * The timeStamp of the RTP packets in which H263+ packets are to be sent.
     */
    private long timeStamp = 0;

    /**
     * The list of H263+ "Start code" video packets to be sent as payload in RTP
     * packets.
     */
    private final List<byte[]> videoPkts = new LinkedList<byte[]>();

    /**
     * Initializes a new <tt>Packetizer</tt> instance which is to packetize
     * H.263+ encoded data into RTP packets in accord with
     * RFC 4529 "RTP Payload Format for ITU-T Rec. H.263 Video".
     */
    public Packetizer() {
        inputFormats = new Format[] { new VideoFormat(Constants.H263P) };
        inputFormat = null;
        outputFormat = null;
    }

    @Override
    public synchronized void close() {
        if (opened) {
            videoPkts.clear();
            opened = false;
            super.close();
        }
    }

    /**
     * Get the matching output formats for a specific format.
     *
     * @param inputFormat input format
     * @return array for formats matching input format
     */
    private Format[] getMatchingOutputFormats(Format inputFormat) {
        VideoFormat inputVideoFormat = (VideoFormat) inputFormat;
        return new Format[] { new VideoFormat(Constants.H263P_RTP, inputVideoFormat.getSize(), /* maxDataLength */
        Format.NOT_SPECIFIED, Format.byteArray, inputVideoFormat.getFrameRate()) };
    }

    /**
     * Get codec name.
     *
     * @return codec name
     */
    @Override
    public String getName() {
        return PLUGIN_NAME;
    }

    /**
     * Return the list of formats supported at the output.
     * @param inputFormat input <tt>Format</tt> to determine corresponding
     * output <tt>Format/tt>s
     * @return array of formats supported at output
     */
    public Format[] getSupportedOutputFormats(Format inputFormat) {
        if (inputFormat == null)
            return DEFAULT_OUTPUT_FORMATS;
        // mismatch input format
        if (!(inputFormat instanceof VideoFormat) || (AbstractCodec2.matches(inputFormat, inputFormats) == null)) {
            return new Format[0];
        }
        return getMatchingOutputFormats(inputFormat);
    }

    /**
     * Open this <tt>Packetizer</tt>.
     *
     * @throws ResourceUnavailableException if something goes wrong during
     * initialization of the Packetizer.
     */
    @Override
    public synchronized void open() throws ResourceUnavailableException {
        if (!opened) {
            videoPkts.clear();
            sequenceNumber = 0;
            super.open();
            opened = true;
        }
    }

    /**
     * Packetizes H.263+ encoded data so that it becomes ready to be sent as the
     * payload of RTP packets.
     *
     * @param data the bytes which contain the H.263+ encoded data to be
     * packetized
     * @param offset the offset of H.263+ encoded data to be packetized begins
     * @param length the length  of the H.263+ encoded data starting at offset
     * @return <tt>true</tt> if at least one RTP packet payload has been
     * packetized i.e. prepared for sending; otherwise, <tt>false</tt>
     */
    private boolean packetize(byte[] data, int offset, int length) {
        boolean pktAdded = false;
        while (length > 0) {
            boolean isPsc = false;
            int pos = 0;
            int maxPayloadLength = MAX_PAYLOAD_SIZE;
            byte[] pkt = null;
            int payloadLength = 0;
            /* is we are at synchronization point (PSC, GSBC, EOS, EOSBS) */
            if (data.length > 3 && data[offset] == 0x00 && data[offset + 1] == 0x00) {
                isPsc = true;
                pos = 2;
            } else {
                maxPayloadLength -= 2;
            }
            if (length > maxPayloadLength) {
                payloadLength = maxPayloadLength;
            } else {
                payloadLength = length;
            }
            pkt = new byte[payloadLength + (isPsc ? 0 : 2)];
            /* add H263+ payload header */
            /* no VRC and no extra picture header */
            pkt[0] = (byte) (isPsc ? 0x04 : 0x00);
            pkt[1] = 0x00;
            System.arraycopy(data, offset + pos, pkt, 2, payloadLength - pos);
            pktAdded = videoPkts.add(pkt) || pktAdded;
            offset += payloadLength;
            length -= payloadLength;
        }
        return pktAdded;
    }

    /**
     * Processes/packetizes a buffer.
     *
     * @param inBuffer input buffer
     * @param outBuffer output buffer
     * @return <tt>BUFFER_PROCESSED_OK</tt> if buffer has been successfully
     * processed
     */
    @Override
    public int process(Buffer inBuffer, Buffer outBuffer) {
        int inLength = inBuffer.getLength();
        byte[] inData = (byte[]) inBuffer.getData();
        int inOffset = inBuffer.getOffset();
        boolean pktAdded = false;
        if (videoPkts.size() > 0) {
            byte[] pktData = videoPkts.remove(0);
            // Send the packet.
            outBuffer.setData(pktData);
            outBuffer.setLength(pktData.length);
            outBuffer.setOffset(0);
            outBuffer.setTimeStamp(timeStamp);
            outBuffer.setSequenceNumber(sequenceNumber++);
            // If there are other packets, send them as well.
            if (videoPkts.size() > 0) {
                return (BUFFER_PROCESSED_OK | INPUT_BUFFER_NOT_CONSUMED);
            } else {
                // It's the last packet of the current frame so mark it.
                outBuffer.setFlags(outBuffer.getFlags() | Buffer.FLAG_RTP_MARKER);
                return BUFFER_PROCESSED_OK;
            }
        }
        if (isEOM(inBuffer)) {
            propagateEOM(outBuffer);
            reset();
            return BUFFER_PROCESSED_OK;
        }
        if (inBuffer.isDiscard()) {
            outBuffer.setDiscard(true);
            reset();
            return BUFFER_PROCESSED_OK;
        }
        Format inFormat = inBuffer.getFormat();
        if ((inFormat != inputFormat) && !inFormat.matches(inputFormat))
            setInputFormat(inFormat);
        int endIndex = inOffset + inLength;
        int beginIndex = findStartcode(inData, inOffset, endIndex);
        if (beginIndex < endIndex) {

        }
        timeStamp = inBuffer.getTimeStamp();
        if (pktAdded) {
            return process(inBuffer, outBuffer);
        } else {
            /* first frame is not a synchronization point, discard ?*/
            return BUFFER_PROCESSED_FAILED;
        }
    }

    /**
     * Sets the input format.
     *
     * @param in format to set
     * @return format
     */
    @Override
    public Format setInputFormat(Format in) {
        /*
         * Return null if the specified input Format is incompatible with this
         * Packetizer.
         */
        if (!(in instanceof VideoFormat) || null == AbstractCodec2.matches(in, inputFormats))
            return null;
        inputFormat = in;
        return in;
    }

    /**
     * Sets the <tt>Format</tt> in which this <tt>Codec</tt> is to output media
     * data.
     *
     * @param format the <tt>Format</tt> in which this <tt>Codec</tt> is to
     * output media data
     * @return the <tt>Format</tt> in which this <tt>Codec</tt> is currently
     * configured to output media data or <tt>null</tt> if <tt>format</tt> was
     * found to be incompatible with this <tt>Codec</tt>
     */
    @Override
    public Format setOutputFormat(Format format) {
        /*
         * Return null if the specified output Format is incompatible with this
         * Packetizer.
         */
        if (!(format instanceof VideoFormat) || (null == AbstractCodec2.matches(format, getMatchingOutputFormats(inputFormat))))
            return null;
        VideoFormat videoFormat = (VideoFormat) format;
        /*
         * A Packetizer translates raw media data in RTP payloads. Consequently,
         * the size of the output is equal to the size of the input.
         */
        Dimension size = null;
        if (inputFormat != null)
            size = ((VideoFormat) inputFormat).getSize();
        if ((size == null) && format.matches(outputFormat))
            size = ((VideoFormat) outputFormat).getSize();
        outputFormat = new VideoFormat(videoFormat.getEncoding(), size, /* maxDataLength */
        Format.NOT_SPECIFIED, Format.byteArray, videoFormat.getFrameRate());
        // Return the outputFormat which is actually set.
        return outputFormat;
    }
}


public ProtectionConfig fingerprint(String fingerprint)

package org.onosproject.net.behaviour.protection;

import static com.google.common.base.Preconditions.checkNotNull;
import static org.onosproject.net.behaviour.protection.ProtectedTransportEndpointDescription.buildDescription;
import java.util.List;
import org.onosproject.net.DeviceId;
import org.onosproject.net.config.BaseConfig;
import com.fasterxml.jackson.databind.ObjectMapper;

// FIXME Move this to Protection handling Intent related package?
/**
 * Config object for protection end-point.
 *

 * Contains equivalent of {@link ProtectedTransportEndpointDescription}.
 */
public class ProtectionConfig extends BaseConfig<DeviceId> {

    /**
     * {@value #CONFIG_KEY} : a netcfg ConfigKey for {@link ProtectionConfig}.
     */
    public static final String CONFIG_KEY = "protection";

    /**
     * JSON key for paths.
     *

     * Value is list of {@link TransportEndpointDescription} in JSON.
     */
    private static final String PATHS = "paths";

    /**
     * JSON key for Peer {@link DeviceId}.
     */
    private static final String PEER = "peer";

    private static final String FINGERPRINT = "fingerprint";

    @Override
    public boolean isValid() {
        return isString(PEER, FieldPresence.MANDATORY) && isString(FINGERPRINT, FieldPresence.MANDATORY) && hasField(PATHS);
    }

    /**
     * Returns List of underlying transport entity endpoints in priority order.
     *
     * @return the transport entity endpoint descriptions
     */
    public List<TransportEndpointDescription> paths() {
        return getList(PATHS, jsonStr -> decode(jsonStr, TransportEndpointDescription.class));
    }

    /**
     * Sets the List of underlying transport entity endpoints in priority order.
     *
     * @param paths the transport entity endpoint descriptions
     * @return self
     */
    public ProtectionConfig paths(List<TransportEndpointDescription> paths) {
        setList(PATHS, elm -> encode(elm, TransportEndpointDescription.class).toString(), paths);
        return this;
    }

    /**
     * Returns DeviceId of remote peer of this endpoint.
     *
     * @return the peer
     */
    public DeviceId peer() {
        return DeviceId.deviceId(get(PEER, ""));
    }

    /**
     * Sets the DeviceId of remote peer of this endpoint.
     *
     * @param peer DeviceId
     * @return self
     */
    public ProtectionConfig peer(DeviceId peer) {
        setOrClear(PEER, peer.toString());
        return this;
    }

    /**
     * Returns fingerprint to identify this protected transport entity.
     *
     * @return the fingerprint
     */
    public String fingerprint() {
        return get(FINGERPRINT, "");
    }

    /**
     * Sets the fingerprint to identify this protected transport entity.
     *
     * @param fingerprint the fingerprint
     * @return self
     */

{
        setOrClear(FINGERPRINT, checkNotNull(fingerprint));
        return this;
    }

    /**
     * Returns equivalent of this Config as {@link ProtectedTransportEndpointDescription}.
     *
     * @return {@link ProtectedTransportEndpointDescription}
     */
    public ProtectedTransportEndpointDescription asDescription() {
        return buildDescription(paths(), peer(), fingerprint());
    }

    @Override
    public String toString() {
        return object.toString();
    }

    /**
     * Create a {@link ProtectionConfig}.
     *

     * Note: created instance needs to be initialized by #init(..) before using.
     */
    public ProtectionConfig() {
        super();
    }

    /**
     * Create a {@link ProtectionConfig} for specified Device.
     *

     * Note: created instance is not bound to NetworkConfigService,
     * cannot use {@link #apply()}. Must be passed to the service
     * using NetworkConfigService#applyConfig
     *
     * @param did DeviceId
     */
    public ProtectionConfig(DeviceId did) {
        ObjectMapper mapper = new ObjectMapper();
        init(did, CONFIG_KEY, mapper.createObjectNode(), mapper, null);
    }
}


{
        return get(FINGERPRINT, "");
    }

package org.onosproject.net.behaviour.protection;

import static com.google.common.base.Preconditions.checkNotNull;
import static org.onosproject.net.behaviour.protection.ProtectedTransportEndpointDescription.buildDescription;
import java.util.List;
import org.onosproject.net.DeviceId;
import org.onosproject.net.config.BaseConfig;
import com.fasterxml.jackson.databind.ObjectMapper;

// FIXME Move this to Protection handling Intent related package?
/**
 * Config object for protection end-point.
 *

 * Contains equivalent of {@link ProtectedTransportEndpointDescription}.
 */
public class ProtectionConfig extends BaseConfig<DeviceId> {

    /**
     * {@value #CONFIG_KEY} : a netcfg ConfigKey for {@link ProtectionConfig}.
     */
    public static final String CONFIG_KEY = "protection";

    /**
     * JSON key for paths.
     *

     * Value is list of {@link TransportEndpointDescription} in JSON.
     */
    private static final String PATHS = "paths";

    /**
     * JSON key for Peer {@link DeviceId}.
     */
    private static final String PEER = "peer";

    private static final String FINGERPRINT = "fingerprint";

    @Override
    public boolean isValid() {
        return isString(PEER, FieldPresence.MANDATORY) && isString(FINGERPRINT, FieldPresence.MANDATORY) && hasField(PATHS);
    }

    /**
     * Returns List of underlying transport entity endpoints in priority order.
     *
     * @return the transport entity endpoint descriptions
     */
    public List<TransportEndpointDescription> paths() {
        return getList(PATHS, jsonStr -> decode(jsonStr, TransportEndpointDescription.class));
    }

    /**
     * Sets the List of underlying transport entity endpoints in priority order.
     *
     * @param paths the transport entity endpoint descriptions
     * @return self
     */
    public ProtectionConfig paths(List<TransportEndpointDescription> paths) {
        setList(PATHS, elm -> encode(elm, TransportEndpointDescription.class).toString(), paths);
        return this;
    }

    /**
     * Returns DeviceId of remote peer of this endpoint.
     *
     * @return the peer
     */
    public DeviceId peer() {
        return DeviceId.deviceId(get(PEER, ""));
    }

    /**
     * Sets the DeviceId of remote peer of this endpoint.
     *
     * @param peer DeviceId
     * @return self
     */
    public ProtectionConfig peer(DeviceId peer) {
        setOrClear(PEER, peer.toString());
        return this;
    }

    /**
     * Returns fingerprint to identify this protected transport entity.
     *
     * @return the fingerprint
     */
    public String fingerprint()

/**
     * Sets the fingerprint to identify this protected transport entity.
     *
     * @param fingerprint the fingerprint
     * @return self
     */
    public ProtectionConfig fingerprint(String fingerprint) {
        setOrClear(FINGERPRINT, checkNotNull(fingerprint));
        return this;
    }

    /**
     * Returns equivalent of this Config as {@link ProtectedTransportEndpointDescription}.
     *
     * @return {@link ProtectedTransportEndpointDescription}
     */
    public ProtectedTransportEndpointDescription asDescription() {
        return buildDescription(paths(), peer(), fingerprint());
    }

    @Override
    public String toString() {
        return object.toString();
    }

    /**
     * Create a {@link ProtectionConfig}.
     *

     * Note: created instance needs to be initialized by #init(..) before using.
     */
    public ProtectionConfig() {
        super();
    }

    /**
     * Create a {@link ProtectionConfig} for specified Device.
     *

     * Note: created instance is not bound to NetworkConfigService,
     * cannot use {@link #apply()}. Must be passed to the service
     * using NetworkConfigService#applyConfig
     *
     * @param did DeviceId
     */
    public ProtectionConfig(DeviceId did) {
        ObjectMapper mapper = new ObjectMapper();
        init(did, CONFIG_KEY, mapper.createObjectNode(), mapper, null);
    }
}


package org.onosproject.net.behaviour.protection;

import static com.google.common.base.Preconditions.checkNotNull;
import static org.onosproject.net.behaviour.protection.ProtectedTransportEndpointDescription.buildDescription;
import java.util.List;
import org.onosproject.net.DeviceId;
import org.onosproject.net.config.BaseConfig;
import com.fasterxml.jackson.databind.ObjectMapper;

// FIXME Move this to Protection handling Intent related package?
/**
 * Config object for protection end-point.
 *

 * Contains equivalent of {@link ProtectedTransportEndpointDescription}.
 */
public class ProtectionConfig extends BaseConfig<DeviceId> {

    /**
     * {@value #CONFIG_KEY} : a netcfg ConfigKey for {@link ProtectionConfig}.
     */
    public static final String CONFIG_KEY = "protection";

    /**
     * JSON key for paths.
     *

     * Value is list of {@link TransportEndpointDescription} in JSON.
     */
    private static final String PATHS = "paths";

    /**
     * JSON key for Peer {@link DeviceId}.
     */
    private static final String PEER = "peer";

    private static final String FINGERPRINT = "fingerprint";

    @Override
    public boolean isValid() {
        return isString(PEER, FieldPresence.MANDATORY) && isString(FINGERPRINT, FieldPresence.MANDATORY) && hasField(PATHS);
    }

    /**
     * Returns List of underlying transport entity endpoints in priority order.
     *
     * @return the transport entity endpoint descriptions
     */
    public List<TransportEndpointDescription> paths() {
        return getList(PATHS, jsonStr -> decode(jsonStr, TransportEndpointDescription.class));
    }

    /**
     * Sets the List of underlying transport entity endpoints in priority order.
     *
     * @param paths the transport entity endpoint descriptions
     * @return self
     */
    public ProtectionConfig paths(List<TransportEndpointDescription> paths) {
        setList(PATHS, elm -> encode(elm, TransportEndpointDesc

return this;
    }

    /**
     * Returns DeviceId of remote peer of this endpoint.
     *
     * @return the peer
     */
    public DeviceId peer() {
        return DeviceId.deviceId(get(PEER, ""));
    }

    /**
     * Sets the DeviceId of remote peer of this endpoint.
     *
     * @param peer DeviceId
     * @return self
     */
    public ProtectionConfig peer(DeviceId peer) {
        setOrClear(PEER, peer.toString());
        return this;
    }

    /**
     * Returns fingerprint to identify this protected transport entity.
     *
     * @return the fingerprint
     */
    public String fingerprint() {
        return get(FINGERPRINT, "");
    }

    /**
     * Sets the fingerprint to identify this protected transport entity.
     *
     * @param fingerprint the fingerprint
     * @return self
     */
    public ProtectionConfig fingerprint(String fingerprint) {
        setOrClear(FINGERPRINT, checkNotNull(fingerprint));
        return this;
    }

    /**
     * Returns equivalent of this Config as {@link ProtectedTransportEndpointDescription}.
     *
     * @return {@link ProtectedTransportEndpointDescription}
     */
    public ProtectedTransportEndpointDescription asDescription() {
        return buildDescription(paths(), peer(), fingerprint());
    }

    @Override
    public String toString() {
        return object.toString();
    }

    /**
     * Create a {@link ProtectionConfig}.
     *

     * Note: created instance needs to be initialized by #init(..) before using.
     */
    public ProtectionConfig() {
        super();
    }

    /**
     * Create a {@link ProtectionConfig} for specified Device.
     *

     * Note: created instance is not bound to NetworkConfigService,
     * cannot use {@link #apply()}. Must be passed to the service
     * using NetworkConfigService#applyConfig
     *
     * @param did DeviceId
     */
    public ProtectionConfig(DeviceId did) {
        ObjectMapper mapper = new ObjectMapper();
        init(did, CONFIG_KEY, mapper.createObjectNode(), mapper, null);
    }
}


public String fingerprint() {
        return get(FINGERPRINT, "");
    }

package org.onosproject.net.behaviour.protection;

import static com.google.common.base.Preconditions.checkNotNull;
import static org.onosproject.net.behaviour.protection.ProtectedTransportEndpointDescription.buildDescription;
import java.util.List;
import org.onosproject.net.DeviceId;
import org.onosproject.net.config.BaseConfig;
import com.fasterxml.jackson.databind.ObjectMapper;

// FIXME Move this to Protection handling Intent related package?
/**
 * Config object for protection end-point.
 *

 * Contains equivalent of {@link ProtectedTransportEndpointDescription}.
 */
public class ProtectionConfig extends BaseConfig<DeviceId> {

    /**
     * {@value #CONFIG_KEY} : a netcfg ConfigKey for {@link ProtectionConfig}.
     */
    public static final String CONFIG_KEY = "protection";

    /**
     * JSON key for paths.
     *

     * Value is list of {@link TransportEndpointDescription} in JSON.
     */
    private static final String PATHS = "paths";

    /**
     * JSON key for Peer {@link DeviceId}.
     */
    private static final String PEER = "peer";

    private static final String FINGERPRINT = "fingerprint";

    @Override
    public boolean isValid() {
        return isString(PEER, FieldPresence.MANDATORY) && isString(FINGERPRINT, FieldPresence.MANDATORY) && hasField(PATHS);
    }

    /**
     * Returns List of underlying transport entity endpoints in priority order.
     *
     * @return the transport entity endpoint descriptions
     */
    public List<TransportEndpointDescription> paths() {
        return getList(PATHS, jsonStr -> decode(jsonStr, TransportEndpointDescription.class));
    }

    /**
     * Sets the List of underlying transport entity endpoints in priority order.
     *
     * @param paths the transport entity endpoint descriptions
     * @return self
     */
    public ProtectionConfig paths(List<TransportEndpointDescription> paths) {
        setList(PATHS, elm -> encode(elm, TransportEndpointDescription.class).toString(), paths);
        return this;
    }

    /**
     * Returns DeviceId of remote peer of this endpoint.
     *
     * @return the peer
     */
    public DeviceId peer() {
        return DeviceId.deviceId(get(PEER, ""));
    }

    /**
     * Sets the DeviceId of remote peer of this endpoint.
     *
     * @param peer DeviceId
     * @return self
     */
    public ProtectionConfig peer(DeviceId peer) {
        setOrClear(PEER, peer.toString());
        return this;
    }

    /**
     * Returns fingerprint to identify this protected transport entity.
     *
     * @return the fingerprint
     */