Pregunta

I establece profile_idc, level_idc, ExtraData et extradata_size de AvCodecContext con el perfil de nivel-id et sprop-parámetro-conjunto de la SDP.

I separar la decodificación de Coded Slice, SPS, PPS y NAL_IDR_SLICE paquete:

Init:

uint8_t start_sequence [] = {0, 0, 1}; int size = recv (id_de_la_socket, (char *) rtpReceive, 65535,0);

Coded Slice:

char *z = new char[size-16+sizeof(start_sequence)];
    memcpy(z,&start_sequence,sizeof(start_sequence));
    memcpy(z+sizeof(start_sequence),rtpReceive+16,size-16);
    ConsumedBytes = avcodec_decode_video(codecContext,pFrame,&GotPicture,(uint8_t*)z,size-16+sizeof(start_sequence));
    delete z;

Resultados: ConsumedBytes> 0 y GotPicture> 0 (a menudo)

SPS y PPS:

código idénticos. Resultado: ConsumedBytes> 0 y GotPicture = 0

Es normal creo

Cuando encuentro una nueva pareja SPS / PPS, me actualización extradata y extrada_size con las cargas útiles de este paquete y su tamaño.

NAL_IDR_SLICE:

El tipo de unidad NAL es 28 => IDR Frame están fragmentados para ello I tryed dos método para decodificar

1) I prefijar el primer fragmento (sin cabecera RTP) con la secuencia 0x000001 y enviarlo a avcodec_decode_video. A continuación, le envío el resto de fragmentos para esta función.

2) I prefijar el primer fragmento (sin cabecera RTP) con la secuencia 0x000001 y concatenar el resto de fragmentos a ella. Envío este búfer al decodificador.

En ambos casos, no tengo ningún error (ConsumedBytes> 0), pero detecto ningún marco (GotPicture = 0) ...

¿Cuál es el problema?

¿Fue útil?

Solución

En RTP todo H264 I-Frames (IDR) se usualy fragmentado. Cuando recibe RTP primero debe omitir el encabezado (usualy primeros 12 bytes) y luego llegar a la unidad NAL (primer byte de carga útil). Si el NAL es 28 (1C), entonces significa que después de la carga útil representa uno H264 IDR (I-Frame) fragmento y que es necesario recoger todos ellos para reconstruir H264 IDR (I-Frame).

La fragmentación se produce a causa de la MTU limitado, y mucho más grande IDR. Un fragmento puede tener este aspecto:

Fragmento que tiene START BIT = 1:

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

Otros fragmentos:

First byte:  [ 3 NAL UNIT BITS | 5 FRAGMENT TYPE BITS]  
Other bytes: [... IDR FRAGMENT DATA...]

Para reconstruir IDR debe recopilar esta 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 la carga útil siguiente es un fragmento de IDR. cheque siguiente es conjunto start_bit, si lo es, entonces ese fragmento es el primero en una secuencia. Se utiliza para reconstruir bytes NAL del IDR mediante la adopción de los primeros 3 bits del primer byte de la carga útil y (3 NAL UNIT BITS) combinan con últimos 5 bits de la segunda carga útil (5 NAL UNIT BITS) bytes por lo que se obtendría un byte como esto [3 NAL UNIT BITS | 5 NAL UNIT BITS]. A continuación, escribir ese byte NAL primero en una memoria intermedia clara con el resto de los bytes siguientes a partir de ese fragmento. Recuerde saltarse primer byte de una secuencia, ya que no es una parte del IDR, pero sólo identifica el fragmento.

Si start_bit y end_bit son 0, entonces acaba de escribir la carga útil (se pierden primer byte de carga útil que identifica el fragmento) a la memoria intermedia.

Si start_bit es 0 y end_bit es 1, que significa que es el último fragmento, y que acaba de escribir su carga útil (omitiendo el primer byte que identifica el fragmento) en el búfer, y ahora se han reconstruido su IDR.

Si necesita algo de código, sólo hay que preguntar en el comentario, voy a publicar, pero creo que esto está bastante claro cómo hacerlo ... =)

SOBRE LA DECODING

Se me ocurrió hoy por qué se obtiene error en la decodificación de la IDR (I supone que usted ha reconstruido bueno). ¿Cómo está usted construyendo su AVC Decoder Registro de configuración? ¿El lib que se utiliza tiene que automatizadas? Si no es así, y usted no ha oído hablar de esto, continuar leyendo ...

AVCDCR se especifica para permitir que los decodificadores a analizar rápidamente todos los datos que necesitan para decodificar flujo de vídeo H264 (AVC). Y los datos es el siguiente:

  • ProfileIDC
  • ProfileIOP
  • LevelIDC
  • SPS (conjuntos de secuencias de parámetros)
  • PPS (sistemas del cuadro de parámetros)

Todos estos datos se envían en sesión RTSP en SDP bajo los campos:. profile-level-id y sprop-parameter-sets

decodificación de perfil de nivel-ID

cadena de ID Prifile nivel se divide en 3 sub-cadenas, cada una de 2 caracteres:

[PROFILE IDC][PROFILE IOP][LEVEL IDC]

Cada subcadena representa un byte en Base16 ! Así pues, si el perfil de IDC es de 28, eso significa que es actualy 40 en base10. Más tarde va a utilizar para la construcción de los valores Base10 AVC Decoder Registro de configuración.

DECODING sprop-parámetro-Sets

Sprops son usualy 2 cuerdas (podría ser más) que están separados por comas, y base64 codificado ! Puede decodificar los dos, pero no hay necesidad de hacerlo. Su trabajo aquí es sólo para convertirlos de cadena de base 64 en matriz de bytes para su uso posterior. Ahora usted tiene 2 matrices de bytes, primera matriz nos SPS, segundo es el PPS.

CONSTRUCCIÓN DEL AVCDCR

Ahora, usted tiene todo lo que necesita para construir AVCDCR, que empezar por hacer nuevo buffer limpio, ahora escribir estas cosas en él en el orden explicado aquí:

1 - Byte que tiene valor 1 y representa versión

2 - Perfil de IDC bytes

3 - Prifile bytes PIO

4 - El nivel de byte IDC

5 - Byte con valor 0xFF (google del Registro de configuración AVC decodificador para ver de qué se trata)

6 - Byte con valor 0xE1

7 - Short con valor de la longitud de la matriz SPS

8 - SPS bytes array

9 - Byte con el número de matrices de PPS (que podría tener más de ellos en sprop-parámetro-set)

10 - corta con la longitud del siguiente matriz PPS

11 - array PPS

descodificación de vídeo STREAM

Ahora usted tiene matriz de bytes que indica al descodificador cómo decodificar H264 flujo de vídeo. Creo que usted necesita esto si su lib no construye él mismo desde SDP ...

Otros consejos

No sé sobre el resto de su aplicación, pero parece probable que los 'fragmentos' que está recibiendo son unidades NAL. Por lo tanto cada uno, cada uno puede necesitar la NALU la puesta en código (00 00 01 o 00 00 00 01) anexado al reconstruir el flujo de bits antes de enviarlo a ffmpeg.

En cualquier caso, es posible encontrar el RFC para H264 RTP paquetización útil:

http://www.rfc-editor.org/rfc/rfc3984.txt

Espero que esto ayude!

Tengo una implementación de este @ https://net7mma.codeplex.com/ para C #, pero el proceso es el mismo en todas partes.

Este es el código correspondiente

/// <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 de RFC que ayudan a conseguir los medios para jugar en un MediaElement o en otro software o simplemente guardarlo en el disco.

Escribir en un formato contenedor está en marcha.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top