Tout d'abord, permettez-moi de préciser que je ne connais pas grand chose en JavaScript. Je ne connais que la partie traitement audio et vidéo. Il est inévitable que je fasse des erreurs.
flv.jsLe code du projet a une certaine envergure Si vous souhaitez l'étudier, je vous suggère de commencer par demux. Si vous comprenez le demux, vous maîtriserez les étapes clés. du traitement des données multimédias. Le téléchargement des données multimédias précédentes et la lecture ultérieure des données multimédias deviennent faciles à comprendre.
Tout d’abord, partageons quelques connaissances de base. Pourquoi la lecture vidéo HTML5 utilise-t-elle le format flv ?
À cause de Flash. Mon image de titre utilise "flash RIP". Flash est en train de mourir, mais son influence est toujours là. La technologie Flash a été la technologie de base de la vidéo sur Internet au cours des 10 dernières années. Un grand nombre d'infrastructures associées sont construites autour de Flash, comme le CDN. . Prise en charge des protocoles RTMP et flv sur http. Afin d'être compatibles avec la lecture Flash sur le Web, les entreprises diffusant en direct sur Internet choisissent invariablement le format multimédia FLV. Pendant la période de transition de Flash vers HTML5, ce serait formidable si HTML5 pouvait supporter le protocole flash, ce qui permettrait une transition en douceur. Cependant, HTML5 ne supporte pas nativement le protocole flash. Le projet flv.js résout le problème de la prise en charge du protocole Flash par HTML5. C'est le contexte historique de l'émergence et de la popularité à court terme de flv.js.
Le démultiplexage dans flv.js est un ensemble d'analyseurs pour le format de données multimédia FLV Si vous souhaitez comprendre le format FLV, les documents suivants doivent être lus attentivement.
Description officielle du format flv d'Adobe
http://www.adobe.com/content/dam/Adobe/en/devnet/flv/pdfs/video_file_format_spec_v10.pdf
flv. utiliser js ? Passons maintenant au fait, interprétation du code flv.js : partie demux
Ouvrez le code https://github.com/Bilibili/flv.js/blob/master/src/demux/flv-demuxer.js
static probe(buffer) { let data = new Uint8Array(buffer); let mismatch = {match: false}; if (data[0] !== 0x46 || data[1] !== 0x4C || data[2] !== 0x56 || data[3] !== 0x01) { return mismatch; }
0x46 0x4c 0x56 Ces numéros sont en fait les codes ASCII de « F », « L » « V », qui représentent l'en-tête du fichier flv. Le 0x01 suivant est le numéro de version du format flv. Utilisez-le pour détecter si les données sont au format flv. .
let hasAudio = ((data[4] & 4) >>> 2) !== 0; let hasVideo = (data[4] & 1) !== 0;
Retirez le cinquième octet. Ses sixième et huitième bits indiquent respectivement si les données audio et vidéo existent. Les autres bits sont des bits réservés et peuvent être ignorés.
Cette sonde est appelée par parseChunks. Après avoir lu au moins 13 octets, il est jugé s'il s'agit de données flv, puis poursuit l'analyse ultérieure. Pourquoi est-ce 13 ? Parce que l'en-tête du fichier flv fait 13 octets. Reportez-vous à « L'en-tête FLV » dans le PDF ci-dessus. Ces 13 octets incluent la taille de quatre octets suivante. puisque la première balise n’existe pas dans la précédente, la première taille est toujours 0.
Le code derrière parseChunks analyse constamment les balises. flv appelle un élément de données multimédia TAG. En fait, il n'y a que trois types réellement utilisés, 8, 9 et 18 correspondant aux données audio, vidéo et de script. .
if (tagType !== 8 && tagType !== 9 && tagType !== 18) { Log.w(this.TAG, `Unsupported tag type ${tagType}, skipped`); // consume the whole tag (skip it) offset += 11 + dataSize + 4; continue; }
Ce code juge le type de balise. Faites attention au nombre 11, car l'en-tête de la balise fait 11 octets, suivi du corps de la balise, donc le décalage plus ces décalages permettent de passer à la position de balise suivante.
Le format de l'en-tête de la balise est le suivant : UI représente un entier non signé, suivi du nombre de bits.
Type de balise UI8
Taille des données UI24
Horodatage UI24
UI8 TimestampExtended
UI24 StreamID
Voyez-vous s'il s'agit exactement de 11 octets ? Afin d'économiser du trafic, Adobe n'utilisera jamais 32 bits s'il peut être exprimé en 24 bits, mais il définit toujours un bit d'extension pour l'horodatage afin de stocker l'octet le plus élevé. Cette conception est très pénible, ce qui conduit à ce qui suit. Ce code étrange prend d'abord trois octets, les convertit en entiers selon le Big-Endian, puis place le quatrième octet dans les bits de poids fort.
let ts2 = v.getUint8(4); let ts1 = v.getUint8(5); let ts0 = v.getUint8(6); let ts3 = v.getUint8(7); let timestamp = ts0 | (ts1 << 8) | (ts2 << 16) | (ts3 << 24);
Après avoir analysé l'en-tête de la balise, différentes fonctions d'analyse sont appelées en fonction des différents types de balises.
switch (tagType) { case 8: // Audio this._parseAudioData(chunk, dataOffset, dataSize, timestamp); break; case 9: // Video this._parseVideoData(chunk, dataOffset, dataSize, timestamp, byteStart + offset); break; case 18: // ScriptDataObject this._parseScriptData(chunk, dataOffset, dataSize); break; }
Type de TAG : 8 audio
La structure audio est relativement simple. Le premier octet de AUDIODATA représente le format audio. En fait, il s'agit essentiellement d'un échantillonnage stéréo ACC 16 bits à 44,1 kHz, donc le nombre le plus courant est 0xAF, suivi de AACAUDIODATA
. Type de TAG : 9 vidéo
L'essentiel à regarder est la vidéo,
let frameType = (spec & 240) >>> 4; let codecId = spec & 15;
Deux valeurs importantes sont prises ici. frameType indique le type de trame 1 est une image clé et 2 est une image non clé codeId est le type d'encodage. Bien que flv prenne en charge six formats vidéo, en fait, seul le H.264 est réellement utilisé pour les diffusions en direct à la demande sur Internet. Le codecId est donc essentiellement 7. L'auteur utilise ici des nombres décimaux, qui sont en fait des valeurs au niveau du bit. Il sera préférable de comprendre en utilisant des nombres hexadécimaux.
_parseAVCVideoPacket est utilisé pour analyser la structure AVCVIDEOPACKET, qui est le package vidéo H.264
let packetType = v.getUint8(0); let cts = v.getUint32(0, !le) & 0x00FFFFFF;
Expliquez le concept de CTS, CompositionTime. Nous avons obtenu un horodatage dans l'en-tête de la balise plus tôt. Cela correspond au DTS dans la vidéo, qui est l'horodatage de décodage qui est en fait un décalage, indiquant le décalage de PTS par rapport à DTS. la différence entre PTS et DTS.
这里有个坑,参考adobe的文档,这是CTS是个有符号的24位整数,SI24,就是说它有可能是个负数,所以我怀疑flv.js解析cts的代码有bug,没有处理负数情况。因为负数的24位整型到32位负数转换的时候要手工处理高位的符号位和补码问题。(我只是怀疑,没有调试确认过,但是我在处理YY直播数据的时候是踩过这个坑的,个别包含 B frame的视频是会出现CTS为负数的情况的)
packetType有两种,0 表示 AVCDecoderConfigurationRecord,这个是H.264的视频信息头,包含了 sps 和 pps,AVCDecoderConfigurationRecord的格式不是flv定义的,而是264标准定义的,如果用ffmpeg去解码,这个结构可以直接放到 codec的extradata里送给ffmpeg去解释。
flv.js作者选择了自己来解析这个数据结构,也是迫不得已,因为JS环境下没有ffmpeg,解析这个结构主要是为了提取 sps和pps。虽然理论上sps允许有多个,但其实一般就一个。
let config = SPSParser.parseSPS(sps);
pps的信息没什么用,所以作者只实现了sps的分析器,说明作者下了很大功夫去学习264的标准,其中的Golomb解码还是挺复杂的,能解对不容易,我在PC和手机平台都是用ffmpeg去解析的。SPS里面包括了视频分辨率,帧率,profile level等视频重要信息。
packetTtype 为 1 表示 NALU,NALU= network abstract layer unit,这是H.264的概念,网络抽象层数据单元,其实简单理解就是一帧视频数据。
NALU的头有两种标准,一种是用 00 00 00 01四个字节开头这叫 start code,另一个叫mp4风格以Big-endian的四字节size开头,flv用了后一种,而我们在H.264的裸流里常见的是前一种。
TAG type : 18 Script Data
除了音视频数据外还有 ScriptData,这是一种类似二进制json的对象描述数据格式,JavaScript比较惨只能自己写实现,其它平台可以用 librtmp的代码去做。
我觉得作者处理解决flv播放问题外,也为前端贡献了 amf 解析,sps解析,Golomb解码等基础代码,这些是可以用在其他项目里的。
在用传输协议获取了flv数据流后,用demux分离出音视频数据的属性和数据包,这为后面的播放打下了基础,从demux入手去读代码是个不错的切入点,而且一定要配合 flv file format spec一起看,反复多看几遍争取熟记在心。我现在已经可以从wireshark的抓包数据里人肉分析flv数据包了,对于debug相当有帮助。
相关文章:
如何看待B站 (bilibili) 开源 HTML5 播放器内核 flv.js?
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!