Flex:恢复 Spark VideoDisplay 流

发布于 2024-12-20 01:10:20 字数 2208 浏览 6 评论 0原文

编辑 如果有人至少能告诉我如何在流断开连接时接收事件,那就太好了。

这个控件的文档简直太糟糕了。我有一个具有实时视频流的应用程序,我正在寻找一种方法,使 VideoDisplay 控件在发生以下任何特定情况时恢复其连接:

  1. 应用程序启动且流尚未在线。
  2. 应用程序正在流式传输,并且用户与互联网断开连接。
  3. 应用程序正在流式传输,视频服务器崩溃并重新启动。

我正在使用 Wowza Media Server 和 Wirecast 来测试这一点。 1 和 3 不起作用,我不确定 2 是否起作用。我通过添加这段非常有问题的代码来实现第一点工作:

    protected function onMediaPlayerStateChange(event:MediaPlayerStateChangeEvent):void
    {
        if (event.state == MediaPlayerState.PLAYBACK_ERROR)
        {
            var videoSource:DynamicStreamingVideoSource = this.videoDisplay.source as DynamicStreamingVideoSource;

            try
            {
                this.videoDisplay.source = null;
                this.videoDisplay.source = videoSource;
            }
            catch (any:*) {}
        }
    }

如您所见,我需要一个 try/catch 块,因为对 source 的两次调用都会导致异常,但在这些异常之前发生的任何事情似乎都可以解决问题#1.这并不能解决问题 #3,因为当您停止视频服务器时,显然不会发生媒体状态更改事件。

这是我的控制声明:

<s:VideoDisplay id="videoDisplay" click="onVideoStreamClick(event)" mediaPlayerStateChange="onMediaPlayerStateChange(event)" muted="{this.videoMuted}" top="10" width="280" height="220" autoPlay="true" horizontalCenter="0">
    <s:source>
        <s:DynamicStreamingVideoSource id="videoSource" streamType="live" host="{FlexGlobals.topLevelApplication.parameters.videoStreamURL}">
            <s:DynamicStreamingVideoItem id="videoItemLow" streamName="{FlexGlobals.topLevelApplication.parameters.videoLow}" bitrate="{FlexGlobals.topLevelApplication.parameters.videoLowBitrate}" />
            <s:DynamicStreamingVideoItem id="videoItemMedium" streamName="{FlexGlobals.topLevelApplication.parameters.videoMedium}" bitrate="{FlexGlobals.topLevelApplication.parameters.videoMediumBitrate}" />
            <s:DynamicStreamingVideoItem id="videoItemHigh" streamName="{FlexGlobals.topLevelApplication.parameters.videoHigh}" bitrate="{FlexGlobals.topLevelApplication.parameters.videoHighBitrate}" />
        </s:DynamicStreamingVideoSource>
    </s:source>
</s:VideoDisplay>

有谁知道如何使 VideoDisplay 从这些问题中恢复?如有任何帮助,我们将不胜感激,谢谢。

EDIT
If someone can at least tell me how to receive an event when the streams disconnects that would be great.

The documentation for this control is simply horrible. I have an application that will have a live video stream and I'm looking for a way to make the VideoDisplay control restore its connection in case of the occurrence of any of these specific scenarios:

  1. The application starts and the stream is not online yet.
  2. The application is streaming and the user is disconnected from the internet.
  3. The application is streaming and the video server crashes and reboots.

I'm using Wowza Media Server and Wirecast to test this. 1 and 3 don't work, I'm not sure number 2 does. I made number 1 work by adding this very questionable piece of code:

    protected function onMediaPlayerStateChange(event:MediaPlayerStateChangeEvent):void
    {
        if (event.state == MediaPlayerState.PLAYBACK_ERROR)
        {
            var videoSource:DynamicStreamingVideoSource = this.videoDisplay.source as DynamicStreamingVideoSource;

            try
            {
                this.videoDisplay.source = null;
                this.videoDisplay.source = videoSource;
            }
            catch (any:*) {}
        }
    }

As you can see I need a try/catch block since both calls to source cause exceptions, yet whatever happens before those exceptions seems to fix problem #1. This doesn't fix problem #3 because a media state change event apparently doesn't occur when you stop the video server.

This is my control declaration:

