我的需求是验证Java能否实现将已有的RTSP流A转为RTSP流B。
可以的,需要引入这个版本的javacv(老板会报直接错,用这个版本能播放个10来秒才报错,不过我解决标题这个问题后就没问题了)
这个错误的意思是当前包的编译时间戳(DTS)比上一次的DTS小,然后ffmpeg不允许这种情况,所以就报错了。
解决办法就是当出现这种情况时,让当前包的dts 赋值为 上一次dts+1,保证比上一次dts大就行了。
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacv-platform</artifactId>
<version>1.5.9</version>
</dependency>
关键代码为:
if (packet.pts() == AV_NOPTS_VALUE) {
if (packet.dts() != AV_NOPTS_VALUE) {
packet.pts(packet.dts());
lastDTS = packet.dts();
} else {
packet.pts(lastDTS + 1);
packet.dts(packet.pts());
lastDTS = packet.pts();
}
} else {
if (packet.dts() != AV_NOPTS_VALUE) {
if (packet.dts() < lastDTS) {
packet.dts(lastDTS + 1);
}
lastDTS = packet.dts();
} else {
packet.dts(packet.pts());
lastDTS = packet.dts();
}
}
完整的代码为:
package org.jfjy.ecctv.core.jtr;
import org.bytedeco.ffmpeg.avcodec.AVPacket;
import org.bytedeco.ffmpeg.avformat.AVFormatContext;
import org.bytedeco.ffmpeg.global.avcodec;
import org.bytedeco.ffmpeg.global.avformat;
import org.bytedeco.ffmpeg.global.avutil;
import org.bytedeco.javacv.*;
import java.text.SimpleDateFormat;
import static org.bytedeco.ffmpeg.global.avutil.AV_NOPTS_VALUE;
/**
* RTSP转RTMP
*
* @author zhout
*/
public class RTSPToRTMPWithAVPacket {
private static final int RECORD_LENGTH = 5000;
private static final boolean AUDIO_ENABLED = false;
public static void main(String[] args) throws FrameRecorder.Exception, FrameGrabber.Exception {
avutil.av_log_set_level(avutil.AV_LOG_DEBUG);
FFmpegLogCallback.set();
String inputFile = "rtsp://127.0.0.1:8554/live";
//String inputFile = "D:\\cData\\rtsp\\orange.mp4";
/* // Decodes-encodes
String outputFile = "/tmp/" + DATE_FORMAT.format(new Date()) + "_frameRecord.mp4";
RTSPToRTMPWithAVPacket.frameRecord(inputFile, outputFile);*/
// copies codec (no need to re-encode)
//String outputFile = "/tmp/" + DATE_FORMAT.format(new Date()) + "_packetRecord.mp4";
String outputFile = "rtsp://127.0.0.1:8554/live2";
RTSPToRTMPWithAVPacket.packetRecord(inputFile, outputFile);
}
public static void frameRecord(String inputFile, String outputFile) throws FrameGrabber.Exception, FrameRecorder.Exception {
int audioChannel = AUDIO_ENABLED ? 1 : 0;
FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(inputFile);
FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(outputFile, 1280, 720, audioChannel);
grabber.start();
recorder.start();
Frame frame;
long t1 = System.currentTimeMillis();
while ((frame = grabber.grabFrame(AUDIO_ENABLED, true, true, false)) != null) {
recorder.record(frame);
if ((System.currentTimeMillis() - t1) > RECORD_LENGTH) {
break;
}
}
recorder.stop();
grabber.stop();
}
public static void packetRecord(String inputFile, String outputFile) throws FrameGrabber.Exception, FrameRecorder.Exception {
int audioChannel = AUDIO_ENABLED ? 1 : 0;
FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(inputFile);
FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(outputFile, 600, 400, audioChannel);
grabber.start();
recorder.setInterleaved(true);
recorder.setVideoOption("tune", "zerolatency");
recorder.setVideoOption("preset", "ultrafast");
recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);
recorder.setFormat("rtsp");
System.out.println("grabber.getFrameRate():" + grabber.getFrameRate());
recorder.setFrameRate(grabber.getFrameRate());
recorder.setGopSize((int) grabber.getFrameRate() * 2);
AVFormatContext formatContext = grabber.getFormatContext();
formatContext.max_interleave_delta(0);
formatContext.flags(avformat.AVFMT_TS_NONSTRICT);
recorder.setTimestamp(0);
recorder.start(formatContext);
AVPacket packet;
long startTime = System.currentTimeMillis();
long videoTS = 0;
long lastDTS = 0;
while ((packet = grabber.grabPacket()) != null) {
if (packet.pts() == AV_NOPTS_VALUE) {
if (packet.dts() != AV_NOPTS_VALUE) {
packet.pts(packet.dts());
lastDTS = packet.dts();
} else {
packet.pts(lastDTS + 1);
packet.dts(packet.pts());
lastDTS = packet.pts();
}
} else {
if (packet.dts() != AV_NOPTS_VALUE) {
if (packet.dts() < lastDTS) {
packet.dts(lastDTS + 1);
}
lastDTS = packet.dts();
} else {
packet.dts(packet.pts());
lastDTS = packet.dts();
}
}
if (packet.pts() < packet.dts()) {
packet.pts(packet.dts());
}
videoTS = 1000 * (System.currentTimeMillis() - startTime);
if (videoTS < 0 || packet.dts() < 0 || packet.pts() < 0) {
continue;
}
// Check for AV drift
System.out.println("videots:%d; recorder timestamp:%d".formatted(videoTS, recorder.getTimestamp()));
if (videoTS > recorder.getTimestamp()) {
System.out.println(
"Lip-flap correction: "
+ videoTS + " : "
+ recorder.getTimestamp() + " -> "
+ (videoTS - recorder.getTimestamp()));
// We tell the recorder to write this frame at this timestamp
recorder.setTimestamp(videoTS);
}
recorder.recordPacket(packet);
}
recorder.stop();
grabber.stop();
}
}