Android系统攻城狮头像
Linux FFmpeg深度解析之av_packet_pack_dictionary调用流程与实战(四十二)封面图

Linux FFmpeg深度解析之av_packet_pack_dictionary调用流程与实战(四十二)

简介: CSDN博客专家、《Android系统多媒体进阶实战》作者

博主新书推荐:《Android系统多媒体进阶实战》🚀
Android Audio工程师专栏地址: Audio工程师进阶系列【原创干货持续更新中……】🚀
Android多媒体专栏地址: 多媒体系统工程师系列【原创干货持续更新中……】🚀
专题一 二:AAOS车载系统+AOSP14系统攻城狮入门视频实战课 🚀
专题三:Android14 Binder之HIDL与AIDL通信实战课 🚀
专题四:Android15快速自定义与集成音效实战课 🚀
专题五:Android15音频策略实战课 🚀
专题六:Android15音频性能实战课(无声/杂音/断音/爆音实战案例) 🚀

人生格言: 人生从来没有捷径,只有行动才是治疗恐惧和懒惰的唯一良药.

更多原创,欢迎关注:Android系统攻城狮

点击获取二维码

欢迎关注Android系统攻城狮

🌻1.前言

本篇目的:理解av_packet_pack_dictionary()如何将AVDictionary编码为字节序列,分析序列内存的所有权,并通过Packet侧数据完成打包与解包。

FFmpeg需要把字典形式的元数据附加到AVPacket时,不能直接把AVDictionary结构体作为Packet字节数据传递。av_packet_pack_dictionary()按键和值的顺序生成一段由NUL字符分隔的字节序列,供调用者添加为Packet侧数据。

它只负责生成序列,不会创建AVPacket、修改原字典或自动把序列附加到Packet。

AVDictionary元数据

av_packet_pack_dictionary

键和值组成的NUL分隔序列

添加为Packet侧数据

后续读取侧数据并解包

🌻2.应用场景和用法

2.1把字典元数据放入Packet侧数据

这个接口适合把一组键值元数据打包后附加到Packet,例如图像序列解复用器把当前图像的来源路径和文件名保存到AV_PKT_DATA_STRINGS_METADATA侧数据中。

场景调用动作结果
为图像Packet添加来源信息打包路径和文件名字典Packet携带对应侧数据
保存容器或流元数据打包AVDictionary字典成为可传递的字节序列
需要在下游恢复字典配合av_packet_unpack_dictionary()从侧数据重建键值项
元数据不存在传入NULL字典返回NULL并将长度设为0
2.2函数原型与内存所有权

函数原型如下:

uint8_t *
av_packet_pack_dictionary (AVDictionary *dict,
                          size_t *size);
参数或返回值含义
dict待打包的字典,不会被函数修改
size必须指向有效的size_t变量,函数写入打包后的字节数
返回值使用av_malloc()分配的序列;失败时返回NULL

每个字典项依次写成key\0value\0,多项按字典迭代顺序连续排列。返回序列由调用者持有,成功交给av_packet_add_side_data()后,所有权转移给Packet;添加失败时仍由调用者释放。

函数没有独立错误码。返回NULL时,调用者无法从返回值区分字典为空、长度计算溢出或内存分配失败等情况。若字典是预期输入,应用通常需要把NULL当作打包未成功处理。

相邻接口职责如下:

接口职责
av_packet_pack_dictionary()将字典打包成新分配的字节序列
av_packet_add_side_data()将字节序列关联到Packet指定类型的侧数据
av_packet_get_side_data()从Packet读取指定类型的侧数据
av_packet_unpack_dictionary()从字节序列恢复字典项
av_free()释放尚未转交Packet所有权的打包序列

🌻3.调用流程剖析

3.1两次遍历计算并生成字节序列

av_packet_pack_dictionary()先将*size设为0。字典为NULL时立即返回NULL。

有字典时,函数遍历两次。第一次遍历每个键和值,计算包括字符串结尾NUL字符在内的总长度,并检查size_t加法溢出。长度计算成功后,函数通过av_malloc()分配缓冲区。

第二次遍历按key\0value\0的顺序把字符串拷贝到缓冲区。每次拷贝都包含末尾NUL字符,因此相邻字典项可以连续存储。

