Cómo procesar paquetes UDP sin procesar para que puedan ser decodificados por un filtro decodificador en un filtro de origen de directshow


Larga historia:

  1. Hay una fuente H264/MPEG-4
  2. Puedo conectar esta fuente con el protocolo RTSP.
  3. Puedo obtener paquetes UDP sin procesar con el protocolo RTP.
  4. Luego envíe esos paquetes UDP sin procesar a un Decodificador [h264 / mpeg-4] [DS Source Filter]
  5. Pero esos paquetes UDP "raw" no pueden ser decodificados por el Decodificador[h264/mpeg-4] filter

En breve:

¿Cómo puedo procesar esos datos UDP sin procesar para ser decodificables por Filtro decodificador H264 / MPEG-4? ¿Puede alguien identificar claramente los pasos que tengo que ver con la transmisión H264/MPEG?

Información Adicional:

Puedo hacer esto con FFmpeg... Pero realmente no puedo averiguar cómo FFMPEG procesa los datos en bruto para que sea decodificable por un decodificador.

Author: Matt Wolfe, 2011-10-05

4 answers

Paz de la torta!

1. Obtener los datos

Como puedo ver, ya sabes cómo hacerlo (iniciar sesión RTSP, CONFIGURAR un transporte RTP/AVP/UDP;unicast; y obtener datagramas de usuario)... pero si tienes dudas, pregunta.

No importa el transporte (UDP o TCP) el formato de datos es principalmente el mismo:

  • Datos RTP: [RTP Header - 12bytes][Video data]
  • UDP: [RTP Data]
  • TCP: [$ - 1byte][Transport Channel - 1byte][RTP data length - 2bytes][RTP data]

Así que para obtener datos de UDP, solo tiene que quitar los primeros 12 bytes que representan Encabezado RTP. Pero ten cuidado, lo necesitas para obtener información de tiempo de video, y para MPEG4 la información de packetización!

Para TCP necesita leer el primer byte hasta obtener el byte $. Luego lea el siguiente byte, que será el canal de transporte al que pertenecen los siguientes datos (cuando el servidor responde a la solicitud de CONFIGURACIÓN dice: Transport: RTP/AVP/TCP;unicast;interleaved=0-1 esto significa que los DATOS de VIDEO tendrán TRANSPORT_CHANNEL=0 y los DATOS de VIDEO RTCP tendrán TRANSPORT_CHANNEL=1). Desea obtener DATOS DE VÍDEO, por lo que esperamos 0... entonces lee uno corto (2 bytes) que representa la longitud de los datos de RTP que siguen, así que lea tantos bytes, y ahora haga lo mismo que para UDP.

2. Despacketizar datos

Los datos H264 y MPEG4 generalmente se empaquetan (en SDP hay packetization-mode parámetro que puede tener valores 0, 1 y 2 lo que significa cada uno de ellos, y cómo despacketizarlo, se puede ver AQUÍ) porque hay un cierto límite de red que un extremo puede enviar a través de TCP o UDP que se llama MTU. Por lo general es 1500 bytes o menos. Entonces, si el fotograma de video es más grande que eso (y generalmente lo es), debe fragmentarse (empaquetarse) en fragmentos del tamaño de MTU. Esto se puede hacer mediante codificador/streamer en el transporte TCP y UDP, o puede retransmitir en IP para fragmentar y volver a montar el marco de vídeo en el otro lado... el primero es mucho mejor si desea tener un video propenso a errores suave sobre UDP y TCP.

H264: Para verificar si los datos RTP (que llegaron a través de UDP, o intercalados a través de TCP) se mantienen fragmento de un fotograma de video H264 más grande, debe saber cómo se ve el fragmento cuando se empaqueta:

FRAGMENTO H264

First byte:  [ 3 NAL UNIT BITS | 5 FRAGMENT TYPE BITS] 
Second byte: [ START BIT | END BIT | RESERVED BIT | 5 NAL UNIT BITS] 
Other bytes: [... VIDEO FRAGMENT DATA...]

Ahora, obtenga los primeros DATOS de VIDEO en una matriz de bytes llamada Data y obtenga la siguiente información:

int fragment_type = Data[0] & 0x1F;
int nal_type = Data[1] & 0x1F;
int start_bit = Data[1] & 0x80;
int end_bit = Data[1] & 0x40;

