USB采集卡如何打pts

简介: USB采集卡如何打pts

一、使用采集卡提供的pts

二、手动打pts

1.usb采集设备pts的问题

2.采集卡驱动,UVC/UAC,ffmpeg的关系

3.如何自己打pts

4.音视频同步调优

5.NTP等联网调时工具带来的不同步问题


一、使用采集卡提供的pts


我们用使用pc摄像头和使用pc麦克风声卡里的方法,用ffmpeg采集后会得到音视频的pts。

我们应该尽量用好这些pts,因为他们的时间间隔很精准,比如48000,每次采集1024,发现每帧间隔21ms;视频采集1080p25,每帧间隔40ms。

使用之前我们需要把他们同步到同一个时间戳,其中一个方法是:

dif = 格林威治时间 - 第一帧采集到的视频时间戳

然后以后每次pts = pts + dif。

V:

AVDictionary *options = NULL;
av_dict_set(&options, “video_size”, “1920x1080”, 0);
av_dict_set(&options, “framerate”, “30”, 0);
//以上参数如果不设置的话,ffmpeg就会用默认值,但默认值摄像头不一定支持
int re = avformat_open_input(&ic, “/dev/video0”, ifmt, &options);


A:

av_dict_set(&options, "sample_rate", "48000", 0); // 只能是48000不支持改动
av_dict_set(&options, "channels", "2", 0);        // 无法改,不支持改动
long long GetCurTime()
{
    struct timeval tv;
    gettimeofday(&tv, NULL);
    // ZlogInfo("second:%ld\n",tv.tv_sec);  //秒
    // ZlogInfo("millisecond:%ld\n",tv.tv_sec*1000 + tv.tv_usec/1000);  //毫秒
    // ZlogInfo("microsecond:%ld\n",tv.tv_sec*1000000 + tv.tv_usec);  //微秒
    long long temp_time = tv.tv_sec * 1000000 + tv.tv_usec;
    return temp_time;
}
if(this->dif_PTS == 0) this->dif_PTS = GetCurTime() - read_pkt.pts;
read_pkt.pts = read_pkt.pts + this->dif_PTS;


二、手动打pts

1.usb采集设备pts的问题

decklink的采集卡,会把音视频的pts给好,并且是可靠的。但小的厂商就不一定了。我这里使了一个台湾和国内的厂商,一个是pci采集卡(sdi/hdmi采集转pci口),这个采集卡支持UVC和UAC协议,另一个是usb(hdmi转usb口)采集卡。这两个价格都在千元左右。


跟这两个采集卡的原厂技术沟通,pts存在以下问题

1:音频和视频的pts没有关系,没有共同的起点或相关的联系。

2.pts经过一夜测试后发现,有跳变的现象。

3.采集1080p30时,视频的pts,每帧之间只增加20ms,而不是33ms,其他分辨率无此问题。

因为这些设备的用户大多数不是开发者,所以这些问题都不会用到,对于开发者来说则需要原厂定制开发了。


2.采集卡驱动,UVC/UAC,ffmpeg的关系


在与原厂电话微信后,对usb的采集卡有初步的了解,对于音频来说,采集卡的驱动并不会提供pts给系统,因为对于音频来说,确定好采样率,声道数,位深,那么在播放端,它的采样数对应的播放时间是固定的,采集卡只有一个晶振在控制着采集频率,音频从采集卡采集到其提供给系统,延时可以忽略。

视频采集卡的驱动也不会有pts,它的启动只会对上层提供类似于1,2,3…等视频的序号的标签,采集卡从采集到提供给系统,可能会有几帧,如4帧延时。

那么我们用ffmpeg采集到的音视频,都含有pts,那么这些pts是什么呢。音频是1970年开始到现在的us,视频则是开机的时间us。他们都是UVC和UAC协议对采集卡驱动提供的音视频帧打的pts。

因此数据的传输是这样的,采集卡->采集卡驱动->系统内核->UVC/UAC->ffmpeg

硬件厂商改一个问题,要改好几个版本,经常引入其他bug,看来天下程序员都一样。


3.如何自己打pts


那么我们该怎么自己打pts呢?

在两个线程里分别采集音视频:

av_read_frame()


我们把格林威治的PTS赋值给每次采集到的音视频数据。

A:

re = av_read_frame(ic_a, &read_pkt_a);   
read_pkt_a.pts = GetCurTime();


V:

re = av_read_frame(ic, &read_pkt);   
read_pkt.pts = GetCurTime();
ZlogInfo("dif:%ld\n", (shm_->GetCurTime() - LAST_PTS) / 1000);
LAST_PTS = shm_->GetCurTime();


视频每次从这个函数返回的时间间隔分别是:

//1080p25 39 40 39 40 39 40 39 40 39 40 39 40 ms

音频分别是:

//1080p25 41 0 42 0 42 0 41 0 41 0 42 0 ms

有时是

//1080p25 21 0 41 21 0 40 22 0 41 1 40 0 42 0 41 22 0 41 0 42 0 42 1 40 22 0 41 21 1 40


0并非没有间隔而是小于1ms,这里有精度损失。

