<commit_before>package org.mangui.hls.stream {
    import org.mangui.hls.event.HLSPlayMetrics;
    import org.mangui.hls.constant.HLSSeekMode;
    import org.mangui.hls.event.HLSError;
    import org.mangui.hls.event.HLSMediatime;
    import org.mangui.hls.HLSSettings;
    import org.mangui.hls.constant.HLSSeekStates;
    import org.mangui.hls.constant.HLSPlayStates;
    import org.mangui.hls.flv.FLVTag;
    import org.mangui.hls.HLS;
    import org.mangui.hls.event.HLSEvent;
    import org.mangui.hls.utils.Hex;

    import flash.events.Event;
    import flash.events.NetStatusEvent;
    import flash.events.TimerEvent;
    import flash.net.*;
    import flash.utils.*;

    CONFIG::LOGGING {
        import org.mangui.hls.utils.Log;
    }
    /** Class that keeps the buffer filled. **/
    public class HLSNetStream extends NetStream {
        /** Reference to the framework controller. **/
        private var _hls : HLS;
        /** reference to auto buffer manager */
        private var _autoBufferManager : AutoBufferManager;
        /** FLV tags buffer vector **/
        private var _flvTagBuffer : Vector.<FLVTag>;
        /** FLV tags buffer duration **/
        private var _flvTagBufferDuration : Number;
        /** The fragment loader. **/
        private var _fragmentLoader : FragmentLoader;
        /** means that last fragment of a VOD playlist has been loaded */
        private var _reached_vod_end : Boolean;
        /** Timer used to check buffer and position. **/
        private var _timer : Timer;
        /** requested start position **/
        private var _seek_position_requested : Number;
        /** real start position , retrieved from first fragment **/
        private var _seek_position_real : Number;
        /** seek date in ms , since epoch **/
        private var _seek_date_real : Number = 0;
        /** is a seek operation in progress ? **/
        private var _seek_in_progress : Boolean;
        /** Current play position (relative position from beginning of sliding window) **/
        private var _playback_current_position : Number;
        /** playlist sliding (non null for live playlist) **/
        private var _playlist_sliding_duration : Number;
        /** total duration of buffered data before last discontinuity */
        private var _buffered_before_last_continuity : Number;
        /** buffer min PTS since last discontinuity  */
        private var _buffer_cur_min_pts : Number;
        /** buffer max PTS since last discontinuity  */
        private var _buffer_cur_max_pts : Number;
        /** previous buffer time. **/
        private var _last_buffer : Number;
        /** Current playback state. **/
        private var _playbackState : String;
        /** Current seek state. **/
        private var _seekState : String;
        /** threshold to get out of buffering state
         * by default it is set to _buffer_low_len
         * however if buffer gets empty, its value is moved to _buffer_min_len
         */
        private var _buffer_threshold : Number;
        /** playlist duration **/
        private var _playlist_duration : Number = 0;
        /** level/sn used to detect fragment change **/
        private var _cur_level : int;
        private var _cur_sn : int;
        private var _playbackLevel : int;
        /** Netstream client proxy */
        private var _client : HLSNetStreamClient;

        /** Create the buffer. **/
        public function HLSNetStream(connection : NetConnection, hls : HLS, fragmentLoader : FragmentLoader) : void {
            super(connection);
            super.bufferTime = 0.1;
            _hls = hls;
            _autoBufferManager = new AutoBufferManager(hls);
            _fragmentLoader = fragmentLoader;
            _hls.addEventListener(HLSEvent.LAST_VOD_FRAGMENT_LOADED, _lastVODFragmentLoadedHandler);
            _hls.addEventListener(HLSEvent.PLAYLIST_DURATION_UPDATED, _playlistDurationUpdated);
            _playbackState = HLSPlayStates.IDLE;
            _seekState = HLSSeekStates.IDLE;
            _timer = new Timer(100, 0);
            _timer.addEventListener(TimerEvent.TIMER, _checkBuffer);
            _client = new HLSNetStreamClient();
            _client.registerCallback("onHLSFragmentChange", onHLSFragmentChange);
            _client.registerCallback("onID3Data", onID3Data);
            super.client = _client;
        };

        public function onHLSFragmentChange(level : int, seqnum : int, cc : int, audio_only : Boolean, width : int, height : int, ... tags) : void {
            CONFIG::LOGGING {
                Log.debug("playing fragment(level/sn/cc):" + level + "/" + seqnum + "/" + cc);
            }
            _playbackLevel = level;
            var tag_list : Array = new Array();
            for (var i : uint = 0; i < tags.length; i++) {
                tag_list.push(tags[i]);
                CONFIG::LOGGING {
                    Log.debug("custom tag:" + tags[i]);
                }
            }
            _hls.dispatchEvent(new HLSEvent(HLSEvent.FRAGMENT_PLAYING, new HLSPlayMetrics(level, seqnum, cc, audio_only, width, height, tag_list)));
        }

        // function is called by SCRIPT in FLV
        public function onID3Data( data:ByteArray ) : void {
            var dump : String = "unset";
			
            // we dump the content as hex to get it to the Javascript in the browser.
            // from lots of searching, we could use base64, but even then, the decode would 
            // not be native, so hex actually seems more efficient
            dump = Hex.fromArray(data);
			
            CONFIG::LOGGING {
                Log.debug("id3:"+dump);
            }
            _hls.dispatchEvent(new HLSEvent(HLSEvent.ID3_UPDATED, dump));
        }
		
        /** Check the bufferlength. **/
        private function _checkBuffer(e : Event) : void {
            var playback_absolute_position : Number;
            var playback_relative_position : Number;
            var buffer : Number = this.bufferLength;
            // Calculate the buffer and position.
            if (_seek_in_progress) {
                playback_relative_position = playback_absolute_position = _seek_position_requested;
            } else {
                /** Absolute playback position (start position + play time) **/
                playback_absolute_position = super.time + _seek_position_real;
                /** Relative playback position (Absolute Position - playlist sliding, non null for Live Playlist) **/
                playback_relative_position = playback_absolute_position - _playlist_sliding_duration;
            }
            // only send media time event if data has changed
            if (playback_relative_position != _playback_current_position || buffer != _last_buffer) {
                _playback_current_position = playback_relative_position;
                _last_buffer = buffer;
                var playback_date : Number = _seek_date_real ? 1000 * super.time + _seek_date_real : 0;
                _hls.dispatchEvent(new HLSEvent(HLSEvent.MEDIA_TIME, new HLSMediatime(_playback_current_position, _playlist_duration, buffer, _playlist_sliding_duration, playback_date)));
            }

            // Set playback state. no need to check buffer status if first fragment not yet received
            if (!_seek_in_progress) {
                // check low buffer condition
                if (buffer < HLSSettings.lowBufferLength) {
                    if (buffer <= 0.1) {
                        if (_reached_vod_end) {
                            // reach end of playlist + playback complete (as buffer is empty).
                            // stop timer, report event and switch to IDLE mode.
                            _timer.stop();
                            CONFIG::LOGGING {
                                Log.debug("reached end of VOD playlist, notify playback complete");
                            }
                            _hls.dispatchEvent(new HLSEvent(HLSEvent.PLAYBACK_COMPLETE));
                            _setPlaybackState(HLSPlayStates.IDLE);
                            _setSeekState(HLSSeekStates.IDLE);
                            return;
                        } else {
                            // pause Netstream in really low buffer condition
                            super.pause();
                            if (HLSSettings.minBufferLength == -1) {
                                _buffer_threshold = _autoBufferManager.minBufferLength;
                            } else {
                                _buffer_threshold = HLSSettings.minBufferLength;
                            }
                        }
                    }
                    // dont switch to buffering state in case we reached end of a VOD playlist
                    if (!_reached_vod_end) {
                        if (_playbackState == HLSPlayStates.PLAYING) {
                            // low buffer condition and play state. switch to play buffering state
                            _setPlaybackState(HLSPlayStates.PLAYING_BUFFERING);
                        } else if (_playbackState == HLSPlayStates.PAUSED) {
                            // low buffer condition and pause state. switch to paused buffering state
                            _setPlaybackState(HLSPlayStates.PAUSED_BUFFERING);
                        }
                    }
                }
                // in case buffer is full enough or if we have reached end of VOD playlist
                if (buffer >= _buffer_threshold || _reached_vod_end) {
                    /* after we reach back threshold value, set it buffer low value to avoid
                     * reporting buffering state to often. using different values for low buffer / min buffer
                     * allow to fine tune this 
                     */
                    if (HLSSettings.minBufferLength == -1) {
                        // in automode, low buffer threshold should be less than min auto buffer
                        _buffer_threshold = Math.min(_autoBufferManager.minBufferLength / 2, HLSSettings.lowBufferLength);
                    } else {
                        _buffer_threshold = HLSSettings.lowBufferLength;
                    }

                    // no more in low buffer state<commit_msg>notify end of playback event when buffer size is less than 10ms to avoid missing frames at the end of the playback related to https://github.com/mangui/flashls/issues/119<commit_after>package org.mangui.hls.stream {
    import org.mangui.hls.event.HLSPlayMetrics;
    import org.mangui.hls.constant.HLSSeekMode;
    import org.mangui.hls.event.HLSError;
    import org.mangui.hls.event.HLSMediatime;
    import org.mangui.hls.HLSSettings;
    import org.mangui.hls.constant.HLSSeekStates;
    import org.mangui.hls.constant.HLSPlayStates;
    import org.mangui.hls.flv.FLVTag;
    import org.mangui.hls.HLS;
    import org.mangui.hls.event.HLSEvent;
    import org.mangui.hls.utils.Hex;

    import flash.events.Event;
    import flash.events.NetStatusEvent;
    import flash.events.TimerEvent;
    import flash.net.*;
    import flash.utils.*;

    CONFIG::LOGGING {
        import org.mangui.hls.utils.Log;
    }
    /** Class that keeps the buffer filled. **/
    public class HLSNetStream extends NetStream {
        /** Reference to the framework controller. **/
        private var _hls : HLS;
        /** reference to auto buffer manager */
        private var _autoBufferManager : AutoBufferManager;
        /** FLV tags buffer vector **/
        private var _flvTagBuffer : Vector.<FLVTag>;
        /** FLV tags buffer duration **/
        private var _flvTagBufferDuration : Number;
        /** The fragment loader. **/
        private var _fragmentLoader : FragmentLoader;
        /** means that last fragment of a VOD playlist has been loaded */
        private var _reached_vod_end : Boolean;
        /** Timer used to check buffer and position. **/
        private var _timer : Timer;
        /** requested start position **/
        private var _seek_position_requested : Number;
        /** real start position , retrieved from first fragment **/
        private var _seek_position_real : Number;
        /** seek date in ms , since epoch **/
        private var _seek_date_real : Number = 0;
        /** is a seek operation in progress ? **/
        private var _seek_in_progress : Boolean;
        /** Current play position (relative position from beginning of sliding window) **/
        private var _playback_current_position : Number;
        /** playlist sliding (non null for live playlist) **/
        private var _playlist_sliding_duration : Number;
        /** total duration of buffered data before last discontinuity */
        private var _buffered_before_last_continuity : Number;
        /** buffer min PTS since last discontinuity  */
        private var _buffer_cur_min_pts : Number;
        /** buffer max PTS since last discontinuity  */
        private var _buffer_cur_max_pts : Number;
        /** previous buffer time. **/
        private var _last_buffer : Number;
        /** Current playback state. **/
        private var _playbackState : String;
        /** Current seek state. **/
        private var _seekState : String;
        /** threshold to get out of buffering state
         * by default it is set to _buffer_low_len
         * however if buffer gets empty, its value is moved to _buffer_min_len
         */
        private var _buffer_threshold : Number;
        /** playlist duration **/
        private var _playlist_duration : Number = 0;
        /** level/sn used to detect fragment change **/
        private var _cur_level : int;
        private var _cur_sn : int;
        private var _playbackLevel : int;
        /** Netstream client proxy */
        private var _client : HLSNetStreamClient;

        /** Create the buffer. **/
        public function HLSNetStream(connection : NetConnection, hls : HLS, fragmentLoader : FragmentLoader) : void {
            super(connection);
            super.bufferTime = 0.1;
            _hls = hls;
            _autoBufferManager = new AutoBufferManager(hls);
            _fragmentLoader = fragmentLoader;
            _hls.addEventListener(HLSEvent.LAST_VOD_FRAGMENT_LOADED, _lastVODFragmentLoadedHandler);
            _hls.addEventListener(HLSEvent.PLAYLIST_DURATION_UPDATED, _playlistDurationUpdated);
            _playbackState = HLSPlayStates.IDLE;
            _seekState = HLSSeekStates.IDLE;
            _timer = new Timer(100, 0);
            _timer.addEventListener(TimerEvent.TIMER, _checkBuffer);
            _client = new HLSNetStreamClient();
            _client.registerCallback("onHLSFragmentChange", onHLSFragmentChange);
            _client.registerCallback("onID3Data", onID3Data);
            super.client = _client;
        };

        public function onHLSFragmentChange(level : int, seqnum : int, cc : int, audio_only : Boolean, width : int, height : int, ... tags) : void {
            CONFIG::LOGGING {
                Log.debug("playing fragment(level/sn/cc):" + level + "/" + seqnum + "/" + cc);
            }
            _playbackLevel = level;
            var tag_list : Array = new Array();
            for (var i : uint = 0; i < tags.length; i++) {
                tag_list.push(tags[i]);
                CONFIG::LOGGING {
                    Log.debug("custom tag:" + tags[i]);
                }
            }
            _hls.dispatchEvent(new HLSEvent(HLSEvent.FRAGMENT_PLAYING, new HLSPlayMetrics(level, seqnum, cc, audio_only, width, height, tag_list)));
        }

        // function is called by SCRIPT in FLV
        public function onID3Data(data : ByteArray) : void {
            var dump : String = "unset";

            // we dump the content as hex to get it to the Javascript in the browser.
            // from lots of searching, we could use base64, but even then, the decode would
            // not be native, so hex actually seems more efficient
            dump = Hex.fromArray(data);

            CONFIG::LOGGING {
                Log.debug("id3:" + dump);
            }
            _hls.dispatchEvent(new HLSEvent(HLSEvent.ID3_UPDATED, dump));
        }

        /** Check the bufferlength. **/
        private function _checkBuffer(e : Event) : void {
            var playback_absolute_position : Number;
            var playback_relative_position : Number;
            var buffer : Number = this.bufferLength;
            // Calculate the buffer and position.
            if (_seek_in_progress) {
                playback_relative_position = playback_absolute_position = _seek_position_requested;
            } else {
                /** Absolute playback position (start position + play time) **/
                playback_absolute_position = super.time + _seek_position_real;
                /** Relative playback position (Absolute Position - playlist sliding, non null for Live Playlist) **/
                playback_relative_position = playback_absolute_position - _playlist_sliding_duration;
            }
            // only send media time event if data has changed
            if (playback_relative_position != _playback_current_position || buffer != _last_buffer) {
                _playback_current_position = playback_relative_position;
                _last_buffer = buffer;
                var playback_date : Number = _seek_date_real ? 1000 * super.time + _seek_date_real : 0;
                _hls.dispatchEvent(new HLSEvent(HLSEvent.MEDIA_TIME, new HLSMediatime(_playback_current_position, _playlist_duration, buffer, _playlist_sliding_duration, playback_date)));
            }

            // Set playback state. no need to check buffer status if first fragment not yet received
            if (!_seek_in_progress) {
                // check low buffer condition
                if (buffer < HLSSettings.lowBufferLength) {
                    if (buffer <= 0.01 && _reached_vod_end) {
                        /* reach end of playlist + empty buffer :
                        playback complete : stop timer, report event and switch to IDLE mode. */
                        _timer.stop();
                        CONFIG::LOGGING {
                            Log.debug("reached end of VOD playlist, notify playback complete");
                        }
                        _hls.dispatchEvent(new HLSEvent(HLSEvent.PLAYBACK_COMPLETE));
                        _setPlaybackState(HLSPlayStates.IDLE);
                        _setSeekState(HLSSeekStates.IDLE);
                        return;
                    } else if (buffer <= 0.1 && !_reached_vod_end) {
                        // pause Netstream in really low buffer condition
                        super.pause();
                        if (HLSSettings.minBufferLength == -1) {
                            _buffer_threshold = _autoBufferManager.minBufferLength;
                        } else {
                            _buffer_threshold = HLSSettings.minBufferLength;
                        }
                    }
                    // dont switch to buffering state in case we reached end of a VOD playlist
                    if (!_reached_vod_end) {
                        if (_playbackState == HLSPlayStates.PLAYING) {
                            // low buffer condition and play state. switch to play buffering state
                            _setPlaybackState(HLSPlayStates.PLAYING_BUFFERING);
                        } else if (_playbackState == HLSPlayStates.PAUSED) {
                            // low buffer condition and pause state. switch to paused buffering state
                            _setPlaybackState(HLSPlayStates.PAUSED_BUFFERING);
                        }
                    }
                }
                // in case buffer is full enough or if we have reached end of VOD playlist
                if (buffer >= _buffer_threshold || _reached_vod_end) {
                    /* after we reach back threshold value, set it buffer low value to avoid
                     * reporting buffering state to often. using different values for low buffer / min buffer
                     * allow to fine tune this 
                     */
                    if (HLSSettings.minBufferLength == -1) {
                        // in automode, low buffer threshold should be less than min auto buffer
                        _buffer_threshold = Math.min(_autoBufferManager.minBufferLength / 2, HLSSettings.lowBufferLength);
                    } else {
                        _buffer_threshold = HLSSettings.lowBufferLength;
                    }

                    // no more in low buffer state
{'fraction_non_alphanumeric': 0.07780575191246247, 'fraction_numerical': 0.002905006294180304, 'mean_word_length': 1.8090575275397798, 'pattern_counts': {'":': 0, '<': 12, '<?xml version=': 0, '>': 7, 'https://': 1, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2127676', 'n_tokens_mistral': 5441, 'n_tokens_neox': 5185, 'n_words': 1513}