Je travaille sur un décodeur H.264 et je me demande où trouver le SPS et le PPS. Ma littérature de référence me dit que ce sont des unités NAL codées dans le flux H.264, mais lorsque je regarde un exemple de fichier MP4 avec IsoViewer, il est indiqué que le SPS et PPS sont dans la boîte avcC.
Comment ça marche exactement? À quoi ressemble-t-il les fichiers .mkv ou autres conteneurs H.264?
Merci d'avance!
Tout d’abord, il est important de comprendre qu’il n’existe pas de format de flux binaire élémentaire standard H.264. Le document de spécification contient une annexe, en particulier l'annexe B, qui décrit un format possible, mais il ne s'agit pas d'une exigence réelle. La norme spécifie comment la vidéo est codée en paquets individuels. La façon dont ces paquets sont stockés et transmis est laissée à l'intégrateur.
Les paquets sont appelés unités de couche d'abstraction de réseau. Souvent abrégé en NALU (ou parfois juste en NAL), chaque paquet peut être analysé et traité individuellement. Le premier octet de chaque NALU contient le type NALU, en particulier les bits 3 à 7. (le bit 0 est toujours désactivé et les bits 1 à 2 indiquent si une NALU est référencée par une autre NALU).
Il existe 19 types NALU différents définis en deux catégories, VCL et non-VCL:
Un seul NALU, ou même un VCL NALU, n'est pas la même chose qu'un cadre. Un cadre peut être "découpé" en plusieurs NALU. Tout comme vous pouvez couper une pizza. Une ou plusieurs tranches sont ensuite virtuellement regroupées dans une unité d’accès (AU) contenant une image. Le tranchage a un faible coût de qualité, il est donc peu utilisé.
Vous trouverez ci-dessous un tableau de toutes les NALU définies.
0 Unspecified non-VCL
1 Coded slice of a non-IDR picture VCL
2 Coded slice data partition A VCL
3 Coded slice data partition B VCL
4 Coded slice data partition C VCL
5 Coded slice of an IDR picture VCL
6 Supplemental enhancement information (SEI) non-VCL
7 Sequence parameter set non-VCL
8 Picture parameter set non-VCL
9 Access unit delimiter non-VCL
10 End of sequence non-VCL
11 End of stream non-VCL
12 Filler data non-VCL
13 Sequence parameter set extension non-VCL
14 Prefix NAL unit non-VCL
15 Subset sequence parameter set non-VCL
16 Depth parameter set non-VCL
17..18 Reserved non-VCL
19 Coded slice of an auxiliary coded picture without partitioning non-VCL
20 Coded slice extension non-VCL
21 Coded slice extension for depth view components non-VCL
22..23 Reserved non-VCL
24..31 Unspecified non-VCL
Il existe quelques types de NALU où il peut être utile d’avoir des connaissances plus tard.
Un NALU ne contient pas sa taille. Par conséquent, il suffit de concaténer les NALU pour créer un flux, car vous ne saurez pas où l’on s’arrête et où commence le suivant.
La spécification de l’Annexe B résout ce problème en exigeant que les ‘Codes de démarrage’ précèdent chaque NALU. Un code de départ est 2 ou 3 0x00
octets suivis d'un 0x01
octet. par exemple. 0x000001
ou 0x00000001
.
La variation de 4 octets est utile pour la transmission sur une connexion série car il est trivial d'aligner le flux d'octets en recherchant 31 bits nuls suivis d'un. Si le bit suivant est 0 (car chaque NALU commence par un bit 0), c'est le début d'une NALU. La variation de 4 octets n'est généralement utilisée que pour signaler des points d'accès aléatoires dans le flux, tels qu'un SPS PPS AUD et IDR Where) car la variation de 3 octets est utilisée partout pour économiser de l'espace.
Les codes de démarrage fonctionnent parce que les séquences à quatre octets 0x000000
, 0x000001
, 0x000002
et 0x000003
sont illégaux dans une NALU non RBSP. Ainsi, lors de la création d'une NALU, on prend soin d'échapper à ces valeurs qui pourraient autrement être confondues avec un code de départ. Ceci est accompli en insérant un octet ‘Prévention de l’émulation’ 0x03
, pour que 0x000001
devient 0x00000301
.
Lors du décodage, il est important de rechercher et d'ignorer les octets de prévention d'émulation. Étant donné que les octets de prévention d'émulation peuvent se trouver presque n'importe où dans une NALU, il est souvent plus pratique dans la documentation de supposer qu'ils ont déjà été supprimés. Une représentation sans octet de prévention d'émulation est appelée charge utile de séquence d'octet brut (RBSP).
Regardons un exemple complet.
0x0000 | 00 00 00 01 67 64 00 0A AC 72 84 44 26 84 00 00
0x0010 | 03 00 04 00 00 03 00 CA 3C 48 96 11 80 00 00 00
0x0020 | 01 68 E8 43 8F 13 21 30 00 00 01 65 88 81 00 05
0x0030 | 4E 7F 87 DF 61 A5 8B 95 EE A4 E9 38 B7 6A 30 6A
0x0040 | 71 B9 55 60 0B 76 2E B5 0E E4 80 59 27 B8 67 A9
0x0050 | 63 37 5E 82 20 55 FB E4 6A E9 37 35 72 E2 22 91
0x0060 | 9E 4D FF 60 86 CE 7E 42 B7 95 CE 2A E1 26 BE 87
0x0070 | 73 84 26 BA 16 36 F4 E6 9F 17 DA D8 64 75 54 B1
0x0080 | F3 45 0C 0B 3C 74 B3 9D BC EB 53 73 87 C3 0E 62
0x0090 | 47 48 62 CA 59 EB 86 3F 3A FA 86 B5 BF A8 6D 06
0x00A0 | 16 50 82 C4 CE 62 9E 4E E6 4C C7 30 3E DE A1 0B
0x00B0 | D8 83 0B B6 B8 28 BC A9 EB 77 43 FC 7A 17 94 85
0x00C0 | 21 CA 37 6B 30 95 B5 46 77 30 60 B7 12 D6 8C C5
0x00D0 | 54 85 29 D8 69 A9 6F 12 4E 71 DF E3 E2 B1 6B 6B
0x00E0 | BF 9F FB 2E 57 30 A9 69 76 C4 46 A2 DF FA 91 D9
0x00F0 | 50 74 55 1D 49 04 5A 1C D6 86 68 7C B6 61 48 6C
0x0100 | 96 E6 12 4C 27 AD BA C7 51 99 8E D0 F0 ED 8E F6
0x0110 | 65 79 79 A6 12 A1 95 DB C8 AE E3 B6 35 E6 8D BC
0x0120 | 48 A3 7F AF 4A 28 8A 53 E2 7E 68 08 9F 67 77 98
0x0130 | 52 DB 50 84 D6 5E 25 E1 4A 99 58 34 C7 11 D6 43
0x0140 | FF C4 FD 9A 44 16 D1 B2 FB 02 DB A1 89 69 34 C2
0x0150 | 32 55 98 F9 9B B2 31 3F 49 59 0C 06 8C DB A5 B2
0x0160 | 9D 7E 12 2F D0 87 94 44 E4 0A 76 EF 99 2D 91 18
0x0170 | 39 50 3B 29 3B F5 2C 97 73 48 91 83 B0 A6 F3 4B
0x0180 | 70 2F 1C 8F 3B 78 23 C6 AA 86 46 43 1D D7 2A 23
0x0190 | 5E 2C D9 48 0A F5 F5 2C D1 FB 3F F0 4B 78 37 E9
0x01A0 | 45 DD 72 CF 80 35 C3 95 07 F3 D9 06 E5 4A 58 76
0x01B0 | 03 6C 81 20 62 45 65 44 73 BC FE C1 9F 31 E5 DB
0x01C0 | 89 5C 6B 79 D8 68 90 D7 26 A8 A1 88 86 81 DC 9A
0x01D0 | 4F 40 A5 23 C7 DE BE 6F 76 AB 79 16 51 21 67 83
0x01E0 | 2E F3 D6 27 1A 42 C2 94 D1 5D 6C DB 4A 7A E2 CB
0x01F0 | 0B B0 68 0B BE 19 59 00 50 FC C0 BD 9D F5 F5 F8
0x0200 | A8 17 19 D6 B3 E9 74 BA 50 E5 2C 45 7B F9 93 EA
0x0210 | 5A F9 A9 30 B1 6F 5B 36 24 1E 8D 55 57 F4 CC 67
0x0220 | B2 65 6A A9 36 26 D0 06 B8 E2 E3 73 8B D1 C0 1C
0x0230 | 52 15 CA B5 AC 60 3E 36 42 F1 2C BD 99 77 AB A8
0x0240 | A9 A4 8E 9C 8B 84 DE 73 F0 91 29 97 AE DB AF D6
0x0250 | F8 5E 9B 86 B3 B3 03 B3 AC 75 6F A6 11 69 2F 3D
0x0260 | 3A CE FA 53 86 60 95 6C BB C5 4E F3
Ceci est une AU complète contenant 3 NALU. Comme vous pouvez le constater, nous commençons par un code de début suivi d'un SPS (le SPS commence par 67). Dans le SPS, vous verrez deux octets de prévention d'émulation. Sans ces octets, la séquence illégale 0x000000
se produirait à ces positions. Ensuite, vous verrez un code de départ suivi d'un PPS (PPS commence par 68) et d'un code de départ final suivi d'une tranche IDR. Il s'agit d'un flux H.264 complet. Si vous les tapez valeurs dans un éditeur hexadécimal et enregistrez le fichier avec un .264
_ extension, vous pourrez le convertir en cette image:
L'Annexe B est couramment utilisée dans les formats en direct et en continu tels que les flux de transport, les émissions en direct et les DVD. Dans ces formats, il est courant de répéter le SPS et PPS régulièrement, précédant généralement chaque IDR, créant ainsi un point d'accès aléatoire pour le décodeur. Cela permet de rejoindre un flux déjà en cours.
L’autre méthode courante de stockage d’un flux H.264 est le format AVCC. Dans ce format, chaque NALU est précédée de sa longueur (au format big endian). Cette méthode est plus facile à analyser, mais vous perdez les fonctionnalités d'alignement d'octet de l'Annexe B. Juste pour compliquer les choses, la longueur peut être codée en utilisant 1, 2 ou 4 octets. Cette valeur est stockée dans un objet d'en-tête. Cet en-tête est souvent appelé "extradata" ou "en-tête de séquence". Son format de base est le suivant:
bits
8 version ( always 0x01 )
8 avc profile ( sps[0][1] )
8 avc compatibility ( sps[0][2] )
8 avc level ( sps[0][3] )
6 reserved ( all bits on )
2 NALULengthSizeMinusOne
3 reserved ( all bits on )
5 number of SPS NALUs (usually 1)
repeated once per SPS:
16 SPS size
variable SPS NALU data
8 number of PPS NALUs (usually 1)
repeated once per PPS
16 PPS size
variable PPS NALU data
En utilisant le même exemple ci-dessus, l'extradata AVCC ressemblera à ceci:
0x0000 | 01 64 00 0A FF E1 00 19 67 64 00 0A AC 72 84 44
0x0010 | 26 84 00 00 03 00 04 00 00 03 00 CA 3C 48 96 11
0x0020 | 80 01 00 07 68 E8 43 8F 13 21 30
Vous remarquerez que SPS et PPS sont maintenant stockés hors bande. En d'autres termes, ils sont distincts des données du flux élémentaire. Le stockage et la transmission de ces données constituent le travail du conteneur de fichiers et sortent du cadre. Notez que, même si nous n’utilisons pas de codes de début, des octets de prévention d’émulation sont toujours insérés.
De plus, il existe une nouvelle variable appelée NALULengthSizeMinusOne
. Cette variable nommée qui porte à confusion nous indique le nombre d'octets à utiliser pour stocker la longueur de chaque NALU. Ainsi, si NALULengthSizeMinusOne
est défini sur 0, chaque NALU est précédée d’un seul octet indiquant sa longueur. En utilisant un seul octet pour stocker la taille, la taille maximale d’un NALU est de 255 octets. C'est évidemment assez petit. Bien trop petit pour une image clé entière. L'utilisation de 2 octets nous donne 64k par NALU. Cela fonctionnerait dans notre exemple, mais reste une limite assez basse. 3 octets seraient parfaits, mais pour une raison quelconque, ils ne sont pas universellement pris en charge. Par conséquent, 4 octets est de loin le plus commun, et c'est ce que nous avons utilisé ici:
0x0000 | 00 00 02 41 65 88 81 00 05 4E 7F 87 DF 61 A5 8B
0x0010 | 95 EE A4 E9 38 B7 6A 30 6A 71 B9 55 60 0B 76 2E
0x0020 | B5 0E E4 80 59 27 B8 67 A9 63 37 5E 82 20 55 FB
0x0030 | E4 6A E9 37 35 72 E2 22 91 9E 4D FF 60 86 CE 7E
0x0040 | 42 B7 95 CE 2A E1 26 BE 87 73 84 26 BA 16 36 F4
0x0050 | E6 9F 17 DA D8 64 75 54 B1 F3 45 0C 0B 3C 74 B3
0x0060 | 9D BC EB 53 73 87 C3 0E 62 47 48 62 CA 59 EB 86
0x0070 | 3F 3A FA 86 B5 BF A8 6D 06 16 50 82 C4 CE 62 9E
0x0080 | 4E E6 4C C7 30 3E DE A1 0B D8 83 0B B6 B8 28 BC
0x0090 | A9 EB 77 43 FC 7A 17 94 85 21 CA 37 6B 30 95 B5
0x00A0 | 46 77 30 60 B7 12 D6 8C C5 54 85 29 D8 69 A9 6F
0x00B0 | 12 4E 71 DF E3 E2 B1 6B 6B BF 9F FB 2E 57 30 A9
0x00C0 | 69 76 C4 46 A2 DF FA 91 D9 50 74 55 1D 49 04 5A
0x00D0 | 1C D6 86 68 7C B6 61 48 6C 96 E6 12 4C 27 AD BA
0x00E0 | C7 51 99 8E D0 F0 ED 8E F6 65 79 79 A6 12 A1 95
0x00F0 | DB C8 AE E3 B6 35 E6 8D BC 48 A3 7F AF 4A 28 8A
0x0100 | 53 E2 7E 68 08 9F 67 77 98 52 DB 50 84 D6 5E 25
0x0110 | E1 4A 99 58 34 C7 11 D6 43 FF C4 FD 9A 44 16 D1
0x0120 | B2 FB 02 DB A1 89 69 34 C2 32 55 98 F9 9B B2 31
0x0130 | 3F 49 59 0C 06 8C DB A5 B2 9D 7E 12 2F D0 87 94
0x0140 | 44 E4 0A 76 EF 99 2D 91 18 39 50 3B 29 3B F5 2C
0x0150 | 97 73 48 91 83 B0 A6 F3 4B 70 2F 1C 8F 3B 78 23
0x0160 | C6 AA 86 46 43 1D D7 2A 23 5E 2C D9 48 0A F5 F5
0x0170 | 2C D1 FB 3F F0 4B 78 37 E9 45 DD 72 CF 80 35 C3
0x0180 | 95 07 F3 D9 06 E5 4A 58 76 03 6C 81 20 62 45 65
0x0190 | 44 73 BC FE C1 9F 31 E5 DB 89 5C 6B 79 D8 68 90
0x01A0 | D7 26 A8 A1 88 86 81 DC 9A 4F 40 A5 23 C7 DE BE
0x01B0 | 6F 76 AB 79 16 51 21 67 83 2E F3 D6 27 1A 42 C2
0x01C0 | 94 D1 5D 6C DB 4A 7A E2 CB 0B B0 68 0B BE 19 59
0x01D0 | 00 50 FC C0 BD 9D F5 F5 F8 A8 17 19 D6 B3 E9 74
0x01E0 | BA 50 E5 2C 45 7B F9 93 EA 5A F9 A9 30 B1 6F 5B
0x01F0 | 36 24 1E 8D 55 57 F4 CC 67 B2 65 6A A9 36 26 D0
0x0200 | 06 B8 E2 E3 73 8B D1 C0 1C 52 15 CA B5 AC 60 3E
0x0210 | 36 42 F1 2C BD 99 77 AB A8 A9 A4 8E 9C 8B 84 DE
0x0220 | 73 F0 91 29 97 AE DB AF D6 F8 5E 9B 86 B3 B3 03
0x0230 | B3 AC 75 6F A6 11 69 2F 3D 3A CE FA 53 86 60 95
0x0240 | 6C BB C5 4E F3
Un avantage de ce format est la possibilité de configurer le décodeur au début et de sauter au milieu d’un flux. Il s'agit d'un cas d'utilisation courant dans lequel le support est disponible sur un support à accès aléatoire, tel qu'un disque dur, et est donc utilisé dans des formats de conteneur courants tels que MP4 et MKV.
J'ai récemment travaillé sur quelque chose comme ça.
Découvrez un inspecteur MP4. Comme vous pouvez le voir sur cette image, un MP4 comporte de nombreuses zones qui doivent être analysées pour trouver les données dont vous avez besoin.
Ici, j'ai étiqueté les parties de la boîte avcc
Je viens tout juste d'écrire un blog à plusieurs posts sur mon utilisation de h264. Je pense que c'est trop long pour poster ici http://cagneymoreau.com/stream-video-Android/