Frage

I gesetzt profile_idc, level_idc, Extrainformationen et extradata_size von AvCodecContext mit dem Profil-Ebene-id et sprop-Parameter-Set des SDP.

I trennen die Decodierung codierter Slice, SPS, PPS und NAL_IDR_SLICE Paket:

Init:

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

Coded Scheibe:

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;

Ergebnis: ConsumedBytes> 0 und GotPicture> 0 (oft)

SPS und PPS:

identischer Code. Ergebnis: ConsumedBytes> 0 und GotPicture = 0

Es ist normal, ich denke,

Wenn ich finde, ein neues Paar SPS / PPS, ich Update Extrainformationen und extrada_size mit den Nutzlasten dieses Paket und deren Größe.

NAL_IDR_SLICE:

Der NAL-Einheit-Typ ist 28 => IDR-Rahmen fragmentiert werden hierfür zwei Verfahren I zu dekodieren Tryed

1) I Präfix das erste Fragment (ohne RTP-Header) mit der Sequenz 0x000001 und senden es an avcodec_decode_video. Dann habe ich den Rest der Fragmente dieser Funktion senden.

2) I Präfix das erste Fragment (ohne RTP-Header) mit der Sequenz 0x000001 und den Rest der Fragmente, um es verketten. Ich sende diesen Puffer zu Decodern.

In beiden Fällen habe ich keine Fehler (ConsumedBytes> 0), aber ich erkennen keinen Rahmen (GotPicture = 0) ...

Was ist das Problem?

War es hilfreich?

Lösung

In RTP alle H264 I-Frames (IDR) sind usualy fragmentiert. Wenn Sie RTP erhalten müssen Sie zuerst den Header (usualy ersten 12 Bytes) überspringen und dann auf die NAL-Einheit (erste Nutzlast-Byte) erhalten. Wenn die NAL 28 (1C) ist, dann bedeutet es, dass Nutzlast folgenden stellt eine H264 IDR (I-Frame) Fragment und Sie müssen alle von ihnen sammeln H264 IDR (I-Frame) zu rekonstruieren.

Fragmentation tritt wegen der begrenzten MTU und viel größeren IDR. Ein Fragment kann wie folgt aussehen:

Fragment, das START BIT = 1 hat:

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...]

Andere Fragmente:

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

IDR zu rekonstruieren, müssen Sie diese Informationen sammeln:

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

Wenn fragment_type == 28 dann Nutzlast folgenden es ist ein Fragment von IDR. Nächste Prüfung ist start_bit gesetzt, wenn es ist, dann ist das Fragment, das die erste in einer Abfolge ist. Sie verwenden es IDRs NAL Byte zu rekonstruieren, indem die ersten 3 Bits von der ersten Nutzbyte (3 NAL UNIT BITS) nehmen und kombinieren sie mit den letzten 5 Bits von dem zweiten Nutzbyte (5 NAL UNIT BITS), so dass Sie ein Byte wie diese [3 NAL UNIT BITS | 5 NAL UNIT BITS] bekommen würde. Dann schreiben, die NAL Byte zuerst in einen klaren Puffer mit allen anderen nachfolgenden Bytes von diesem Fragmente. Denken Sie daran, ersten Byte in einer Sequenz zu überspringen, da sie nicht Teil der IDR ist, sondern identifizieren nur das Fragment.

Wenn start_bit und end_bit sind 0, dann schreiben nur die Nutzlast (Payload Skipping ersten Byte, das identifiziert das Fragment) auf den Puffer.

Wenn start_bit 0 und end_bit 1, das bedeutet, dass es das letzte Fragment ist und Sie nur noch die Nutzlast schreiben (das erste Byte übersprungen, die angibt, das Fragment) auf den Puffer, und jetzt haben Sie Ihre IDR rekonstruieren.

Wenn Sie einen Code benötigen, fragen Sie einfach in Kommentar, ich werde es schreiben, aber ich denke, das ist ziemlich klar ist, wie zu tun ... =)