<s:VideoDisplay id="videoDisplay" click="onVideoStreamClick(event)" mediaPlayerStateChange="onMediaPlayerStateChange(event)" muted="{this.videoMuted}" top="10" width="280" height="220" autoPlay="true" horizontalCenter="0">
    <s:source>
        <s:DynamicStreamingVideoSource id="videoSource" streamType="live" host="{FlexGlobals.topLevelApplication.parameters.videoStreamURL}">
            <s:DynamicStreamingVideoItem id="videoItemLow" streamName="{FlexGlobals.topLevelApplication.parameters.videoLow}" bitrate="{FlexGlobals.topLevelApplication.parameters.videoLowBitrate}" />
            <s:DynamicStreamingVideoItem id="videoItemMedium" streamName="{FlexGlobals.topLevelApplication.parameters.videoMedium}" bitrate="{FlexGlobals.topLevelApplication.parameters.videoMediumBitrate}" />
            <s:DynamicStreamingVideoItem id="videoItemHigh" streamName="{FlexGlobals.topLevelApplication.parameters.videoHigh}" bitrate="{FlexGlobals.topLevelApplication.parameters.videoHighBitrate}" />
        </s:DynamicStreamingVideoSource>
    </s:source>
</s:VideoDisplay>

Does anyone know how to make the VideoDisplay recover from these issues? Any help is appreciated, thanks.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

烟雨凡馨 2024-12-27 01:10:20

如果有人遇到这个问题,这就是我解决的方法。您需要将视频源设置为空白图像才能停止流式传输,否则它永远不会停止。该解决方案适用于上述所有场景:

    private function resetVideo():void
    {           
        //save current source object
        this.videoEventsDisabled = true;
        var videoSource:DynamicStreamingVideoSource = this.videoDisplay.source as DynamicStreamingVideoSource;

        try //switch to blank image, only this will stop the video stream
        {
            this.videoDisplay.source = "assets/images/video_offline.png";
        }
        catch (any:*) {}

        //wait a few seconds and reset video source
        setTimeout(resetVideoSource, 2000, videoSource);
    }

    private function resetVideoSource(videoSource:DynamicStreamingVideoSource):void
    {
        this.videoEventsDisabled = false;
        this.videoDisplay.source = videoSource;
    }

    protected function onMediaPlayerStateChange(event:MediaPlayerStateChangeEvent):void
    {
        if (this.videoEventsDisabled)
        {
            return;
        }

        //something went wrong
        if (event.state == MediaPlayerState.PLAYBACK_ERROR)
        {
            resetVideo();
        }
    }

    protected function onCurrentTimeChange(event:TimeEvent):void
    {
        if (this.videoEventsDisabled)
        {
            return;
        }

        //if there was a number before, and its suddendly NaN, video is offline
        if (isNaN(event.time) && !isNaN(this.previousVideoTime))
        {
            resetVideo();
        }
        else //store event time for future comparisons
        {
            this.previousVideoTime = event.time;
        }
    }

MXML:

<s:VideoDisplay id="videoDisplay" click="onVideoStreamClick(event)" mediaPlayerStateChange="onMediaPlayerStateChange(event)" currentTimeChange="onCurrentTimeChange(event)" muted="{this.videoMuted}" top="10" width="280" height="220" autoPlay="true" horizontalCenter="0">
    <s:source>
        <s:DynamicStreamingVideoSource id="videoSource" streamType="live" host="{FlexGlobals.topLevelApplication.parameters.videoStreamURL}">
            <s:DynamicStreamingVideoItem id="videoItemLow" streamName="{FlexGlobals.topLevelApplication.parameters.videoLow}" bitrate="{FlexGlobals.topLevelApplication.parameters.videoLowBitrate}" />
            <s:DynamicStreamingVideoItem id="videoItemMedium" streamName="{FlexGlobals.topLevelApplication.parameters.videoMedium}" bitrate="{FlexGlobals.topLevelApplication.parameters.videoMediumBitrate}" />
            <s:DynamicStreamingVideoItem id="videoItemHigh" streamName="{FlexGlobals.topLevelApplication.parameters.videoHigh}" bitrate="{FlexGlobals.topLevelApplication.parameters.videoHighBitrate}" />
        </s:DynamicStreamingVideoSource>
    </s:source>
</s:VideoDisplay>