Si fragment_type == 28 entonces los datos de vídeo que le siguen representan el fragmento de fotograma de vídeo. La siguiente comprobación es start_bit establecida, si lo es, entonces ese fragmento es el primero de una secuencia. Se usa para reconstruir el byte NAL de IDR tomando los primeros 3 bits desde el primer byte de carga útil (3 NAL UNIT BITS) y combínelos con los últimos 5 bits del segundo byte de carga útil (5 NAL UNIT BITS) para obtener un byte como este [3 NAL UNIT BITS | 5 NAL UNIT BITS]. Luego escriba ese byte NAL primero en un búfer claro con VIDEO FRAGMENT DATA de ese fragmento.

Si start_bit y end_bit son 0, simplemente escriba el VIDEO FRAGMENT DATA (omitiendo los dos primeros bytes de carga útil que identifican el fragmento) en el búfer.

Si start_bit es 0 y end_bit es 1, eso significa que es el último fragmento, y simplemente escribe su VIDEO FRAGMENT DATA (omitiendo los dos primeros bytes que identifican el fragmento) al búfer, y ahora tienes tu fotograma de vídeo reconstruido!

Tenga en cuenta que los datos RTP contienen el encabezado RTP en los primeros 12 bytes, y que si el marco está fragmentado, nunca escribirá los primeros dos bytes en el búfer de desfragmentación, y que necesita reconstruir el byte NAL y escribirlo primero. Si estropeas algo aquí, la imagen será parcial (la mitad será gris o negro o verás artefacto).

MPEG4: Esta es fácil. Es necesario comprobar el MARKER_BIT en el encabezado RTP. Ese byte se establece (1) si los datos de vídeo representan todo el fotograma de vídeo, y es 0 de los datos de vídeo es un fragmento de fotograma de vídeo. Así que para despacketizar eso, necesitas ver cuál es el MARKER_BIT. Si es 1 eso es todo, simplemente lea los bytes de datos de video.

CUADRO COMPLETO:

   [MARKER = 1]

MARCO EMPAQUETADO:

   [MARKER = 0], [MARKER = 0], [MARKER = 0], [MARKER = 1]

Primer paquete que tiene MARKER_BIT=0 es el primer fragmento de fotograma de vídeo, todos los demás que siguen incluyendo el primero con MARKER_BIT=1 son fragmentos del mismo fotograma de vídeo. Así que lo que tienes que hacer es:

  • Hasta que MARKER_BIT=0 coloque LOS DATOS de VIDEO en el búfer de despacketización
  • Coloque los siguientes DATOS DE VIDEO donde MARKER_BIT=1 en el mismo búfer
  • El búfer de despacketización ahora contiene un fotograma MPEG4 completo

3. Datos de proceso para decodificador (flujo de bytes NAL)

Cuando usted tiene fotogramas de vídeo despacketized, es necesario hacer NAL byte stream. Tiene el siguiente formato:

  • H264: 0x000001[SPS], 0x000001[PPS], 0x000001[VIDEO FRAME], 0x000001...
  • MPEG4: 0x000001[Visual Object Sequence Start], 0x000001[VIDEO FRAME]

REGLAS:

  • Cada fotograma DEBE ir precedido por 0x000001 código de 3 bytes sin importar el códec
  • Cada flujo DEBE comenzar con INFORMACIÓN DE CONFIGURACIÓN, para H264 que son marcos SPS y PPS en ese orden (sprop-parameter-sets en SDP), y para MPEG4 el parámetro VOS frame (config en SDP)

Así que necesitas para construir un búfer de configuración para H264 y MPEG4 precedido de 3 bytes 0x000001, envíelo primero, y luego anteponga cada fotograma de video despacketizado con los mismos 3 bytes y envíelo al decodificador.

Si necesita alguna aclaración, solo comente... :)

 101
Author: Cipi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-05-11 11:48:45

Tengo una implementación de esto @ https://net7mma.codeplex.com /

Aquí está el código relevante

