01 flv 的 binary 解析

news2024/9/24 23:27:50

想要看一下 这个 flv 的格式主要因素为 rtsp视频服务 转换为 rtmp服务 转换为前端可用的服务 , 然后 里面有 flv.js 的代码, 因为之前出现了一些问题 flvjs 播放 ws 服务代理的不存在的 rtsp 连接, Cannot read properties of null (reading ‘flushStashedSamples‘)

然后看了一下 flvjs 的代码, 并大致了解了一下 flv 的格式 以及 相关约束 

然后 后面有点时间, 可以 解析一下 flv 的文件, 以及 websocket 交互的 flv 数据流, 来用一下 

flv 格式的官方文档 http://www.adobe.com/devnet/flv/

参考文章 FLV格式详解_狗蛋儿l的博客-CSDN博客_flv格式

flv 解析

主要是基于 HXCodec 来做基础的 resolve 

这里编辑一下抽象的脉络 

FlvFile = FlvHeader + FlvBody 
FlvBody = prevTagSize0 + FlvTag1 + prevTagSize1 + FlvTag2 + prevTagSize2 + FlvTag3 + prevTagSize3 + ... +  FlvTagN + prevTagSizeN
FlvTag = tagType + dataSize + timeStamp + timeStampExt + streamId + FlvTagData
FlvTagData = FlvTagScriptData/FlvTagAudioData/FlvTagVideoData
FlvTagScriptData = amfData1 + amfData2 
FlvTagAudioData = metadata + audioData
FlvTagVideoData = metadata + videoData

相关实体

大多数的实体 描述了各个字段的编码解码情况, 一部分特殊的实体使用 特殊的 Codec 单独编码解码处理 

FlvFile 

/**
 * FlvFile
 *
 * @author Jerry.X.He <970655147@qq.com>
 * @version 1.0
 * @date 2022-09-12 20:36
 */
@Data
public class FlvFile implements Serializable {

    private FlvHeader header;

    private FlvBody body;


    public FlvFile() {
    }

    @Field(sort = 0, name = "header", dataType = DataType.GENERIC_BEAN, desc = "header", version = {1})
    public FlvHeader getHeader() {
        return header;
    }

    @Field(sort = 10, name = "body", dataType = DataType.GENERIC_BEAN, desc = "body", version = {1})
    public FlvBody getBody() {
        return body;
    }

}

FlvHeader

@Data
public class FlvHeader implements Serializable {

    private String magic;

    private Integer version;

    private Integer typeFlag;

    private Integer dataOffset;

    @Field(sort = 0, name = "magic", dataType = DataType.CHARSET_ENCODING_WITH_FIXED_LEN_STRING, lengthInBytes = 3, desc = "magic", version = {1})
    public String getMagic() {
        return magic;
    }

    @Field(sort = 10, name = "version", dataType = DataType.BYTE, desc = "version", version = {1})
    public Integer getVersion() {
        return version;
    }

    @Field(sort = 20, name = "typeFlag", dataType = DataType.BYTE, desc = "typeFlag", version = {1})
    public Integer getTypeFlag() {
        return typeFlag;
    }

    @Field(sort = 30, name = "dataOffset", dataType = DataType.DWORD, desc = "dataOffset", version = {1})
    public Integer getDataOffset() {
        return dataOffset;
    }

}

FlvBody

/**
 * FlvHeader
 *
 * @author Jerry.X.He <970655147@qq.com>
 * @version 1.0
 * @date 2022-09-12 20:37
 */
@Data
public class FlvBody implements Serializable {

    private Integer prevTagSize0;

    private List<FlvTagAndSize> tagAndSizeList;

    @Field(sort = 0, name = "prevTagSize0", dataType = DataType.DWORD, desc = "prevTagSize0", version = {1})
    public Integer getPrevTagSize0() {
        return prevTagSize0;
    }

    @Field(sort = 10, name = "tagAndSizeList", dataType = DataType.GENERIC_BEAN_COLLECTION, desc = "tagAndSizeList", version = {1})
    public List<FlvTagAndSize> getTagAndSizeList() {
        return tagAndSizeList;
    }

}

FlvTagAndSize

/**
 * FlvHeader
 *
 * @author Jerry.X.He <970655147@qq.com>
 * @version 1.0
 * @date 2022-09-12 20:37
 */
@Data
public class FlvTagAndSize implements Serializable {

    private FlvTag tag;

    private Integer prevTagSize;

    @Field(sort = 0, name = "tag", dataType = DataType.GENERIC_BEAN, desc = "tag", codecFactoryClazz = FlvTagCodecFactory.class, version = {1})
    public FlvTag getTag() {
        return tag;
    }