If anyone has this problem, this is how I solved it. You need to set the video source to a blank image in order to stop the streaming, otherwise it will never, ever stop. This solution works for all scenarios described above:

    private function resetVideo():void
    {           
        //save current source object
        this.videoEventsDisabled = true;
        var videoSource:DynamicStreamingVideoSource = this.videoDisplay.source as DynamicStreamingVideoSource;

        try //switch to blank image, only this will stop the video stream
        {
            this.videoDisplay.source = "assets/images/video_offline.png";
        }
        catch (any:*) {}

        //wait a few seconds and reset video source
        setTimeout(resetVideoSource, 2000, videoSource);
    }

    private function resetVideoSource(videoSource:DynamicStreamingVideoSource):void
    {
        this.videoEventsDisabled = false;
        this.videoDisplay.source = videoSource;
    }

    protected function onMediaPlayerStateChange(event:MediaPlayerStateChangeEvent):void
    {
        if (this.videoEventsDisabled)
        {
            return;
        }

        //something went wrong
        if (event.state == MediaPlayerState.PLAYBACK_ERROR)
        {
            resetVideo();
        }
    }

    protected function onCurrentTimeChange(event:TimeEvent):void
    {
        if (this.videoEventsDisabled)
        {
            return;
        }

        //if there was a number before, and its suddendly NaN, video is offline
        if (isNaN(event.time) && !isNaN(this.previousVideoTime))
        {
            resetVideo();
        }
        else //store event time for future comparisons
        {
            this.previousVideoTime = event.time;
        }
    }

MXML:

<s:VideoDisplay id="videoDisplay" click="onVideoStreamClick(event)" mediaPlayerStateChange="onMediaPlayerStateChange(event)" currentTimeChange="onCurrentTimeChange(event)" muted="{this.videoMuted}" top="10" width="280" height="220" autoPlay="true" horizontalCenter="0">
    <s:source>
        <s:DynamicStreamingVideoSource id="videoSource" streamType="live" host="{FlexGlobals.topLevelApplication.parameters.videoStreamURL}">
            <s:DynamicStreamingVideoItem id="videoItemLow" streamName="{FlexGlobals.topLevelApplication.parameters.videoLow}" bitrate="{FlexGlobals.topLevelApplication.parameters.videoLowBitrate}" />
            <s:DynamicStreamingVideoItem id="videoItemMedium" streamName="{FlexGlobals.topLevelApplication.parameters.videoMedium}" bitrate="{FlexGlobals.topLevelApplication.parameters.videoMediumBitrate}" />
            <s:DynamicStreamingVideoItem id="videoItemHigh" streamName="{FlexGlobals.topLevelApplication.parameters.videoHigh}" bitrate="{FlexGlobals.topLevelApplication.parameters.videoHighBitrate}" />
        </s:DynamicStreamingVideoSource>
    </s:source>
</s:VideoDisplay>
箹锭⒈辈孓 2024-12-27 01:10:20

作为变体,您可以从 NetStream 对象处理 NetStream.Play.PublishNotify。

var _source:DynamicStreamingResource;
_source = new DynamicStreamingResource("rtmp://...", StreamType.LIVE);

var streamItems:Vector.<DynamicStreamingItem> = new Vector.<DynamicStreamingItem>();
streamItems.push(new DynamicStreamingItem(streamName, 0));

_source.streamItems = streamItems;

_rtmpDynamicStreamingNetLoader = new RTMPDynamicStreamingNetLoader();
_rtmpDynamicStreamingNetLoader.addEventListener(LoaderEvent.LOAD_STATE_CHANGE, rtmpDynamicStreamingNetLoaderStateChangeHandler);

var cvp:VideoDisplay = new VideoDisplay();

_source.mediaType = MediaType.VIDEO;

var videoElement:MediaElement = new VideoElement(_source, _rtmpDynamicStreamingNetLoader);

cvp.source = videoElement;

private function rtmpDynamicStreamingNetLoaderStateChangeHandler(event:LoaderEvent):void
{
    var netStream:NetStream = event.loadTrait["netStream"] as NetStream;
    if (netStream != null && !netStream.hasEventListener(NetStatusEvent.NET_STATUS)) {
        netStream.addEventListener(NetStatusEvent.NET_STATUS, netStreamNetStatusHandler, false, 0, true);
    }
}

private function netStreamNetStatusHandler(event:NetStatusEvent):void
{
    if (event.info.code == "NetStream.Play.UnpublishNotify") {

    }
    if (event.info.code == "NetStream.Play.PublishNotify") {

    }
 }

As a variant you may handle NetStream.Play.PublishNotify from NetStream object.

var _source:DynamicStreamingResource;
_source = new DynamicStreamingResource("rtmp://...", StreamType.LIVE);

var streamItems:Vector.<DynamicStreamingItem> = new Vector.<DynamicStreamingItem>();
streamItems.push(new DynamicStreamingItem(streamName, 0));

_source.streamItems = streamItems;

_rtmpDynamicStreamingNetLoader = new RTMPDynamicStreamingNetLoader();
_rtmpDynamicStreamingNetLoader.addEventListener(LoaderEvent.LOAD_STATE_CHANGE, rtmpDynamicStreamingNetLoaderStateChangeHandler);

