ffmpeg TS复用代码详解——mpegtsenc.c

news2024/10/5 14:37:40

一、mpegtsenc.c 整体架构

在这里插入图片描述

二、主要函数

mpegts_write_pes(AVFormatContext *s, AVStream *st, const uint8_t *payload, int payload_size, int64_t pts, int64_t dts)

这个函数就是TS打包的主函数了,这个函数主要功能就是把一帧数据拆分成188字节的TS包,并加入PTS,DTS同步信息,这个函数封装的对象是一帧视频或者音频数据,payload,payload_size分别是数据和大小。

PTS,DTS就是音视频同步时间戳,时间戳其实就是一次采样的颗粒(简单理解就是数据),以视频来举例,视频同步时钟90K hz(27M/300),如果帧率是25fps的话,一帧数据采样时间40ms,那么时间戳就是90K x 40ms = 3600(估算值)。

static void mpegts_write_pes(AVFormatContext *s, AVStream *st,
                             const uint8_t *payload, int payload_size,
                             int64_t pts, int64_t dts, int key)
{   
........
    while (payload_size > 0) {
        retransmit_si_info(s, force_pat);   //评估是否需要插入PAT、PMT
        force_pat = 0;

        write_pcr = 0;
        if (ts_st->pid == ts_st->service->pcr_pid) {    //评估pes中是否需要插入PCR,一个PCR周期
            if (ts->mux_rate > 1 || is_start) // VBR pcr period is based on frames
                ts_st->service->pcr_packet_count++;
            if (ts_st->service->pcr_packet_count >=
                ts_st->service->pcr_packet_period) {
                ts_st->service->pcr_packet_count = 0;
                write_pcr = 1;
            }
        }
		
		// 若dts-pcr>delay 则需要插入空包,但此时也需要插入pcr则优先插入pcr
        if (ts->mux_rate > 1 && dts != AV_NOPTS_VALUE &&
            (dts - get_pcr(ts, s->pb)/300) > delay) {   //CBR考虑(插入pcr_only或null_packet)
            /* pcr insert gets priority over null packet insert */
            if (write_pcr)
                mpegts_insert_pcr_only(s, st);
            else
                mpegts_insert_null_packet(s);
            continue; /* recalculate write_pcr and possibly retransmit si_info */
        }

		// 写入TS包头
        /* prepare packet header */
        q = buf;
        *q++ = 0x47;
        val = (ts_st->pid >> 8);
        if (is_start)
            val |= 0x40;    //payload_unit_start_indicator
        *q++ = val;
        *q++ = ts_st->pid;
        ts_st->cc = (ts_st->cc + 1) & 0xf;  //continuity_counter
        *q++ = 0x10 | ts_st->cc; // payload indicator + CC
        /*********写入adaptation_field(如果需要) *********/
        if (key && is_start && pts != AV_NOPTS_VALUE) { // 是否需要写入PCR
            if (ts_st->pid == ts_st->service->pcr_pid)
                write_pcr = 1;
            set_af_flag(buf, 0x40); //set Random Access for key frames
            q = get_ts_payload_start(buf);
        }
        if (write_pcr) { // 写入PCR
            set_af_flag(buf, 0x10); //PCR_flag
            q = get_ts_payload_start(buf);
            // add 11, pcr references the last byte of program clock reference base
            if (ts->mux_rate > 1)
                pcr = get_pcr(ts, s->pb);
            else
                pcr = (dts - delay)*300;
            if (dts != AV_NOPTS_VALUE && dts < pcr / 300)
                av_log(s, AV_LOG_WARNING, "dts < pcr, TS is invalid\n");
            extend_af(buf, write_pcr_bits(q, pcr)); //写入pcr
            q = get_ts_payload_start(buf);
        }
        /*********写入payload*********/
        if (is_start) {
            int pes_extension = 0;
            int pes_header_stuffing_bytes = 0;
            /* write PES header */
            *q++ = 0x00;
            *q++ = 0x00;
            *q++ = 0x01;
            is_dvb_subtitle = 0;
            is_dvb_teletext = 0;
            /* 写入stream_id */
            if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
                if (st->codec->codec_id == AV_CODEC_ID_DIRAC) {
                    *q++ = 0xfd;
                } else
                    *q++ = 0xe0;    //video stream
            } else if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO &&
                       (st->codec->codec_id == AV_CODEC_ID_MP2 ||
                        st->codec->codec_id == AV_CODEC_ID_MP3 ||
                        st->codec->codec_id == AV_CODEC_ID_AAC)) {
                *q++ = 0xc0;
            } else if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO &&
                        st->codec->codec_id == AV_CODEC_ID_AC3 &&
                        ts->m2ts_mode) {
                *q++ = 0xfd;
            } else {
                *q++ = 0xbd;
                if(st->codec->codec_type == AVMEDIA_TYPE_SUBTITLE) {
                    if (st->codec->codec_id == AV_CODEC_ID_DVB_SUBTITLE) {
                        is_dvb_subtitle = 1;
                    } else if (st->codec->codec_id == AV_CODEC_ID_DVB_TELETEXT) {
                        is_dvb_teletext = 1;
                    }
                }
            }
            header_len = 0;
            flags = 0;
            /* 处理PTS_DTS_flags */
            if (pts != AV_NOPTS_VALUE) {
                header_len += 5;
                flags |= 0x80;
            }
            if (dts != AV_NOPTS_VALUE && pts != AV_NOPTS_VALUE && dts != pts) {
                header_len += 5;
                flags |= 0x40;
            }
