gpt4 book ai didi

java - 如果播放完整流,Red5 RTMPClient 播放示例将开始丢弃消息

转载 作者:行者123 更新时间:2023-12-03 02:59:04 25 4
gpt4 key购买 nike

下面的客户端旨在通过 oflaDemo 应用程序运行,在本地主机上的 red5 上运行。它包含阿凡达电影宣传片。

问题是客户端读取了 15 秒的电影并挂起。为什么?

要编译下面的程序,只需从这里下载red5 http://wiki.red5.org/wiki/1_0_RC1 ,安装并运行它。打开根页面http://localhost:5080并导航到演示安装页面。安装 oflaDemo 示例。然后导航到 oflaDemo 页面并检查它是否正常工作。

然后使用 red5 中的所有 jar 作为库创建新的 Java 项目。使用运行 red5 来运行它。

客户端通过端口1935与服务器通信。

应用程序的结构如下:

1) connect() 方法连接到应用程序

2) connectCallback 根据先前操作的结果创建新流;不使用库函数注入(inject)自定义流类

3) createStreamCallback 它正在注入(inject)流创建的结果

4) 自定义流为MyClientStream;它只是打印发送的内容

在我的机器上,它工作到时间戳 15203 并挂起。

public class SSCCE_RTMPPlayer extends RTMPClient{

private String server = "localhost";
private int port = 1935;
private String application = "oflaDemo";
private String filename = "avatar.flv";

private static boolean finished = false;

public static void main(String[] args) throws InterruptedException {

final SSCCE_RTMPPlayer player = new SSCCE_RTMPPlayer ();
player.connect();

synchronized( SSCCE_RTMPPlayer.class ) {
if( !finished ) SSCCE_RTMPPlayer.class.wait();
}

System.out.println("Ended");
}

public void connect() {
connect(server, port, application, connectCallback);

setExceptionHandler(new ClientExceptionHandler() {

@Override
public void handleException(Throwable throwable) {
throwable.printStackTrace();
}
});
}

private IPendingServiceCallback connectCallback = new IPendingServiceCallback() {
@Override
public void resultReceived(IPendingServiceCall call) {
System.out.println("connectCallback");
invoke("createStream", null, createStreamCallback);
}
};

private IPendingServiceCallback createStreamCallback = new IPendingServiceCallback() {
@Override
public void resultReceived(IPendingServiceCall call) {
Integer streamIdInteger = (Integer) call.getResult();
MyClientStream myClientStream = new MyClientStream();
myClientStream.setStreamId(streamIdInteger.intValue());
myClientStream.setConnection(conn);
conn.addClientStream(myClientStream);


play(streamIdInteger.intValue(), filename, 0, -2);
}
};

protected void onInvoke(RTMPConnection conn, Channel channel, Header header, Notify notify, RTMP rtmp) {
super.onInvoke(conn, channel, header, notify, rtmp);
System.out.println("onInvoke, header = " + header.toString());
System.out.println("onInvoke, notify = " + notify.toString());
System.out.println("onInvoke, rtmp = " + rtmp.toString());

};

public static class MyClientStream extends AbstractClientStream implements IEventDispatcher {

@Override
public void start() {
// TODO Auto-generated method stub

}

@Override
public void stop() {
// TODO Auto-generated method stub

}

@Override
public void close() {
// TODO Auto-generated method stub

}

@Override
public void dispatchEvent(IEvent event) {
System.out.println("AudioListenerClientStream.dispachEvent()" + event.toString());
}

}

}

更新1

使用传统 setStreamEventDispatcher() 的版本的行为方式相同。

public class SSCCE_RTMPPlayer2 extends RTMPClient {

private String server = "localhost";
private int port = 1935;
private String application = "oflaDemo";
private String filename = "avatar.flv";

private static boolean finished = false;

public static void main(String[] args) throws InterruptedException {

final SSCCE_RTMPPlayer2 player = new SSCCE_RTMPPlayer2();
player.connect();

synchronized( SSCCE_RTMPPlayer.class ) {
if( !finished ) SSCCE_RTMPPlayer.class.wait();
}

System.out.println("Ended");
}

public void connect() {

setExceptionHandler(new ClientExceptionHandler() {

@Override
public void handleException(Throwable throwable) {
throwable.printStackTrace();
}
});

setStreamEventDispatcher(streamEventDispatcher);

connect(server, port, application, connectCallback);


}

private IEventDispatcher streamEventDispatcher = new IEventDispatcher() {

@Override
public void dispatchEvent(IEvent event) {
System.out.println("AudioListenerClientStream.dispachEvent()" + event.toString());
}
};

private IPendingServiceCallback connectCallback = new IPendingServiceCallback() {
@Override
public void resultReceived(IPendingServiceCall call) {
System.out.println("connectCallback");
createStream(createStreamCallback);
}
};

private IPendingServiceCallback createStreamCallback = new IPendingServiceCallback() {
@Override
public void resultReceived(IPendingServiceCall call) {
Integer streamIdInteger = (Integer) call.getResult();
play(streamIdInteger.intValue(), filename, 0, -2);
}
};

protected void onInvoke(RTMPConnection conn, Channel channel, Header header, Notify notify, RTMP rtmp) {
super.onInvoke(conn, channel, header, notify, rtmp);

System.out.println("onInvoke, header = " + header.toString());
System.out.println("onInvoke, notify = " + notify.toString());
System.out.println("onInvoke, rtmp = " + rtmp.toString());

/*
ObjectMap<String, String> map = (ObjectMap) notify.getCall().getArguments()[0];
String code = map.get("code");
if (StatusCodes.NS_PLAY_STOP.equals(code)) {

synchronized( SSCCE_RTMPPlayer.class ) {
finished = true;
SSCCE_RTMPPlayer.class.notifyAll();
}

disconnect();
System.out.println("Disconnected");
}
*/
};

}

更新2

我发现挂起后数据包开始丢失。 drop方法是RTMPProtocolEncoder#dropMessage()

更新3

我发现“迟到”正在以实时速度增加。当超过8000时,开始下降。

更新4

更准确地说,在服务器端,进程大约在 8 秒后开始丢弃数据包。这可能是 8000 的值,即容差时间。与此同时,数据包时间戳达到大约 15-16 秒。客户端一直在播放直到此时才停止。

所以这张图显示服务器比客户端运行快两倍,当它达到某个限制时,它不会等待而是开始丢弃数据包。

看起来正确的行为会等到客户端达到时间戳并继续......

更新5

可能 Client Stream 类并不打算监听来自服务器的流,因此不包含适当的同步逻辑?

神奇的解决方案

在使用 oflaDemo 客户端和我的应用程序播放流时观察日志中的差异时,我发现标准客户端报告的缓冲区大小为 5000 毫秒,而我的客户端则没有。我不明白这是如何发挥作用的,但是当我将 RTMP ping 添加到我的应用程序时,它开始工作。魔法线如下

conn.ping(new Ping(Ping.CLIENT_BUFFER, streamId, 5000));

由于它的作用只是通过其值来改变迟到情况,我认为总体问题是由于 red5 bug 造成的,这使得它无法正确计算迟到情况。

最佳答案

某些文件的视频流出现问题,导致播放停止。但我已经在服务器中修复了它;使用修订版 4329 或更高版本。供您引用的问题在这里:http://code.google.com/p/red5/issues/detail?id=200

关于java - 如果播放完整流,Red5 RTMPClient 播放示例将开始丢弃消息,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10047898/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com