    @Field(sort = 10, name = "prevTagSize", dataType = DataType.DWORD, desc = "prevTagSize", version = {1})
    public Integer getPrevTagSize() {
        return prevTagSize;
    }

}

FlvTag 

/**
 * FlvHeader
 *
 * @author Jerry.X.He <970655147@qq.com>
 * @version 1.0
 * @date 2022-09-12 20:37
 */
@Data
public class FlvTag implements Serializable {

    private Integer tagType;

    private Integer[] dataSize;

    private Integer[] timeStamp;

    private Integer timeStampExt;

    private Integer[] streamId;

    private FlvTagData tagData;

    private long offsetInStream;


    @Field(sort = 0, name = "tagType", dataType = DataType.BYTE, desc = "tagType", version = {1})
    public Integer getTagType() {
        return tagType;
    }

    @Field(sort = 10, name = "dataSize", dataType = DataType.BYTE_ARRAY_WITH_EXACTLY_LEN, eleLength = 3, desc = "dataSize", version = {1})
    public Integer[] getDataSize() {
        return dataSize;
    }

    @Field(sort = 20, name = "timeStamp", dataType = DataType.BYTE_ARRAY_WITH_EXACTLY_LEN, eleLength = 3, desc = "timeStamp", version = {1})
    public Integer[] getTimeStamp() {
        return timeStamp;
    }

    @Field(sort = 30, name = "timeStampExt", dataType = DataType.BYTE, desc = "timeStampExt", version = {1})
    public Integer getTimeStampExt() {
        return timeStampExt;
    }

    @Field(sort = 40, name = "streamId", dataType = DataType.BYTE_ARRAY_WITH_EXACTLY_LEN, eleLength = 3, desc = "streamId", version = {1})
    public Integer[] getStreamId() {
        return streamId;
    }

    public FlvTagData getTagData() {
        return tagData;
    }

    public Integer getDataSizeInBytes() {
        return (usnignedSz(dataSize[0]) << 16) | (usnignedSz(dataSize[1]) << 8) | (usnignedSz(dataSize[2]));
    }

    public static Integer usnignedSz(int result) {
        if (result < 0) {
            result += (1 << 8);
        }
        return result;
    }

}

FlvTagScriptData

/**
 * FlvTagData
 *
 * @author Jerry.X.He <970655147@qq.com>
 * @version 1.0
 * @date 2022-09-13 15:45
 */
@Data
public class FlvTagScriptData extends FlvTagData {

    private FlvTagScriptDataAMF amfData1;

    private FlvTagScriptDataAMF amfData2;

    @Field(sort = 0, name = "amfData1", dataType = DataType.GENERIC_BEAN, desc = "amfData1", codecFactoryClazz = FlvTagScriptDataAMFCodecFactory.class, version = {1})
    public FlvTagScriptDataAMF getAmfData1() {
        return amfData1;
    }

    @Field(sort = 10, name = "amfData2", dataType = DataType.GENERIC_BEAN, desc = "amfData2", codecFactoryClazz = FlvTagScriptDataAMFCodecFactory.class, version = {1})
    public FlvTagScriptDataAMF getAmfData2() {
        return amfData2;
    }

}

FlvTagScriptDataAMFData1

/**
 * FlvTagData
 *
 * @author Jerry.X.He <970655147@qq.com>
 * @version 1.0
 * @date 2022-09-13 15:45
 */
@Data
public class FlvTagScriptDataAMFData1 extends FlvTagScriptDataAMFData {

    private String data;

    @Field(sort = 0, name = "data", dataType = DataType.CHARSET_ENCODING_WITH_LEN_STRING, lengthByteType = ByteType.WORD, desc = "data", version = {1})
    public String getData() {
        return data;
    }
}

FlvTagScriptDataAMFData2

/**
 * FlvTagData
 *
 * @author Jerry.X.He <970655147@qq.com>
 * @version 1.0
 * @date 2022-09-13 15:45
 */
@Data
public class FlvTagScriptDataAMFData2 extends FlvTagScriptDataAMFData {

    private List<FlvTagScriptDataAMFDataAttr> data;

    @Field(sort = 0, name = "data", dataType = DataType.GENERIC_BEAN_COLLECTION_WITH_LEN, lengthByteType = ByteType.DWORD, desc = "data", codecFactoryClazz = FlvTagScriptDataAMFData2Data2CodecFactory.class, version = {1})
    public List<FlvTagScriptDataAMFDataAttr> getData() {
        return data;
    }

}

FlvTagScriptDataAMFDataAttr

/**
 * FlvTagData
 *
 * @author Jerry.X.He <970655147@qq.com>
 * @version 1.0
 * @date 2022-09-13 15:45
 */
