JoJo
JoJo

Reputation: 20115

Detect TCP connection close when playing Flash video

On the Flash client side, how do I detect when the server purposely closes the TCP connection to its video stream? I'll need to take action when this occurs - maybe attempt to restart the video or display an error message. Currently, the connection closing and the connection being slow look the same to me. The NetStream object ushers a NetStream.Play.Stop event in both cases. When the connection is slow, it usually recovers by itself within seconds. I wish to only take action when the connection is closed, not when it is slow.

Here's how my general setup looks like. It's the basic NetConnection->NetStream->Video setup.

this.vidConnection = new NetConnection();
this.vidConnection.addEventListener(AsyncErrorEvent.ASYNC_ERROR, this.connectionAsyncError);
this.vidConnection.addEventListener(IOErrorEvent.IO_ERROR, this.connectionIoError);
this.vidConnection.addEventListener(NetStatusEvent.NET_STATUS, this.connectionNetStatus);
this.vidConnection.connect(null);
this.vidStream = new NetStream(this.vidConnection);
this.vidStream.addEventListener(AsyncErrorEvent.ASYNC_ERROR, this.streamAsyncError);
this.vidStream.addEventListener(IOErrorEvent.IO_ERROR, this.streamIoError);
this.vidStream.addEventListener(NetStatusEvent.NET_STATUS, this.streamNetStatus);
this.vid.attachNetStream(this.vidStream);

None of the error events fire when the server closes the TCP or when the connection freezes up. Only the NetStream.Play.Stop event fires. Here's a trace of what happens from initially playing the video to the TCP connection closing.

