当前位置: 首页 > article >正文

音视频入门基础:FLV专题(11)——FFmpeg源码中,解析SCRIPTDATASTRING类型的ScriptDataValue的实现

一、引言

从《音视频入门基础:FLV专题(9)——Script Tag简介》中可以知道,根据《video_file_format_spec_v10_1.pdf》第80到81页,SCRIPTDATAVALUE类型由一个8位(1字节)的Type和一个ScriptDataValue组成。其中Type属性用来指定ScriptDataValue的类型,根据Type值的不同,ScriptDataValue的类型也不同。当Type的值为2时,ScriptDataValue为SCRIPTDATASTRING类型:

根据《video_file_format_spec_v10_1.pdf》第83页,SCRIPTDATASTRING类型用来记录存贮不超过65535个字符的字符串数据。SCRIPTDATASTRING类型由一个16位(2字节)的StringLength属性和一个存贮字符数据的StringData属性组成。其中StringLength属性用来指定要存贮的字符串的长度,单位为字节;StringData属性存贮实际的字符串数据(不超过65535个字符):

FFmpeg源码中通过amf_get_string函数解析SCRIPTDATASTRING类型的ScriptDataValue。

二、amf_get_string函数的定义

amf_get_string函数定义在FFmpeg源码(本文演示用的FFmpeg源码版本为7.0.1)的源文件libavformat/flvdec.c中:

static int amf_get_string(AVIOContext *ioc, char *buffer, int buffsize)
{
    int ret;
    int length = avio_rb16(ioc);
    if (length >= buffsize) {
        avio_skip(ioc, length);
        return -1;
    }

    ret = avio_read(ioc, buffer, length);
    if (ret < 0)
        return ret;
    if (ret < length)
        return AVERROR_INVALIDDATA;

    buffer[length] = '\0';

    return length;
}

该函数的作用就是解析SCRIPTDATASTRING类型的ScriptDataValue。

形参ioc:既是输入型参数也是输出型参数。指向一个AVIOContext(字节流上下文结构体)变量。关于AVIOContext结构体可以参考:《FFmpeg源码:avio_r8、avio_rl16、avio_rl24、avio_rl32、avio_rl64函数分析》。执行amf_get_string函数前,ioc->buf_ptr必须指向SCRIPTDATASTRING类型的ScriptDataValue的第一个字节。

形参buffer:输出型参数。执行amf_get_string函数后,形参buffer指向的缓冲区会得到从SCRIPTDATASTRING类型的ScriptDataValue中解析出来的实际的字符串数据,也就是SCRIPTDATASTRING的StringData属性中存贮的实际的字符串数据。

形参buffsize:输入型参数。形参buffer指向的缓冲区的大小,单位为字节。该值一般为32。

返回值:解析失败返回一个负数,解析成功返回“SCRIPTDATASTRING的StringData属性中存贮的实际的字符串数据的长度,单位为字节”。

三、amf_get_string函数的内部实现原理

amf_get_string函数中,首先通过avio_rb16函数得到SCRIPTDATASTRING的StringLength属性,也就是SCRIPTDATASTRING的StringData属性中存贮的实际的字符串数据的长度,赋值给局部变量length。关于avio_rb16函数的用法可以参考:《FFmpeg源码:avio_r8、avio_rl16、avio_rl24、avio_rl32、avio_rl64函数分析》:

    int length = avio_rb16(ioc);

虽然FLV的官方文档《video_file_format_spec_v10_1.pdf》写着SCRIPTDATASTRING类型可以用来记录存贮不超过65535个字符的字符串数据。但是FFmpeg源码中做了限制,为了避免形参buffer指向的缓冲区不够大无法容纳解析出来的字符串(FFmpeg源码中形参buffer指向的缓冲区的大小一般为32字节),导致数组溢出,如果判断出表达式“length >= buffsize”为真,通过avio_skip函数让AVIOContext文件位置指针跳过length个字节,并且让amf_get_string函数返回-1。关于avio_skip函数的用法可以参考:《FFmpeg源码:avio_skip函数分析》:

    if (length >= buffsize) {
        avio_skip(ioc, length);
        return -1;
    }

通过avio_read函数从AVIOContext输入缓冲区/文件描述符去读取FLV文件中的数据,总共读取length个字节,从而得到SCRIPTDATASTRING的StringData属性中存贮的实际的字符串数据。关于avio_read函数的用法可以参考:《FFmpeg源码:avio_read函数分析》。如果ret小于0,表示调用avio_read函数出错,返回ret的值;如果ret < length表示实际读取到的字节数小于SCRIPTDATASTRING的StringLength属性,也表示出错了, 返回AVERROR_INVALIDDATA:

    ret = avio_read(ioc, buffer, length);
    if (ret < 0)
        return ret;
    if (ret < length)
        return AVERROR_INVALIDDATA;

在形参buffer指向的缓冲区的有效数据之后添加字符'\0',用于标记字符串的结束:

    buffer[length] = '\0';

解析成功返回“SCRIPTDATASTRING的StringData属性中存贮的实际的字符串数据的长度,单位为字节”:

    return length;


http://www.kler.cn/a/331388.html

相关文章:

  • 山景BP1048蓝牙音频任务后台运行设置方法
  • go面试问题
  • 前端和后端解决跨域问题的方法
  • 简洁清爽epub 阅读器
  • ubuntu22.04编译安装Opencv4.8.0+Opencv-contrib4.8.0教程
  • HIPT论文阅读
  • string的实现(下)
  • 会议平台后端优化方案
  • 如何在 DAX 中计算多个周期的移动平均线
  • 第二十二章 rust数据库使用:sea-orm详解
  • 【有啥问啥】表示学习(Representation Learning)详解:理论、方法与应用
  • 新品:新一代全双工音频对讲模块SA618F22-C1
  • 【JVM】垃圾释放方式:标记-清除、复制算法、标记-整理、分代回收
  • Oracle datafile 数目限制是多少
  • rsync数据备份实时同步
  • Linux中find命令详解
  • PHP常用的超全局变量(8个)
  • WebSocket 2024/9/30
  • MATLAB使用眼图分析QPSK通信系统接收端匹配滤波后的信号
  • windows 11 LTSC 26100.1742 官方简体中文版
  • 免杀对抗—GOC#反VT沙盒资源分离混淆加密
  • 【Flutter】- 基础语法
  • 【go入门】常量
  • Leetcode 11.乘最多水的容器(字节,快手面试题)
  • 探索Spring Boot:实现“衣依”服装电商平台
  • 【Java序列化】使用Java 自带的Serializer进行对象序列化和反序列化