Décodage de vidéo H264 sur RTP avec ffmpeg (libavcodec) [Résolu]

bbenator 12 Messages postés lundi 21 juin 2010Date d'inscription 31 août 2010 Dernière intervention - 16 août 2010 à 13:16 - Dernière réponse : slist 5 Messages postés mardi 17 août 2010Date d'inscription 27 août 2010 Dernière intervention
- 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
Afficher la suite 

Votre réponse

11 réponses

Meilleure réponse
bbenator 12 Messages postés lundi 21 juin 2010Date d'inscription 31 août 2010 Dernière intervention - 26 août 2010 à 15:01
3
Merci
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

Merci bbenator 3

Avec quelques mots c'est encore mieux Ajouter un commentaire

Codes Sources a aidé 102 internautes ce mois-ci

Commenter la réponse de bbenator
slist 5 Messages postés mardi 17 août 2010Date d'inscription 27 août 2010 Dernière intervention - 17 août 2010 à 19:07
0
Merci
Bonjour,

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

Steph
Commenter la réponse de slist
slist 5 Messages postés mardi 17 août 2010Date d'inscription 27 août 2010 Dernière intervention - 17 août 2010 à 19:08
0
Merci
Et sinon, j'ai 12 et pas 16 comme taille d'en tete RTP.
Commenter la réponse de slist
bbenator 12 Messages postés lundi 21 juin 2010Date d'inscription 31 août 2010 Dernière intervention - 18 août 2010 à 11:05
0
Merci
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 ?
Commenter la réponse de bbenator
bbenator 12 Messages postés lundi 21 juin 2010Date d'inscription 31 août 2010 Dernière intervention - 18 août 2010 à 13:37
0
Merci
Pour les extradatas, ma méthode est expliquée sur ce site :http://aviadr1.blogspot.com/2010/05/h264-extradata-partially-explained-for.html
Commenter la réponse de bbenator
bbenator 12 Messages postés lundi 21 juin 2010Date d'inscription 31 août 2010 Dernière intervention - 20 août 2010 à 11:52
0
Merci
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.
Commenter la réponse de bbenator
slist 5 Messages postés mardi 17 août 2010Date d'inscription 27 août 2010 Dernière intervention - 21 août 2010 à 11:08
0
Merci
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==
Commenter la réponse de slist
bbenator 12 Messages postés lundi 21 juin 2010Date d'inscription 31 août 2010 Dernière intervention - 23 août 2010 à 11:51
0
Merci
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 ?
Commenter la réponse de bbenator
slist 5 Messages postés mardi 17 août 2010Date d'inscription 27 août 2010 Dernière intervention - 23 août 2010 à 18:41
0
Merci
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
Commenter la réponse de slist
bbenator 12 Messages postés lundi 21 juin 2010Date d'inscription 31 août 2010 Dernière intervention - 24 août 2010 à 09:37
0
Merci
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
Commenter la réponse de bbenator
slist 5 Messages postés mardi 17 août 2010Date d'inscription 27 août 2010 Dernière intervention - 27 août 2010 à 21:02
0
Merci
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
Commenter la réponse de slist

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.