connection net status = NetConnection.Connect.Success
playStream(http://192.168.0.44/flv/4d29104a9aefa)
NetStream.Play.Start
NetStream.Buffer.Flush
NetStream.Buffer.Full
NetStream.Buffer.Empty
checkDimensions 0 0
onMetaData
NetStream.Buffer.Full
NetStream.Buffer.Flush
checkDimensions 960 544
NetStream.Buffer.Empty
NetStream.Buffer.Flush
NetStream.Play.Stop

When I do a dump on various properties during the connection closing and connection being slow, I see no distinctive values that could help me differentiate closing and slowness.

NetConnection->connected = true
NetConnection->connectedProxyType = none
NetConnection->proxyType = none
NetConnection->uri = null
NetConnection->usingTLS = false
VidStream->bufferLength = 0
VidStream->bufferTime = 0.1
VidStream->bytesLoaded = 3204116
VidStream->bytesTotal = 3204116
VidStream->currentFPS = 0
VidStream->time = 63.797

Upvotes: 2

Views: 1733

Answers (3)

Atari
Atari

Reputation: 143

Use the NetConnection statuses rather: NetConnection.Connect.Closed

I ran into a similar issue & noticed that the NetStream.bytesTotal value is truncated when a video connection fails. Thus, capture the video length in bytes according to the NetStream to begin with & use it to detect a failure when NetConnection.Connect.Closed is called.

Because video code is so impossible to explain out of context I put this together, building on Flash's example @ Flash NetConnection Example

Here's the code, comments inline:

public class NonTruncatedNetConnectionExample extends Sprite {
    private var videoURL:String = "http://www.helpexamples.com/flash/video/cuepoints.flv";
    private var connection:NetConnection;
    private var stream:NetStream;
    private var video:Video = new Video();
    // ADDITION: special length variable to check for truncation
    private var videoBytes:uint;

    public function NonTruncatedNetConnectionExample() {
        connection = new NetConnection();
        connection.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
        connection.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
        connection.connect(null);
    }

    private function netStatusHandler(event:NetStatusEvent):void {
        switch (event.info.code) {
            case "NetConnection.Connect.Success" :
                connectStream();
                break;
            case "NetStream.Play.StreamNotFound" :
                trace("Stream not found: " + videoURL);
                break;
            // ADDITION: this will be triggered when the connection is closed
            // on completion, or on failure
            case "NetConnection.Connect.Closed" :
                if (this.videoBytes != this.stream.bytesTotal) {
                    // failure
                    // you can throw the error here
                    // or in the loadingProgress function below
                } else {
                    // success
                    // the video loaded completely
                }
                break;
        }
    }

    private function securityErrorHandler(event:SecurityErrorEvent):void {
        trace("securityErrorHandler: " + event);
    }

    private function connectStream():void {
        var stream:NetStream = new NetStream(connection);
        stream.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
        stream.client = new CustomClient();
        video.attachNetStream(stream);
        stream.play(videoURL);
        addChild(video);
        // ADDITION: the loadingProgress function
        this.addEventListener(Event.ENTER_FRAME, loadingProgress);
    }

    /***
    ** ADDITION : loadingProgress captures the length of the video in bytes for use in comparison when
    ** NetConnection.Connect.Closed is called
    ** It also tracks current loading progress so you can use it as a buffer indicator
    */
    private function loadingProgress(E:Event):void {
        // check that this.stream.client has initialised & you have correct access to this.stream variables
        // bytesTotal is also a divisor so must be greater than 0 before continuing, or it will hard-fail
        if (this.stream.client && this.stream.bytesTotal > 0) {
            // sanity checker ;)
            trace("bytesLoaded = " + this.stream.bytesLoaded + " :: bytesTotal = " + this.stream.bytesTotal);
            // capture the video's total bytes only if the variable does not yet exist
            // before this point this.stream.bytesTotal returns a bogus (really big) number
            // watch out for capturing this.stream.totalBytes before this point, or you create a double negative,
            // because it won't match the actual bytesTotal & will therefore error [ it got me :( ; but then I got it ;) ]
            if (!this.videoBytes) this.videoBytes = this.stream.bytesTotal;
            // compare this to stream.totalBytes to detect truncation
            if (this.videoBytes != this.stream.bytesTotal) {
                // error
                // you can throw the error here if you want, or wait for the NetConnection.Connect.Closed switch above
            } else {
                // or you can detach this event listener here while just holding on to the videoBytes variable
                // & wait for the NetConnection.Connect.Closed switch above
                // e.g. this.removeEventListener(Event.ENTER_FRAME, loadingProgress);
            }
            // use this to drive a buffer bar if you want
            var radian:Number = (this.stream.bytesLoaded / this.totalBytes);
            var percent:Number = radian * 100;
        }
    }

}

And also:

class CustomClient {
    public function onMetaData(info:Object):void {
        trace("metadata: duration=" + info.duration + " width=" + info.width + " height=" + info.height + " framerate=" + info.framerate);
    }
    public function onCuePoint(info:Object):void {
        trace("cuepoint: time=" + info.time + " name=" + info.name + " type=" + info.type);
    }
}

Upvotes: 2

oxygen
oxygen

Reputation: 6039

After intensive testing using NetLimiter on Windows (to kill the TCP connection "unexpectedly") only NetStream.Buffer.Empty fires.

The solution weltraumpirat gave you is the only one available to you (starting a Timer to see if you are still actively receiving data).

Apparently URLStream::connected remains true after losing all TCP connections, and halts (without any IO_ERROR, or other events/exceptions, just like NetStream). If you are targeting >10.1 Flash, you could use URLStream to load data with into NetStream::appendBytes (see docs for Data Generation Mode(enable by passing null to NetStream::play())), and then, when receiving NetStream.Buffer.Empty immediately check URLStream::connected to see if you are still connected to the server, instead of starting a Timer.

Upvotes: 1

weltraumpirat
weltraumpirat

Reputation: 22604

I don't know of any event to signal a dropped connection, other than "NetStream.Failed", which only works with Flash Media Server (and I don't even know if or when that is ever fired, either).

You have to find a solution base on "NetStream.Buffer.Empty": Start a Timer whenever this event occurs, have it wait for a long enough time to make sure the connection is not likely to recover, then start a new attempt. You can reset the timer on each "NetStream.Buffer.Full", or when the movie ends or is paused manually, so that it won't do any harm unless it is really needed.

Upvotes: 4

Related Questions