Domanda

I set profile_idc, level_idc, extradata et extradata_size della AvCodecContext con il profilo-level-id et sprop-parametro-set del SDP.

I separare la decodifica di Coded Fetta, SPS, PPS e NAL_IDR_SLICE pacchetto:

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;

risultati: ConsumedBytes> 0 e GotPicture> 0 (spesso)

SPS e PPS:

codice identico. Risultato: ConsumedBytes> 0 e GotPicture = 0

E 'normale penso

Quando trovo una nuova coppia SPS / PPS, aggiorno extradata e extrada_size con i carichi utili di questo pacchetto e le loro dimensioni.

NAL_IDR_SLICE:

Il tipo di unità è Nal 28 => idr frame vengono frammentati Perciò ho provato due metodo per decodificare

1) I premettere il primo frammento (senza RTP) con la sequenza 0x000001 e inviarlo a avcodec_decode_video. Poi mando il resto dei frammenti a questa funzione.

2) I premettere il primo frammento (senza RTP) con la sequenza 0x000001 e concatenare il resto dei frammenti di esso. Mando questo buffer di decoder.

In entrambi i casi, ho errore (ConsumedBytes> 0) ma rilevo senza cornice (GotPicture = 0) ...

Qual è il problema?

È stato utile?

Soluzione

In RTP tutti H264 I-Frame (IDRS) sono quando arrivavano frammentato. Quando si riceve RTP è necessario prima di passare l'intestazione (solitamente i primi 12 byte) e poi raggiungere l'unità NAL (primo byte payload). Se il NAL è 28 (1C) allora significa che segue payload rappresenta uno H264 IDR (I-Frame) frammento e che è necessario raccogliere tutti ricostruire H264 IDR (I-frame).

La frammentazione si verifica a causa del MTU limitata, e IDR molto più grande. Un frammento può assomigliare a questo:

Frammento che ha AVVIO 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...]

Altri frammenti:

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

Per ricostruire IDR è necessario raccogliere queste informazioni:

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

Se poi fragment_type == 28 payload seguente è un frammento di IDR. spunta è impostato start_bit, se lo è, allora quel frammento è il primo in una sequenza. Lo si utilizza per ricostruire byte NAL di IDR prendendo i primi 3 bit dal primo (3 NAL UNIT BITS) payload byte e combinarle con ultimi 5 bit dal secondo payload (5 NAL UNIT BITS) byte così si otterrebbe un byte come questo [3 NAL UNIT BITS | 5 NAL UNIT BITS]. Poi scrivere che byte NAL prima in un buffer chiara con tutti gli altri byte seguenti da quel frammento. Ricordarsi di ignorare primo byte in una sequenza poiché non è una parte di IDR, ma identifica solo il frammento.

Se start_bit e end_bit sono 0 poi basta scrivere il payload (saltando primo byte payload che identifica il frammento) al tampone.

Se start_bit è 0 e end_bit è 1, il che significa che è l'ultimo frammento, e basta scrivere il suo carico (saltando il primo byte che identifica il frammento) al tampone, e ora avete il vostro IDR ricostruito.

Se avete bisogno di un po 'di codice, basta chiedere in commento, vi posterò, ma penso che questo sia abbastanza chiaro come fare ... =)

CONCERNENTE LA DECODIFICA

E 'passato per la mente oggi perché si ottiene l'errore sulla decodifica del IDR (I presume che avete ricostruito è buono). Come stai costruendo il vostro AVC Decoder Configuration Record? Fa il lib che usate hanno quel automatizzati? In caso contrario, e havent sentito parlare di questo, Continua a leggere ...

AVCDCR è specificata per consentire decoder per analizzare rapidamente tutti i dati di cui hanno bisogno per decodificare flussi video H264 (AVC). E il dato è il seguente:

  • ProfileIDC
  • ProfileIOP
  • LevelIDC
  • SPS (Imposta sequenza dei parametri)
  • PPS (Imposta Picture Parameter)

Tutti questi dati vengono inviati in sessione RTSP in SDP sotto i campi:. profile-level-id e sprop-parameter-sets

decodifica a livello di profilo-ID

Prifile stringa di livello ID è diviso in 3 stringhe, ogni 2 caratteri:

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

Ogni stringa rappresenta un byte in Base16 ! Quindi, se il profilo IDC è 28, che significa che è actualy 40 in Base10. In seguito si utilizzerà Base10 valori per costruire AVC Decoder record di configurazione.

DECODIFICA sprop-parametro-SET

Sprops sono usualy 2 stringhe (potrebbe essere più) separati virgola e base64 codificato ! È possibile decodificare entrambi, ma non c'è bisogno di. Il tuo lavoro qui è solo per convertirli da stringa Base64 in array di byte per un uso successivo. Ora avete 2 array di byte, primo array noi SPS, secondo è PPS.

COSTRUTTORI AVCDCR

Ora, avete tutto il necessario per costruire AVCDCR, si inizia facendo nuovo buffer pulito, ora scrivere queste cose in esso nell'ordine spiegato qui:

1 - Byte che ha valore 1 e rappresenta la versione

2 - Profilo IDC byte

3 - Prifile IOP byte

4 - Livello IDC byte

5 - Byte con il valore 0xFF (google il record di configurazione AVC Decoder per vedere di cosa si tratta)

6 - Byte con valore 0xE1

7 - corto con valore della lunghezza della matrice SPS

8 - SPS byte array

9 - Byte con il numero di array PPS (si potrebbe avere più di loro in sprop-parametro-set)

10 - corto con la lunghezza del seguente array PPS

11 - serie PPS

decodifica video STREAM

Ora avete array di byte che racconta il decoder come decodificare H264 flusso video. Credo che avete bisogno di questo se il vostro lib non costruisce essa stessa da SDP ...

Altri suggerimenti

Non so per il resto della tua applicazione, ma sembra probabile che i 'frammenti' si ricevono sono unità NAL. Pertanto ciascuno, ognuno può essere necessario l'avvio di codice del NALU (00 00 01 o 00 00 00 01) aggiunto quando si ricostruire il flusso di bit prima di inviarlo a ffmpeg.

In ogni caso, si potrebbe trovare il RFC per H264 RTP pacchettizzazione utili:

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

Spero che questo aiuti!

Ho un'attuazione della presente @ https://net7mma.codeplex.com/ per C #, ma la processo è lo stesso in tutto il mondo.

Ecco il codice relativo

/// <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
    }

Ci sono anche implementazioni per diversi altri di RFC che aiutano a ottenere i mezzi per giocare in un MediaElement o in altri software o semplicemente salvarlo su disco.

La scrittura di un formato contenitore è in corso.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top