/// <summary>
    /// Implements Packetization and Depacketization of packets defined in <see href="https://tools.ietf.org/html/rfc6184">RFC6184</see>.
    /// </summary>
    public class RFC6184Frame : Rtp.RtpFrame
    {
        /// <summary>
        /// Emulation Prevention
        /// </summary>
        static byte[] NalStart = { 0x00, 0x00, 0x01 };

        public RFC6184Frame(byte payloadType) : base(payloadType) { }

        public RFC6184Frame(Rtp.RtpFrame existing) : base(existing) { }

        public RFC6184Frame(RFC6184Frame f) : this((Rtp.RtpFrame)f) { Buffer = f.Buffer; }

        public System.IO.MemoryStream Buffer { get; set; }

        /// <summary>
        /// Creates any <see cref="Rtp.RtpPacket"/>'s required for the given nal
        /// </summary>
        /// <param name="nal">The nal</param>
        /// <param name="mtu">The mtu</param>
        public virtual void Packetize(byte[] nal, int mtu = 1500)
        {
            if (nal == null) return;

            int nalLength = nal.Length;

            int offset = 0;

            if (nalLength >= mtu)
            {
                //Make a Fragment Indicator with start bit
                byte[] FUI = new byte[] { (byte)(1 << 7), 0x00 };

                bool marker = false;

                while (offset < nalLength)
                {
                    //Set the end bit if no more data remains
                    if (offset + mtu > nalLength)
                    {
                        FUI[0] |= (byte)(1 << 6);
                        marker = true;
                    }
                    else if (offset > 0) //For packets other than the start
                    {
                        //No Start, No End
                        FUI[0] = 0;
                    }

                    //Add the packet
                    Add(new Rtp.RtpPacket(2, false, false, marker, PayloadTypeByte, 0, SynchronizationSourceIdentifier, HighestSequenceNumber + 1, 0, FUI.Concat(nal.Skip(offset).Take(mtu)).ToArray()));

                    //Move the offset
                    offset += mtu;
                }
            } //Should check for first byte to be 1 - 23?
            else Add(new Rtp.RtpPacket(2, false, false, true, PayloadTypeByte, 0, SynchronizationSourceIdentifier, HighestSequenceNumber + 1, 0, nal));
        }

        /// <summary>
        /// Creates <see cref="Buffer"/> with a H.264 RBSP from the contained packets
        /// </summary>
        public virtual void Depacketize() { bool sps, pps, sei, slice, idr; Depacketize(out sps, out pps, out sei, out slice, out idr); }

        /// <summary>
        /// Parses all contained packets and writes any contained Nal Units in the RBSP to <see cref="Buffer"/>.
        /// </summary>
        /// <param name="containsSps">Indicates if a Sequence Parameter Set was found</param>
        /// <param name="containsPps">Indicates if a Picture Parameter Set was found</param>
        /// <param name="containsSei">Indicates if Supplementatal Encoder Information was found</param>
        /// <param name="containsSlice">Indicates if a Slice was found</param>
        /// <param name="isIdr">Indicates if a IDR Slice was found</param>
        public virtual void Depacketize(out bool containsSps, out bool containsPps, out bool containsSei, out bool containsSlice, out bool isIdr)
        {
            containsSps = containsPps = containsSei = containsSlice = isIdr = false;

            DisposeBuffer();

            this.Buffer = new MemoryStream();

            //Get all packets in the frame
            foreach (Rtp.RtpPacket packet in m_Packets.Values.Distinct()) 
                ProcessPacket(packet, out containsSps, out containsPps, out containsSei, out containsSlice, out isIdr);

            //Order by DON?
            this.Buffer.Position = 0;
        }

        /// <summary>
        /// Depacketizes a single packet.
        /// </summary>
        /// <param name="packet"></param>
        /// <param name="containsSps"></param>
        /// <param name="containsPps"></param>
        /// <param name="containsSei"></param>
        /// <param name="containsSlice"></param>
        /// <param name="isIdr"></param>
        internal protected virtual void ProcessPacket(Rtp.RtpPacket packet, out bool containsSps, out bool containsPps, out bool containsSei, out bool containsSlice, out bool isIdr)
        {
            containsSps = containsPps = containsSei = containsSlice = isIdr = false;

            //Starting at offset 0
            int offset = 0;

            //Obtain the data of the packet (without source list or padding)
            byte[] packetData = packet.Coefficients.ToArray();

            //Cache the length
            int count = packetData.Length;

            //Must have at least 2 bytes
            if (count <= 2) return;

            //Determine if the forbidden bit is set and the type of nal from the first byte
            byte firstByte = packetData[offset];

            //bool forbiddenZeroBit = ((firstByte & 0x80) >> 7) != 0;

            byte nalUnitType = (byte)(firstByte & Common.Binary.FiveBitMaxValue);

            //o  The F bit MUST be cleared if all F bits of the aggregated NAL units are zero; otherwise, it MUST be set.
            //if (forbiddenZeroBit && nalUnitType <= 23 && nalUnitType > 29) throw new InvalidOperationException("Forbidden Zero Bit is Set.");

            //Determine what to do
            switch (nalUnitType)
            {
                //Reserved - Ignore
                case 0:
                case 30:
                case 31:
                    {
                        return;
                    }
                case 24: //STAP - A
                case 25: //STAP - B
                case 26: //MTAP - 16
                case 27: //MTAP - 24
                    {
                        //Move to Nal Data
                        ++offset;

                        //Todo Determine if need to Order by DON first.
                        //EAT DON for ALL BUT STAP - A
                        if (nalUnitType != 24) offset += 2;

                        //Consume the rest of the data from the packet
                        while (offset < count)
                        {
                            //Determine the nal unit size which does not include the nal header
                            int tmp_nal_size = Common.Binary.Read16(packetData, offset, BitConverter.IsLittleEndian);
                            offset += 2;

                            //If the nal had data then write it
                            if (tmp_nal_size > 0)
                            {
                                //For DOND and TSOFFSET
                                switch (nalUnitType)
                                {
                                    case 25:// MTAP - 16
                                        {
                                            //SKIP DOND and TSOFFSET
                                            offset += 3;
                                            goto default;
                                        }
                                    case 26:// MTAP - 24
                                        {
                                            //SKIP DOND and TSOFFSET
                                            offset += 4;
                                            goto default;
                                        }
                                    default:
                                        {
                                            //Read the nal header but don't move the offset
                                            byte nalHeader = (byte)(packetData[offset] & Common.Binary.FiveBitMaxValue);

                                            if (nalHeader > 5)
                                            {
                                                if (nalHeader == 6)
                                                {
                                                    Buffer.WriteByte(0);
                                                    containsSei = true;
                                                }
                                                else if (nalHeader == 7)
                                                {
                                                    Buffer.WriteByte(0);
                                                    containsPps = true;
                                                }
                                                else if (nalHeader == 8)
                                                {
                                                    Buffer.WriteByte(0);
                                                    containsSps = true;
                                                }
                                            }

                                            if (nalHeader == 1) containsSlice = true;

                                            if (nalHeader == 5) isIdr = true;

                                            //Done reading
                                            break;
                                        }
                                }

                                //Write the start code
                                Buffer.Write(NalStart, 0, 3);

                                //Write the nal header and data
                                Buffer.Write(packetData, offset, tmp_nal_size);

                                //Move the offset past the nal
                                offset += tmp_nal_size;
                            }
                        }

                        return;
                    }
                case 28: //FU - A
                case 29: //FU - B
                    {
                        /*
                         Informative note: When an FU-A occurs in interleaved mode, it
                         always follows an FU-B, which sets its DON.
                         * Informative note: If a transmitter wants to encapsulate a single
                          NAL unit per packet and transmit packets out of their decoding
                          order, STAP-B packet type can be used.
                         */
                        //Need 2 bytes
                        if (count > 2)
                        {
                            //Read the Header
                            byte FUHeader = packetData[++offset];

                            bool Start = ((FUHeader & 0x80) >> 7) > 0;

                            //bool End = ((FUHeader & 0x40) >> 6) > 0;

                            //bool Receiver = (FUHeader & 0x20) != 0;

                            //if (Receiver) throw new InvalidOperationException("Receiver Bit Set");

                            //Move to data
                            ++offset;

                            //Todo Determine if need to Order by DON first.
                            //DON Present in FU - B
                            if (nalUnitType == 29) offset += 2;

                            //Determine the fragment size
                            int fragment_size = count - offset;

                            //If the size was valid
                            if (fragment_size > 0)
                            {
                                //If the start bit was set
                                if (Start)
                                {
                                    //Reconstruct the nal header
                                    //Use the first 3 bits of the first byte and last 5 bites of the FU Header
                                    byte nalHeader = (byte)((firstByte & 0xE0) | (FUHeader & Common.Binary.FiveBitMaxValue));

                                    //Could have been SPS / PPS / SEI
                                    if (nalHeader > 5)
                                    {
                                        if (nalHeader == 6)
                                        {
                                            Buffer.WriteByte(0);
                                            containsSei = true;
                                        }
                                        else if (nalHeader == 7)
                                        {
                                            Buffer.WriteByte(0);
                                            containsPps = true;
                                        }
                                        else if (nalHeader == 8)
                                        {
                                            Buffer.WriteByte(0);
                                            containsSps = true;
                                        }
                                    }

                                    if (nalHeader == 1) containsSlice = true;

                                    if (nalHeader == 5) isIdr = true;

                                    //Write the start code
                                    Buffer.Write(NalStart, 0, 3);

                                    //Write the re-construced header
                                    Buffer.WriteByte(nalHeader);
                                }

                                //Write the data of the fragment.
                                Buffer.Write(packetData, offset, fragment_size);
                            }
                        }
                        return;
                    }
                default:
                    {
                        // 6 SEI, 7 and 8 are SPS and PPS
                        if (nalUnitType > 5)
                        {
                            if (nalUnitType == 6)
                            {
                                Buffer.WriteByte(0);
                                containsSei = true;
                            }
                            else if (nalUnitType == 7)
                            {
                                Buffer.WriteByte(0);
                                containsPps = true;
                            }
                            else if (nalUnitType == 8)
                            {
                                Buffer.WriteByte(0);
                                containsSps = true;
                            }
                        }

                        if (nalUnitType == 1) containsSlice = true;

                        if (nalUnitType == 5) isIdr = true;

                        //Write the start code
                        Buffer.Write(NalStart, 0, 3);

                        //Write the nal heaer and data data
                        Buffer.Write(packetData, offset, count - offset);

                        return;
                    }
            }
        }

        internal void DisposeBuffer()
        {
            if (Buffer != null)
            {
                Buffer.Dispose();
                Buffer = null;
            }
        }

        public override void Dispose()
        {
            if (Disposed) return;
            base.Dispose();
            DisposeBuffer();
        }

        //To go to an Image...
        //Look for a SliceHeader in the Buffer
        //Decode Macroblocks in Slice
        //Convert Yuv to Rgb
    }