@Data
public class FlvTagScriptDataAMFDataAttr implements Serializable {

    private String key;

    private Object value;

    @Field(sort = 0, name = "key", dataType = DataType.CHARSET_ENCODING_WITH_LEN_STRING, lengthByteType = ByteType.WORD, desc = "key", version = {1})
    public String getKey() {
        return key;
    }

    @Field(sort = 10, name = "value", dataType = DataType.CHARSET_ENCODING_WITH_LEN_STRING, lengthByteType = ByteType.WORD, desc = "value", version = {1})
    public Object getValue() {
        return value;
    }
}

FlvTagAudioData

/**
 * FlvTagData
 *
 * @author Jerry.X.He <970655147@qq.com>
 * @version 1.0
 * @date 2022-09-13 15:45
 */
@Data
public class FlvTagAudioData extends FlvTagData {

    private Integer metadata;

    private Integer[] data;

    @Field(sort = 0, name = "metadata", dataType = DataType.UNSIGNED_BYTE, desc = "metadata", version = {1})
    public Integer getMetadata() {
        return metadata;
    }

    @Field(sort = 10, name = "data", dataType = DataType.BYTE_ARRAY, desc = "data", version = {1})
    public Integer[] getData() {
        return data;
    }

    // resolved by metadata
    public Integer getSoundFormat() {
        return (metadata >> 4) & 0xf;
    }

    public Integer getSoundRate() {
        return (metadata >> 2) & 0x3;
    }

    public Integer getSoundSize() {
        return (metadata >> 1) & 0x1;
    }

    public Integer getSoundType() {
        return (metadata) & 0x1;
    }

}

FlvTagVideoData

/**
 * FlvTagData
 *
 * @author Jerry.X.He <970655147@qq.com>
 * @version 1.0
 * @date 2022-09-13 15:45
 */
@Data
public class FlvTagVideoData extends FlvTagData {

    private Integer metadata;

    private Integer[] data;

    @Field(sort = 0, name = "metadata", dataType = DataType.UNSIGNED_BYTE, desc = "metadata", version = {1})
    public Integer getMetadata() {
        return metadata;
    }

    @Field(sort = 10, name = "data", dataType = DataType.BYTE_ARRAY, desc = "data", version = {1})
    public Integer[] getData() {
        return data;
    }

    // resolved by metadata
    public Integer getVideoFrameType() {
        return (metadata >> 4) & 0xf;
    }

    public Integer getVideoCodecId() {
        return metadata & 0xf;
    }

}

特殊的Codec

FlvTagCodec

/**
 * FlvTagCodec
 *
 * @author Jerry.X.He <970655147@qq.com>
 * @version 1.0
 * @date 2022-09-13 15:43
 */
public class FlvTagCodec extends AbstractCodec<FlvTag, FlvTag> {

    private AbstractCodec<Integer, Integer> byteCodec = new ByteCodec();
    private AbstractCodec<Integer[], Integer[]> byteArray3Codec = new ByteArrayWithExactlyLenCodec(3);
    private AbstractCodec<FlvTagScriptData, FlvTagScriptData> scriptCodec = CodecUtils.createCodecForClazz(FlvTagScriptData.class, 1);
    // 3 codec

    @Override
    public void encode(FlvTag entity, ByteBuf buf) {
        byteCodec.encode(entity.getTagType(), buf);
        byteArray3Codec.encode(entity.getDataSize(), buf);
        byteArray3Codec.encode(entity.getTimeStamp(), buf);
        byteCodec.encode(entity.getTimeStampExt(), buf);
        byteArray3Codec.encode(entity.getStreamId(), buf);

        int tagType = entity.getTagType();
        Integer dataSizeInBytes = entity.getDataSizeInBytes();
        if (tagType == 0x12) {
            scriptCodec.encode((FlvTagScriptData) entity.getTagData(), buf);
        } else if (tagType == 0x08) {
            FlvTagAudioDataCodec codec = new FlvTagAudioDataCodec(dataSizeInBytes - 1);
            codec.encode((FlvTagAudioData) entity.getTagData(), buf);
        } else if (tagType == 0x09) {
            FlvTagVideoDataCodec codec = new FlvTagVideoDataCodec(dataSizeInBytes - 1);
            codec.encode((FlvTagVideoData) entity.getTagData(), buf);
        }
    }