可以看到间隔并不标准,因为av_read_frame()维护了一个缓存,他每次从采集卡拿到一阵或多帧音频或视频数据放入缓存,然后分包,返回一帧音频或视频数据,下次再调用直接去缓存里拿,如果缓存没有则阻塞住,直到采集卡提供了数据。


我们需要让他们的时间间隔相对均匀,那么每次的pts就和上次比较,如果少于一定时间则睡眠。

ZlogInfo("dif:%ld\n", (shm_->GetCurTime() - LAST_PTS) / 1000);
if((shm_->GetCurTime() - LAST_PTS) / 1000 < 10) usleep(10000);
pkt->pts = shm_->GetCurTime();
ZlogInfo("dif_last:%ld\n", (shm_->GetCurTime() - LAST_PTS) / 1000);
LAST_PTS = shm_->GetCurTime();


那么再次打印如下:

V:

//39 40 39 40 39 40 39 40 39 39 39 39 40 40

A:

//1080p25 31 12 30 10 32 10 31 11 31 10 32 11 30 10 32 10 32 10 30 10 31 11 31 10 31 10


4.音视频同步调优

因为采集视频到交给ffmpeg可能有几帧的缓存,因此每次的视频数据可以减去100ms左右,看看播出后的视频效果。

pkt->pts = shm_->GetCurTime() - 100000;


播放器的音视频同步策略一般都是按以音频为基准,音频是线性均匀的,那么音频的pts本身是没有意义的,两帧之间的间隔并不代表播放的时间间隔,它的意义主要是用于视频与他同步。

如果想要音频的间隔再均匀一些,可以调节以下睡眠的时间。


5.NTP等联网调时工具带来的不同步问题

pc每次开机联网后,系统自带NTP开始核对时间,并改变自身时间,那么你每次打的pts就会产生突变。NTP每次调剂的幅度可能在500ms左右,可以关闭它,或换小一点的工具,每次调解幅度在10ms左右的。


thxchtb3wcn3k_d11fc1ff3db9431d8ad7575e9297ae48.png

相关文章
51单片机学习-HC-05蓝牙模块-LCD12864显示
51单片机学习-HC-05蓝牙模块-LCD12864显示
156 0
51单片机学习-HC-05蓝牙模块-LCD12864显示
|
数据采集 索引
NI采集卡USB-6361多通道模拟输入采集报错解决方案
折腾一块 USB-6361 采集卡很久了,之前都是单通道采集模拟信号,突然接到要使用双通道采集模拟信号,本想着就新增加一路 Analog Input task 即可,但事情总没有想象的那么简单,因此记录一下解决的方法。
291 0
|
监控 Linux
uvc摄像头驱动uvc设备的注册分析
uvc摄像头驱动uvc设备的注册分析
394 0
|
传感器 芯片
可编程 USB 转串口适配器开发板 与温湿度传感器芯片
SHT3x-DIS 是 IIC 接口的温度、湿度传感器芯片,可工作于单次测量或连续自动测量模式。USB2S 已有 1 片 SHT31-DIS 芯片,芯片地址为 0x88。
可编程 USB 转串口适配器开发板 与温湿度传感器芯片
|
传感器 芯片 内存技术
可编程 USB 转串口适配器开发板电压设置
通过电压选择器跳线可设置 USB2S 的工作电压,如下图所示,跳线帽位于 3.3 一侧时工作电压为 3.3V,跳线帽位于 5.0 侧时工作电压为VIN(即USB 供电时的 5.0V)。 设置工作电压时必须两个跳线帽同时调整。
可编程 USB 转串口适配器开发板电压设置
|
缓存 编解码 API
ZYNQ-使用HDMI显示器进行VDMA彩条显示测试
ZYNQ-使用HDMI显示器进行VDMA彩条显示测试
313 0
ZYNQ-使用HDMI显示器进行VDMA彩条显示测试
|
传感器 开发工具
ZYNQ-实现外设驱动开发-iic接口的温湿度传感器si7021
ZYNQ-实现外设驱动开发-iic接口的温湿度传感器si7021
280 0
ZYNQ-实现外设驱动开发-iic接口的温湿度传感器si7021
|
传感器 芯片
可编程 USB 转串口适配器开发板 SHT3x-DIS 温湿度传感器芯片
SHT3x-DIS 是 IIC 接口的温度、湿度传感器芯片,可工作于单次测量或连续自动测量模式。USB2S 已有 1 片 SHT31-DIS 芯片,芯片地址为 0x88。 SHT3X-DIS 的输出温度和湿度均为 3 字节,前两字节是温湿度值,第 3 字节是校验字节。转换 公式如下:(数值=第 1 个字节*256+第 2 个字节)。
可编程 USB 转串口适配器开发板  SHT3x-DIS 温湿度传感器芯片
|
数据采集
基于DMA的ADC数据采集
嵌入式系统开发经常会遇到采集模拟量的功能需求,这时就会用到ADC,STM32系列MCU提供了强大的ADC功能,其提供的多通道采集和DMA方式极大的方便了ADC相关的开发流程,本文主要记录一下,如何使用ADC实现基于DMA的多通道模拟量采集功能。
507 0