Question

I défini profile_idc, level_idc, extradata et extradata_size de AvCodecContext avec le profil de niveau-id et sprop-paramètre-ensemble du SDP.

Je sépare le décodage de tranche Codé, SPS, paquet PPS et NAL_IDR_SLICE:

Init:

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

Codé Tranche:

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;

Résultat: ConsumedBytes> 0 et GotPicture> 0 (souvent)

SPS et PPS:

code identique. Résultat: ConsumedBytes> 0 et GotPicture = 0

Il est je pense normale

Quand je trouve un nouveau couple SPS / PPS, je mise à jour extradata et extrada_size avec les charges utiles de ce paquet et leur taille.

NAL_IDR_SLICE:

Le type d'unité Nal est 28 => idr cadre sont fragmentés par ce procédé I tryed deux pour décoder

1) Je préfixe le premier fragment (sans en-tête RTP) avec la séquence 0x000001 et l'envoyer à avcodec_decode_video. Puis-je envoyer le reste des fragments à cette fonction.

2) Je préfixe le premier fragment (sans en-tête RTP) avec la séquence 0x000001 et concaténer les autres fragments à elle. Je vous envoie ce tampon au décodeur.

Dans les deux cas, je n'ai pas d'erreur (ConsumedBytes> 0) mais je perçois pas de cadre (GotPicture = 0) ...

Quel est le problème?

Était-ce utile?

La solution

Dans RTP tous H264 I-Frames () sont usualy IDR fragmenté. Lorsque vous recevez RTP vous devez d'abord ignorer l'en-tête (usualy premiers 12 octets), puis se rendre à l'unité NAL (premier octet de charge utile). Si le NAL est 28 (1C), cela signifie que la charge utile suivante représente un H264 IDR (I-Frame) fragment et que vous avez besoin de collecter tous pour reconstruire H264 IDR (I-Frame).

La fragmentation se produit en raison de la MTU limitée et IDR beaucoup plus. Un fragment peut ressembler à ceci:

Fragment ayant bit de départ = 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...]

D'autres fragments:

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

Pour reconstruire IDR vous devez collecter cette information:

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 alors la charge utile suivante est un fragment de IDR. Suivant l'enregistrement est réglée en start_bit, si elle est, alors que le fragment est le premier dans une séquence. Vous pouvez l'utiliser pour reconstruire octet NAL IDR en prenant les premiers 3 bits de premier (3 NAL UNIT BITS) octet de charge utile et de les combiner avec les 5 derniers bits du deuxième (5 NAL UNIT BITS) octet de charge utile de sorte que vous obtiendrez un octet comme celui-ci [3 NAL UNIT BITS | 5 NAL UNIT BITS]. Ensuite, écrire l'octet NAL premier dans un tampon clair avec tous les autres octets suivants de ce fragment. Rappelez-vous de sauter le premier octet dans une séquence, car il ne fait pas partie de IDR, mais identifie uniquement le fragment.

Si start_bit et end_bit sont 0, alors il suffit d'écrire la charge utile (sauter premier octet de la charge utile qui identifie le fragment) sur le tampon.

Si start_bit est 0 et end_bit est 1, cela signifie qu'il est le dernier fragment, et vous écrivez simplement sa charge utile (sauter le premier octet qui identifie le fragment) dans la mémoire tampon, et maintenant vous avez votre IDR reconstruit.

Si vous avez besoin du code, il suffit de demander dans le commentaire, je vais le poster, mais je pense que cela est assez clair comment faire ... =)

CONCERNANT LE DÉCODAGE

Il a traversé mon esprit aujourd'hui pourquoi vous obtenez l'erreur sur le décodage du IDR (je suppose que vous avez reconstruit bien). Comment construisez-vous votre configuration AVC Decoder Record? La lib que vous utilisez ont que automatisé? Sinon, et vous ne avez entendu parler de cela, Lire la suite ...

AVCDCR est spécifiée pour permettre des décodeurs pour analyser rapidement toutes les données dont ils ont besoin pour décoder flux vidéo H264 (AVC). Et les données suivantes:

  • ProfileIDC
  • ProfileIOP
  • LevelIDC
  • SPS (jeux de séquences de paramètres)
  • PPS (Sets d'image Paramètre)

Toutes ces données sont envoyées en session RTSP dans SDP sous les champs. profile-level-id et sprop-parameter-sets

DECODAGE PROFIL NIVEAU-ID

Prifile chaîne niveau d'identité est divisée en 3 sous-chaînes, chaque 2 caractères:

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

Chaque sous-chaîne représente un octet base16 ! Donc, si le profil IDC est 28, ce qui signifie qu'il est 40 Base10 actuellement. Plus tard, vous utiliserez Base10 valeurs pour construire AVC Decoder Enregistrement de configuration.

DÉCODAGE SPROP-parameter-ENSEMBLES

Sprops sont usualy 2 chaînes (de plus) qui sont séparés par une virgule, et base64 codé ! Vous pouvez décoder tous les deux, mais il n'y a pas besoin. Votre travail est ici juste pour les convertir de chaîne en base64 octet tableau pour une utilisation ultérieure. Maintenant, vous avez 2 tableaux d'octets, d'abord nous SPS tableau, deuxième est le PPS.

LA CONSTRUCTION AVCDCR

Maintenant, vous avez tout ce que vous devez construire AVCDCR, vous commencez par faire un nouveau tampon propre, écrire maintenant ces choses dans l'ordre indiqué ici:

1 - octet qui a une valeur 1 et représente la version

2 - Profil IDC octet

3 - octet Prifile PIO

4 - Niveau IDC octet

5 - octets avec la valeur 0xFF (google le dossier Configuration AVC Decoder pour voir ce que cela est)

6 - octet de valeur 0xE1

7 - Short avec la valeur de la longueur du tableau SPS

8 - SPS octet array

9 - octets avec le nombre de tableaux PPS (vous pourriez avoir plus d'entre eux dans sprop paramètres-set)

10 - Short avec la longueur du tableau suivant PPS

11 - tableau PPS

DECODAGE VIDEO STREAM

Maintenant, vous avez octet tableau qui indique au décodeur comment décoder H264 flux vidéo. Je crois que vous avez besoin si votre lib ne se construit pas du SDP ...

Autres conseils

Je ne sais pas sur le reste de votre mise en œuvre, mais il semble probable que les « fragments » que vous recevez sont des unités NAL. Par conséquent, chaque, chacun peut avoir besoin du Nalu démarrage code (ou 00 00 01 00 00 00 01) en annexe lorsque vous reconstruisez le bitstream avant de l'envoyer à ffmpeg.

En tout cas, vous pourriez trouver le RFC pour H264 RTP utile packetization:

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

Hope this helps!

J'ai une implémentation de cette @ https://net7mma.codeplex.com/ pour c #, mais la processus est le même partout.

Voici le code correspondant

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

Il y a aussi des mises en œuvre pour divers autres de RFC qui aident à amener les médias à jouer dans un MediaElement ou dans d'autres logiciels ou d'enregistrer tout le disque.

L'écriture à un format de conteneur est en cours.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top