    @Override
    public FlvTag decode(ByteBuf buf) {
        long offsetInStream = buf.readerIndex();
        Integer tagType = byteCodec.decode(buf);
        Integer[] dataSize = byteArray3Codec.decode(buf);
        Integer[] timestamp = byteArray3Codec.decode(buf);
        Integer timestampExt = byteCodec.decode(buf);
        Integer[] streamId = byteArray3Codec.decode(buf);

        FlvTag result = new FlvTag();
        result.setOffsetInStream(offsetInStream);
        result.setTagType(tagType);
        result.setDataSize(dataSize);
        result.setTimeStamp(timestamp);
        result.setTimeStampExt(timestampExt);
        result.setStreamId(streamId);

        FlvTagData tagData = null;
        Integer dataSizeInBytes = result.getDataSizeInBytes();
        if (tagType == 0x12) {
            // consume 00 00 09
            tagData = scriptCodec.decode(buf);
        } else if (tagType == 0x08) {
            FlvTagAudioDataCodec codec = new FlvTagAudioDataCodec(dataSizeInBytes - 1);
            tagData = codec.decode(buf);
        } else if (tagType == 0x09) {
            FlvTagVideoDataCodec codec = new FlvTagVideoDataCodec(dataSizeInBytes - 1);
            tagData = codec.decode(buf);
        }
        result.setTagData(tagData);
        return result;
    }

    @Override
    public boolean isFixedLength() {
        return false;
    }

    @Override
    public int length() {
        return 0;
    }

}

FlvTagScriptDataAMFCodec

/**
 * FlvTagScriptDataAMFScriptDataAMFCodec
 *
 * @author Jerry.X.He <970655147@qq.com>
 * @version 1.0
 * @date 2022-09-13 16:01
 */
public class FlvTagScriptDataAMFCodec extends AbstractCodec<FlvTagScriptDataAMF, FlvTagScriptDataAMF> {

    private AbstractCodec<Integer, Integer> byteCodec = new ByteCodec();
    private AbstractCodec<FlvTagScriptDataAMFData1, FlvTagScriptDataAMFData1> data1Codec = CodecUtils.createCodecForClazz(FlvTagScriptDataAMFData1.class, 1);
    private AbstractCodec<FlvTagScriptDataAMFData2, FlvTagScriptDataAMFData2> data2Codec = CodecUtils.createCodecForClazz(FlvTagScriptDataAMFData2.class, 1);

    @Override
    public void encode(FlvTagScriptDataAMF entity, ByteBuf buf) {
        byteCodec.encode(entity.getAmfType(), buf);
        FlvTagScriptDataAMFData data = entity.getData();

        if (data instanceof FlvTagScriptDataAMFData1) {
            data1Codec.encode((FlvTagScriptDataAMFData1) data, buf);
        } else if (data instanceof FlvTagScriptDataAMFData2) {
            data2Codec.encode((FlvTagScriptDataAMFData2) data, buf);
        }
    }

    @Override
    public FlvTagScriptDataAMF decode(ByteBuf buf) {
        Integer amfType = byteCodec.decode(buf);

        FlvTagScriptDataAMFData data = null;
        if (amfType == 0x02) {
            data = data1Codec.decode(buf);
        } else if (amfType == 0x08) {
            data = data2Codec.decode(buf);

            Integer last1 = byteCodec.decode(buf);
            Integer last2 = byteCodec.decode(buf);
            Integer last3 = byteCodec.decode(buf);
            AssertUtils.assert0(last1 == 0, " ex ");
            AssertUtils.assert0(last2 == 0, " ex ");
            AssertUtils.assert0(last3 == 9, " ex ");
        }

        FlvTagScriptDataAMF result = new FlvTagScriptDataAMF();
        result.setAmfType(amfType);
        result.setData(data);
        return result;
    }

    @Override
    public boolean isFixedLength() {
        return false;
    }

    @Override
    public int length() {
        return 0;
    }
}

FlvTagScriptDataAMFData2Codec

/**
 * FlvTagScriptDataAMFDataAttrCodec
 *
 * @author Jerry.X.He <970655147@qq.com>
 * @version 1.0
 * @date 2022-09-13 17:28
 */
public class FlvTagScriptDataAMFData2Codec extends AbstractCodec<List<FlvTagScriptDataAMFDataAttr>, List<FlvTagScriptDataAMFDataAttr>> {

    private DWordCodec dWordCodec = new DWordCodec();
    private FlvTagScriptDataAMFDataAttrCodec keyCodec = new FlvTagScriptDataAMFDataAttrCodec();

    @Override
    public void encode(List<FlvTagScriptDataAMFDataAttr> entity, ByteBuf buf) {
        dWordCodec.encode(entity.size(), buf);
        for (FlvTagScriptDataAMFDataAttr attr : entity) {
            keyCodec.encode(attr, buf);
        }
    }

