- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在使用 https://jitsi.org/Projects/LibJitsi 处的 Java 库.
我想通过 RTP 流式传输 H264 视频(在本例中视频是桌面/屏幕流),然后渲染它。我可以弄清楚如何流式传输它,但不知道如何渲染流。给定以下代码(完全可编译并可使用 Libjitsi Jars 和 native 库运行),接下来我该怎么做才能将视频流渲染到 Swing JFrame 或 JPanel 中?显然有某种 JMF JAWTRenderer 或者我可以使用 Java Media Framework (JMF)、Freedom for Media in Java (FMJ)、Swing 嵌入中的 JavaFX 或带有 VLCj 库的 VLC 媒体播放器 Swing 嵌入。将该 RTP 视频流渲染到 Java Swing 应用程序中的最佳(最简单、性能良好、无错误、不弃用)方法是什么?
此外,在最底部,我还有一些相关问题。
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketException;
import java.util.List;
import java.util.Map;
import org.jitsi.service.libjitsi.LibJitsi;
import org.jitsi.service.neomedia.DefaultStreamConnector;
import org.jitsi.service.neomedia.MediaDirection;
import org.jitsi.service.neomedia.MediaService;
import org.jitsi.service.neomedia.MediaStream;
import org.jitsi.service.neomedia.MediaStreamTarget;
import org.jitsi.service.neomedia.MediaType;
import org.jitsi.service.neomedia.MediaUseCase;
import org.jitsi.service.neomedia.StreamConnector;
import org.jitsi.service.neomedia.device.MediaDevice;
import org.jitsi.service.neomedia.format.MediaFormat;
/**
* This class streams screen recorded video. It can either send an H264 encoded
* RTP stream or receive one depending on the value of the variable
* isReceivingVideo_.
*/
public class VideoStreamer {
// Set to false if sending video, set to true if receiving video.
private static final boolean isReceivingVideo_ = true;
public final MediaService mediaService_;
private final Map<MediaFormat, Byte> RTP_payload_number_map_;
public static final int LOCAL_BASE_PORT_NUMBER = 15000;
public static final String REMOTE_HOST_IP_ADDRESS = "127.0.0.1";
public static final int REMOTE_BASE_PORT_NUMBER = 10000;
private MediaStream videoMediaStream_;
private final int localBasePort_;
private final InetAddress remoteAddress_;
private final int remoteBasePort_;
/**
* Initializes a new VideoStreamer instance which is to send or receive
* video from a specific host and a specific port.
*
* @param isReceiver - true if this instance of VideoStreamer is receiving a
* video stream, false if it is sending a video stream.
*/
public VideoStreamer(boolean isReceiver) throws IOException {
this.remoteAddress_ = InetAddress.getByName(REMOTE_HOST_IP_ADDRESS);
mediaService_ = LibJitsi.getMediaService();
RTP_payload_number_map_ = mediaService_.getDynamicPayloadTypePreferences();
if (isReceiver) {
this.localBasePort_ = LOCAL_BASE_PORT_NUMBER;
this.remoteBasePort_ = REMOTE_BASE_PORT_NUMBER;
startVideoStream(MediaDirection.RECVONLY);
} else {
// switch the local and remote ports for the transmitter so they hook up with the receiver.
this.localBasePort_ = REMOTE_BASE_PORT_NUMBER;
this.remoteBasePort_ = LOCAL_BASE_PORT_NUMBER;
startVideoStream(MediaDirection.SENDONLY);
}
}
/**
* Initializes the receipt of video, starts it, and tries to record any
* incoming packets.
*
* @param intended_direction either sending or receiving an RTP video
* stream.
*/
public final void startVideoStream(final MediaDirection intended_direction) throws SocketException {
final MediaType video_media_type = MediaType.VIDEO;
final int local_video_port = localBasePort_;
final int remote_video_port = remoteBasePort_;
MediaDevice video_media_device = mediaService_.getDefaultDevice(video_media_type, MediaUseCase.DESKTOP);
final MediaStream video_media_stream = mediaService_.createMediaStream(video_media_device);
video_media_stream.setDirection(intended_direction);
// Obtain the list of formats that are available for a specific video_media_device and pick H264 if availible.
MediaFormat video_format = null;
final List<MediaFormat> supported_video_formats = video_media_device.getSupportedFormats();
for (final MediaFormat availible_video_format : supported_video_formats) {
final String encoding = availible_video_format.getEncoding();
final double clock_rate = availible_video_format.getClockRate();
if (encoding.equals("H264") && clock_rate == 90000) {
video_format = availible_video_format;
}
}
if (video_format == null) {
System.out.println("You do not have the H264 video codec");
System.exit(-1);
}
final byte dynamic_RTP_payload_type_for_H264 = getRTPDynamicPayloadType(video_format);
if (dynamic_RTP_payload_type_for_H264 < 96 || dynamic_RTP_payload_type_for_H264 > 127) {
System.out.println("Invalid RTP payload type number");
System.exit(-1);
}
video_media_stream.addDynamicRTPPayloadType(dynamic_RTP_payload_type_for_H264, video_format);
video_media_stream.setFormat(video_format);
final int local_RTP_video_port = local_video_port + 0;
final int local_RTCP_video_port = local_video_port + 1;
final StreamConnector video_connector = new DefaultStreamConnector(
new DatagramSocket(local_RTP_video_port),
new DatagramSocket(local_RTCP_video_port)
);
video_media_stream.setConnector(video_connector);
final int remote_RTP_video_port = remote_video_port + 0;
final int remote_RTCP_video_port = remote_video_port + 1;
video_media_stream.setTarget(new MediaStreamTarget(
new InetSocketAddress(remoteAddress_, remote_RTP_video_port),
new InetSocketAddress(remoteAddress_, remote_RTCP_video_port))
);
video_media_stream.setName(video_media_type.toString());
this.videoMediaStream_ = video_media_stream;
videoMediaStream_.start();
listenForVideoPackets(video_connector.getDataSocket());
}
public void listenForVideoPackets(final DatagramSocket videoDataSocket) {
new Thread(new Runnable() {
@Override
public void run() {
boolean socket_is_closed = false;
while (!socket_is_closed) {
final byte[] buffer = new byte[5000];
final DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
try {
videoDataSocket.receive(packet);
final byte[] packet_data = new byte[packet.getLength()];
System.arraycopy(packet.getData(), packet.getOffset(), packet_data, 0, packet.getLength());
final StringBuilder string_builder = new StringBuilder();
for (int i = 0; i < ((packet_data.length > 30) ? 30 : packet_data.length); ++i) {
byte b = packet_data[i];
string_builder.append(String.format("%02X ", b));
}
System.out.println("First thirty (or fewer) bytes of packet in hex: " + string_builder.toString());
} catch (SocketException socket_closed) {
System.out.println("Socket is closed");
socket_is_closed = true;
} catch (IOException exception) {
exception.printStackTrace();
}
}
}
}).start();
}
/**
* Checks if the given format exists in the list of formats with listed
* dynamic RTP payload numbers and returns that number.
*
* @param format - format to look up an RTP payload number for
* @return - RTP payload on success or -1 either if payload number cannot be
* found or if payload number is static.
*/
public byte getRTPDynamicPayloadType(final MediaFormat format) {
for (Map.Entry<MediaFormat, Byte> entry : RTP_payload_number_map_.entrySet()) {
final MediaFormat map_format = (MediaFormat) entry.getKey();
final Byte rtp_payload_type = (Byte) entry.getValue();
if (map_format.getClockRate() == format.getClockRate() && map_format.getEncoding().equals(format.getEncoding())) {
return rtp_payload_type;
}
}
return -1;
}
/**
* Close the MediaStream.
*/
public void close() {
try {
this.videoMediaStream_.stop();
} finally {
this.videoMediaStream_.close();
this.videoMediaStream_ = null;
}
}
public static void main(String[] args) throws Exception {
LibJitsi.start();
try {
VideoStreamer rtp_streamer
= new VideoStreamer(isReceivingVideo_);
try {
/*
* Wait for the media to be received and (hopefully) played back.
* Transmits for 1 minute and receives for 30 seconds to allow the
* tranmission to have a delay (if necessary).
*/
final long then = System.currentTimeMillis();
final long waiting_period;
if (isReceivingVideo_) {
waiting_period = 30000;
} else {
waiting_period = 60000;
}
try {
while (System.currentTimeMillis() - then < waiting_period) {
Thread.sleep(1000);
}
} catch (InterruptedException ie) {
}
} finally {
rtp_streamer.close();
}
System.err.println("Exiting VideoStreamer");
} finally {
LibJitsi.stop();
}
}
}
当我运行上述代码时,首先链接 Libjitsi jar 文件(将它们列在“库”下)并通过“-Djava.library.path=/path”指定 native (.so、.dll)库的位置/to/native/libraries”,我首先使用 Final boolean isReceivingVideo = true 运行它,然后使用 Final boolean isReceivingVideo = false 运行另一个实例,然后该应用程序的两个实例相互传输。此外,我还有一个函数 public void ListenForVideoPackets,它以十六进制格式打印出每个数据包的前 30 个字节。当我运行它时,我得到以下十六进制字节值:
我只是一名本科生,所以我的网络知识有限。有人可以解释所有这些十六进制模式的含义吗?为什么RTP数据包的第四个字节总是增加(33、35、37、39等)?为什么第一个数据包只有 16 个字节,而其他数据包却更长?第一个数据包是什么意思?为什么所有数据包的前 12 个左右字节都是相同的,除了第四个字节,它总是在增加?这些数字意味着什么以及如何处理此 RTP 流?
最佳答案
我在一个人的 Libjitsi 示例文件夹(不是库附带的文件夹)中找到了一个名为“PacketPlayer”的文件夹。他们的 git 可能包含一些有用的提示... https://github.com/Metaswitch/libjitsi/tree/master/src/org/jitsi/examples/PacketPlayer
请注意,有一个可能有用的“VideoContainer”类。请参阅https://github.com/jitsi/libjitsi/blob/master/src/org/jitsi/util/swing/VideoContainer.java
此外,前 12 个字节是 RTP header 。使用http://www.siptutorial.net/RTP/header.html处的标题图事实上,上述代码中的 RTP 负载类型为 99,上面的 RTP header 可分解为:
RTP版本:2,填充:0,扩展名:0,CSRC计数:0,[第一个字节]
标记:0,负载类型:99,[第二个字节]
序列号:-11221[第3、第4字节]
时间戳:1082411848
SSRC来源:-504863636
奇怪的是,序列号绝对没有按应有的方式增加 1。它增加了 2。这可能意味着您的数据报套接字正在获取每个其他数据包,而不是每个数据包。
关于java - 在 Swing 中使用 Libjitsi 库处理 H264 编码的 RTP 视频流 - 如何渲染流?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30283485/
我正在尝试从 RTP URL 流式传输 RTP 数据包(正在流式传输音频),例如rtp://@225.0.0.0经过如此多的研究后,我在我的设备中流式传输了 URL 并使用 https://githu
如果客户端想要观看我的 RTSP 服务器上的流,它首先尝试通过 UDP 协议(protocol)设置流。我如何告诉它我的服务器只支持 RTP/AVP/TCP 并且它应该切换传输? 我想终止我服务器上的
我需要在 RTP 数据包中检测 MPEG4 I-Frame。我知道如何删除 RTP header 并在其中获取 MPEG4 帧,但我不知道如何识别 I 帧。 它有特定的签名/标题吗? 最佳答案 好的,
我是 VoIP 新手。我想创建一个使用 RTP 使用客户端/服务器架构流式传输音频的应用程序。可以使用不同的 API,但我需要在核心级别上进行理解。我研究过RFC。任何人都可以建议我如何制作一个音频
我像这样通过 ffmpeg 开始视频传输: ffmpeg -f video4linux2 -i /dev/video0 -vcodec libx264 -preset ultrafast -crf 2
我正在尝试通过 ffserver 从 usbcam 和 mic throw ffmpeg 流式传输视频和音频 我有 2 个错误: - ffmpeg 似乎运行正常,但显示“数据看起来不像 RTP 数据包
来自 Mozilla 网站:https://developer.mozilla.org/en-US/docs/Web/API/Media_Streams_API “一个 MediaStream 由零个
几天来,我已经在寻找如何将 MJPEG rtp 流转换为 MP4 rtp 流的解决方案。 已经尝试过这样的事情: ffmpeg -i rtsp://192.168.10.8:554/stream1/m
我一直在试图找出一种计算以下内容的方法: 带宽、延迟、当前上传和下载速度 . 并且对我为 INBOUND-RTP、OUTBOUND-RTP 和 REMOTE-INBOUND-RTP 获得的值感到困惑。
我需要将一个 .rtp 文件(已使用 RTP 代理录制)转换为 .wav 文件。如果有人知道如何做到这一点,请给我您的解决方案。 提前致谢:) 最佳答案 聚会可能有点晚了,但我最近遇到了同样的问题,我
我正在使用 ffmpeg libavformat 库编写仅视频的 webm 文件。我在我的服务器上收到了 VP8 编码的 rtp 流。我已经成功地将 rtp 字节流(来自 rtp 有效负载)分组到单独
我正在尝试通过 RTP 多播流式传输 .wav 音频文件。我正在使用以下命令: ffmpeg -re -i Melody_file.wav -f rtp rtp://224.0.1.211:5001
关闭。这个问题是off-topic .它目前不接受答案。 想改进这个问题? Update the question所以它是on-topic对于堆栈溢出。 9年前关闭。 Improve this que
我有一个要发送到机顶盒的 RTP 视频流。不幸的是,机顶盒不支持 RTP,我已将其转换为 Smooth Streaming。 我尝试过使用 Wowza Media Server 进行流转换,但没有成功
我试图弄清楚哪个是 RTP 数据包的最大大小。我知道最小 header 大小为 12 个字节,但我没有找到任何有关有效负载的信息。 RTP 数据包的最大大小可能与 UDP 有效负载的最大大小相同吗?我
我们有捕获的 pcap 文件,其中包含每个 rfc6716 的 RTP opus 有效负载,现在我们可以切断 RTP header 并提取 opus 有效负载,我们希望根据规范将有效负载封装到 ogg
我正在尝试通过RTP将AAC音频流传输到Wowza服务器。我设法使其正常工作,但交替听到的声音非常快,然后出现1s的空白。采样率是22050,每个数据包的帧数是1024。 目前,我的时间戳是这样生成的
我正在尝试在我的 iPhone 中接收实时 RTP 音频流,但我不知道如何开始。我正在寻找一些 sample ,但我在任何地方都找不到它们。 我有一个 Windows 桌面应用程序,它从选定的音频接口
我正在尝试用 C 语言构建一个 RTP 数据包度量分析器,但最终遇到了一个奇怪的问题,我正在削减实现细节以便于公开: 由于 RTP 数据包包含在 UDP 中,因此我的套接字使用以下参数进行初始化: s
我有一个客户端和一个服务器,服务器通过封装在 UDP 内的 RTP 数据包发送音频数据。客户端接收数据包。由于 UDP 没有流量控制,客户端会检查数据包的序列号,如果序列号不正确,则重新排列它们。我的
我是一名优秀的程序员,十分优秀!