9

AvCodecContextのprofile_idc、level_idc、extradata、extradata_sizeを、SDPのprofile-level-idetsprop-parameter-setで設定しました。

コード化されたスライス、SPS、PPS、およびNAL_IDR_SLICEパケットのデコードを分離します。

初期化:

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

コード化されたスライス:

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;

結果:ConsumedBytes>0およびGotPicture>0(多くの場合)

SPSおよびPPS:

同一のコード。結果:ConsumedBytes>0およびGotPicture= 0

普通だと思います

新しいカップルSPS/PPSを見つけたら、extradataとextrada_sizeをこのパケットのペイロードとそのサイズで更新します。

NAL_IDR_SLICE:

Nalユニットタイプは28です=>idrフレームが断片化されているため、2つの方法でデコードを試みました

1)最初のフラグメント(RTPヘッダーなし)の前にシーケンス0x000001を付けて、avcodec_decode_videoに送信します。次に、残りのフラグメントをこの関数に送信します。

2)最初のフラグメント(RTPヘッダーなし)の前にシーケンス0x000001を付け、残りのフラグメントをそれに連結します。このバッファをデコーダに送信します。

どちらの場合も、エラーは発生しません(ConsumedBytes> 0)が、フレームは検出されません(GotPicture = 0)...

何が問題ですか ?

4

3 に答える 3

26

RTPでは、すべてのH264 Iフレーム(IDR)は通常断片化されています。RTPを受信するときは、最初にヘッダー(通常は最初の12バイト)をスキップしてから、NALユニット(最初のペイロードバイト)に到達する必要があります。NALが28(1C)の場合、次のペイロードが1つのH264 IDR(I-Frame)フラグメントを表し、H264 IDR(I-Frame)を再構築するためにそれらすべてを収集する必要があることを意味します。

断片化は、MTUが制限され、IDRがはるかに大きいために発生します。1つのフラグメントは次のようになります。

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

その他のフラグメント:

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

IDRを再構築するには、次の情報を収集する必要があります。

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

その場合fragment_type == 28、それに続くペイロードはIDRの1つのフラグメントです。次のチェックがstart_bit設定されている場合は、そのフラグメントがシーケンスの最初のフラグメントになります。これを使用して、最初のペイロードバイトから最初の3ビットを取得(3 NAL UNIT BITS)し、それらを2番目のペイロードバイトの最後の5ビットと組み合わせて、 IDRのNALバイトを再構築し、次(5 NAL UNIT BITS)のようなバイトを取得します[3 NAL UNIT BITS | 5 NAL UNIT BITS]。次に、そのNALバイトを最初にクリアバッファに書き込み、そのフラグメントに続く他のすべてのバイトを書き込みます。IDRの一部ではなく、フラグメントを識別するだけなので、シーケンスの最初のバイトをスキップすることを忘れないでください。

start_bitおよびが0の場合end_bit、ペイロード(フラグメントを識別する最初のペイロードバイトをスキップ)をバッファーに書き込むだけです。

start_bitが0でend_bitが1の場合、それは最後のフラグメントであることを意味し、そのペイロードをバッファーに書き込む(フラグメントを識別する最初のバイトをスキップする)だけで、IDRが再構築されます。

コードが必要な場合は、コメントで質問してください。投稿しますが、これは非常に明確な方法だと思います... =)

デコーディングについて

今日、IDRのデコードでエラーが発生する理由が頭に浮かびました(IDRを適切に再構築したと思います)。AVCデコーダー構成レコードをどのように作成していますか?使用しているライブラリは自動化されていますか?そうでない場合、そしてあなたがこれを聞いたことがないなら、読み続けてください...

AVCDCRは、デコーダーがH264(AVC)ビデオストリームのデコードに必要なすべてのデータをすばやく解析できるように指定されています。そして、データは次のとおりです。

  • ProfileIDC
  • ProfileIOP
  • LevelIDC
  • SPS(シーケンスパラメータセット)
  • PPS(画像パラメータセット)

このすべてのデータは、SDPのRTSPセッションでフィールド:profile-level-idおよび。の下に送信されsprop-parameter-setsます。

デコードプロファイル-レベルID

PrifileレベルID文字列は、それぞれ2文字の長さの3つのサブ文字列に分割されます。

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

各サブストリングは、base16の1バイトを表します。したがって、プロファイルIDCが28の場合、base10では実際には40であることを意味します。後で、base10値を使用してAVCデコーダー構成レコードを作成します。

SPROP-PARAMETER-SETSのデコード

小道具は通常、コンマで区切られ、base64でエンコードされた2つの文字列(それ以上の場合もあります)です。両方をデコードできますが、デコードする必要はありません。ここでのあなたの仕事は、後で使用するためにそれらをbase64文字列からバイト配列に変換することです。これで2バイトの配列ができました。最初の配列はSPS、2番目の配列はPPSです。

AVCDCRの構築

これで、AVCDCRを構築するために必要なものがすべて揃いました。まず、新しいクリーンバッファーを作成し、ここで説明する順序でこれらを書き込みます。

1-値が1で、バージョンを表すバイト

2-プロファイルIDCバイト

3-PrifileIOPバイト

4-レベルIDCバイト

5-値0xFFのバイト(これが何であるかを確認するには、AVCデコーダー構成レコードをグーグルで検索してください)

6-値が0xE1のバイト

7-SPS配列の長さの値で短い

8-SPSバイト配列

9-PPS配列の数を含むバイト(sprop-parameter-setにさらに多くの配列を含めることができます)

10-次のPPSアレイの長さで短い

11-PPSアレイ

ビデオストリームのデコード

これで、H264ビデオストリームをデコードする方法をデコーダーに指示するバイト配列ができました。libがSDPからそれ自体を構築しない場合は、これが必要だと思います...

于 2010-08-17T11:38:09.350 に答える
1

残りの実装についてはわかりませんが、受け取っている「フラグメント」はNALユニットである可能性があります。したがって、それぞれ、ffmpegに送信する前にビットストリームを再構築するときに、NALU開始コード(00 00 01または)を追加する必要がある場合があります。00 00 00 01

とにかく、H264RTPパケット化のRFCが役立つと思うかもしれません。

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

お役に立てれば!

于 2010-08-16T17:21:23.750 に答える
1

私はこの@https ://net7mma.codeplex.com/for c#の実装を持っていますが、プロセスはどこでも同じです。

関連するコードは次のとおりです

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

メディアをMediaElementまたは他のソフトウェアで再生したり、単にディスクに保存したりするのに役立つ、他のさまざまなRFCの実装もあります。

コンテナ形式への書き込みが進行中です。

于 2014-11-14T19:01:42.687 に答える