    @Override
    public List<FlvTagScriptDataAMFDataAttr> decode(ByteBuf buf) {
        Integer length = dWordCodec.decode(buf);
        List<FlvTagScriptDataAMFDataAttr> list = new ArrayList<>();
        for (int i = 0; i < length; i++) {
            FlvTagScriptDataAMFDataAttr attr = keyCodec.decode(buf);
            list.add(attr);
        }

        return list;
    }

    @Override
    public boolean isFixedLength() {
        return false;
    }

    @Override
    public int length() {
        return 0;
    }

}

FlvTagScriptDataAMFDataAttrCodec

/**
 * FlvTagScriptDataAMFDataAttrCodec
 *
 * @author Jerry.X.He <970655147@qq.com>
 * @version 1.0
 * @date 2022-09-13 17:28
 */
public class FlvTagScriptDataAMFDataAttrCodec extends AbstractCodec<FlvTagScriptDataAMFDataAttr, FlvTagScriptDataAMFDataAttr> {

    private CharsetEncodingStringWithLenCodec keyCodec = new CharsetEncodingStringWithLenCodec(ByteType.WORD);
    private ByteCodec byteCodec = new ByteCodec();
    private WordCodec wordCodec = new WordCodec();
    private DWordCodec dwordCodec = new DWordCodec();
    private QWordCodec qwordCodec = new QWordCodec();
    private ByteArrayWithExactlyLenCodec eightByteCodec = new ByteArrayWithExactlyLenCodec(8);

    @Override
    public void encode(FlvTagScriptDataAMFDataAttr entity, ByteBuf buf) {
        keyCodec.encode(entity.getKey(), buf);

        Object value = entity.getValue();
        if (value instanceof Number) {
            byteCodec.encode(0, buf);
            qwordCodec.encode((Long) value, buf);
        } else if (value instanceof Boolean) {
            byteCodec.encode(1, buf);
            byteCodec.encode((Boolean) value ? 1 : 0, buf);
        } else if (value instanceof String) {
            byteCodec.encode(2, buf);
            keyCodec.encode((String) value, buf);
        } else if (value instanceof JSONObject) {
            byteCodec.encode(3, buf);
            // impl
            // ECMA script Array
        } else if (value instanceof JSONArray) {
            byteCodec.encode(8, buf);
            // impl
            // ScriptDataObjectEnd
        } else if (value instanceof JSONArray) {
            byteCodec.encode(9, buf);
            // impl
            // Strict array type
        } else if (value instanceof JSONArray) {
            byteCodec.encode(10, buf);
            // impl
            // Date type
        } else if (value instanceof JSONArray) {
            byteCodec.encode(11, buf);
            // impl
            // Long string type
        } else if (value instanceof JSONArray) {
            byteCodec.encode(12, buf);
            // impl
        }
    }

    @Override
    public FlvTagScriptDataAMFDataAttr decode(ByteBuf buf) {
        String key = keyCodec.decode(buf);

        Integer type = byteCodec.decode(buf);
//        System.out.println(key + " -> " + type + " -> " + buf.readerIndex());
        Object value = null;
        if (type == 0) {
            value = eightByteCodec.decode(buf);
        } else if (type == 1) {
            Integer tmp = byteCodec.decode(buf);
            value = (tmp == 1);
        } else if (type == 2) {
            value = keyCodec.decode(buf);
        } else if (type == 3) {
            // impl
            // ECMA script Array
        } else if (type == 8) {
            // impl
            // ScriptDataObjectEnd
        } else if (type == 9) {
            // impl
            // Strict array type
        } else if (type == 10) {
            // impl
            // Date type
        } else if (type == 11) {
            // impl
            // Long string type
        } else if (type == 12) {
            // impl
        }

        FlvTagScriptDataAMFDataAttr result = new FlvTagScriptDataAMFDataAttr();
        result.setKey(key);
        result.setValue(value);
        return result;
    }

    @Override
    public boolean isFixedLength() {
        return false;
    }

    @Override
    public int length() {
        return 0;
    }

}

FlvTagAudioDataCodec

/**
 * FlvTagAudioDataCodec
 *
 * @author Jerry.X.He <970655147@qq.com>
 * @version 1.0
 * @date 2022-09-13 15:43
 */
public class FlvTagAudioDataCodec extends AbstractCodec<FlvTagAudioData, FlvTagAudioData> {

    private int lengthInBytes;

    private AbstractCodec<Integer, Integer> byteCodec = new UnsignedByteCodec();


    public FlvTagAudioDataCodec(int lengthInBytes) {
        this.lengthInBytes = lengthInBytes;
    }

    @Override
    public void encode(FlvTagAudioData entity, ByteBuf buf) {
        ByteArrayWithExactlyLenCodec dataCodec = new ByteArrayWithExactlyLenCodec(lengthInBytes);
        byteCodec.encode(entity.getMetadata(), buf);
        dataCodec.encode(entity.getData(), buf);
    }