........
            len = payload_size + header_len + 3;
            /* 3 extra bytes should be added to DVB subtitle payload: 0x20 0x00 at the beginning and trailing 0xff */
            if (is_dvb_subtitle) {
                len += 3;
                payload_size++;
            }
            if (len > 0xffff)
                len = 0;
            if (ts->omit_video_pes_length && st->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
                len = 0;
            }
            *q++ = len >> 8;    //PES_packet_length
            *q++ = len;
            val = 0x80; //'10'
            /* data alignment indicator is required for subtitle and data streams */
            if (st->codec->codec_type == AVMEDIA_TYPE_SUBTITLE || st->codec->codec_type == AVMEDIA_TYPE_DATA)
                val |= 0x04;
            *q++ = val;
            *q++ = flags;
            *q++ = header_len;  //PES_header_data_length
            if (pts != AV_NOPTS_VALUE) {
                write_pts(q, flags >> 6, pts);  //写入pts
                q += 5;
            }
            if (dts != AV_NOPTS_VALUE && pts != AV_NOPTS_VALUE && dts != pts) {
                write_pts(q, 1, dts);   //写入dts
                q += 5;
            }
........
        /* 处理完header,下面开始处理数据 */
        /* header size */
        header_len = q - buf;
        /* data len */
        len = TS_PACKET_SIZE - header_len;
        if (len > payload_size) //pes包太大,一个ts包发不完,就得拆包。
            len = payload_size;
        stuffing_len = TS_PACKET_SIZE - header_len - len;   //相反,如果太小,就加入填充。
        if (stuffing_len > 0) {
            /* add stuffing with AFC */     //
            if (buf[3] & 0x20) {
                /* stuffing already present: increase its size */
                afc_len = buf[4] + 1;
                memmove(buf + 4 + afc_len + stuffing_len,
                        buf + 4 + afc_len,
                        header_len - (4 + afc_len));
                buf[4] += stuffing_len;
                memset(buf + 4 + afc_len, 0xff, stuffing_len);
            } else {
                /* add stuffing */
                memmove(buf + 4 + stuffing_len, buf + 4, header_len - 4);
                buf[3] |= 0x20;
                buf[4] = stuffing_len - 1;
                if (stuffing_len >= 2) {
                    buf[5] = 0x00;
                    memset(buf + 6, 0xff, stuffing_len - 2);
                }
            }
        }

        if (is_dvb_subtitle && payload_size == len) {
            memcpy(buf + TS_PACKET_SIZE - len, payload, len - 1);
            buf[TS_PACKET_SIZE - 1] = 0xff; /* end_of_PES_data_field_marker: an 8-bit field with fixed contents 0xff for DVB subtitle */
        } else {
            memcpy(buf + TS_PACKET_SIZE - len, payload, len);   //写入payload数据
        }

        payload += len;
        payload_size -= len;
        mpegts_prefix_m2ts_header(s);
        avio_write(s->pb, buf, TS_PACKET_SIZE); //写入avio(缓存到avio_buf中)
    }
    avio_flush(s->pb);  //把avio_buf的数据写入后端(file/udp等)
    ts_st->prev_payload_key = key;
}

三、原理分析

1、ffmpeg TS复用什么时候写入PCR?

  • 满足一个PCR周期, ts_st->service->pcr_packet_period,会写入一个PCR;PCR周期计算方式:

(int64_t)ts->mux_rate * ts->pcr_period / (TS_PACKET_SIZE * 8 * 1000);