También hay implementaciones para varios otros RFC que ayudan a que los medios se reproduzcan en un MediaElement o en otro software o simplemente guardarlos en el disco.

Se está escribiendo en un formato contenedor.

 3
Author: Jay,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-11-14 18:57:12

Con paquetes UDP recibe bits de flujo H. 264 que se espera que despacketize en Unidades NAL H. 264 , que, a su vez, normalmente está empujando hacia la canalización DirectShow desde su filtro.

Las Unidades NAL se formatearán como muestras de medios de DirectShow, y posiblemente también, como parte del tipo de medio (SPS/PPS Unidades NAL).

Los pasos de despacketización se describen en RFC 6184 - RTP Payload Format for H. 264 Video. Esta es la parte de carga útil de Tráfico RTP, definido por RFC 3550 - RTP: Un Protocolo de Transporte para Aplicaciones en Tiempo Real.

Claro, pero no muy corto sin embargo.

 1
Author: Roman R.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-10-05 18:02:19

Recientemente he transmitido h264 y me he encontrado con problemas similares. Aquí está mi clase de depacketizer. Escribí una larga entrada de blog para ahorrar otro tiempo en la comprensión de este proceso http://cagneymoreau.com/stream-video-android /

  Package networking;

import org.apache.commons.logging.Log;
import utility.Debug;