    @Override
    public FlvTagAudioData decode(ByteBuf buf) {
        ByteArrayWithExactlyLenCodec dataCodec = new ByteArrayWithExactlyLenCodec(lengthInBytes);
        Integer metadata = byteCodec.decode(buf);
        Integer[] data = dataCodec.decode(buf);

        FlvTagAudioData result = new FlvTagAudioData();
        result.setMetadata(metadata);
        result.setData(data);
        return result;
    }

    @Override
    public boolean isFixedLength() {
        return false;
    }

    @Override
    public int length() {
        return 0;
    }

}

FlvTagVideoDataCodec

/**
 * FlvTagVideoDataCodec
 *
 * @author Jerry.X.He <970655147@qq.com>
 * @version 1.0
 * @date 2022-09-13 15:43
 */
public class FlvTagVideoDataCodec extends AbstractCodec<FlvTagVideoData, FlvTagVideoData> {

    private int lengthInBytes;

    private AbstractCodec<Integer, Integer> byteCodec = new UnsignedByteCodec();


    public FlvTagVideoDataCodec(int lengthInBytes) {
        this.lengthInBytes = lengthInBytes;
    }

    @Override
    public void encode(FlvTagVideoData entity, ByteBuf buf) {
        ByteArrayWithExactlyLenCodec dataCodec = new ByteArrayWithExactlyLenCodec(lengthInBytes);
        byteCodec.encode(entity.getMetadata(), buf);
        dataCodec.encode(entity.getData(), buf);
    }

    @Override
    public FlvTagVideoData decode(ByteBuf buf) {
        ByteArrayWithExactlyLenCodec dataCodec = new ByteArrayWithExactlyLenCodec(lengthInBytes);
        Integer metadata = byteCodec.decode(buf);
        Integer[] data = dataCodec.decode(buf);

        FlvTagVideoData result = new FlvTagVideoData();
        result.setMetadata(metadata);
        result.setData(data);
        return result;
    }

    @Override
    public boolean isFixedLength() {
        return false;
    }

    @Override
    public int length() {
        return 0;
    }

}

解析结果展示

解析 flv 文件 

/**
 * Test17ResolveFlv
 *
 * @author Jerry.X.He <970655147@qq.com>
 * @version 1.0
 * @date 2022-09-12 16:52
 */
public class Test17ResolveFlv {

    // Test17ResolveFlv
    public static void main(String[] args) throws Exception {

        String path = "/Users/jerry/Jobs/12_flvResolve/dump.flv";
        byte[] bytes = IOUtils.toByteArray(new FileInputStream(path));

        AbstractCodec<FlvFile, FlvFile> flvCodec = CodecUtils.createCodecForClazz(FlvFile.class, 1);
        ByteBuf buf = Unpooled.wrappedBuffer(bytes);
        FlvFile flvFile = flvCodec.decode(buf);

        for (FlvTagAndSize tagAndSize : flvFile.getBody().getTagAndSizeList()) {
            AssertUtils.assert0(tagAndSize.getPrevTagSize() == tagAndSize.getTag().getDataSizeInBytes() + 11, " ex data size ");
        }

        int x = 0;

    }

}

解析效果如下 

解析 websocket 传输的 flv 视频数据 

这里从 websocket 请求吧传输的数据拿过来, 然后 这里仅仅保留了两个 videoData 的数据 

/**
 * Test17ResolveFlv
 *
 * @author Jerry.X.He <970655147@qq.com>
 * @version 1.0
 * @date 2022-09-12 16:52
 */
public class Test17ResolveWebsocketFlv {

    // Test17ResolveFlv
    public static void main(String[] args) throws Exception {

        String path = "/Users/jerry/Tmp/09_flv_from_websocket/test_rtps.flv.txt";
        List<String> lines = Tools.getContentWithList(path);
        byte[] fullBytes = websocketHex2Bytes(lines);
        byte[] bytes = retainTwoVideoTag(fullBytes);

        AbstractCodec<FlvFile, FlvFile> flvCodec = CodecUtils.createCodecForClazz(FlvFile.class, 1);
        ByteBuf buf = Unpooled.wrappedBuffer(bytes);
        FlvFile flvFile = flvCodec.decode(buf);

        for (FlvTagAndSize tagAndSize : flvFile.getBody().getTagAndSizeList()) {
            AssertUtils.assert0(tagAndSize.getPrevTagSize() == tagAndSize.getTag().getDataSizeInBytes() + 11, " ex data size ");
        }

        int x = 0;

    }