ts->pcr_period 默认为20ms;

  • 如果当前帧是关键帧,并且当前TS包数据是一帧的开始,pts 值正常,会写入一个PCR;

2、怎样得到一个PCR?

在ffmpeg中,get_pcr() 方法获取一个pcr,代码如下:

av_rescale(avio_tell(pb) + 11, 8 * PCR_TIME_BASE, ts->mux_rate) +
           ts->first_pcr;
  • first_pcr 和 muxdelay参数有关,默认为0.7,可以通过命令行设置,first_pcr计算方式:

     ts->first_pcr = av_rescale(s->max_delay, 27000000, 1000000);
    

3、什么时候插入一个空包呢?

可以看下面代码,当 dts - pcr > delay 的时候就会插入一个空包,但若此时正好到达一个PCR周期,即需要插入一个PCR了,需要优先插入一个只有PCR的TS包(PID为视频PID)。

if (ts->mux_rate > 1 && dts != AV_NOPTS_VALUE &&
    (dts - get_pcr(ts, s->pb)/300) > delay) {   //CBR考虑(插入pcr_only或null_packet)
        /* pcr insert gets priority over null packet insert */
        if (write_pcr)
            mpegts_insert_pcr_only(s, st);
        else
            mpegts_insert_null_packet(s);
        continue; /* recalculate write_pcr and possibly retransmit si_info */
    }

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

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

相关文章

自助点餐系统微信小程序,支持外卖、到店等

总体介绍 系统总共分为三个端&#xff1a;后端&#xff0c;后台管理系统、微信小程序。 基于当前流行技术组合的前后端分离商城系统&#xff1a; SpringBoot2MybatisPlusSpringSecurityjwtredisVue的前后端分离的商城系统&#xff0c; 包含分类、sku、积分、多门店等 预览图…

FariyGUI × Cocos Creator 入门

前言 程序员向的初探Cocos Creator结和FairyGUI的使用&#xff0c;会比较偏向FairyGUI一点&#xff0c;默认各位读者都熟练掌握Cocos Creator以及js/ts脚本编写。 初探门径&#xff0c;欢迎大佬指教&#xff0c;欢迎在评论区或私信与本人交流&#xff0c;谢谢&#xff01; 下…

DBSCAN密度聚类介绍 样本点 样本集合 半径 邻域 核心对象 边界点 密度直达 密度可达 密度相连

DBSCAN密度聚类介绍 样本点 样本集合 半径 邻域 核心对象 边界点 密度直达 密度可达 密度相连 简介概念定义原理DBSCAN的优点DBSCAN的缺点小尝试制作不易&#xff0c;感谢三连&#xff0c;谢谢啦 简介 DBSCAN&#xff08;Density-Based Spatial Clustering of Applications wi…

Codeforces Round 927 (Div. 3)(A,B,C,D,E,F,G)

这场简单些&#xff0c;E题是个推结论的数学题&#xff0c;沾点高精的思想。F是个需要些预处理的DP&#xff0c;G题是用exgcd算边权的堆优化dijkstra。C题有点骗&#xff0c;硬啃很难做。 A Thorns and Coins 题意&#xff1a; 在你的电脑宇宙之旅中&#xff0c;你偶然发现了…

LeetCode 0105.从前序与中序遍历序列构造二叉树:分治(递归)——五彩斑斓的题解(若不是彩色的可以点击原文链接查看)

【LetMeFly】105.从前序与中序遍历序列构造二叉树&#xff1a;分治&#xff08;递归&#xff09;——五彩斑斓的题解&#xff08;若不是彩色的可以点击原文链接查看&#xff09; 力扣题目链接&#xff1a;https://leetcode.cn/problems/construct-binary-tree-from-preorder-a…

java数据类型、运算符

一、数据的表示详解 1.1 整数在计算机中的存储原理 任何数据在计算机中都是以二进制表示的。那这里肯定有人问&#xff0c;什么是二进制啊&#xff1f;所谓二进制其实就是一种数据的表示形式&#xff0c;它的特点是逢2进1。 数据的表示形式除了二进制&#xff08;逢2进1&…

Https证书续签-acme.sh-腾讯云之DnsPod

ename 域名切换到 DnsPod 上面解析 可以先看下之前的 acme.sh 介绍文章然后再来次补充更多。 之前说过了 acme.sh 在阿里云下的使用。 这里做个后续补充 之前的域名是在 ename 上的 &#xff0c;为了自动续签切换到 DnsPod 上面解析 注意事项 可以把原来 ename 上的解析先导出…