import java.io.Console;
import java.io.IOException;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.util.*;


/**
 * This class is used to re-assemble udp packets filled with rtp packets into network abstraction layer units
 *
 */
public class VideoDecoder {

    private static final String TAG = "VideoDecoder";

   private PipedOutputStream pipedOutputStream; //this is where we pass the nalus we extract


   private Map<Integer, NaluBuffer> assemblyLine = new HashMap<>();  // This holds nalus we are building. Ideally only 1 and if it exceeds 3 there might be a problem
    private final int thresh = 30;
    private int assemblyThresh = thresh;
    private final int trashDelay = 3000;

   //unpacking
   private final static int HEADER_SIZE = 12;
   private final static int rtpByteHeader1 = 128; //rtp header byte 1 should always equal
    private final static int typeSPSPPS = 24;
    private final static byte typeFUA = 0b01111100;
    private final static byte[] startcode = new byte[] { 0x00, 0x00, 0x00, 0x01};

    //experimental bools that can mix piped data
    private boolean annexB = true; //remove lengths and dd aprefix
    private boolean mixed = false;  //keep lengths and add pefix dont use with annexb
    private boolean prelStyle = false; //include avcc 6 byte data
    private boolean directPipe = false; //send in the data with no editing




    public VideoDecoder(PipedOutputStream pipedOutputStream)
    {
        this.pipedOutputStream = pipedOutputStream;

    }




