Décodage de vidéo H264 sur RTP avec ffmpeg (libavcodec)

Résolu
bbenator Messages postés 12 Date d'inscription lundi 21 juin 2010 Statut Membre Dernière intervention 31 août 2010 - 16 août 2010 à 13:16
slist Messages postés 5 Date d'inscription mardi 17 août 2010 Statut Membre Dernière intervention 27 août 2010 - 27 août 2010 à 21:02
J'ai un peu avancé sur la question mais le décodage global ne fonctionne pas.

Tout d'abord, j'ai récupéré quelques données du SDP que j'avais oublié (profile-level-id et sprop-parameter-set) afin d'alimenter les champs profile_idc, level_idc, extradata et extradata_size de ma structure AvCodecContext.

Ensuite j'ai séparé le décodage des paquets dits Coded Slice, SPS, PPS et NAL_IDR_SLICE.

Pour faire simple, les paquets Coded Slice allégés du header RTP sont directement passés au décodeur (avcodec_decode_video) avec le préfixe 0x000001. La fonction me renvoie bien la taille du paquet ConsumedBytes (indique que tous les bytes ont été utilisés pour le décodage) et le paramètre GotPicture indiquant que la frame est bien décodée devient supérieur à zéro à la suite du décodage.


     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;



Les paquets SPS et PPS subissent les mêmes instructions. Cependant, le &GotPicture reste à zéro ce qui est normal.

A chaque fois qu'un nouveau couple SPS/PPS est trouvé, j'actualise les champs extradata et extradata_size de mon AvCodecContext. Cela devrait normalement me permettre de décoder ma prochaine Idr frame ...


Mes iDR Frame sont fragmentés selon le type FU-A. J'ai donc tenté deux méthodes pour les décoder :
1) à chaque fragment ayant le start_bit à 1, je lui préfixe la séquence 0x000001 et l'envoie à avcodec_decode_video. J'envoie le reste des fragments FU-A jusqu'au paquet ayant le end_bit =1.

2) je préfixe la séquence 0x000001 au premier paquet FU-A et lui concatène ensuite tous les autres fragments arrivant. Une fois de dernier fragment arrivé, j'envoie cette chaîne au décodeur.


Dans les deux cas la fonction ne me renvoie pas d'erreurs (ConsumedBytes >0) cependant elle ne détecte aucune frame (GotPicture reste à zéro).


Avez-vous des idées ? Qu'est-ce que je règle mal ? Quelle autre méthode puis-je employer ?

Merci d'avance

11 réponses

bbenator Messages postés 12 Date d'inscription lundi 21 juin 2010 Statut Membre Dernière intervention 31 août 2010 1
26 août 2010 à 15:01
Bon j'ai trouvé une solution à mon problème et mon décodeur fonctionne. Je décode moi aussi aussi une vidéo provenant d'une caméra AXIS mais je ne connais pas le modèle.

Voici comment je procède , j'espère que cela pourra t'aider pour la suite:

[*] Je ne parse pas le sprops-parameter du SDP
[*] paquet de nal_unit_type= 1,7,8 : envoie du payload RTP entier préfixé de 0x000001 à avcodec_decode_video
[*] paquet de nal_unit_type=28 : je reconstitue la NALU fragmentée:
[list]
[*] si start_bit = 1: j'enlève les deux premiers bytes du payload et préfixe ce que j'obtiens de 0x000001 et d'un byte contenant le forbidden_bit, le NRI et le nal_type. J'obtiens un BYTE* start (5)
[*] si start_bit=0 et end_bit=0 : j'enlève les deux premiers byte.j'obtiens une liste de BYTE*.
[*] si start_bit=0 et end_bit=1 : j'enlève les deux premiers BYTEs. j'obtiens un BYTE* end.
/list

Pour finir, je concatène tout ca: start.(liste de BYTE*).end et j'envoie ca à avcodec_decode_video


Voilà ca marche pour mois, en espérant que ca peut t'aider
1
slist Messages postés 5 Date d'inscription mardi 17 août 2010 Statut Membre Dernière intervention 27 août 2010
17 août 2010 à 19:07
Bonjour,

Je suis sur le même problème que toi.
Comment est-ce que tu construits extradata et extradata_size ?

Steph
0
slist Messages postés 5 Date d'inscription mardi 17 août 2010 Statut Membre Dernière intervention 27 août 2010
17 août 2010 à 19:08
Et sinon, j'ai 12 et pas 16 comme taille d'en tete RTP.
0
bbenator Messages postés 12 Date d'inscription lundi 21 juin 2010 Statut Membre Dernière intervention 31 août 2010 1
18 août 2010 à 11:05
RTP header according to RFC 3550
       0                   1                   2                   3
       0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |V=2|P|X|  CC   |M|     PT      |       sequence number         |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |                           timestamp                           |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      |           synchronization source (SSRC) identifier            |
      +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
      |            contributing source (CSRC) identifiers             |
      |                             ....                              |
      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+


V+P+X+CC =1 byte
M+PT = 1byte
sequence number= 2 bytes
timestamp= 4bytes
SSRC=4 bytes
CSRC 4 bytes
> 16bytes

Le CSRC n'est pas toujours présent en RTP, si tu ne l'a pas, c'est normal que la taille de ton header RTP soit 12bytes.


J'ai fais mon propre parser de SDP. Pour remplir les extradata et extradata size à partir du SDP j'utilise un bout de code issu de ffmpeg : lien.