    // websocketHex2Bytes
    public static byte[] websocketHex2Bytes(List<String> lines) throws Exception {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        for (String line : lines) {
            String[] towByteSplits = line.split("\\s+");
            for (String twoByteStr : towByteSplits) {
                byte[] twoByte = HexUtils.decodeHex(twoByteStr);
                baos.write(twoByte);
            }
        }
        return baos.toByteArray();
    }

    // retainTwoVideoTag
    public static byte[] retainTwoVideoTag(byte[] bytes) {
        int secondVideoTagEnd = 46701;
        ByteArrayOutputStream baos = new ByteArrayOutputStream(secondVideoTagEnd);
        baos.write(bytes, 0, secondVideoTagEnd);
        return baos.toByteArray();
    }

}

解析效果如下 

本文更加侧重于是对于 flv 的结构理解 

并不涉及具体的 音频数据, 视频数据 的处理 

完 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/131713.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Codeforces Round #664 (Div. 2) C. Boboniu and Bit Operations

Problem - C - Codeforces 翻译&#xff1a; Boboniu喜欢位操作。他想和你玩个游戏。 Boboniu给你两个序列的非负整数&#x1d44e;1&#x1d44e;2,…,&#x1d44e;&#x1d45b;&#x1d44f;1,&#x1d44f;2,…,&#x1d44f;&#x1d45a;。 为每个&#x1d456;(1≤&…

FPGA模拟SENSOR,MIPI CSI-2发送图像到RV1126

FPGA模拟SENSOR&#xff0c;MIPI CSI-2发送图像到RV1126 1&#xff1a;FPGA模拟Sensor&#xff0c; 使用MIPI CSI-2 Transmit&#xff0c;发送图像到RV1126。 2&#xff1a;MIPI CSI-2信号LANE&#xff0c;共5对。图像分辨率1920*1080&#xff0c;格式YUV422 8Bit&#xff0c…

整理了上千个 Python 工具库,涵盖24个大方向

Python 生态&#xff0c;向来以各种类库齐全而闻名&#xff0c;这也是这门语言如此受欢迎的重要原因。 今天就给大家分享一下这几天的战果&#xff0c;宵衣旰食&#xff0c;不眠不休的整理了近千个 Python 库&#xff0c;梳理不易啊&#xff0c;收藏的同时&#xff0c;记得点赞…

数据可视化之seaborn绘图