    // raw udp rtp packets come in here from the the udp.packet.getdata filled at socket
    public void addPacket(byte[] incoming)
    {
        if (directPipe){
            transferTOFFmpeg(incoming);
            return;
        }


        if (incoming[0] != (byte) rtpByteHeader1){
            System.out.println(TAG + " rtpHeaderError " + Byte.toString(incoming[0]));
        }

        if (incoming[1] == typeSPSPPS){
            System.out.println(TAG + "addPacket type: 24" );
            unpackType24(incoming);
        }
        else if (incoming[1] == typeFUA){
            //System.out.println(TAG + "addPacket type: 28" );
            unpackType28(incoming);
        }
        else if (incoming[1] == 1){
            System.out.println(TAG + "addPacket type: 1" );
            unpackType1(incoming);

        }else if (incoming[1] == 5){
            System.out.println(TAG + "addPacket type: 5" );
            unpackType5(incoming);

        }else{
            System.out.println(TAG + "addPacket unknown type - ERROR " + String.valueOf(incoming[1]) );
        }




    }

    //SPS & PPS this will get hit before every type 5
    //im not rtp compliant.
    //  length  sps   length pps    prel = 6length
    //  LL SPSPSPSPSP LL PPSPPSPPSPPS 123456
    private void unpackType24(byte[] twentyFour)
    {
        if (annexB){

            int sp = (twentyFour[13] << 8 | twentyFour[14]  & 0XFF);
            int pp = (twentyFour[sp + 15] << 8 | twentyFour[sp + 16]  & 0XFF);

            byte[] sps = new byte[sp];
            byte[] pps = new byte[pp];

            System.arraycopy(twentyFour,15, sps,0,sp);
            System.arraycopy(twentyFour,sp + 17, pps,0,pps.length);

            transferTOFFmpeg(sps);
            transferTOFFmpeg(pps);

        }else if (prelStyle)
        {

            //Debug.debugHex("unpack24 " , twentyFour, twentyFour.length);

            int spsl = (twentyFour[14] & 0xff) + 2;
            int ppsl = (twentyFour[14+ spsl] & 0xff) +2;
            int prel = 6;

            byte[] buf = new byte[spsl + ppsl + prel];  //rtp header length - type + experimental data

            System.arraycopy(twentyFour, 13, buf, 6,spsl + ppsl);
            System.arraycopy(twentyFour, spsl + ppsl + 13, buf,0, 6);

            transferTOFFmpeg(buf);

        }else{

            int spsl = (twentyFour[14] & 0xff) + 2;
            int ppsl = (twentyFour[14+ spsl] & 0xff) +2;


            byte[] buf = new byte[spsl + ppsl ];  //rtp header length - type + experimental data

            System.arraycopy(twentyFour, 13, buf, 0,spsl + ppsl);
            //System.arraycopy(twentyFour, spsl + ppsl + 13, buf,0, 6);

            transferTOFFmpeg(buf);


        }




    }

    //Single NON IDR Nal - This seems liekly to never occur
    private void unpackType1(byte[] one)
    {

        byte[] buf = new byte[one.length-12];

        System.arraycopy(one, 12, buf, 0,buf.length);

        transferTOFFmpeg(buf);

    }

    //Single IDR Nal - This seems likely to never occur
    private void unpackType5(byte[] five)
    {
        byte[] buf = new byte[five.length-12];

        System.arraycopy(five, 12, buf, 0,buf.length);

        transferTOFFmpeg(buf);

    }

    // Unpack either any split up nalu - This will get 99.999999 of nalus
    synchronized private void unpackType28(byte[] twentyEight)
    {
        //Debug.deBugHexTrailing("unpack 28 ", twentyEight, 20 );

        int ts = (twentyEight[4] << 24 | twentyEight[5] << 16 | twentyEight[6] << 8 | twentyEight[7] & 0XFF);   //each nalu has a unique timestamp
        //int seqN = (twentyEight[2] << 8 | twentyEight[3] & 0xFF);                                               //each part of that nalu is numbered in order.
                                                                                                                // numbers are from every packet ever. not this nalu. no zero or 1 start
        //check if already building this nalu
        if (assemblyLine.containsKey(ts)){

            assemblyLine.get(ts).addPiece(twentyEight);

        }
        //add a new nalu
        else
            {

            assemblyLine.put(ts, new NaluBuffer(ts, twentyEight));

        }

    }



    //this will transfer the assembled nal units to the media codec/trans-coder/decoder/whatever?!?
    private void transferTOFFmpeg(byte[] nalu)
    {

        Debug.debugHex("VideoDecoder transferTOFFmpg -> ", nalu, 30);



        try{
            if (annexB || mixed){
                pipedOutputStream.write(startcode);
            }

            pipedOutputStream.write(nalu,0,nalu.length);


        }catch (IOException ioe){
            System.out.println(TAG + " transferTOFFmpeg - unable to lay pipe ;)");


        }

        if (assemblyLine.size() > assemblyThresh){
            System.err.println(TAG + "transferToFFmpeg -> assemblyLine grows to a count of " + String.valueOf(assemblyLine.size()));
            assemblyThresh += thresh;
        }


    }



