美文网首页我爱编程
flv.js 1.0 源码学习(三)

flv.js 1.0 源码学习(三)

作者: 云峰yf | 来源:发表于2018-04-16 10:23 被阅读0次

    整个库的核心部分之一。
    参考:https://blog.csdn.net/g332065255/article/details/71158863

    flv-demuxer.js-FLVDemuxer类-FLV解码器

    FLVDemuxer

    肯定有个什么mp4-demuxer

    1. 属性分为基本属性、媒体信息、事件处理函数,构造函数传 probeData 和 config

      • 1.1 基本属性
        • 1.1.1 TAG 构造器名称
        • 1.1.2 _config 全局配置,初始化为 conifg
        • 1.1.3 _littleEndian 判断当前是否支持小端
      • 1.2 媒体信息
        • 1.2.1 _dataOffset 数据偏移量,初始化为 probeData.dataOffset
        • 1.2.2 _firstParse 是否首次解析,初始化为 true
        • 1.2.3 _dispatch 是否已分发,初始化为 false
        • 1.2.4 _hasAudio 是否有音轨,初始化为 probeData.hasAudioTrack
        • 1.2.5 _hasVideo 是否有视轨,初始化为 probeData.hasVideoTrack
        • 1.2.6 _audioInitialMetadataDispatched 音频初始元数据是否分发,初始化为 false
        • 1.2.7 _videoInitialMetadataDispatched 视频初始元数据是否分发,初始化为 false
        • 1.2.8 _mediaInfo 媒体信息,初始化为 MediaInfo 的实例,并合并视频的是否有音轨和是否有视轨属性
        • 1.2.9 _metadata 元数据,初始化为 null
        • 1.2.10 _audioMetadata 音频元数据,初始化为 null
        • 1.2.11 _videoMetadata 视频元数据,初始化为 null
        • 1.2.12 _naluLengthSize nalu 长度,初始化为 4
        • 1.2.13 _timestampBase 基本时间戳,初始化为 0,关联了实例的 timestampBase 属性
        • 1.2.14 _timescale 时间步长,初始化为 1000
        • 1.2.15 _duration 持续时间,初始化为 0
          • 关联了实例的 overridedDuration 属性,set 的时候除了改变实例持续时间还会改变实例持续时间是否覆盖属性为 true、实例媒体信息的持续时间为它
        • 1.2.16 _durationOverrided 持续时间是否覆盖,初始化为 false
        • 1.2.17 _referenceFrameRate 参考帧率,初始化为 { fixed: true, fps: 2- 3.976, fps_num: 23976, fps_den: 1000 }
        • 1.2.18 _videoTrack 视轨,初始化为 {type: 'video', id: 1, sequenceNumber: 0, samples: [], length: 0}
        • 1.2.19 _audioTrack 音轨,初始化为 {type: 'audio', id: 2, sequenceNumber: 0, samples: [], length: 0}
      • 1.3 事件处理函数,初始化都为 null,都关联了实例的对应属性
        • 1.3.1 _onError 出错事件处理函数
        • 1.3.2 _onMediaInfo 媒体信息事件处理函数
        • 1.3.3 _onTrackMetadata 轨道元数据事件处理函数
        • 1.3.4 _onDataAvailable 数据可用事件处理函数
      • 1.4 静态属性方法
        • 1.4.1 probe(buffer) 探测数据能不能解析
          • 1.4.1.1 创建 data 作为 buffer 的视图,类型为 Uint8Array
          • 1.4.1.2 如果 data 前四位不是 0x464c5601(flv头),说明不能解析,结束
          • 1.4.1.3 探测是否有音轨,依据是将 data 的第五位和 4 与运算再有符号右移 2 位不为 0
          • 1.4.1.4 探测是否有视轨,依据是将 data 的第五位和 1 与运算不为 0
          • 1.4.1.5 如果既没有音轨也没有视轨,说明不能解析,结束
          • 1.4.1.6 通过大端 32 位法读 data,index 传 5,获取 offset
          • 1.4.1.7 如果 offset 小于 9,说明不能解析,结束
          • 1.4.1.8 返回一个对象:{ match: true, consumed: offset, dataOffset: offset, hasAudioTrack: hasAudio, hasVideoTrack: hasVideo }
    2. 工具方法

      • 2.1 Swap16(src) 16位二进制数据交换,得到先右移 8 位再和 256 做与运算的结果,再得到先和 256 做与运算再左移 8 位的结果,最后一起做或运算
      • 2.2 Swap32(src) 32位二进制数据交换,得到先和 2的32次方 做与运算再右移 24 位,再得到先和 2的24次方 做与运算再右移 8 位,再得到内的数 先和 2的16次方 做与运算再左移 24 位,再得到内的数 先和 256 做与运算再左移 24 位,最后一起做或运算
      • 2.3 ReadBig32(array, index) 大端 32 位法读数据,将二进制数组的第 1 个元素向左偏移 24 位,第 2 个元素向左偏移 16 位,第 3 个元素向左偏移 8 位,第 4 个元素不偏移,最后一起做或运算
    3. 方法
      方法分为获取状态的方法、操作数据的方法、解析的方法

      • 3.1 获取状态的方法
        • 3.1.1 _isInitialMetadataDispatched() 是否是分配的初始元数据,根据实例是否有音轨和视轨来返回相应的分发元数据


          3.1.1
      • 3.2 操作数据的方法
        • 3.2.1 destroy() 销毁实例,将若干属性设为 null


          3.2.1
        • 3.2.2 bindDataSource(loader) 绑定数据源
          • 3.2.2.1 将 loader 的数据抵达事件处理函数设为实例的 parseChunks 方法
          • 3.2.2.2 返回实例
      • 3.3 解析的方法
        • 3.3.1 parseChunks(chunk, byteStart)
          • 3.3.1.1 如果实例没有四个事件处理函数,报错
          • 3.3.1.2 如果 byteStart 为 0
            • 3.3.1.2.1 如果 chunk 的字节长度大于 13
              • 3.3.1.2.1.1 探测 chunk,将探测结果保存到 probeData 中
              • 3.3.1.2.1.2 将 probeDatadataOffset 保存在 offset 中
              • 3.3.1.2.1.3 设置 byteStart 为 probeDatadataOffset
            • 3.3.1.2.2 否则,返回 0
          • 3.3.1.3 如果实例的首次解析为 true
            • 3.3.1.3.1 设置实例的首次解析为 false
            • 3.3.1.3.2 校验 byteStart 的有效性
            • 3.3.1.3.3 创建 chunk 和 offset 的 DataView
            • 3.3.1.3.4 获取这个视图的 Uint32 数据,即 prevTagSize0,期望为 0
            • 3.3.1.3.5 略过开头这 4 个字节的 tag size(prevTagSize0)
          • 3.3.1.4 只要 chunk 还没有解析完
            • 3.3.1.4.1 设置实例的分发标志位为 true
            • 3.3.1.4.2 创建 chunk 和 offset 的 DataView
            • 3.3.1.4.3 如果数据不足以解析 flv 标签,跳出循环
            • 3.3.1.4.4 从视图中获取 tagType,通过 getUint8(0)
            • 3.3.1.4.5 从视图中获取 dataSize,通过 getUint32(0, !le) & 0x00FFFFFF
            • 3.3.1.4.6 如果数据不足以解析实际的数据体,跳出循环
            • 3.3.1.4.7 如果 tagType 不是 8、9、18,直接略过 11 + dataSize + 4 个字节,重新循环
            • 3.3.1.4.8 读取视图的第 4、5、6、7 个元素,获取 ts2、ts1、ts0、ts3
            • 3.3.1.4.9 通过上一步获取时间戳
            • 3.3.1.4.10 通过视图获取流 ID,期望为 0
            • 3.3.1.4.11 创建 dataOffset 为 offset + 11
            • 3.3.1.4.12 根据 tagType 进入相应的解析子程序:8-音频、9-视频、18-脚本


              3.3.1.4.12
            • 3.3.1.4.13 获取这个视图的 Uint32 数据,即 prevTagSize0,期望为 11 + dataSize
            • 3.3.1.4.14 将 offset 加上 11 + dataSize + 4
          • 3.3.1.5 将分析后的帧分发给消费者(通常是remuxer)
            • 3.3.1.5.1 如果实例是初始化元数据分发且实例分发属性为 true 且存在视轨或音轨,调用实例的数据可用事件处理函数,传入实例视轨和实例音轨
          • 3.3.1.6 返回 offset
        • 3.3.2 _parseScriptData(arrayBuffer, dataOffset, dataSize) 解析脚本数据
          • 3.3.2.1 通过 AMF 的 parseScriptData 获取 scriptData
          • 3.3.2.2 将 scriptData 填充进实例的属性里去
            • 3.3.2.2.1 简单地将 scriptData 赋值给实例的元数据
            • 3.3.2.2.2 将实例元数据的 onMetaData 属性单独拿出来作为数据源,填充前还要判断数据类型..
            • 3.3.2.2.3 hasAudio->实例是否有音频,hasAudio->实例媒体信息是否有音频
            • 3.3.2.2.4 hasVideo->实例是否有视频,hasAudio->实例媒体信息是否有视频
            • 3.3.2.2.5 audiodatarate->实例媒体信息音率
            • 3.3.2.2.6 videodatarate->实例媒体信息视率
            • 3.3.2.2.7 width->实例媒体信息宽
            • 3.3.2.2.8 height->实例媒体信息高
            • 3.3.2.2.9 duration->实例媒体信息持续时间和实例持续时间,要处理一下时间步长
            • 3.3.2.2.10 framerate->实例引用帧率和实例媒体信息 fps
            • 3.3.2.2.11 keyframes->实例是否有帧索引和实例帧索引
            • 3.3.2.2.12 设置实例分发属性为 false
            • 3.3.2.2.13 onMetaData->实例元数据
            • 3.3.2.2.14 如果实例媒体信息是完成状态,调用媒体信息事件处理函数,传入实例媒体信息
        • 3.3.3 _parseKeyframesIndex(keyframes) - 3.3.2 的子程序,用于解析帧索引
          • 3.3.3.1 遍历 [1,keyframestimes],忽略实际上是AVC Sequence Header(AVCDecoderConfigurationRecord)的第一个关键帧,并且对 times 取了整
          • 3.3.3.2 返回一个对象,有 times 和 filepositions 两个属性
        • 3.3.4 _parseAudioData(arrayBuffer, dataOffset, dataSize, tagTimestamp) 解析音频数据
          • 3.3.4.1 如果 dataSize <= 1,结束
          • 3.3.4.2 获取实例音频元数据,保存到 meta
          • 3.3.4.3 获取实例音轨,保存到 track
          • 3.3.4.4 如果需要初始化 meta
            • 3.3.4.4.1 设置 meta 的 type、id、timescale、duration


              3.3.4.4
            • 3.3.4.4.2 获取 arrayBuffer、dataOffset、dataSize 的视图
            • 3.3.4.4.3 从视图中获取 soundSpec(第一个 Uint8 字节)
            • 3.3.4.4.4 从 soundSpec 获得 soundFormat,预期是 10,也就是 AAC 格式
            • 3.3.4.4.5 从 soundSpec 获得 soundRateIndex
            • 3.3.4.4.6 根据 soundRateTable 和 soundRateIndex 获得 soundRate
            • 3.3.4.4.7 根据 soundSpec 获得 soundSize
            • 3.3.4.4.8 根据 soundSpec 获得 soundType
            • 3.3.4.4.9 设置 meta 的 audioSampleRate、channelCount、refSampleDuration、codec


              3.3.4.4.9
          • 3.3.4.5 调用 _parseAACAudioData 获取 aacData,传参数的时候偏移量加 1,大小 - 1
          • 3.3.4.6 如果 aacData 为空,结束
          • 3.3.4.7 如果 AAC 序列头存在(aacData.packetType 为 0)
            • 3.3.4.7.1 如果 meta 有 config 属性,提示冲突
            • 3.3.4.7.2 从 aacDatadata 获得 misc
            • 3.3.4.7.3 设置 meta 的 audioSampleRate、channelCount、codec、config、refSampleDuration


              3.3.4.7.3
            • 3.3.4.7.4 如果实例是初始化元数据分发且实例分发属性为 true 且有音轨或者视轨,调用实例的数据可用事件处理函数,传入实例视轨和实例音轨
            • 3.3.4.7.4 否则,设置实例的音频初始化元数据分发属性为 true
            • 3.3.4.7.5 通知新的元数据,设置实例的分发属性为 false
            • 3.3.4.7.6 调用轨道元数据处理函数,传入 'audio' 和 meta
            • 3.3.4.7.7 从实例媒体信息获取 mi,设置 mi 的 audioCodec、audioSampleRate、audioChannelCount
            • 3.3.4.7.8 如果 mi 有视轨且 mi 的视频编码不为空,设置 mi 的 mimeType 为 video/x-flv; codecs= 打头
            • 3.3.4.7.8 否则,设置 mi 的mimeType 为 video/x-flv; codecs= 打头
            • 3.3.4.7.9 如果 mi 已完成,调用实例的媒体信息事件处理函数
            • 3.3.4.7.10 结束
          • 3.3.4.7 如果是 AAC 原帧数据
            • 3.3.4.7.1 将 dts 设置为实例基本时间戳 + tagTimestamp
            • 3.3.4.7.2 获取 aacSample
            • 3.3.4.7.3 往 tracksamples 里推入 aacSample
            • 3.3.4.7.4 给 track 的长度加上 aacDatadata 的长度
          • 3.3.4.7 否则打印不支持的 AAC 数据信息
        • 3.3.5 _parseAACAudioData(arrayBuffer, dataOffset, dataSize) 解析 AAC 音频数据
          • 3.3.5.1 如果 dataSize 小于 1,说明 AAC 数据无效,结束
          • 3.3.5.2 创建一个 array,Uint8Array 类型,指向 arrayBuffer,起点为 dataOffset,大小为 dataSize
          • 3.3.5.3 如果 array[0] 为 0,通过调用 _parseAACAudioSpecificConfig 获取 data
          • 3.3.5.3 否则,data 为 arraysubarray(1)
          • 3.3.5.4 返回一个对象,键为 packetType(array[0])和 data
        • 3.3.6 _parseAACAudioSpecificConfig(arrayBuffer, dataOffset, dataSize) 获取 AAC 数据描述
          • 3.3.6.1 创建 array,实例化一个 Uint8Array,传入 arrayBuffer, dataOffset, dataSize
          • 3.3.6.2 创建 audioObjectType 和 originalAudioObjectType,初始值是 array[0] 右移三位
          • 3.3.6.3 创建 samplingIndex,期望是有效的 AAC 采样频率指数
          • 3.3.6.4 创建 samplingFrequence 为 mpeg 采样率
          • 3.3.6.5 创建 channelConfig,期望是有效的信道配置
          • 3.3.6.6 如果 audioObjectType 为 5,即 HE-AAC,设置 extensionSamplingIndex 和 audioExtensionObjectType
          • 3.3.6.7 如果是火狐浏览器
            • 3.3.6.7.1 如果频率小于24kHz,则使用 SBR(HE-AAC)
            • 3.3.6.7.2 否则,使用 LC-AAC
          • 3.3.6.7 如果是安卓浏览器,使用 LC-AAC
          • 3.3.6.7 否则,使用 HE-AAC
          • 3.3.6.8 装填 config
          • 3.3.6.9 返回一个描述对象


            3.3.6.9
        • 3.3.7 _parseVideoData(arrayBuffer, dataOffset, dataSize, tagTimestamp, tagPosition) 解析视频数据
          • 3.3.7.1 如果 dataSize <= 1,结束
          • 3.3.7.2 创建一个 Uint8Array 数组,指向 arrayBuffer,起点为 dataOffset,大小为 dataSize,创建 spec 为它的第一个元素
          • 3.3.7.3 创建 frameType、codecId,期望 codecId 为 7
          • 3.3.7.4 调用 _parseAVCVideoPacket,略过一位偏移量
        • 3.3.8 _parseAVCVideoPacket(arrayBuffer, dataOffset, dataSize, tagTimestamp, tagPosition, framType) 解析 AVC 视频数据包
          • 3.3.8.1 如果 dataSize <= 4,结束
          • 3.3.8.2 创建一个视图,指向 arrayBuffer,起点为 dataOffset,大小为 dataSize
          • 3.3.8.3 从视图中获取 packetType,为第一个 Uint8 数据
          • 3.3.8.4 从视图中获取 cts
          • 3.3.8.5 如果 AVCDecoderConfigurationRecord(packetType 是 0),调用 _parseAVCDecoderConfigurationRecord,传入 arrayBuffer, dataOffset + 4, dataSize - 4
          • 3.3.8.5 如果有一个或多个 Nalus(packetType 是 1),传入 arrayBuffer, dataOffset + 4, dataSize - 4, tagTimestamp, tagPosition, frameType, cts
          • 3.3.8.5 如果为空的 AVC 序列结束,什么也不做
          • 3.3.8.5 否则,调用出错事件处理函数
        • 3.3.9 _parseAVCDecoderConfigurationRecord(arrayBuffer, dataOffset, dataSize) 解析 AVC 解码器配置记录
          • 3.3.9.1 如果 dataSize <= 7,结束
          • 3.3.9.2 创建一个视图,指向 arrayBuffer,起点为 dataOffset,大小为 dataSize
          • 3.3.9.3 获取实例视频元数据,保存到 meta
          • 3.3.9.4 获取实例视轨,保存到 track
          • 3.3.9.5 如果需要初始化 meta
            • 3.3.9.5.1 设置 meta 的 type、id、timescale、duration


              3.3.9.5.1
          • 3.3.9.6 从视图中获取 version(第一个 Uint8 字节),期望为 1
          • 3.3.9.7 从视图中获取 avcProfile(第二个 Uint8 字节),期望为 0
          • 3.3.9.8 从视图中获取 profileCompatibility(第三个 Uint8 字节)
          • 3.3.9.9 从视图中获取 avcLevel(第四个 Uint8 字节)
          • 3.3.9.10 从视图中获取 nalu 长度放进实例 nalu 长度属性中,期望为 3 或者 4
          • 3.3.9.11 从视图中获取 spsCount,期望为 1
          • 3.3.9.12 创建 offset,初始化为 6
          • 3.3.9.13 循环 spsCount 次
            • 3.3.9.13.1 从视图中获取 len
            • 3.3.9.13.2 offset 加 2
            • 3.3.9.13.3 如果 len 为 0,继续循环
            • 3.3.9.13.4 创建 sps 为一个 Uint8Array 数组,指向 arrayBuffer,起点是 dataOffset + offset,长度是 len
            • 3.3.9.13.5 offset 加 len
            • 3.3.9.13.6 创建 config,值是 SPSParserparseSPS(sps) 得到的
            • 3.3.9.13.7 设置 meta 的基本视频描述信息


              3.3.9.13.7
            • 3.3.9.13.8 校正 metaframeRate


              3.3.9.13.8
            • 3.3.9.13.9 校正 metarefSampleDuration


              3.3.9.13.9
            • 3.3.9.13.10 拼接出来 metacodec


              3.3.9.13.10
            • 3.3.9.13.11 设置实例媒体信息的基本视频描述信息


              3.3.9.13.11
            • 3.3.9.13.12 拼接实例媒体信息的 mimeType 属性
            • 3.3.9.13.13 如果实例媒体信息已完成,调用媒体信息事件处理函数
          • 3.3.9.14 从视图获取 ppsCount(图片参数集合),预期是 1,从 offset 开始算
          • 3.3.9.15 offset 加 1
          • 3.3.9.16 循环 offset 次
            • 3.3.9.16.1 从视图中获取 len
            • 3.3.9.16.2 offset 加 2
            • 3.3.9.16.3 如果 len 为 0,继续循环
            • 3.3.9.16.4 offset 加 len
          • 3.3.9.17 设置 meta 的 avcc
          • 3.3.9.18 如果实例是初始化元数据分发且实例分发属性为 true 且存在视轨或音轨,调用实例的数据可用事件处理函数,传入实例视轨和实例音轨
          • 3.3.9.18 否则设置实例的视频初始化元数据分发属性为 true
          • 3.3.9.19 通知新的元数据,设置实例的分发属性为 false
          • 3.3.9.20 调用轨道元数据事件处理函数,传入 'video'meta
        • 3.3.10 _parseAVCVideoData(arrayBuffer, dataOffset, dataSize, tagTimestamp, tagPosition, frameType, cts) 解析 AVC 视频数据
          • 3.3.10.1 创建一个视图,指向 arrayBuffer,起点为 dataOffset,大小为 dataSize
          • 3.3.10.2 创建 units、length、offset
          • 3.3.10.3 获取 lengthSize、dts、keyframe


            3.3.10.3
          • 3.3.10.4 只要 offset < dataSize,循环
            • 3.3.10.4.1 如果 offset + 4 >= dataSize,结束
            • 3.3.10.4.2 获取 naluSize,并根据 lengthSize 校正
            • 3.3.10.4.3 获取 unitType,如果是 5,设置 keyframe 为 true
            • 3.3.10.4.4 获取 data,是一个 Uint8Array 的数组,指向 arrayBuffer,起点是 dataOffset + offset,长度是 lengthSize + naluSize
            • 3.3.10.4.5 创建 unit,初始化为 {type: unitType, data: data}
            • 3.3.10.4.6 往 units 推入 unit
            • 3.3.10.4.7 给 length 加上 data 的字节长度
            • 3.3.10.4.8 给 offset 加上 lengthSize + naluSize
          • 3.3.10.5 如果 units 不为空
            • 3.3.10.5.1 创建 track,初始化为实例视轨
            • 3.3.10.5.2 创建 avcSample,填充 avc 的采样数据


              3.3.10.5.2
            • 3.3.10.5.3 在 tracksamples 里推入 avcSample
            • 3.3.10.5.4 给 tracklength 加上 length

    demux-errors.js-DemuxErrors常量-解码错误

    1. OK 成功
    2. FORMAT_ERROR 格式错误
    3. FORMAT_UNSUPPORTED 不支持的格式
    4. CODEC_UNSUPPORTED 不支持的编码

    amf-parser.js-AMF类-AMF解析器

    AMF

    parseValue 是入口
    解析十二选七种二进制数据,将他们序列化格式化:

    1. Number √
    2. Boolean √
    3. String √
    4. Object √
    5. MovieClip (reserved, not supported)
    6. Null
    7. Undefined
    8. Reference
    9. ECMA array √
    10. Object end marker √
    11. Strict array √
    12. Date √
    13. Long string √

    二进制->对象描述信息

    utf8-conv-decodeUTF8函数-UTF8转Unicode器

    移植自c++的一个库:https://github.com/m13253/libWinTF8
    可以将一个 TypeArray 二进制数组的内容转成 Unicode 码

    sps-parser.js-SPSParser类-SPS解码器

    SPSParser
    获取视频的宽、高、帧等信息,俗称序列参数集
    https://zhuanlan.zhihu.com/p/27896239

    exp-golomb.js-ExpGolomb类-哥伦布指数解码器

    ExpGolomb

    http://www.cnblogs.com/DwyaneTalk/p/4035206.html

    相关文章

      网友评论

        本文标题:flv.js 1.0 源码学习(三)

        本文链接:https://www.haomeiwen.com/subject/btmzhftx.html