Anatomie du format FLV
Le format FLV (Flash Video) structure ses données en blocs séquentiels : un en-tête suivi d'une série de tags. L'implémentation dans la bibliothèque flv.js, via le module src/demux/flv-demuxer.js, repose sur la classe FLVDemuxer pour effectuer cette analyse.
Structure de l'en-tête binaire
Les neuf premiers octets d'un fichier FLV forment l'en-tête, définissant les métadonnées fondamentales du fichier.
| Décalage | Champ | Taille | Description |
|---|---|---|---|
| 0-2 | Signature | 3 octets | Identifiant de fichier, toujours "FLV" (hexadécimal : 0x46, 0x4C, 0x56) |
| 3 | Version | 1 octet | Numéro de version du format, couramment 0x01 |
| 4 | Drapeaux | 1 octet | Indicateurs : le bit 0 signale la présence de vidéo, le bit 2 signale la présence d'audio |
| 5-8 | OffsetDonnées | 4 octets | Taille de l'en-tête (généralement 9 octets) |
Vérification de conformité du fichier
La fonction verifierFormat valide l'en-tête et extrait les informations initiales.
static verifierFormat(tampon) {
let octets = new Uint8Array(tampon);
// Vérification de la signature et de la version
let signatureValide = (octets[0] === 0x46) && (octets[1] === 0x4C) && (octets[2] === 0x56) && (octets[3] === 0x01);
if (!signatureValide) {
return { correspondance: false };
}
// Décodage des drapeaux de présence des pistes
let presenceAudio = ((octets[4] >> 2) & 1) === 1;
let presenceVideo = (octets[4] & 1) === 1;
// Lecture de l'offset des données (Big-Endian)
let offsetDonnees = (octets[5] << 24) | (octets[6] << 16) | (octets[7] << 8) | octets[8];
return {
correspondance: true,
pisteAudio: presenceAudio,
pisteVideo: presenceVideo,
offsetDonnees: offsetDonnees
};
}
Flux d'analyse des données
L'analyse des blocs de données (chunks) qui suivent l'en-tête est orchestrée en plusieurs étapes : validation, découpage en tags, et aiguillage vers les parseusr spécifiques. La méthode traiterBlocsDonnees est le point d'entrée principal pour ce processus.
Analyse des tags FLV
Chaque tag est précédé d'un champ PreviousTagSize0 et contient des métadonnées temporelles et structurelles. L'extraction de ces données est essentielle.
traiterBlocsDonnees(bloc, positionDepart) {
// Gestion du premier bloc
if (this._premiereAnalyse) {
this._premiereAnalyse = false;
let vue = new DataView(bloc, this._positionCourante);
let tailleTagPrecedent = vue.getUint32(0);
if (tailleTagPrecedent !== 0) {
console.warn('En-tête attendu incorrect : PreviousTagSize0 != 0');
}
this._positionCourante += 4;
}
// Boucle de lecture des tags
while (this._positionCourante < bloc.byteLength) {
let vue = new DataView(bloc, this._positionCourante);
let typeTag = vue.getUint8(0);
let tailleDonnees = vue.getUint32(1) & 0x00FFFFFF; // Masquage sur 24 bits
// Reconstruction du timestamp (32 bits en big-endian)
let timeStamp = (vue.getUint8(7) << 24) | (vue.getUint8(6) << 16) | (vue.getUint8(5) << 8) | vue.getUint8(4);
let offsetData = this._positionCourante + 11;
// Aiguillage selon le type de tag
switch (typeTag) {
case 0x08: // Audio
this._analyserDonneesAudio(bloc, offsetData, tailleDonnees, timeStamp);
break;
case 0x09: // Vidéo
this._analyserDonneesVideo(bloc, offsetData, tailleDonnees, timeStamp);
break;
case 0x12: // Données de script (métadonnées)
this._analyserDonneesScript(bloc, offsetData, tailleDonnees);
break;
}
// Avancer la position courante : 11 octets d'en-tête + taille des données + 4 octets de taille du tag précédent
this._positionCourante += 11 + tailleDonnees + 4;
}
}
Extraction des métadonnées
Les tags de type script (0x12) contiennent souvent un objet onMetaData décrivant les propriétés du média. L'analyse de ces données permet de configurer le lecteur.
Structure de onMetaData
La méthode _extraireMetadonnees interprète les données du script et peuple une structure d'informations média standardisée.
_extraireMetadonnees(tampon, debutDonnees, taille) {
let objetScript = AMF.analyserDonneesScript(tampon, debutDonnees, taille);
if (objetScript && objetScript.onMetaData) {
let meta = objetScript.onMetaData;
this._infosMedia = {
dimensions: { largeur: meta.width, hauteur: meta.height },
dureeTotale: Math.round((meta.duration || 0) * 1000),
cadence: meta.framerate,
codecs: {
audio: this._determinerCodecAudio(meta),
video: this._determinerCodecVideo(meta)
},
debits: {
audio: meta.audiodatarate,
video: meta.videodatarate
}
};
// Gestion de l'index des images clés si présent
if (meta.keyframes && typeof meta.keyframes === 'object') {
this._infosMedia.indexImagesCles = this._construireIndexImagesCles(meta.keyframes);
}
}
}
Les informations extraites sont cruciales pour des fonctionnalités comme l'affichage des métadonnées, la génération de barres de progression précises, la sélection de flux adaptatifs et l'initialisation du lecteur.
Problématiques courantes et diagnostics
L'analyse de fichiers FLV peut présenter divers défis techniques.
Codecs non supportés
Le logiciel peut rejeter des fichiers utilisant des codecs non implémentés.
// Exemple de vérification du codec audio
if (identifiantCodec !== 2 && identifiantCodec !== 10) { // Support limité à MP3 et AAC
throw new Error(`Codec audio non pris en charge: ID ${identifiantCodec}`);
}
Pistes de résolution : Conversion du fichier vers un codec supporté ou extension des capacités du parseur.
Incohérences temporelles
Un décodage incorrect des timestamps entraîne une désynchronisation audio-vidéo. La reconstruction précise de la valeur à partir des quatre octets épars est fondamentale.
// Exemple de reconstruction complexe d'un timestamp
let valeurTemporelle = ((octet[3] & 0x7F) << 24) | (octet[2] << 16) | (octet[1] << 8) | octet[0];
Pistes de résolution : Vérification de l'outil de génération du fichier et application d'un décalage via une base de temps (timestampBase).
Métadonnées manquantes
Certains fichiers FLV peuvent ne pas contenir de tag onMetaData valide. Il faut alors se rabattre sur l'analyse des premières trames pour déduire les paramètres essentiels comme les codecs et les dimensions.
Maîtriser l'analyse des en-têtes et des métadonnées FLV fournit les fondations nécessaires pour construire des applications web multimédia robustes, capables de gérer le streaming adaptatif, la synchronisation précise des flux et le débogage de fichiers corrompus.