是

否

是

否

传入字典和size指针

将size初始化为0

字典是否为空指针

返回NULL

第一次遍历计算键值长度

长度计算是否溢出

返回NULL

av_malloc申请序列空间

第二次遍历复制键值字符串

返回序列地址和总长度

3.2将打包结果附加并在读取侧恢复

打包函数返回后,调用者需要将结果交给av_packet_add_side_data()。添加成功后,Packet负责持有侧数据;添加失败时,调用者仍负责av_free()该缓冲区。

读取侧先通过av_packet_get_side_data()取得侧数据地址和长度,再调用av_packet_unpack_dictionary()恢复字典。解包函数会检查序列结尾及键值边界,遇到无效数据时返回负值。

打包字节序列

av_packet_add_side_data

AVPacket持有侧数据

av_packet_get_side_data

av_packet_unpack_dictionary

恢复AVDictionary

🌻4.实战案例

4.1准备字典和空Packet

示例创建包含source_path和source_basename两项的AVDictionary,并分配一个空AVPacket。字典值使用普通C字符串,打包序列可以直接作为Packet侧数据保存。

4.2打包并添加为侧数据

程序调用av_packet_pack_dictionary()得到数据指针和长度,再将其添加为AV_PKT_DATA_STRINGS_METADATA。添加成功后,程序将本地指针置为NULL,避免清理阶段重复释放Packet已经接管的内存。

4.3完整代码
#include <libavcodec/packet.h>
#include <libavutil/dict.h>
#include <libavutil/mem.h>
#include <string.h>

int
main (void)
{
    AVPacket *pkt = NULL;
    AVDictionary *metadata = NULL;
    AVDictionary *restored = NULL;
    const AVDictionaryEntry *entry;
    uint8_t *packed = NULL;
    const uint8_t *side_data;
    size_t packed_size = 0;
    size_t side_data_size = 0;
    int ret = 1;

    /* 创建Packet,用于承载字符串元数据侧数据 */
    pkt = av_packet_alloc ();
    if (!pkt)
        goto end;

    /* 准备图像来源路径和文件名 */
    if (av_dict_set (&metadata,
                "lavf.image2dec.source_path",
                "/data/frame001.jpg", 0) < 0)
        goto end;

    if (av_dict_set (&metadata,
                "lavf.image2dec.source_basename",
                "frame001.jpg", 0) < 0)
        goto end;

    /* 将字典打包为键值交替的NUL分隔字节序列 */
    packed = av_packet_pack_dictionary (metadata, &packed_size);
    if (!packed)
        goto end;

    /* 添加成功后由Packet接管packed缓冲区 */
    if (av_packet_add_side_data (
                pkt,
                AV_PKT_DATA_STRINGS_METADATA,
                packed,
                packed_size) < 0)
        goto end;
    packed = NULL;

    /* 从Packet中取回侧数据及其实际字节数 */
    side_data = av_packet_get_side_data (
            pkt,
            AV_PKT_DATA_STRINGS_METADATA,
            &side_data_size);
    if (!side_data)
        goto end;

    /* 将打包的字节序列恢复为字典 */
    if (av_packet_unpack_dictionary (
                side_data,
                side_data_size,
                &restored) < 0)
        goto end;

    /* 检查恢复后的文件名字段 */
    entry = av_dict_get (
            restored,
            "lavf.image2dec.source_basename",
            NULL,
            0);
    if (!entry || strcmp (entry->value, "frame001.jpg") != 0)
        goto end;

    ret = 0;

end:
    /* 仅释放尚未转交给Packet的打包缓冲区 */
    av_free (packed);
    av_dict_free (&metadata);
    av_dict_free (&restored);
    av_packet_free (&pkt);

    return ret;
}

🌻5.总结

av_packet_pack_dictionary()把AVDictionary编码为NUL分隔的键值序列,供调用者添加到Packet侧数据,并可由av_packet_unpack_dictionary()恢复。

转载自 CSDN-专业IT技术社区

原文链接:https://blog.csdn.net/u010164190/article/details/170193683

文章来源转载

评论

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--