    private void clearList()
    {
        String n = "\n";
        List<Integer> toremove = new ArrayList<>();
        StringBuilder description = new StringBuilder();

        for(Map.Entry<Integer, NaluBuffer> entry : assemblyLine.entrySet()) {
           Integer key = entry.getKey();
            NaluBuffer value = entry.getValue();

            if (value.age < System.currentTimeMillis() - trashDelay){
                toremove.add(key);
                description
                        .append(String.valueOf(value.timeStamp)).append(" timestamp").append(n)
                        .append(String.valueOf(value.payloadType)).append(" type").append(n)
                        .append(String.valueOf(value.count)).append(" count").append(n)
                        .append(String.valueOf(value.start)).append(" ").append(String.valueOf(value.finish)).append(n)
                        .append(n);
            }

        }

        for (Integer i :
                toremove) {
            assemblyLine.remove(i);
        }
        if (toremove.size() > 0){
            System.out.println(TAG + " cleaList current size : " + String.valueOf(assemblyLine.size()) + n + "deleting: " + toremove.size() + n + description);
            assemblyThresh = thresh;
        }

    }

    private void deletMe(int key)
    {
        assemblyLine.remove(key);

        if (assemblyLine.size() > 3){
            clearList();
        }
    }



    /*
    Once a multipart FU-A rtp packet is found it is added to a hashset containing this class
    Here we do everything needed to either complete assembly and send or destroy if not completed due to presumable packet loss

    ** Example Packet From First FU-A with SER = 100 **
    description->         |-------RTP--HEADER------|       |FU-A--HEADER|         |-NAL--HEADER|
    byte index->          0|1|2|3|4|5|6|7|8|9|10|11|           12|13              14|15|16|17|18
                          | | | | | | | | |S S R C|             |  |__header       |  |  |  |  |__type
                          | | | | |TIMESTM|                     |__indicator       |  |  |  |__length
                          | | | |__sequence number                                 |  |  |__length
                          | | |____sequence number                                 |  |___length
                          | |__payload                                             |__length
                          |___version padding extension

    */
    private class NaluBuffer
    {
        private final static String TAG = "NaluBuffer";
        //private static final int BUFF_SIZE = 200005;  // this is the max nalu size + 5 byte header we searched for in our androids nalu search
        long age;
        //List<String> sizes = new ArrayList<>();

        NaluePiece[] buffer = new NaluePiece[167];
        int count = 0;
        int start;
        int finish;

        int timeStamp;          //from rtp packets.
        int completedSize;      //this is number of nalu
        int payloadType;        //nalu type  5 or 1
        int byteLength;
        int naluByteArrayLength = 0;

        //if it doesnt exist
        NaluBuffer(int timeStamp, byte[] piece)
        {

            //System.out.println(TAG + " constructor "  + String.valueOf(timeStamp) );

            this.timeStamp = timeStamp;
            age = System.currentTimeMillis();

            addPieceToBuffer(piece);
            count++;

        }

        //adding another piece
       synchronized public void addPiece(byte[] piece)
        {
            //System.out.println(TAG + " addPiece "  + String.valueOf(timeStamp));
            addPieceToBuffer(piece);
            count++;

        }

