javacv ffmpeg拉取rtsp流怎么获取到绝对时间戳,目前我使用javacv拉流dts和pts都是从0开始计数和rame.timestamp 也是从0开始,想问问如何获取到真实的时间戳。
要获取真实的时间戳,您需要使用视频帧的时间基准和时间戳。
时间基准是帧的时间单位,通常是“时钟频率”,也就是每秒钟的帧数。时间基准可以通过FFmpeg的AVStream结构体来获取。
针对每个AVPacket,可以通过解码器解码出一组AVFrame。每个AVFrame都有自己的时间戳,可以使用av_frame_get_best_effort_timestamp()函数获取。该函数会使用不同的时间戳来源来计算最佳时间戳,通常会优先使用PTS。
示例代码片段,可以获取视频帧的真实时间戳:
AVStream videoStream = container.getStream(videoStreamIndex);
int timeBaseNum = videoStream.getTimeBase().num();
int timeBaseDen = videoStream.getTimeBase().den();
// 解码一帧视频
AVPacket packet = new AVPacket();
av_read_frame(container, packet);
AVFrame frame = avcodec_alloc_frame();
int gotFrame = 0;
avcodec_decode_video2(decoder, frame, gotFrame, packet);
// 获取最佳时间戳
long bestEffortTimestamp = av_frame_get_best_effort_timestamp(frame);
if (bestEffortTimestamp != AV_NOPTS_VALUE) {
// 计算真实时间戳
long realTimestamp = (long) (bestEffortTimestamp * 1000.0 * timeBaseNum / timeBaseDen);
}
在这个示例中,我们首先获取了视频流的时间基准。然后,解码一个视频帧,并使用av_frame_get_best_effort_timestamp()函数获取最佳时间戳。最后,我们使用视频流的时间基准,以毫秒为单位计算出真实时间戳。
基于最新版ChatGPT4的回答,望采纳!!!有其他问题也可以询问我哦💕(最新版更智能,功能更加强大):
您可以通过使用FFmpeg的AVStream.time_base属性和AVFrame.best_effort_timestamp属性来获取RTSP流的时间戳。以下是一个示例代码,演示如何获取绝对时间戳:
java
import org.bytedeco.ffmpeg.avcodec.AVPacket;
import org.bytedeco.ffmpeg.avutil.AVFrame;
import org.bytedeco.javacv.*;
import java.util.concurrent.TimeUnit;
public class RTSPCapture {
public static void main(String[] args) throws FrameGrabber.Exception, StreamException, InterruptedException {
String rtspUrl = "rtsp://example.com/stream"; // 您的 RTSP URL
FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(rtspUrl);
grabber.start();
// 获取流的time_base属性
double timeBase = av_q2d(grabber.getFormatContext().streams(0).time_base());
while (true) {
Frame frame = grabber.grab();
if (frame == null) {
break;
}
if (frame instanceof VideoFrame) {
AVFrame avFrame = ((VideoFrame) frame).avFrame();
// 获取帧的best_effort_timestamp属性
long timestamp = avFrame.best_effort_timestamp();
// 将相对时间戳转换为绝对时间戳
long absoluteTimestamp = (long) (timestamp * timeBase * 1000);
// 将绝对时间戳转换为可读格式
String formattedTime = String.format("%02d:%02d:%02d.%03d",
TimeUnit.MILLISECONDS.toHours(absoluteTimestamp),
TimeUnit.MILLISECONDS.toMinutes(absoluteTimestamp) % TimeUnit.HOURS.toMinutes(1),
TimeUnit.MILLISECONDS.toSeconds(absoluteTimestamp) % TimeUnit.MINUTES.toSeconds(1),
absoluteTimestamp % 1000);
System.out.println("Absolute timestamp: " + formattedTime);
}
}
grabber.stop();
}
// 辅助方法,将AVRational转换为double
private static double av_q2d(org.bytedeco.ffmpeg.avutil.AVRational a) {
return a.num() / (double) a.den();
}
}
在上述代码中,我们首先获取流的time_base属性,并将其用作相对时间戳(即best_effort_timestamp)的比例因子,然后将其转换为绝对时间戳。最后,我们使用String.format()方法将绝对时间戳格式化为可读的时间格式。
所有画面的真实时间都遵循如下公式:
Time = PTS * 1/时间基
因此一个视频的总时长为:
Time = Duration_PTS * 1/时间基
我觉得要获取RTSP流的绝对时间戳,可以使用以下步骤:
1、获取取RTSP流中的帧的PTS和DTS,这可以通过使用FFmpeg的AVFrame结构体的pts和pkt_dts字段来完成。
2、获取RTSP流的开始时间戳,这可以通过使用FFmpeg的AVFormatContext结构体的start_time字段来完成。
3、计算帧的真实时间戳,将帧的PTS或DTS加上RTSP流的开始时间戳,就可以得到帧的真实时间戳。
以下代码为检索pts,dts实例代码:
// Grab a frame from the input stream
Frame frame = grabber.grab();
if (frame != null) {
// Get the packet associated with the frame
AVPacket packet = frame.opaque;
// Get the packet's pts or dts timestamp (whichever is available)
long timestamp = packet.pts != AV_NOPTS_VALUE ? packet.pts : packet.dts;
// Convert the timestamp from the packet's time base to seconds
double timestampInSeconds = timestamp * av_q2d(packet.time_base);
}
参考GPT和自我理解,思路如下:
您可以使用JavaCV中的FFmpegFrameGrabber来获取RTSP流中每一帧的时间戳。这个时间戳是相对于第一帧的时间戳,因此您可以通过把它与第一帧的时间戳相加来得到绝对时间戳。
示例代码片段:
FFmpegFrameGrabber grabber = new FFmpegFrameGrabber("rtsp://your_rtsp_stream_url");
grabber.start();
long firstTimestamp = 0; // 第一帧时间戳
long currentTimestamp = 0; // 当前帧时间戳
while (true) {
Frame frame = grabber.grab();
if (frame == null) {
break;
}
if (firstTimestamp == 0) { // 如果是第一帧
firstTimestamp = frame.timestamp;
}
currentTimestamp = frame.timestamp;
// 计算绝对时间戳
long absoluteTimestamp = firstTimestamp + currentTimestamp;
// 在这里处理您的代码逻辑,并使用absoluteTimestamp作为需要的绝对时间戳
}
grabber.stop();
请注意,由于网络延迟和音视频处理等因素,实际上获取到的时间戳可能与真实时间存在偏差。
该回答引用ChatGPT GPT-4
1、引入所需的依赖项(Maven为例):
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacv-platform</artifactId>
<version>1.5.7</version>
</dependency>
2、使用JavaCV从RTSP流获取AVPacket:
import org.bytedeco.ffmpeg.avcodec.*;
import org.bytedeco.ffmpeg.avformat.*;
import org.bytedeco.ffmpeg.avutil.*;
import org.bytedeco.javacv.*;
import static org.bytedeco.ffmpeg.global.avcodec.*;
import static org.bytedeco.ffmpeg.global.avformat.*;
import static org.bytedeco.ffmpeg.global.avutil.*;
public class RTSPStream {
public static void main(String[] args) {
AVFormatContext inputContext = new AVFormatContext(null);
int ret;
avformat_network_init();
// 打开RTSP流
String rtspUrl = "rtsp://your_rtsp_url";
if ((ret = avformat_open_input(inputContext, rtspUrl, null, null))
String rtspUrl = "rtsp://your_rtsp_url";
if ((ret = avformat_open_input(inputContext, rtspUrl, null, null)) < 0) {
System.out.println("Error opening input");
return;
}
// 获取流信息
if (avformat_find_stream_info(inputContext, (AVDictionary) null) < 0) {
System.out.println("Error finding stream info");
return;
}
// 遍历所有流,找到视频流
int videoStreamIndex = -1;
for (int i = 0; i < inputContext.nb_streams(); i++) {
if (inputContext.streams(i).codecpar().codec_type() == AVMEDIA_TYPE_VIDEO) {
videoStreamIndex = i;
break;
}
}
if (videoStreamIndex == -1) {
System.out.println("No video stream found");
return;
}
// 获取视频流
AVStream videoStream = inputContext.streams(videoStreamIndex);
// 循环读取AVPacket并处理
AVPacket packet = av_packet_alloc();
while (av_read_frame(inputContext, packet) >= 0) {
if (packet.stream_index() == videoStreamIndex) {
// 获取原始PTS和DTS
long rawPts = packet.pts();
long rawDts = packet.dts();
// 将原始PTS和DTS转换为可读格式(例如,以秒为单位)
double timeBase = av_q2d(videoStream.time_base());
double ptsInSeconds = rawPts * timeBase;
double dtsInSeconds = rawDts * timeBase;
// 输出时间戳信息
System.out.printf("PTS: %f, DTS: %f\n", ptsInSeconds, dtsInSeconds);
// 处理AVPacket的其它部分,例如解码、存储等
}
// 释放AVPacket资源
av_packet_unref(packet);
}
// 释放资源
av_packet_free(packet);
avformat_close_input(inputContext);
avformat_network_deinit();
回答引用ChatGPT:
1 您可以通过以下方式获取 RTSP 流的真实时间戳:
获取 RTSP 流的帧速率(即每秒钟传输的帧数)和起始时间戳。您可以使用 FFmpeg 命令 ffprobe 获取这些信息。例如,以下命令将返回 RTSP 流的基本信息:
ffprobe -v quiet -print_format json -show_streams rtsp://example.com/stream
2 使用帧速率和起始时间戳计算每一帧的绝对时间戳。例如,假设 RTSP 流的起始时间戳为 1600000000,帧速率为 30 帧每秒,当前帧的 pts 为 3000,则当前帧的绝对时间戳为:
1600000000 + (3000 / 30) = 1600001000
3 因此,您可以使用上面的公式来计算每一帧的绝对时间戳。
在 JavaCV 中,您可以使用 av_q2d() 函数将时间戳从 AVStream.time_base 转换为秒。例如,以下代码将计算出从起始时间戳到当前帧的时间(以秒为单位):
AVPacket pkt = new AVPacket();
// 从 RTSP 流中读取一帧
if (av_read_frame(ic, pkt) >= 0) {
AVStream stream = ic.streams(pkt.stream_index());
AVRational timeBase = stream.time_base();
long pts = pkt.pts();
double timestamp = pts * av_q2d(timeBase);
// 计算当前帧的绝对时间戳
double absoluteTimestamp = startTime + timestamp;
}
其中,ic 是 AVFormatContext 对象,代表了输入流的上下文信息。startTime 是起始时间戳,可以从 ffprobe 命令中获取。
需要注意的是,AVPacket 中的时间戳是相对时间戳,它的单位是 AVStream.time_base。因此,在计算绝对时间戳之前,您需要将时间戳转换为以秒为单位的时间戳。在上面的代码中,我们使用了 av_q2d() 函数来进行时间戳转换。
最后,您可以将绝对时间戳用作您需要的时间戳。如果您需要将绝对时间戳转换为其他时间格式,可以使用 Java 提供的时间库(如 java.time)。
该回答引用ChatGPT以下答案由GPT-3.5大模型与博主波罗歌共同编写:
在使用Javacv拉取rtsp流时,获取真实时间戳的方法如下:
import org.bytedeco.javacpp.opencv_core.*;
import org.bytedeco.javacpp.avformat.*;
import org.bytedeco.javacpp.avcodec.*;
import org.bytedeco.javacpp.avutil.*;
import org.bytedeco.javacpp.swscale.*;
AVFormatContext inputContext;
AVCodecContext decoderContext;
AVFrame inputFrame, outputFrame;
AVStream inputStream;
AVPacket inputPacket;
SwigCallback allocatedFrameDimensionsCallback;
SwigCallback allocatedFrameCallback;
int videoStreamIndex;
int64_t firstPts, lastPts, ptsDiff;
int64_t firstDts, lastDts, dtsDiff;
int64_t startTime;
AVRational streamTimeBase, outputTimeBase;
public void init() {
// 打开输入
if (avformat_open_input(inputContext, inputFile.getAbsolutePath(), null, null) != 0) {
System.out.println("Could not open input file " + inputFile);
System.exit(1);
}
// 找到流信息
if (avformat_find_stream_info(inputContext, (AVDictionary) null) < 0) {
System.out.println("Could not find stream information");
System.exit(1);
}
// 找到视频流
videoStreamIndex = av_find_best_stream(inputContext, AVMEDIA_TYPE_VIDEO, -1, -1, null, 0);
if (videoStreamIndex < 0) {
System.out.println("Could not find video stream in the input file");
System.exit(1);
}
// 获取流信息
inputStream = inputContext.streams(videoStreamIndex);
streamTimeBase = inputStream.time_base();
// 打印相关信息
av_dump_format(inputContext, 0, inputFile.getAbsolutePath(), 0);
// 查找解码器
AVCodec codec = avcodec_find_decoder(inputStream.codecpar().codec_id());
decoderContext = avcodec_alloc_context3(codec);
// 设置解码器参数
if (avcodec_parameters_to_context(decoderContext, inputStream.codecpar()) < 0) {
System.out.println("Failed to copy codec parameters to decoder context");
System.exit(1);
}
// 打开解码器
if (avcodec_open2(decoderContext, codec, null) < 0) {
System.out.println("Failed to open codec");
System.exit(1);
}
// 初始化输入帧
inputFrame = av_frame_alloc();
if (inputFrame == null) {
System.out.println("Could not allocate input frame");
System.exit(1);
}
// 初始化输出帧
outputFrame = av_frame_alloc();
if (outputFrame == null) {
System.out.println("Could not allocate output frame");
System.exit(1);
}
// 初始化输出包裹
av_init_packet(inputPacket);
if (inputPacket == null) {
System.out.println("Could not allocate input packet");
System.exit(1);
}
// 分配空间,获得回调
allocatedFrameDimensionsCallback = new SwigCallback() {
public long getCallback() {
return frameAllocatedDimensions();
}
};
allocatedFrameCallback = new SwigCallback() {
public long getCallback() {
return frameAllocated();
}
};
}
private long frameAllocatedDimensions() {
return avcodec_align_dimensions2(decoderContext, outputFrame.width(), outputFrame.height(), null);
}
private long frameAllocated() {
return 0;
}
private long getAbsoluteTimestamp(AVPacket packet) {
// 获取时间戳
long pts = av_packet_get_pts(packet);
long dts = av_packet_get_dts(packet);
// 更新时间戳差
if (firstPts == 0) {
firstPts = pts;
}
lastPts = pts;
ptsDiff = lastPts - firstPts;
if (firstDts == 0) {
firstDts = dts;
}
lastDts = dts;
dtsDiff = lastDts - firstDts;
if (startTime == 0) {
startTime = System.currentTimeMillis();
}
// 获取流时间基,以及获取视频帧时间基
AVRational avTs = packet.stream_index() == videoStreamIndex ? streamTimeBase : AV_TIME_BASE_Q;
// 获取绝对时间戳(单位为毫秒)
long time = (pts * avTs.den() * 1000) / (avTs.num() * AV_TIME_BASE);
long absoluteTime = startTime + time;
return absoluteTime;
}
while (av_read_frame(inputContext, inputPacket) >= 0) {
// 如果包是视频包
if (inputPacket.stream_index() == videoStreamIndex) {
// 解码数据
int response = avcodec_send_packet(decoderContext, inputPacket);
while (response >= 0) {
response = avcodec_receive_frame(decoderContext, inputFrame);
if (response == AVERROR_EOF || response == AVERROR_EAGAIN()) {
break;
}
if (response < 0) {
System.out.println("Error decoding video frame");
System.exit(1);
}
// 复制解码后的数据到输出帧
av_image_copy(outputFrame.data(), outputFrame.linesize(), new PointerPointer<>(inputFrame.data()), inputFrame.linesize(), decoderContext.pix_fmt(),
decoderContext.width(), decoderContext.height());
// 获取时间戳
long absoluteTimestamp = getAbsoluteTimestamp(inputPacket);
}
}
av_packet_unref(inputPacket);
}
完整代码示例:
import org.bytedeco.javacpp.*;
import org.bytedeco.javacpp.annotation.*;
import org.bytedeco.javacpp.avcodec.*;
import org.bytedeco.javacpp.avformat.*;
import org.bytedeco.javacpp.avutil.*;
import org.bytedeco.javacpp.postproc.*;
import org.bytedeco.javacpp.swresample.*;
import org.bytedeco.javacpp.swscale.*;
import java.io.*;
public class Test {
private static File inputFile = new File("your_file_path");
AVFormatContext inputContext;
AVCodecContext decoderContext;
AVFrame inputFrame, outputFrame;
AVStream inputStream;
AVPacket inputPacket;
SwigCallback allocatedFrameDimensionsCallback;
SwigCallback allocatedFrameCallback;
int videoStreamIndex;
int64_t firstPts, lastPts, ptsDiff;
int64_t firstDts, lastDts, dtsDiff;
int64_t startTime;
AVRational streamTimeBase, outputTimeBase;
public static void main(String[] args) {
Test test = new Test();
test.init();
test.getAbsoluteTimestamp(test.inputPacket);
}
public void init() {
// 初始化格式上下文
inputContext = new AVFormatContext(null);
// 打开输入
if (avformat_open_input(inputContext, inputFile.getAbsolutePath(), null, null) != 0) {
System.out.println("Could not open input file " + inputFile);
System.exit(1);
}
// 找到流信息
if (avformat_find_stream_info(inputContext, (AVDictionary) null) < 0) {
System.out.println("Could not find stream information");
System.exit(1);
}
// 找到视频流
videoStreamIndex = av_find_best_stream(inputContext, AVMEDIA_TYPE_VIDEO, -1, -1, null, 0);
if (videoStreamIndex < 0) {
System.out.println("Could not find video stream in the input file");
System.exit(1);
}
// 获取流信息
inputStream = inputContext.streams(videoStreamIndex);
streamTimeBase = inputStream.time_base();
// 打印相关信息
av_dump_format(inputContext, 0, inputFile.getAbsolutePath(), 0);
// 查找解码器
AVCodec codec = avcodec_find_decoder(inputStream.codecpar().codec_id());
decoderContext = avcodec_alloc_context3(codec);
if (decoderContext == null) {
System.out.println("Could not allocate decoder context");
System.exit(1);
}
// 设置解码器参数
if (avcodec_parameters_to_context(decoderContext, inputStream.codecpar()) < 0) {
System.out.println("Failed to copy codec parameters to decoder context");
System.exit(1);
}
// 打开解码器
if (avcodec_open2(decoderContext, codec, null) < 0) {
System.out.println("Failed to open codec");
System.exit(1);
}
// 初始化输入帧
inputFrame = av_frame_alloc();
if (inputFrame == null) {
System.out.println("Could not allocate input frame");
System.exit(1);
}
// 初始化输出帧
outputFrame = av_frame_alloc();
if (outputFrame == null) {
System.out.println("Could not allocate output frame");
System.exit(1);
}
// 初始化输出包裹
inputPacket = new AVPacket();
av_init_packet(inputPacket);
if (inputPacket == null) {
System.out.println("Could not allocate input packet");
System.exit(1);
}
// 分配空间,获得回调
allocatedFrameDimensionsCallback = new SwigCallback() {
public long getCallback() {
return frameAllocatedDimensions();
}
};
allocatedFrameCallback = new SwigCallback() {
public long getCallback() {
return frameAllocated();
}
};
}
private long frameAllocatedDimensions() {
return avcodec_align_dimensions2(decoderContext, outputFrame.width(), outputFrame.height(), null);
}
private long frameAllocated() {
return 0;
}
private long getAbsoluteTimestamp(AVPacket packet) {
// 获取时间戳
long pts = av_packet_get_pts(packet);
long dts = av_packet_get_dts(packet);
// 更新时间戳差
if (firstPts == 0) {
firstPts = pts;
}
lastPts = pts;
ptsDiff = lastPts - firstPts;
if (firstDts == 0) {
firstDts = dts;
}
lastDts = dts;
dtsDiff = lastDts - firstDts;
if (startTime == 0) {
startTime = System.currentTimeMillis();
}
// 获取流时间基,以及获取视频帧时间基
AVRational avTs = packet.stream_index() == videoStreamIndex ? streamTimeBase : AV_TIME_BASE_Q;
// 获取绝对时间戳(单位为毫秒)
long time = (pts * avTs.den() * 1000) / (avTs.num() * AV_TIME_BASE);
long absoluteTime = startTime + time;
return absoluteTime;
}
}
如果我的回答解决了您的问题,请采纳!
要将相对时间戳转换成绝对时间戳,需要获取视频流的起始时间戳,也就是基准时间戳。通常情况下可以通过 FFmpeg 的 av_find_stream_info() 函数获取到媒体文件的码流信息,在其中可以找到视频流的起始时间戳(AVStream.start_time),它表示时间戳的起始值。
然后可以通过计算当前帧的时间戳与基准时间戳之间的差值,得到当前帧的绝对时间戳。示例代码如下:
import static org.bytedeco.ffmpeg.global.avcodec.*;
import static org.bytedeco.ffmpeg.global.avformat.*;
import static org.bytedeco.ffmpeg.global.avutil.*;
public class Main {
public static void main(String[] args) {
AVFormatContext formatContext = new AVFormatContext(null);
int ret = avformat_open_input(formatContext, "rtsp://your.rtsp.url", null, null);
if (ret < 0) {
System.err.println("Failed to open input!");
System.exit(1);
}
ret = avformat_find_stream_info(formatContext, null);
if (ret < 0) {
System.err.println("Failed to find stream info!");
System.exit(1);
}
AVCodec codec = null;
AVCodecParameters codecpar = null;
AVStream stream = null;
int videoStreamIndex = -1;
for (int i = 0; i < formatContext.nb_streams(); i++) {
AVStream currStream = formatContext.streams(i);
codecpar = currStream.codecpar();
codec = avcodec_find_decoder(codecpar.codec_id());
if (codec == null) {
continue;
}
if (codecpar.codec_type() == AVMEDIA_TYPE_VIDEO) {
stream = currStream;
videoStreamIndex = i;
break;
}
}
if (stream == null) {
System.err.println("Failed to find video stream!");
System.exit(1);
}
AVCodecContext codecContext = avcodec_alloc_context3(codec);
if (codecContext == null) {
System.err.println("Failed to allocate codec context!");
System.exit(1);
}
ret = avcodec_parameters_to_context(codecContext, codecpar);
if (ret < 0) {
System.err.println("Failed to copy codec parameters to context!");
System.exit(1);
}
ret = avcodec_open2(codecContext, codec, null);
if (ret < 0) {
System.err.println("Failed to open codec!");
System.exit(1);
}
AVPacket packet = new AVPacket();
av_init_packet(packet);
long baseTimestamp = stream.start_time(); // 获取视频流的起始时间戳(基准时间戳)
AVRational timeBase = stream.time_base();
while (av_read_frame(formatContext, packet) >= 0) {
if (packet.stream_index() != videoStreamIndex) {
av_packet_unref(packet);
continue;
}
AVFrame frame = av_frame_alloc();
if (frame == null) {
System.err.println("Failed to allocate frame!");
System.exit(1);
}
ret = avcodec_send_packet(codecContext, packet);
if (ret < 0) {
System.err.println("Error sending packet to decoder!");
System.exit(1);
}
while (ret >= 0) {
ret = avcodec_receive_frame(codecContext, frame);
if (ret == AVERROR_EOF || ret == AVERROR_EAGAIN) {
break;
}
if (ret < 0) {
System.err.println("Failed to receive frame from decoder!");
System.exit(1);
}
long dts = frame.best_effort_timestamp(); // 获取帧的 DTS (相对时间戳)
long pts = av_frame_get_best_effort_timestamp(frame); // 获取帧的 PTS (相对时间戳)
long duration = frame.pkt_duration(); // 获取帧的 duration (相对时间戳)
// 计算当前帧的绝对时间戳
long absoluteTimestamp = baseTimestamp + dts * (timeBase.num() * 1000000L / timeBase.den());
// TODO: 处理当前帧,使用 absoluteTimestamp 作为当前帧的时间戳
av_frame_unref(frame);
}
av_packet_unref(packet);
}
avcodec_free_context(codecContext);
avformat_close_input(formatContext);
avformat_free_context(formatContext);
}
}
其中,AVStream.start_time 表示视频流的起始时间戳,AVStream.time_base 表示时间基准,用于将相对时间戳转换成绝对时间戳。注意,time_base 的分子表示 1 秒的时间单位数,分母表示每个时间单位有多少个基本时间单位,例如 time_base 是 1/90000,则表示 1 秒有 90000 个基本时间单位。
在上述代码中,请将"rtsp://your.rtsp.url"替换成你的 RTSP 视频流地址,然后根据自己的需求修改 TODO 部分的代码来处理当前帧。
基于new Bing 的精确模式的回答:
使用ffmpeg的av_rescale_q或av_rescale函数,将javacv获取的dts和pts从1000000为基转换为90000为基,这样就可以和rtp_timestamp对应起来。
使用ffmpeg的rtcp_parse_packet函数,获取rtcp_ntp_timestamp和rtcp_timestamp,这两个时间戳分别是绝对时间和相对时间,可以用来计算rtp_timestamp的偏移量。
使用ffmpeg的rtp_parse_packet_internal函数,获取rtp_timestamp,这个时间戳是网络传输时的时间基,可以用来同步音视频流。
希望这些方法能对你有所帮助。😊