Problem beim Decodieren von H264-Video über RTP mit ffmpeg (libavcodec)

7

Ich habe profile_idc, level_idc, extradata und extradata_size von AvCodecContext mit der profile-level-id und sprop-parameter-set des SDP gesetzt.

Ich teile die Decodierung von Coded Slice, SPS, PPS und NAL_IDR_SLICE Paket:

Init:

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

Codiertes Slice:

%Vor%

Ergebnis: ConsumedBytes & gt; 0 und GotPicture & gt; 0 (oft)

SPS und PPS:

identischer Code Ergebnis: ConsumedBytes & gt; 0 und GotPicture = 0

Es ist normal, denke ich

Wenn ich ein neues Paar SPS / PPS finde, aktualisiere ich extradata und extrada_size mit den Nutzdaten dieses Pakets und ihrer Größe.

NAL_IDR_SLICE:

Der Nal Einheitentyp ist 28 = & gt; idr Rahmen sind fragmentiert dafür habe ich versucht zwei Methoden zu dekodieren

1) Ich setze das erste Fragment (ohne RTP-Header) mit der Sequenz 0x000001 voran und sende es an avcodec_decode_video. Dann sende ich die restlichen Fragmente an diese Funktion.

2) Ich setze das erste Fragment (ohne RTP-Header) mit der Sequenz 0x000001 vor und verknüpfe den Rest der Fragmente damit. Ich sende diesen Puffer zum Decoder.

In beiden Fällen habe ich keinen Fehler (ConsumedBytes & gt; 0), aber ich erkenne keinen Rahmen (GotPicture = 0) ...

Was ist das Problem?

    
bben 16.08.2010, 13:49
quelle

3 Antworten

22

In RTP sind alle H264 I-Frames (IDRs) üblicherweise fragmentiert. Wenn Sie RTP empfangen, müssen Sie zuerst die Kopfzeile (normalerweise die ersten 12 Byte) überspringen und dann zur NAL-Einheit (erstes Nutzdatenbyte) gelangen. Wenn die NAL 28 (1C) ist, bedeutet dies, dass die folgende Nutzinformation ein H264 IDR (I-Frame) -Fragment darstellt und dass Sie alle sammeln müssen, um H264 IDR (I-Frame) zu rekonstruieren.

Fragmentierung tritt aufgrund der begrenzten MTU und viel größeren IDR auf. Ein Fragment kann so aussehen:

Fragment mit START BIT = 1:

%Vor%

Andere Fragmente:

%Vor%

Um IDR zu rekonstruieren, müssen Sie diese Information sammeln:

%Vor%

Wenn fragment_type == 28 , dann ist die folgende Payload ein Fragment von IDR. Nächste Überprüfung ist start_bit gesetzt, wenn dies der Fall ist, dann ist das Fragment das erste in einer Sequenz. Sie verwenden es, um das NAL-Byte von IDR zu rekonstruieren, indem Sie die ersten 3 Bits aus dem ersten Nutzbyte (3 NAL UNIT BITS) nehmen und sie mit den letzten 5 Bits aus dem zweiten Nutzbyte (5 NAL UNIT BITS) kombinieren, um ein Byte wie dieses [3 NAL UNIT BITS | 5 NAL UNIT BITS] zu erhalten. Schreiben Sie dann dieses NAL-Byte zuerst in einen Löschpuffer mit allen anderen folgenden Bytes von diesem Fragment. Denken Sie daran, das erste Byte in einer Sequenz zu überspringen, da es nicht Teil von IDR ist, sondern nur das Fragment identifiziert.

Wenn start_bit und end_bit 0 sind, dann schreibe einfach die Nutzdaten (überspringe das erste Nutzdatenbyte, das das Fragment identifiziert) in den Puffer.

Wenn start_bit 0 ist und end_bit 1 ist, bedeutet dies, dass es das letzte Fragment ist, und Sie schreiben einfach seine Nutzdaten (das erste Byte, das das Fragment identifiziert) in den Puffer und Sie haben Ihre IDR rekonstruiert / p>