        //add to buffer. incoming data is still raw rtp packet
        private void addPieceToBuffer(byte[] piece)
        {
            //System.out.println(TAG + " addPiecetobuffer "  + String.valueOf(piece[13]));

            int seqN = (piece[2] << 8 | piece[3] & 0xFF);


            //add to buffer
            buffer[count] = new NaluePiece(seqN, Arrays.copyOfRange(piece, 14,piece.length)); // 14 because we skip rtp header of 12 and fu-a header of 2

            int in = ( piece.length - 14); //we save each byte[] copied size so we can easily construct a completed array later

            //sizes.add(String.valueOf(in));

            naluByteArrayLength += in;

            //check if first or last, completed size type etc
            if ((start == 0) && (piece[13] & 0b11000000) == 0b10000000){
                //start of nalu
                start =  (piece[2] << 8 | piece[3] & 0xFF);

                //type
                payloadType = (piece[13] & 0b00011111); //could have used [18]                                      //get type
                byteLength = (piece[17]&0xFF | (piece[16]&0xFF)<<8 | (piece[15]&0xFF)<<16 | (piece[14]&0xFF)<<24); //get the h264 encoded length
                byteLength += 4;                                                                                //Now add 4 bytes for the length encoding itself

                if (payloadType == 1 || payloadType == 5 && byteLength < 200000){

                }else{
                    System.err.println(TAG + " addpiecetobuffer type: " + String.valueOf(payloadType) + "length: " + String.valueOf(byteLength) );
                }
                //System.out.println(TAG + " addpiecetobuffer start "  + String.valueOf(start) + " type " + String.valueOf(payloadType));

            }else if ((finish == 0) && (piece[13] & 0b11000000) == 0b01000000){
                //end of nalu
                finish =  (piece[2] << 8 | piece[3] & 0xFF);
                //System.out.println(TAG + " addpiecetobuffer finish "  + String.valueOf(finish));
            }

            if (finish != 0 && start != 0 && completedSize == 0){

                //completed size in packet sequnce number NOT in byte length
                completedSize = finish - start;
                //System.out.println(TAG + " addpiecetobuffer completedsize "  + String.valueOf(completedSize));
                        //originally put in bytes but thats not what I was counting ...duh!
            // (piece[14] <<24 | piece[15] << 16 | piece[16] << 8 | piece[17] & 0xFF);

            }


            //check if complete

            if (completedSize != 0 && count == completedSize){
                assembleDeliver();
            }


        }

        // we have every sequence number accounted for.
        // reconstruct the nalu and send it to the decoder
        private void assembleDeliver()
        {
            count++; //make up for the ount that didn't get called following addpiecetobuffer method
           // System.out.println(TAG + " assembleDeliver "  + String.valueOf(timeStamp));

            //create a new array the exact length needed and sort each nalu by sequence number
            NaluePiece[] newbuf = new NaluePiece[count];
            System.arraycopy(buffer,0,newbuf,0, count);
            Arrays.sort(newbuf);

            // TODO: 9/28/2018 we have no gaps in data here checking newbuff !!!!!

            //this will be an array we feed/pipe to our videoprocessor
            byte[] out;

            if (annexB){
                 out = new byte[naluByteArrayLength-4]; //remove the 4 bytes of length
                int tally = 0;

                int destPos = 0;
                int src = 4;
                for (int i = 0; i < count; i++) {
                    if (i == 1){
                        src = 0;
                    }
                    tally += newbuf[i].piece.length;
                    System.arraycopy(newbuf[i].piece, src, out, destPos, newbuf[i].piece.length - src);

                    //Debug.fillCompleteNalData(out, destPos, newbuf[i].piece.length);

                    destPos += newbuf[i].piece.length - src;



                }

                /*
                StringBuilder sb = new StringBuilder();
                sb.append("VideoDecoder assembleDeliver out.length ").append(String.valueOf(out.length))
                        .append(" destPos ").append(String.valueOf(destPos)).append(" tally ").append(String.valueOf(tally))
                        .append(" count ").append(String.valueOf(count)).append(" obuf ").append(String.valueOf(completedSize));

                for (String s :
                        sizes) {
                    sb.append(s).append(" ");
                }

                System.out.println(sb.toString());
                */

            }else{
                 out = new byte[naluByteArrayLength];

                int destPos = 0;
                for (int i = 0; i < count; i++) {

                    System.arraycopy(newbuf[i].piece, 0, out, destPos, newbuf[i].piece.length);

                    destPos += newbuf[i].piece.length;

                }


            }

            if (naluByteArrayLength != byteLength){
                System.err.println(TAG + " assembleDeliver -> ERROR - h264 encoded length: " + String.valueOf(byteLength) + " and byte length found: " + String.valueOf(naluByteArrayLength) + " do not match");
            }

            // TODO: 9/28/2018 we have gaps in data here
                //Debug.checkNaluData(out);


            transferTOFFmpeg(out);
            deletMe(timeStamp);
        }



    }


    //This class stores the payload and ordering info
    private class NaluePiece implements Comparable<NaluePiece>
    {
        int sequenceNumber; //here is the number we can access to order them
        byte[] piece;       //here we store the raw payload data to be aggregated


        public NaluePiece(int sequenceNumber, byte[] piece)
        {
            this.sequenceNumber = sequenceNumber;
            this.piece = piece;
            //Debug.checkNaluPieceData(piece);
        }


        @Override
        public int compareTo(NaluePiece o) {
            return Integer.compare(this.sequenceNumber, o.sequenceNumber);
        }
    }



}
 0
Author: cagney,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-10-03 23:36:58