var cvp:VideoDisplay = new VideoDisplay();

_source.mediaType = MediaType.VIDEO;

var videoElement:MediaElement = new VideoElement(_source, _rtmpDynamicStreamingNetLoader);

cvp.source = videoElement;

private function rtmpDynamicStreamingNetLoaderStateChangeHandler(event:LoaderEvent):void
{
    var netStream:NetStream = event.loadTrait["netStream"] as NetStream;
    if (netStream != null && !netStream.hasEventListener(NetStatusEvent.NET_STATUS)) {
        netStream.addEventListener(NetStatusEvent.NET_STATUS, netStreamNetStatusHandler, false, 0, true);
    }
}

private function netStreamNetStatusHandler(event:NetStatusEvent):void
{
    if (event.info.code == "NetStream.Play.UnpublishNotify") {

    }
    if (event.info.code == "NetStream.Play.PublishNotify") {

    }
 }
好倦 2024-12-27 01:10:20

我使用了 Johnatan 的代码和 JayPea 的想法来解决我的问题!

我使用了 Johnatan 提供的所有内容,并对 netStreamNetStatusHandler() 进行了以下小更改:

private function netStreamNetStatusHandler(event:NetStatusEvent):void
{
    trace(event.info.code);

    if (event.info.code == "NetStream.Play.PublishNotify")
    {
        try
        {
            cvp.source = ''; //Doesn't need to be a valid path. Empty string should suffice.
        }
        catch (error:Error)
        {
            trace('Video source error: ' + error.message);
        }

        cvp.source = videoElement;
    }
}

这会在流停止并重新启动时重置源,从而导致视频显示再次播放。

注意:视频显示的 autoPlay 属性需要设置为“true”。

I used Johnatan's code along with JayPea's idea to solve my problem!

I used all that Johnatan gave, with the following small change in the netStreamNetStatusHandler():

private function netStreamNetStatusHandler(event:NetStatusEvent):void
{
    trace(event.info.code);

    if (event.info.code == "NetStream.Play.PublishNotify")
    {
        try
        {
            cvp.source = ''; //Doesn't need to be a valid path. Empty string should suffice.
        }
        catch (error:Error)
        {
            trace('Video source error: ' + error.message);
        }

        cvp.source = videoElement;
    }
}

This resets the source when the stream stops and restarts, thus causing the video display to play again.

NOTE: The video display's autoPlay property needs to be set as 'true'.

陪你到最终 2024-12-27 01:10:20

我知道这个问题是很多年前提出的,但它仍然帮助我解决了我的问题,鼓励我深入研究 VideoDisplay 的来源(因为显然没有“干净”的解决方案;-(

对于那些有同样问题的人,我的“解决方案“,可能也会有帮助:

在用户单击“中止”后,我必须重新启动同一视频,但随后决定再次播放它。如果不“重置”VideoDisplay,这会导致黑屏。

以下是我发现后如何解决该问题的方法在VideoDisplay内部在 commitProperties() 函数中没有定义thumnailSource 时,调用了函数“setupSource()”,

因为 setupSource() 函数不可公开访问,所以我使用了这个:

        myVideoDisplaySpark.enabled = false; // changes properties
        myVideoDisplaySpark.source = "";    // does not harm ;-)
        myVideoDisplaySpark.mx_internal::thumbnailSource = null; // forces a setupSource in commitProperties
        myVideoDisplaySpark.enabled = true;  // finally results in the call if setupSource

这至少是我所理解的;最后它对我有用; -)

I know this question was asked many years ago, but it still helped me solving my problem encouraging me diving into the sources of VideoDisplay (since there obviously is no "clean" solution ;-(

And for those who have the same problem my "solution" ,ight be helpful too:

I had to restart the same video after the user clicked abort but then decided to play it again. Without "resetting" VideoDisplay this resulted in a black screen.

Here's how I solved the issue after I found out that in VideoDisplay an internal function "setupSource()" was called when in the commitProperties() function no thumnailSource was defined.

Since the setupSource() function was not publicly accessible I used this:

        myVideoDisplaySpark.enabled = false; // changes properties
        myVideoDisplaySpark.source = "";    // does not harm ;-)
        myVideoDisplaySpark.mx_internal::thumbnailSource = null; // forces a setupSource in commitProperties
        myVideoDisplaySpark.enabled = true;  // finally results in the call if setupSource

That's at least what I understood; and at the end it worked for me ;-)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文