Wenn Sie etwas Code brauchen, fragen Sie einfach im Kommentar, ich werde es posten, aber ich denke, das ist ziemlich klar, wie es geht ... =)

BEZÜGLICH DER DEKODIERUNG

Es ist mir heute in den Sinn gekommen, warum Sie beim Decodieren der IDR einen Fehler bekommen (ich vermutete, dass Sie ihn gut rekonstruiert haben). Wie bauen Sie Ihren AVC Decoder Configuration Record auf? Hat die Lib, die Sie benutzen, das automatisiert? Wenn nicht, und Sie haben davon noch nicht gehört, lesen Sie weiter ...

AVCDCR wird angegeben, damit Decoder schnell alle Daten analysieren können, die sie zum Dekodieren des H264 (AVC) -Videostreams benötigen. Und die Daten folgen:

  • ProfilIDC
  • ProfilIOP
  • LevelIDC
  • SPS (Sequenzparametersätze)
  • PPS (Bildparametersätze)

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

DECODING PROFILE-LEVEL-ID

ID der Prifile-ID-Zeichenfolge ist in 3 Teilstrings mit je 2 Zeichen unterteilt:

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

Jede Teilkette repräsentiert ein Byte in base16 ! Wenn also Profil-IDC 28 ist, bedeutet das, dass es in Base10 tatsächlich 40 ist. Später werden Sie base10-Werte verwenden, um AVC Decoder Configuration Record zu erstellen.

DECODING SPROP-PARAMETER-SETS

Sprops sind normalerweise 2 Zeichenfolgen (könnten mehr sein), die durch Kommata getrennt sind, und base64-codiert ! Sie können beide dekodieren, aber das ist nicht nötig. Ihr Job hier ist nur, sie von base64 String in Byte-Array für die spätere Verwendung zu konvertieren. Jetzt haben Sie 2 Byte-Arrays, erste Array uns SPS, zweite ist PPS.

BUILDING AVCDCR

Nun haben Sie alles, was Sie brauchen, um AVCDCR zu erstellen. Sie beginnen damit, einen neuen sauberen Puffer zu erstellen. Schreiben Sie nun diese Dinge in der hier beschriebenen Reihenfolge:

1 - Byte, das den Wert 1 hat und Version

darstellt

2 - Profil-IDC-Byte

3 - Prile IOP-Byte

4 - Level IDC-Byte

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

6 - Byte mit Wert 0xE1

7 - Kurz mit dem Wert der SPS-Array-Länge

8 - SPS-Byte-Array

9 - Byte mit der Anzahl der PPS-Arrays (Sie könnten mehr davon in sprop-parameter-set haben)

10 - Kurz mit der Länge des folgenden PPS-Arrays

11 - PPS-Array

DECODIEREN VON VIDEO STREAM

Nun haben Sie ein Byte-Array, das dem Decoder mitteilt, wie der H264-Videostream zu decodieren ist. Ich glaube, dass Sie das brauchen, wenn Ihre Bibliothek es nicht von SDP selbst erstellt ...

    
Cipi 17.08.2010, 11:38
quelle
1

Ich weiß nichts über den Rest Ihrer Implementierung, aber es scheint wahrscheinlich, dass die "Fragmente", die Sie erhalten, NAL-Einheiten sind. Daher muss jeder der NALU-Startcode ( 00 00 01 oder 00 00 00 01 ) angehängt werden, wenn Sie den Bitstream rekonstruieren, bevor Sie ihn an ffmpeg senden.

Auf jeden Fall könnte die RFC für H264 RTP-Paketierung nützlich sein:

Ссылка

Hoffe, das hilft!

    
Scott 16.08.2010 17:21
quelle
1

Ich habe eine Implementierung von diesem Ссылка für c #, aber der Prozess ist überall derselbe.

Hier ist der relevante Code

%Vor%

Es gibt auch Implementierungen für verschiedene andere RFCs, die dazu beitragen, dass die Medien in einem MediaElement oder einer anderen Software abgespielt oder einfach auf der Festplatte gespeichert werden.

Schreiben in ein Containerformat ist im Gange.

    
Jay 14.11.2014 19:01
quelle

Tags und Links