声明:笔者所使用的为如下代码,另外,数据为某厂家摄像头sdk回调返回的flv数据,第一个回调带有flv的metadata,后续为一个回调byte[]为一帧数据,可以随意丢弃
public class FlvToRtmpPusher extends Thread {
PipedInputStream pipedInputStream;
PipedOutputStream pipedOutputStream;
FFmpegFrameRecorder recorder;
AtomicBoolean exit = new AtomicBoolean(false);
FFmpegFrameGrabber grabber;
public FlvToRtmpPusher() {
pipedInputStream = new PipedInputStream();
try {
pipedOutputStream = new PipedOutputStream(pipedInputStream);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public void run() {
try {
System.out.println("JJ..." + avcodec.AV_CODEC_ID_AAC);
grabber = new FFmpegFrameGrabber(pipedInputStream);
grabber.start();
System.out.println("gggg:" + grabber.getAudioBitrate());
System.out.println("gggg:" + grabber.getSampleRate());
System.out.println("gggg:" + grabber.getAudioCodec());
System.out.println("gggg:" + grabber.getPixelFormat());
System.out.println("gggg:" + grabber.getFormat());
System.out.println("gggg:" + grabber.getVideoCodec());
System.out.println("gggg:" + grabber.getFrameRate());
System.out.println("gggg:" + grabber.getImageWidth());
System.out.println("gggg:" + grabber.getImageHeight());
// recorder.setAudioBitrate(grabber.getAudioBitrate());
recorder = new FFmpegFrameRecorder("rtmp://localhost/live/test", grabber.getImageWidth(), grabber.getImageHeight(), 2);
// recorder.setInterleaved(true);
//recorder.setVideoOption("crf","28");
// recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264); // 28
recorder.setVideoCodec(avcodec.AV_CODEC_ID_NONE); // 28
// recorder.setFormat("flv"); // rtmp的类型
// recorder.setFrameRate(25);
// recorder.setImageWidth(1280);
// recorder.setImageHeight(720);
// recorder.setPixelFormat(avutil.AV_PIX_FMT_YUV420P); // yuv420p
recorder.setSampleRate(grabber.getSampleRate());
// recorder.setAudioCodec(grabber.getAudioCodec());
recorder.setAudioCodec(avcodec.AV_CODEC_ID_NONE);
// recorder.setPixelFormat(grabber.getPixelFormat());
recorder.setFormat(grabber.getFormat());
// recorder.setVideoCodec(grabber.getVideoCodec()); // 28
// recorder.setFrameRate(grabber.getFrameRate());
recorder.setImageWidth(grabber.getImageWidth());
recorder.setImageHeight(grabber.getImageHeight());
recorder.start();
System.out.println("开始执行推流");
while (!exit.get()) {
Frame frame = grabber.grab();
if(frame == null){
continue;
}
recorder.record(frame);
}
grabber.stop();
grabber.release();
recorder.stop();
recorder.release();
pipedInputStream.close();
pipedOutputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public void stopPush() {
this.exit.set(true);
}
public PipedInputStream getPipedInputStream() {
return pipedInputStream;
}
public PipedOutputStream getPipedOutputStream() {
return pipedOutputStream;
}
public FFmpegFrameRecorder getRecorder() {
return recorder;
}
public AtomicBoolean getExit() {
return exit;
}
public FFmpegFrameGrabber getGrabber() {
return grabber;
}
......
//此处为写数据处
pipedOutputStream.write(buffer);
......
wwjj
FFmpegFrameRecorder推流到rtmp如何设置网络缓冲区和超时时间
可以使用 FFmpegFrameRecorder 的 setOption 方法来设置网络缓冲区和超时时间等参数。以下是一个示例代码,可以将网络缓冲区设置为 2 秒,超时时间设置为 10 秒:
import org.bytedeco.javacpp.avcodec;
import org.bytedeco.javacv.FFmpegFrameRecorder;
public class H264Streamer {
public static void main(String\[] args) {
String rtmpUrl = "rtmp\://your.rtmp.server/live/stream";
try {
FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(rtmpUrl, 640, 480);
recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);
recorder.setFormat("flv");
recorder.setPixelFormat(avutil.AV_PIX_FMT_YUV420P);
// set network buffer size to 2 seconds
recorder.setOption("buffer_size", "2M");
recorder.setOption("max_delay", "500000");
recorder.setOption("rtmp_buffer", "100");
recorder.setOption("rtmp_live", "live");
recorder.setOption("rtmp_timeout", "10000");
recorder.start();
// push frames to the RTMP server
// ...
recorder.stop();
recorder.release();
} catch (Exception e) {
e.printStackTrace();
}
}
}
在上面的示例代码中,我们使用 FFmpegFrameRecorder 的 setOption 方法来设置网络缓冲区和超时时间等参数。具体地,我们设置 “buffer_size” 为 “2M”,即网络缓冲区大小为 2MB;设置 “max_delay” 为 “500000”,即最大延迟时间为 500ms;设置 “rtmp_buffer” 为 “100”,即 RTMP 协议缓冲区大小为 100KB;设置 “rtmp_live” 为 “live”,表示推流是实时的;设置 “rtmp_timeout” 为 “10000”,即超时时间为 10 秒。您可以根据自己的需要修改这些参数值。
wwjj
javacv 1.4.4版本也是这么设置么
是的,即使是在 javacv 1.4.4 版本中,您仍然可以使用相同的方式来设置网络缓冲区和超时时间等参数。因为这些参数是通过 FFmpeg 库进行设置的,因此只要您使用的是支持 FFmpeg 的版本,都可以使用相同的方式设置这些参数。
以下是一个示例代码,展示了如何在 javacv 1.4.4 版本中设置网络缓冲区和超时时间等参数:
import org.bytedeco.javacpp.avcodec;
import org.bytedeco.javacpp.avutil;
import org.bytedeco.javacv.FFmpegFrameRecorder;
public class H264Streamer {
public static void main(String\[] args) {
String rtmpUrl = "rtmp\://your.rtmp.server/live/stream";
try {
FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(rtmpUrl, 640, 480);
recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);
recorder.setFormat("flv");
recorder.setPixelFormat(avutil.AV_PIX_FMT_YUV420P);
// set network buffer size to 2 seconds
recorder.setOption("buffer_size", "2M");
recorder.setOption("max_delay", "500000");
recorder.setOption("rtmp_buffer", "100");
recorder.setOption("rtmp_live", "live");
recorder.setOption("rtmp_timeout", "10000");
recorder.start();
// push frames to the RTMP server
// ...
recorder.stop();
recorder.release();
} catch (Exception e) {
e.printStackTrace();
}
}
}
在这个示例代码中,我们同样使用了 FFmpegFrameRecorder 的 setOption 方法来设置网络缓冲区和超时时间等参数。具体来说,我们设置了 “buffer_size”、“max_delay”、“rtmp_buffer”、“rtmp_live” 和 “rtmp_timeout” 这些参数。同样,您可以根据自己的需要修改这些参数值。
wwjj
max_delay、rtmp_buffer、rtmp_timeout具体是什么意思
这里解释一下这三个参数的具体含义:
max_delay:最大延迟时间,用于控制编码器输出数据的延迟。编码器输出的数据可能会因为网络状况不稳定或者其他原因而出现延迟,而这个参数就用于控制最大允许的延迟时间,超过这个时间就会强制输出数据。这个参数的单位是微秒(µs),默认值是 500000 微秒(即 0.5 秒)。
rtmp_buffer:RTMP 协议缓冲区大小,用于控制推流的缓冲区大小。当推流时网络状况不稳定或者带宽波动比较大时,会出现数据传输不稳定的情况。为了缓解这种情况,可以增加 RTMP 协议的缓冲区大小,使得数据能够先缓存在本地,然后再进行传输。这个参数的单位是字节(Byte),默认值是 1024 字节(即 1KB)。
rtmp_timeout:RTMP 协议超时时间,用于控制连接到 RTMP 服务器的超时时间。当推流时连接到 RTMP 服务器的网络状况不稳定时,可能会导致连接超时。为了避免连接超时,可以增加连接超时时间。这个参数的单位是毫秒(ms),默认值是 3000 毫秒(即 3 秒)。
这些参数的值可以根据实际情况进行调整,以达到最佳的推流效果。需要注意的是,增加缓冲区和超时时间会增加推流的稳定性,但也会增加延迟。因此,在实际应用中,需要根据需要平衡推流的稳定性和延迟等因素。
wwjj
rtmp_timeout只是设置连接超时么,传输视频数据超时呢?
您说得对,rtmp_timeout 参数只是设置连接超时,而不是传输视频数据的超时。如果您需要设置传输视频数据的超时,可以使用 setInterleavedRTPTimeout() 方法来设置,它的单位是毫秒(ms),表示接收 RTP 数据包的最大时间间隔。如果超过这个时间间隔还没有收到数据包,则会认为传输超时。
以下是一个示例代码,展示了如何使用 setInterleavedRTPTimeout() 方法来设置传输视频数据的超时时间:
import org.bytedeco.javacpp.avcodec;
import org.bytedeco.javacpp.avutil;
import org.bytedeco.javacv.FFmpegFrameRecorder;
public class H264Streamer {
public static void main(String\[] args) {
String rtmpUrl = "rtmp\://your.rtmp.server/live/stream";
try {
FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(rtmpUrl, 640, 480);
recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);
recorder.setFormat("flv");
recorder.setPixelFormat(avutil.AV_PIX_FMT_YUV420P);
// set network buffer size to 2 seconds
recorder.setOption("buffer_size", "2M");
recorder.setOption("max_delay", "500000");
recorder.setOption("rtmp_buffer", "100");
recorder.setOption("rtmp_live", "live");
recorder.setOption("rtmp_timeout", "10000");
// set interleaved RTP timeout to 5000 ms
recorder.setInterleavedRTPTimeout(5000);
recorder.start();
// push frames to the RTMP server
// ...
recorder.stop();
recorder.release();
} catch (Exception e) {
e.printStackTrace();
}
}
}
在这个示例代码中,我们通过 setInterleavedRTPTimeout() 方法设置了传输视频数据的超时时间为 5000 毫秒(即 5 秒)。需要注意的是,这个方法只适用于使用 RTP 协议进行数据传输的情况。如果您使用的是其他协议(如 RTSP 协议),则需要使用相应的方法来设置超时时间。
wwjj
我使用rtmp
对于 RTMP 协议,您可以使用 setOption() 方法来设置超时时间。具体来说,可以使用 rtmp_send_timeout 和 rtmp_receive_timeout 参数来分别设置发送和接收数据的超时时间。这两个参数的单位是毫秒(ms),分别表示发送数据和接收数据的最大时间间隔。如果超过这个时间间隔还没有发送或接收到数据,则会认为传输超时。
以下是一个示例代码,展示了如何使用 setOption() 方法来设置 RTMP 协议的超时时间:
import org.bytedeco.javacpp.avcodec;
import org.bytedeco.javacpp.avutil;
import org.bytedeco.javacv.FFmpegFrameRecorder;
public class H264Streamer {
public static void main(String\[] args) {
String rtmpUrl = "rtmp\://your.rtmp.server/live/stream";
try {
FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(rtmpUrl, 640, 480);
recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);
recorder.setFormat("flv");
recorder.setPixelFormat(avutil.AV_PIX_FMT_YUV420P);
// set network buffer size to 2 seconds
recorder.setOption("buffer_size", "2M");
recorder.setOption("max_delay", "500000");
recorder.setOption("rtmp_buffer", "100");
recorder.setOption("rtmp_live", "live");
recorder.setOption("rtmp_timeout", "10000");
recorder.setOption("rtmp_send_timeout", "5000");
recorder.setOption("rtmp_receive_timeout", "5000");
recorder.start();
// push frames to the RTMP server
// ...
recorder.stop();
recorder.release();
} catch (Exception e) {
e.printStackTrace();
}
}
}
在这个示例代码中,我们使用 setOption() 方法设置了 RTMP 协议的超时时间。具体来说,我们通过 rtmp_send_timeout 和 rtmp_receive_timeout 参数分别设置了发送和接收数据的超时时间为 5000 毫秒(即 5 秒)。需要注意的是,这些参数的值可以根据实际情况进行调整,以达到最佳的推流效果。
wwjj
这些参数在ffmpeg哪个版本支持
这些参数在 FFmpeg 3.1 版本之后的版本中支持。如果您使用的是旧版本的 FFmpeg,可能无法使用这些参数。
在 FFmpeg 3.1 版本之前的版本中,可以使用一些其他的选项来设置超时时间。例如,可以使用 timeout 选项来设置连接超时时间,使用 stimeout 选项来设置数据发送和接收的超时时间。这些选项的使用方式可以参考 FFmpeg 的官方文档。
需要注意的是,不同的 FFmpeg 版本可能会有一些不同的选项,具体使用哪些选项,需要根据您的 FFmpeg 版本和实际情况来确定。
wwjj
为什么官方描述是设置-rw_timeout这个参数
您提到的 -rw_timeout 参数是 FFmpeg 中的一个选项,用于设置输入或输出媒体文件的读写操作超时时间。该选项可以用于所有支持的协议和容器格式,包括 RTMP 协议和 FLV 容器。
在使用 FFmpeg 进行 RTMP 推流时,可以使用 -rw_timeout 参数来设置超时时间,具体方式为:
ffmpeg -i input_file -c:v copy -c:a copy -f flv -rw_timeout timeout rtmp://server/stream
在这个命令中,-rw_timeout 参数指定了读写操作的超时时间,单位是秒(s)。当读写操作的耗时超过该值时,FFmpeg 将停止读写并报告超时错误。
需要注意的是,-rw_timeout 参数只能用于 FFmpeg 命令行工具,而不能在 JavaCV 中使用。在 JavaCV 中,应该使用 setOption() 方法来设置 RTMP 协议的超时时间,具体方式请参见我之前的回答。
wwjj
FFmpegFrameRecorder是否有缓冲区过大自动丢弃部分帧的方案?
FFmpegFrameRecorder 并没有自带缓冲区过大自动丢弃部分帧的方案。默认情况下,FFmpegFrameRecorder 在推流时,会将视频帧直接传输到 RTMP 服务器,不会进行缓存处理。如果推流的帧率过高,导致缓冲区溢出,就可能会发生帧丢失的情况。
如果需要解决帧丢失问题,可以在代码中增加自定义的缓冲区管理逻辑,实现帧缓存和丢帧处理。具体实现方式可以参考以下步骤:
创建一个帧缓存队列,用于存储待推送的视频帧。在调用 FFmpegFrameRecorder.record() 方法推流之前,将待推送的视频帧加入帧缓存队列中。
启动一个单独的线程,不断从帧缓存队列中取出视频帧,并调用 FFmpegFrameRecorder.record() 方法进行推流。在调用 FFmpegFrameRecorder.record() 方法之前,可以判断当前帧缓存队列中的帧数是否过多,如果过多则可以跳过当前帧,直接取出下一帧进行推流,从而实现丢帧处理。
调整帧缓存队列的大小,使得帧缓存能够满足实际场景的需求。需要根据实际场景中的帧率和延迟要求,合理设置帧缓存队列的大小。
需要注意的是,自定义帧缓存管理逻辑的实现可能会增加代码的复杂度和开发难度。在实际开发中,需要根据具体情况选择是否使用该方案。如果推流场景比较简单,不需要严格的延迟控制和帧率控制,可以直接使用 FFmpegFrameRecorder 进行推流,无需增加额外的缓存逻辑。