ÜBER DIE DEKODIERUNGS

Es kam heute meiner Meinung nach, warum Sie Fehler bekommen auf Decodierung des IDR (ich davon aus, dass Sie es gut rekonstruiert). Wie bauen Sie Ihre AVC Decoder Configuration Record? Ist die lib, dass Sie verwenden, dass automatisiert? Wenn nicht, und haben Sie davon gehört, lesen Sie weiter ...

ist AVCDCR angegeben Decoder zu erlauben, schnell zu analysieren alle Daten, die sie benötigen, H264 (AVC) Video-Stream zu dekodieren. Und die Daten werden folgende:

  • ProfileIDC
  • ProfileIOP
  • LevelIDC
  • SPS (Sequence Parameter-Sets)
  • PPS (Bildparameter-Sets)

Dieses Alle Daten werden in RTSP-Sitzung in SDP unter den Feldern gesendet. profile-level-id und sprop-parameter-sets

DEKODIEREINZELSATZ PROFILE-LEVEL-ID

Prifile Ebene ID-String ist in 3 Teil, die jeweils 2 Zeichen lang:

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

Jede Teilkette stellt ein Byte in Base16 ! Also, wenn Profil IDC 28 ist, das heißt, es ist actualy 40 in Base10. Später werden Sie Base10-Werte verwenden, um AVC Decoder Configuration Record aufzubauen.

DEKODIERUNGS sprop-PARAMETER-SETS

Sprops sind usualy 2 Strings (könnte mehr sein), die durch Kommas getrennt sind, und base64 kodierter ! Sie können beide entschlüsseln, aber es gibt keine Notwendigkeit zu. Ihre Aufgabe ist hier nur um sie zu konvertieren von base64 String in Byte-Array für eine spätere Verwendung. Jetzt haben Sie 2 Byte-Arrays, ersten Array wir SPS, zweite ist PPS.

BAU AVCDCR

Nun, Sie haben alles, was Sie AVCDCR bauen müssen, Sie beginnen mit neuen sauberen Puffer zu machen, schreiben Sie jetzt diese Dinge in es in der Reihenfolge erläutert hier:

1 - Byte, das Wert hat 1 und stellt Version

2 - Profil IDC Byte

3 - Prifile IOP Byte

4 - Ebene IDC Byte

5 - Byte mit dem Wert 0xFF (google AVC Decoder Configuration Record, um zu sehen, was das ist)

6 - Byte mit dem Wert 0xE1

7 - Kurz mit dem Wert der SPS Feldlänge

8 - SPS Byte array

9 - Byte mit der Anzahl des PPS-Arrays (man könnte mehr von ihnen in sprop-Parameter-Set hat)

10 - Kurz mit der Länge der folgenden PPS-Array

11 - PPS-Array

DEKODIERUNGS VIDEO STREAM

Jetzt haben Sie Byte-Array, das den Decoder erzählt, wie H264 Video-Stream dekodieren. Ich glaube, dass Sie diese benötigen, wenn Sie Ihre lib es nicht bauen sich aus SDP ...

Andere Tipps

Ich weiß nicht, über den Rest der Implementierung, aber es scheint wahrscheinlich, die ‚Fragmente‘ Sie empfangen sind NAL-Einheiten. Deshalb wird jeder, jeder muß die die NALU Start-Code (00 00 01 oder 00 00 00 01) angehängt, wenn Sie den Bitstrom rekonstruieren, bevor es zu ffmpeg senden.

Auf jeden Fall könnten Sie die RFC für H264 RTP packetization nützlich finden:

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

Hope, das hilft!

habe ich eine Implementierung dieses @ https://net7mma.codeplex.com/ für c #, aber die Prozess ist überall gleich.

Hier ist der relevante Code

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

Es gibt auch Implementierungen für verschiedene andere RFCs, die dazu beitragen, die Medien immer in einem Mediaelement oder in anderer Software zu spielen oder einfach nur auf der Festplatte zu speichern.

Das Schreiben in einem Container-Format ist im Gange.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top