EDIT 如果有人至少可以告诉我如何在流断开连接时接收事件,那就太好了。
此控件的文档简直太可怕了。我有一个将具有实时视频流的应用程序,我正在寻找一种方法来使 VideoDisplay 控件在发生以下任何特定情况时恢复其连接:
- 应用程序启动,流尚未联机。
- 应用程序正在流式传输,用户与互联网断开连接。
- 应用程序正在流式传输,视频服务器崩溃并重新启动。
我正在使用Wowza媒体服务器和Wirecast来测试这一点。 1 和 3 不起作用,我不确定数字 2 是否有效。我通过添加这段非常有问题的代码使数字 1 工作:
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 块,因为对源的两次调用都会导致异常,但在这些异常之前发生的任何事情似乎都可以解决问题 #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>
有谁知道如何使视频显示器从这些问题中恢复过来?任何帮助不胜感激,谢谢。
如果有人有这个问题,这就是我解决它的方式。您需要将视频源设置为空白图像才能停止流式传输,否则它将永远不会停止。此解决方案适用于上述所有方案:
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>
作为变体,您可以从 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") {
}
}
我使用了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;
}
}
这会在流停止并重新启动时重置源,从而导致视频显示再次播放。
注意:视频显示器的自动播放属性需要设置为"true"。
这个问题是多年前问过的,但它仍然帮助我解决了我的问题,鼓励我深入研究 VideoDisplay 的来源(因为显然没有"干净"的解决方案;-(
对于那些有同样问题的人,我的"解决方案"也很有帮助:
用户单击中止后,我不得不重新启动相同的视频,但随后决定再次播放。如果不"重置"视频显示,这会导致黑屏。
这是我在发现在 VideoDisplay 中调用内部函数"setupSource(("后如何解决问题的方法,而在 commitProperties(( 函数中没有定义 thumnailSource。
由于 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
这至少是我所理解的;最后它对我有用;-(