软编码Flv 到Mp4 容器(三) flv metadata tag解析
来源:互联网 发布:centos grub引导修复 编辑:程序博客网 时间:2024/06/02 00:13
https://github.com/332065255/flv2fmp4
代码库
软编码Flv 到Mp4 容器(一)
软编码Flv 到Mp4 容器(二) flv tag拆解
软编码Flv 到Mp4 容器(三) flv metadata tag解析
软编码Flv 到Mp4 容器(四) fmp4 总览和基础讲解
软编码Flv 到Mp4 容器(五) fmp4 ftyp box 和moov>mvhd box详解
软编码Flv 到Mp4 容器(六) fmp4 moov>trak>tkhd box 和 moov>trak>mdia>mdhd box讲解
软编码Flv 到Mp4 容器(七) fmp4 mdia>hdlr box 和 mdia>minf> smhd 和dinf box讲解
软编码Flv 到Mp4 容器(八) fmp4 mdia>stbl>stsd box 讲解
软编码Flv 到Mp4 容器(九) fmp4 stts stsc stsz stco box 讲解
软编码Flv 到Mp4 容器(十) fmp4 mvex box 讲解
软编码Flv 到Mp4 容器(十一) fmp4 moof box详解
软编码Flv 到Mp4 容器(十二) fmp4 mdat box详解
软编码Flv 到Mp4 容器(十三) fmp4 生成ftyp和moov所必要的 flv数据
metadata的内容相对来说比较复杂
http://blog.csdn.net/zengraoli/article/details/7742278 这篇博客中讲了一部分
metadata的内容大致如下
- Name:onMetaData
- Value:Array数组
- key:键
- value:普通类型值
- key:键
- value:普通类型值
- key:键
- value:数组类型值
- key:键
- value:普通类型值
一般情况,只有两层数组
0 = Number
1 = Boolean
2 = String
3 = Object
4 = MovieClip (reserved, not supported)
5 = Null
6 = Undefined
7 = Reference
8 = ECMA array
9 = Object end marker
10 = Strict array
11 = Date
12 = Long string
metadata body的第一个字节,通常是0x02,所以是string类型,
第2-3个字节为长度,通常为10,后面读10个字节,转换charcode,即为onmetadata,
继续下一个字节是0x08,是数组类型,略过4个字节,这是数组长度,
下一个字节理论上也是0x02,读2个字节的字符长度,再读字符长度的uint8,就是字段名
后面的字节就是value值得类型,依次这么往下读
详细代码在flvdemux.js
代码借鉴了flv.js的代码
let le = (function() { let buf = new ArrayBuffer(2); (new DataView(buf)).setInt16(0, 256, true); // little-endian write return (new Int16Array(buf))[0] === 256; // platform-spec read, if equal then LE})();export default class flvDemux { constructor() { } static parseObject(arrayBuffer, dataOffset, dataSize) { let name = flvDemux.parseString(arrayBuffer, dataOffset, dataSize); let value = flvDemux.parseScript(arrayBuffer, dataOffset + name.size); let isObjectEnd = value.objectEnd; return { data: { name: name.data, value: value.data }, size: value.size, objectEnd: isObjectEnd }; } static parseVariable(arrayBuffer, dataOffset, dataSize) { return flvDemux.parseObject(arrayBuffer, dataOffset, dataSize); } static parseLongString(arrayBuffer, dataOffset, dataSize) { if (dataSize < 4) { throw new IllegalStateException('Data not enough when parse LongString'); } let v = new DataView(arrayBuffer, dataOffset); let length = v.getUint32(0, !le); let str; if (length > 0) { str = decodeUTF8(new Uint8Array(arrayBuffer, dataOffset + 4, length)); } else { str = ''; } return { data: str, size: 4 + length }; } static parseDate(arrayBuffer, dataOffset, dataSize) { if (dataSize < 10) { throw new IllegalStateException('Data size invalid when parse Date'); } let v = new DataView(arrayBuffer, dataOffset); let timestamp = v.getFloat64(0, !le); let localTimeOffset = v.getInt16(8, !le); timestamp += localTimeOffset * 60 * 1000; // get UTC time return { data: new Date(timestamp), size: 8 + 2 }; } static parseString(arrayBuffer, dataOffset, dataSize) { let v = new DataView(arrayBuffer, dataOffset); let length = v.getUint16(0, !le); let str; if (length > 0) { str = decodeUTF8(new Uint8Array(arrayBuffer, dataOffset + 2, length)); } else { str = ''; } return { data: str, size: 2 + length }; } /** * 解析metadata */ static parseMetadata(arr) { let name = flvDemux.parseScript(arr, 0); let value = flvDemux.parseScript(arr, name.size, arr.length - name.size); console.log(value); } static parseScript(arr, offset, dataSize) { let dataOffset = offset; let object = {}; let uint8 = new Uint8Array(arr); let buffer = uint8.buffer; let dv = new DataView(buffer, 0, dataSize); let value = null; let type = (dv.getUint8(dataOffset)); dataOffset += 1; switch (type) { case 0: // Number(Double) type value = dv.getFloat64(dataOffset, !le); dataOffset += 8; break; case 1: { // Boolean type let b = dv.getUint8(dataOffset); value = b ? true : false; dataOffset += 1; break; } case 2: { // String type // dataOffset += 1; let amfstr = flvDemux.parseString(buffer, dataOffset); value = amfstr.data; dataOffset += amfstr.size; break; } case 3: { // Object(s) type value = {}; let terminal = 0; // workaround for malformed Objects which has missing ScriptDataObjectEnd if ((dv.getUint32(dataSize - 4, !le) & 0x00FFFFFF) === 9) { terminal = 3; } while (offset < dataSize - 4) { // 4 === type(UI8) + ScriptDataObjectEnd(UI24) let amfobj = flvDemux.parseObject(buffer, dataOffset, dataSize - offset - terminal); if (amfobj.objectEnd) break; value[amfobj.data.name] = amfobj.data.value; dataOffset += amfobj.size; } if (offset <= dataSize - 3) { let marker = v.getUint32(dataOffset - 1, !le) & 0x00FFFFFF; if (marker === 9) { dataOffset += 3; } } break; } case 8: { // ECMA array type (Mixed array) value = {}; // dataOffset += 1; dataOffset += 4; // ECMAArrayLength(UI32) let terminal = 0; // workaround for malformed MixedArrays which has missing ScriptDataObjectEnd if ((dv.getUint32(dataSize - 4, !le) & 0x00FFFFFF) === 9) { terminal = 3; } while (dataOffset < dataSize - 8) { // 8 === type(UI8) + ECMAArrayLength(UI32) + ScriptDataVariableEnd(UI24) let amfvar = flvDemux.parseVariable(buffer, dataOffset); if (amfvar.objectEnd) break; value[amfvar.data.name] = amfvar.data.value; dataOffset = amfvar.size; } if (dataOffset <= dataSize - 3) { let marker = dv.getUint32(dataOffset - 1, !le) & 0x00FFFFFF; if (marker === 9) { dataOffset += 3; } } break; } case 9: // ScriptDataObjectEnd value = undefined; dataOffset = 1; objectEnd = true; break; case 10: { // Strict array type // ScriptDataValue[n]. NOTE: according to video_file_format_spec_v10_1.pdf value = []; let strictArrayLength = dv.getUint32(dataOffset, !le); dataOffset += 4; for (let i = 0; i < strictArrayLength; i++) { let val = flvDemux.parseScript(buffer, dataOffset); value.push(val.data); dataOffset = val.size; } break; } case 11: { // Date type let date = flvDemux.parseDate(buffer, dataOffset + 1, dataSize - 1); value = date.data; dataOffset += date.size; break; } case 12: { // Long string type let amfLongStr = flvDemux.parseString(buffer, dataOffset + 1, dataSize - 1); value = amfLongStr.data; dataOffset += amfLongStr.size; break; } default: // ignore and skip dataOffset = dataSize; console.log('AMF', 'Unsupported AMF value type ' + type); } return { data: value, size: dataOffset, }; }}
- 软编码Flv 到Mp4 容器(三) flv metadata tag解析
- 软编码Flv 到Mp4 容器(二) flv tag拆解
- 软编码Flv 到Mp4 容器(一)
- 软编码Flv 到Mp4 容器(外传一)avcc box
- FLV科普12 FLV脚本数据解析-Metadata Tag解析
- 软编码Flv 到Mp4 容器(十三) fmp4 生成ftyp和moov所必要的 flv数据
- 软编码Flv 到Mp4 容器(四) fmp4 总览和基础讲解
- 软编码Flv 到Mp4 容器(五) fmp4 ftyp box 和moov>mvhd box详解
- 软编码Flv 到Mp4 容器(八) fmp4 mdia>stbl>stsd box 讲解
- 软编码Flv 到Mp4 容器(九) fmp4 stts stsc stsz stco box 讲解
- 软编码Flv 到Mp4 容器(十) fmp4 mvex box 讲解
- 软编码Flv 到Mp4 容器(十一) fmp4 moof box详解
- 软编码Flv 到Mp4 容器(十二) fmp4 mdat box详解
- 软编码Flv 到Mp4 容器(六) fmp4 moov>trak>tkhd box 和 moov>trak>mdia>mdhd box讲解
- 软编码Flv 到Mp4 容器(七) fmp4 mdia>hdlr box 和 mdia>minf> smhd 和dinf box讲解
- FLV科普6 FLV Tag以及Tag头信息解析
- ffmpeg转换mp4到flv的命令
- rtmp flv metadata
- C++后台开发之makefile撰写
- redis结合springMVC配置和使用一
- TCP的三次握手和四次挥手
- AfxEnableControlCon
- springMVC 知识点整理PART 1(基本概念)
- 软编码Flv 到Mp4 容器(三) flv metadata tag解析
- 守形数(解题报告)
- java调用摄像头拍照,使用webcam-capture替换jmf调用摄像头拍照
- Hibernate中枚举Enum类型的映射策略
- windows下面安装Python和pip终极教程
- 洛谷OJ
- 第10章:Shell基础
- django json 接口获取返回
- SQLAlchemy 几种查询方式总结