Android全新UI框架之Jetpack Compose入门基础

Jetpack Compose是什么 如果有跨端开发经验的同学&#xff0c;理解和学习compose可能没有那么大的压力。简单地说&#xff0c;compose可以让Android的原生开发也可以使用类似rn的jsx的语法来开发UI界面。以往&#xff0c;我们开发Android原生页面的时候&#xff0c;通常是在xml…

【八股文面试】Java基础常见面试题总结(上)

Java基础常见面试题总结(上) Java有哪些特性 简单易学&#xff1b;面向对象&#xff08;封装&#xff0c;继承&#xff0c;多态&#xff09;&#xff1b;平台无关性&#xff08; Java 虚拟机实现平台无关性&#xff09;&#xff1b;支持多线程&#xff08; C 语言没有内置的多…

Springcloud:LiteFlow

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 目录 一、LiteFlow简介 二、规则编排关键字及语法 1、THEN&#xff1a; 2、WHEN&#xff1a; 3、AND&#xff1a; 4、OR&#xff1a; 5、IF&#xff1a; 6、ELSE&…

手动代码实现神经网络

网络结构 第一层有13个神经元&#xff0c;第二层8个神经元&#xff0c;第三层是输出层&#xff1b;其中第一层的激活函数是relu&#xff0c;第二层是sigmoid 代码实现 # 导入库 import torch import torch.nn as nn from torch.nn import functional as F # 确定数据 torch…

使用IntelliJ IDEA查看接口的全部实现方法

在大型Java项目中&#xff0c;经常会使用接口和抽象类进行代码设计。为了更好地了解代码结构和功能&#xff0c;我们需要快速查看一个接口的所有实现类。IntelliJ IDEA提供了一些方便的方法来实现这一目标。 1. 点击查看接口的实现子类 在IDEA中&#xff0c;你可以轻松地查看…

swagger 内容信息与代码不一致,已解决

ApiModel是Swagger中的常用到的注解&#xff0c;检查下信息错误的实体类的ApiModel的value值是否一致&#xff0c;应该是写错了&#xff0c;是不是张冠李戴了

STM32 TIM2重映射

STM32定时器 文章目录 STM32定时器[TOC](文章目录) 前言一、问题分析二、代码 前言 最近想弄一个多路输出PWM&#xff0c;但是发现TIM2不能用&#xff0c;根据手册也对它进行重映射了&#xff0c;但是还是不能用&#xff0c;用示波器发现驱动能力比较弱&#xff0c;然后禁用jt…

博途PLC PID仿真(单容水箱液位高度控制)

单容水箱和双荣水箱的微分方程和数值求解,可以参考下面文章链接: https://rxxw-control.blog.csdn.net/article/details/131139432https://rxxw-control.blog.csdn.net/article/details/131139432这篇博客我们利用欧拉求解器在PLC里完成单容水箱的数学建模。PLC也可以和MATL…

【笔记】【算法设计与分析 - 北航童咏昕教授】绪论

算法设计与分析 - 北航童咏昕教授 文章目录 算法的定义定义性质 算法的表示自然语言编程语言伪代码 算法的分析算法分析的原则渐近分析 算法的定义 定义 给定计算问题&#xff0c;算法是一系列良定义的计算步骤&#xff0c;逐一执行计算步骤即可得预期的输出。 性质 有穷性确…

运维07:堡垒机

什么是跳板机 跳板机就是一台服务器而已&#xff0c;运维人员在使用管理服务器的时候&#xff0c;必须先连接上跳板机&#xff0c;然后才能去操控内网中的服务器&#xff0c;才能登录到目标设备上进行维护和操作 开发小张 ---> 登录跳板机 ---> 再登录开发服务器 测试…

vue3 用xlsx 解决 excel 低版本office无法打开问题

需求背景解决思路解决效果将json导出为excel将table导为excel导出样式 需求背景 原使用 vue3-json-excel &#xff0c;导致在笔记本office环境下&#xff0c;出现兼容性问题 <vue3-json-excel class"export-btn" :fetch"excelGetList" :fields"js…

Aster实现一台电脑当两台使——副屏搭配键鼠

前言&#xff1a;笔者每年回家&#xff0c;都面临着想要和小伙伴一起玩游戏&#xff0c;但小伙伴没有电脑/只有低配电脑的问题。与此同时&#xff0c;笔者自身的电脑是高配置的电脑&#xff0c;因此笔者想到&#xff0c;能否在自己的电脑上运行游戏&#xff0c;在小伙伴的电脑上…