Ensuite je les actualise lorsque je reçois un nouveau couple de nal 7 et 8 (PPS et SPS).

En gros soit bufPPS (resp bufSPS) le payload PPS (resp SPS) allégé du header RTP et du premier byte (F|NRI|type ).

extradata_size= taille(bufPPS) + taille (bufSPS) + 2* taille (startcode)
extradata = startcode + bufSPS + startcode + bufPPS


J'ai trouvé cette méthode sur plusieurs projet mais elle ne fonctionne pas vraiment pour le mien puisque lors du décodage de mes idr frame, j'obtiens l'erreur "sps_id out of range" générée par la fonction ff_h264_decode_seq_parameter_set(H264Context *h).

Voilà, et toi quelle est ta méthode ?
0

Vous n’avez pas trouvé la réponse que vous recherchez ?

Posez votre question
bbenator Messages postés 12 Date d'inscription lundi 21 juin 2010 Statut Membre Dernière intervention 31 août 2010 1
18 août 2010 à 13:37
Pour les extradatas, ma méthode est expliquée sur ce site :http://aviadr1.blogspot.com/2010/05/h264-extradata-partially-explained-for.html
0
bbenator Messages postés 12 Date d'inscription lundi 21 juin 2010 Statut Membre Dernière intervention 31 août 2010 1
20 août 2010 à 11:52
Je viens de résoudre une première erreur : ffmpeg stocke le SPS et PPS du SDP dans le format avcC et je lui passais dans le format Nal-Unit.

Ainsi, mon extradata devient :

01 42 00 1e ff e1 00 12 67 42 00 1e e2 90 16 02 4d 81 27 05 01 05 e1 e2 44 54 01 00 06 68 ce 3c 80 00 00

Celui-ci est décodé lors de l'affectation du codec (avcodec_open(codecContext, codec) appelle ff_h264_decode_init qui appelle ff_h264_decode_extradata) et aucune erreur n'est retournée.

Par la suite, lorsque je décode mes paquets arrivant, j'obtiens l'erreur suivante à chaque fois que avcodec_decode_video est appelée :

[h264 @ 00334fe0]AVC: nal size nombre

le nombre étant très grand (8 chiffres).

De plus, lorsque que je veux décoder des NAL_IDR_SLICE (nal de type 5), j'ai en plus l'erreur suivante:

[h264 @ 00334fe0]sps_id out of range


Voilà, si vous avez une idée pour résoudre cela, n'hésitez pas.
0
slist Messages postés 5 Date d'inscription mardi 17 août 2010 Statut Membre Dernière intervention 27 août 2010
21 août 2010 à 11:08
Salut,

Moi j'essaye de décoder le flux h264 d'une caméra IP.

D'après le projet suivant, il n'y a aucune trame h264 !
http://sourceforge.net/projects/h264bitstream/

Et pourtant, vlc arrive bien à lire le flux de la caméra.

Voilà le contenu du SDP vidéo pour la caméra :

a=fmtp:96 packetization-mode=1; profile-level-id=420029;
sprop-parameter-sets=Z0IAKeNQFAe2AtwEBAaQeJEV,aM48gA==
0
bbenator Messages postés 12 Date d'inscription lundi 21 juin 2010 Statut Membre Dernière intervention 31 août 2010 1
23 août 2010 à 11:51
Ce projet est un 'lecteur de NALU'. Pour pouvoir le faire fonctionner, il faut que tu reconstitues les NALUs en mettant 0x000001 ou 0x00000001 devant chacune d'elles.

Cependant ce projet n'est pas un décodeur H264 : il donne juste les données utiles au fur et à mesure de la lecture, non ?
0
slist Messages postés 5 Date d'inscription mardi 17 août 2010 Statut Membre Dernière intervention 27 août 2010
23 août 2010 à 18:41
Mon projet est open source, tu peux le trouver ici :
http://slist.lilotux.net/linux/qipcam/

Le but, c'est d'afficher et d'enregistrer le flux video de ma camera IP Axis M1031-W en H264.
C'est en QT, il faut donc faire :

qmake
make
./qIpCam
0
bbenator Messages postés 12 Date d'inscription lundi 21 juin 2010 Statut Membre Dernière intervention 31 août 2010 1
24 août 2010 à 09:37
je n'utilise pas QT mais je peux déjà te dire quelques trucs :


[list]
[*] le profile IDC et level IDC doivent être stockés en base 10 (là ils sont en base 16) ainsi ton profile_idc= 66 et level_idc=41 (j'ai les mêmes valeurs)


[*] Pour le décodage du sprop_parameter, je te conseille d'aller voir la fonction sdp_parse_fmtp_config_h264. Cela te donnera une idée pour décoder cette partie du sdp. Ensuite, je construis une nal avcC (voir le site que je t'ai joins pour construire les extradatas): j'utilise la fonction ff_isom_write_avcc(ByteIOContext *pb, const uint8_t *data, int len)
/list
0
slist Messages postés 5 Date d'inscription mardi 17 août 2010 Statut Membre Dernière intervention 27 août 2010
27 août 2010 à 21:02
Salut, j'ai un peu avancé...

Je n'ai que des paquets de nal_ubit_type = 28.

J'ai quelques paquets avec des end_bit=1, ca qui est normal, mais je n'ai vraiment aucun paquet avec le start_bit=1 !

Je vais relire la RFC 3984...
http://tools.ietf.org/html/rfc3984
0
Rejoignez-nous