记录一个今天画出来的数据统计图(绝美&#xff0c;当然数据是癌症相关的就不是很美了, 之前一直都用plt.plot&#xff0c;也不太会用 但是现在发现seaborn真的可以 palette sns.color_palette("ocean", 2) sns.kdeplot(datacancer_data,xRadius (worst),shadeTrue…

ext4 extent详解2之内核源码详解

在查看本文前&#xff0c;希望先查看《ext4 extent详解1之示意图演示》这篇文章&#xff0c;有助于理解本文。本文内核源码版本3.10.96&#xff0c;详细内核详细源码注释见https://github.com/dongzhiyan-stack/kernel-code-comment。 什么时候会用到ext4 extent B树呢&#xf…

SQL SERVER 2016安装部署

1&#xff1a;运行SETUP开始安装SQL SERVER 2016。 2&#xff1a;在计划中可以查看支持SQL SERVER 2016安装的软硬件条件等信息。 3&#xff1a;在左侧安装中选择全新SQL Server独立安装或向现有安装添加功能。 4&#xff1a;可以安装指定的版本&#xff0c;Evaluation这个需要…

圣诞节制作一颗HTML的圣诞树吧

目录 前言&#xff1a; 一、vscode里面运行 二、记事本txt保存运行 前言&#xff1a; 来到圣诞节了&#xff0c;那么就可以制作一颗HTML的圣诞树送给朋友&#xff0c;没有编程基础的小白也可以按照步骤操作也可以运行起来代码的 一、vscode里面运行 1.打开vscoede&#x…

Cadence每日一学_13 | 使用 Allegro 制作PCB封装(以STM32为例)

文章目录一、数据手册分析二、新建封装文件Step1. 新建Package SymbolStep2. 设置设计参数Step3. 设置焊盘路径三、放置焊盘四、绘制Place Bound Top五、绘制装配层六、绘制丝印1. 添加芯片边框丝印2. 添加1脚标识3. 添加芯片位号标识一、数据手册分析 焊盘长度&#xff1a;理论…

Github高效搜索方式

Github高效搜索方式 文章目录Github高效搜索方式0、写在前面1、常用的搜索功能1.1 直接搜索1.2 寻找指定用户|大小的仓库1.3 搜索仓库1.4 查找特定star范围的仓库1.5 查找指定主题1.6 查找仓库语言1.7 搜索issue和pull请求2、 傻瓜式搜索3、参考0、写在前面 名词解释 watch&…

【机器学习】深入理解LightGBM

目录&#xff1a;深入理解LightGBM一、LightGBM简介二、LightGBM提出的动机三、 XGBoost的缺点及LightGBM的优化3.1 XGBoost的缺点3.2 LightGBM的优化四、 LightGBM的基本原理4.1 基于Histogram的决策树算法4.1.1 直方图算法4.1.2 直方图做差加速4.2 带深度限制的 Leaf-wise 算…

把连续学习的思路用在基于图像的相机定位问题中( ICCV 2021)

点击上方“3D视觉工坊”,选择“星标” 干货第一时间送达 点击进入—>3D视觉工坊学习交流群 Continual Learning for Image-Based Camera Localization(2021 ICCV顶会文章) 代码地址:https://github.com/AaltoVision/CL_HSCNet 主要内容: 论文探讨了将连续学习用在视觉定…

Nexus迁移到阿里云效packages

点击右上角sign in登录按钮&#xff0c;登录到nexus中 登录后&#xff0c;会出现设置按钮&#xff0c;如图 点击设置按钮&#xff0c;点击左侧的system&#xff0c;点击API&#xff0c;即可打开restFul接口界面 点击打开 Components 组&#xff0c;找到 /v1/components 接…

【cmake】利用ExternalProject_Add解决第三方库target命名冲突问题

参考 cmake菜谱第八章第一节 我们经常会遇到这种情况&#xff1a; project A 是最外层项目 project B 是A使用的外部库 project C 是A和B使用的外部库 . ├── extern │ ├── B │ │ ├── extern │ │ │ └── C │ │ └── src │ └── C …

JVM本地锁(二)ReentrantLock可重入锁源码解析

什么是可重入锁呢 顾名思义&#xff0c;就是可以重复进入的锁&#xff0c;学过操作系统或者计组的可参照理解pv&#xff0c;或者多重中断。 demo1(){lock(); //第一次锁demo2(){lock(); // 第二次锁unlock(); }unlock();}文章目录ReentrantLocklock 加锁1. ReentrantLock.lock…

1990-2022年6月上市公司高管信息数据

1990-2022年6月上市公司高管信息数据、董监高信息数据 1、时间&#xff1a;1990-2022年6月 2、指标&#xff1a;证券代码、统计截止日期、人员ID、姓名、国籍、籍贯、籍贯所在地区代码、出生地、出生地所在地区代码、性别、年龄、毕业院校、学历、专业、职称、个人简历、是否…

给数组创建视图(浅拷贝)修改视图值影响原数组值修改视图形状不影响原数组形状numpy.view()

【小白从小学Python、C、Java】 【计算机等级考试500强双证书】 【Python-数据分析】 给数组创建视图&#xff08;浅拷贝&#xff09; 修改视图值影响原数组值 修改视图形状不影响原数组形状 numpy.view() 对于以下python代码表述错误的一项是? import numpy as np import nu…

代码随想录二刷day5 两数之和 四数相加 (三数之和 四数之和) ->多写几遍(解法双指针放缩)

二刷复习 文章目录二刷复习哈希表和哈希法unordered 和 ordered 的不同242.有效字母的异位词349.两个数组的交集202.快乐数两数之和四数相加2383.赎金信三数之和&#xff08;这道题需要重复做&#xff0c; 双指针&#xff09;四数之和哈希表和哈希法 哈希表&#xff1a;这是两…

BGP建邻实验

目录 1.拓扑图 2.要求 3.实验思路 4.主要配置 5.测试 6.实验总结 1.拓扑图 2.要求 每台路由器都有两个环回&#xff0c;一个24的环回&#xff0c;一个32的环回&#xff1b;32的环回用于建邻&#xff0c;24的环回用于用户网段&#xff0c;最终实现所有24的环回可以ping通即…

BeyondCorp 打造得物零信任安全架构

1. 背景 当前&#xff0c;大部分企业都使用防火墙 (firewall) 来加强网络边界安全。然而&#xff0c;这种安全模型是有缺陷&#xff0c;因为当该边界被破坏&#xff0c;攻击者可以相对容易地访问公司的特权内部网。 边界安全模型通常被比作中世纪城堡&#xff1a;城墙厚厚的堡…

Mysql 报“Finished with error”,该怎么及解决?

用了多年的Mysql,当用navicat导库时&#xff0c;偶尔会遇到“Finished with error”错误&#xff0c; 如下图&#xff1a; 下面是我结合工作经验&#xff0c;总结一下&#xff0c;将相应情况及解决方法提供给网友们&#xff1a; 情况1&#xff1a;导入的sql数据库脚本文件中日…