├── .actionScriptProperties ├── .flexLibProperties ├── .gitignore ├── .project ├── LICENSE ├── README.md ├── bin ├── debug │ ├── flashls.swc │ ├── flashlsChromeless.swf │ ├── flashlsFlowPlayer.swf │ ├── flashlsOSMF.swc │ └── flashlsOSMF.swf └── release │ ├── flashls.swc │ ├── flashlsChromeless.swf │ ├── flashlsFlowPlayer.swf │ ├── flashlsOSMF.swc │ └── flashlsOSMF.swf ├── build ├── add-opt-in.py ├── build.sh ├── build_bemtv.sh └── build_globo.sh ├── examples ├── chromeless │ ├── example.html │ └── index.html ├── flowplayer │ ├── flowplayer-3.2.12.min.js │ ├── flowplayer.controls.swf │ ├── flowplayer.ipad-3.2.12.min.js │ ├── flowplayer.swf │ └── index.html ├── libs │ ├── ParsedQueryString.js │ └── swfobject.js └── osmf │ ├── GrindPlayer.html │ ├── GrindPlayer.swf │ ├── StrobeMediaPlayback.html │ ├── StrobeMediaPlayback.swf │ └── images │ ├── adobe-lq.png │ ├── osmf_horizontal_red.png │ └── poster.png ├── lib ├── blooddy_crypto.swc ├── flowplayer │ ├── flowplayer-classes.xml │ └── flowplayer.swc └── osmf │ ├── OSMF.swc │ └── exclude-sources.xml └── src ├── com └── globo │ └── Player.as ├── org └── mangui │ ├── basic │ └── Player.as │ ├── chromeless │ ├── ChromelessPlayer.as │ └── JSURLStream.as │ ├── flowplayer │ ├── HLSPluginFactory.as │ └── HLSStreamProvider.as │ ├── hls │ ├── HLS.as │ ├── HLSSettings.as │ ├── constant │ │ ├── HLSMaxLevelCappingMode.as │ │ ├── HLSPlayStates.as │ │ ├── HLSSeekMode.as │ │ ├── HLSSeekStates.as │ │ └── HLSTypes.as │ ├── controller │ │ ├── AudioTrackController.as │ │ ├── AutoBufferController.as │ │ └── AutoLevelController.as │ ├── demux │ │ ├── AACDemuxer.as │ │ ├── AVCC.as │ │ ├── AudioFrame.as │ │ ├── DemuxHelper.as │ │ ├── Demuxer.as │ │ ├── ExpGolomb.as │ │ ├── ID3.as │ │ ├── MP3Demuxer.as │ │ ├── Nalu.as │ │ ├── PES.as │ │ ├── SPSInfo.as │ │ ├── TSDemuxer.as │ │ └── VideoFrame.as │ ├── event │ │ ├── HLSError.as │ │ ├── HLSEvent.as │ │ ├── HLSLoadMetrics.as │ │ ├── HLSMediatime.as │ │ └── HLSPlayMetrics.as │ ├── flv │ │ └── FLVTag.as │ ├── loader │ │ ├── FragmentLoader.as │ │ └── ManifestLoader.as │ ├── model │ │ ├── AudioTrack.as │ │ ├── Fragment.as │ │ ├── FragmentData.as │ │ ├── FragmentMetrics.as │ │ └── Level.as │ ├── playlist │ │ ├── AltAudioTrack.as │ │ ├── DataUri.as │ │ └── Manifest.as │ ├── stream │ │ ├── HLSNetStream.as │ │ └── HLSNetStreamClient.as │ └── utils │ │ ├── AES.as │ │ ├── FastAESKey.as │ │ ├── Hex.as │ │ ├── Log.as │ │ ├── PTS.as │ │ ├── Params2Settings.as │ │ └── ScaleVideo.as │ └── osmf │ └── plugins │ ├── HLSDynamicPlugin.as │ ├── HLSMediaElement.as │ ├── HLSPlugin.as │ ├── loader │ ├── HLSLoadFromDocumentElement.as │ ├── HLSLoaderBase.as │ └── HLSNetLoader.as │ ├── traits │ ├── HLSAlternativeAudioTrait.as │ ├── HLSBufferTrait.as │ ├── HLSDisplayObjectTrait.as │ ├── HLSDynamicStreamTrait.as │ ├── HLSNetStreamLoadTrait.as │ ├── HLSPlayTrait.as │ ├── HLSSeekTrait.as │ └── HLSTimeTrait.as │ └── utils │ └── ErrorManager.as └── tv └── bem ├── BemTVPlayer.as ├── BemTVURLStream.as └── PlaybackIdHolder.as /.actionScriptProperties: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /.flexLibProperties: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.swf.cache 2 | *.swc.cache 3 | .metadata 4 | .settings 5 | bin 6 | P2PHLSPlayer.swf 7 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | flashls 4 | 5 | 6 | 7 | 8 | 9 | com.adobe.flexbuilder.project.flexbuilder 10 | 11 | 12 | 13 | 14 | com.powerflasher.fdt.core.FlashBuilder 15 | 16 | 17 | 18 | 19 | 20 | com.adobe.flexbuilder.project.flexlibnature 21 | com.adobe.flexbuilder.project.actionscriptnature 22 | com.powerflasher.fdt.core.FlashNature 23 | 24 | 25 | -------------------------------------------------------------------------------- /bin/debug/flashls.swc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bemtv/flashls/44ac635b7f2311fad65eefb0ea2229d2a94ecc10/bin/debug/flashls.swc -------------------------------------------------------------------------------- /bin/debug/flashlsChromeless.swf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bemtv/flashls/44ac635b7f2311fad65eefb0ea2229d2a94ecc10/bin/debug/flashlsChromeless.swf -------------------------------------------------------------------------------- /bin/debug/flashlsFlowPlayer.swf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bemtv/flashls/44ac635b7f2311fad65eefb0ea2229d2a94ecc10/bin/debug/flashlsFlowPlayer.swf -------------------------------------------------------------------------------- /bin/debug/flashlsOSMF.swc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bemtv/flashls/44ac635b7f2311fad65eefb0ea2229d2a94ecc10/bin/debug/flashlsOSMF.swc -------------------------------------------------------------------------------- /bin/debug/flashlsOSMF.swf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bemtv/flashls/44ac635b7f2311fad65eefb0ea2229d2a94ecc10/bin/debug/flashlsOSMF.swf -------------------------------------------------------------------------------- /bin/release/flashls.swc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bemtv/flashls/44ac635b7f2311fad65eefb0ea2229d2a94ecc10/bin/release/flashls.swc -------------------------------------------------------------------------------- /bin/release/flashlsChromeless.swf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bemtv/flashls/44ac635b7f2311fad65eefb0ea2229d2a94ecc10/bin/release/flashlsChromeless.swf -------------------------------------------------------------------------------- /bin/release/flashlsFlowPlayer.swf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bemtv/flashls/44ac635b7f2311fad65eefb0ea2229d2a94ecc10/bin/release/flashlsFlowPlayer.swf -------------------------------------------------------------------------------- /bin/release/flashlsOSMF.swc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bemtv/flashls/44ac635b7f2311fad65eefb0ea2229d2a94ecc10/bin/release/flashlsOSMF.swc -------------------------------------------------------------------------------- /bin/release/flashlsOSMF.swf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bemtv/flashls/44ac635b7f2311fad65eefb0ea2229d2a94ecc10/bin/release/flashlsOSMF.swf -------------------------------------------------------------------------------- /build/add-opt-in.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Copyright (c) 2012, Adobe Systems Incorporated 3 | # All rights reserved. 4 | # 5 | # Redistribution and use in source and binary forms, with or without 6 | # modification, are permitted provided that the following conditions are 7 | # met: 8 | # 9 | # * Redistributions of source code must retain the above copyright notice, 10 | # this list of conditions and the following disclaimer. 11 | # 12 | # * Redistributions in binary form must reproduce the above copyright 13 | # notice, this list of conditions and the following disclaimer in the 14 | # documentation and/or other materials provided with the distribution. 15 | # 16 | # * Neither the name of Adobe Systems Incorporated nor the names of its 17 | # contributors may be used to endorse or promote products derived from 18 | # this software without specific prior written permission. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 21 | # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 22 | # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 | # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 24 | # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 25 | # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 26 | # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 27 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 28 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 29 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 30 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | 32 | '''See readme or run with no args for usage''' 33 | 34 | import os 35 | import sys 36 | import tempfile 37 | import shutil 38 | import struct 39 | import zlib 40 | import hashlib 41 | import inspect 42 | 43 | supportsLZMA = False 44 | try: 45 | import pylzma 46 | supportsLZMA = True 47 | except: 48 | pass 49 | 50 | #################################### 51 | # Helpers 52 | #################################### 53 | 54 | class stringFile(object): 55 | def __init__(self, data): 56 | self.data = data 57 | 58 | def read(self, num=-1): 59 | result = self.data[:num] 60 | self.data = self.data[num:] 61 | return result 62 | 63 | def close(self): 64 | self.data = None 65 | 66 | def flush(self): 67 | pass 68 | 69 | def consumeSwfTag(f): 70 | tagBytes = b"" 71 | 72 | recordHeaderRaw = f.read(2) 73 | tagBytes += recordHeaderRaw 74 | 75 | if recordHeaderRaw == "": 76 | raise Exception("Bad SWF: Unexpected end of file") 77 | recordHeader = struct.unpack("BB", recordHeaderRaw) 78 | tagCode = ((recordHeader[1] & 0xff) << 8) | (recordHeader[0] & 0xff) 79 | tagType = (tagCode >> 6) 80 | tagLength = tagCode & 0x3f 81 | if tagLength == 0x3f: 82 | ll = f.read(4) 83 | longlength = struct.unpack("BBBB", ll) 84 | tagLength = ((longlength[3]&0xff) << 24) | ((longlength[2]&0xff) << 16) | ((longlength[1]&0xff) << 8) | (longlength[0]&0xff) 85 | tagBytes += ll 86 | tagBytes += f.read(tagLength) 87 | return (tagType, tagBytes) 88 | 89 | def outputInt(o, i): 90 | o.write(struct.pack('I', i)) 91 | 92 | def outputTelemetryTag(o, passwordClear): 93 | 94 | lengthBytes = 2 # reserve 95 | if passwordClear: 96 | sha = hashlib.sha256() 97 | sha.update(passwordClear) 98 | passwordDigest = sha.digest() 99 | lengthBytes += len(passwordDigest) 100 | 101 | # Record header 102 | code = 93 103 | if lengthBytes >= 63: 104 | o.write(struct.pack('= 3 else None 132 | 133 | #################################### 134 | # Process SWF header 135 | #################################### 136 | 137 | swfFH = open(infile, 'rb') 138 | signature = swfFH.read(3) 139 | swfVersion = swfFH.read(1) 140 | struct.unpack("> 3 170 | rrbytes = (7 + (rbits*4) - 3) / 8; 171 | o.write(rs) 172 | o.write(f.read((int)(rrbytes))) 173 | 174 | o.write(f.read(4)) # FrameRate and FrameCount 175 | 176 | #################################### 177 | # Process each SWF tag 178 | #################################### 179 | 180 | while True: 181 | (tagType, tagBytes) = consumeSwfTag(f) 182 | if tagType == 93: 183 | sys.exit(0) 184 | elif tagType == 92: 185 | raise Exception("Bad SWF: Signed SWFs are not supported") 186 | elif tagType == 69: 187 | # FileAttributes tag 188 | o.write(tagBytes) 189 | 190 | # Look ahead for Metadata tag. If present, put our tag after it 191 | (nextTagType, nextTagBytes) = consumeSwfTag(f) 192 | writeAfterNextTag = nextTagType == 77 193 | if writeAfterNextTag: 194 | o.write(nextTagBytes) 195 | 196 | outputTelemetryTag(o, passwordClear) 197 | 198 | # If there was no Metadata tag, we still need to write that tag out 199 | if not writeAfterNextTag: 200 | o.write(nextTagBytes) 201 | 202 | (tagType, tagBytes) = consumeSwfTag(f) 203 | 204 | o.write(tagBytes) 205 | 206 | if tagType == 0: 207 | break 208 | 209 | #################################### 210 | # Finish up 211 | #################################### 212 | 213 | # Fix the FileLength header 214 | uncompressedLength = o.tell() 215 | o.seek(4) 216 | o.write(struct.pack("I", uncompressedLength)) 217 | o.flush() 218 | o.seek(0) 219 | 220 | # Copy the temp file to the outFile, compressing if necessary 221 | outFile = open(infile, "wb") 222 | if signature == b"FWS": 223 | shutil.copyfileobj(o, outFile) 224 | else: 225 | outFile.write(o.read(8)) # File is compressed after header 226 | if signature == b"CWS": 227 | outFile.write(zlib.compress(o.read())) 228 | elif signature == b"ZWS": 229 | compressed = pylzma.compress(o.read()) 230 | outputInt(outFile, len(compressed)-5) # LZMA SWF has CompressedLength header field 231 | outFile.write(compressed) 232 | else: 233 | assert(false) 234 | 235 | outFile.close() 236 | 237 | if passwordClear: 238 | print("Added Telemetry flag with encrypted password " + passwordClear + " in " + infile) 239 | else: 240 | print("Added Telemetry flag in " + infile) 241 | -------------------------------------------------------------------------------- /build/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | if [ -z "$FLEXPATH" ]; then 3 | echo "Usage FLEXPATH=/path/to/flex/sdk sh ./build.sh" 4 | exit 5 | fi 6 | 7 | OPT_DEBUG="-use-network=false \ 8 | -optimize=true \ 9 | -define=CONFIG::LOGGING,true \ 10 | -define=CONFIG::FLASH_11_1,true" 11 | 12 | OPT_RELEASE="-use-network=false \ 13 | -optimize=true \ 14 | -define=CONFIG::LOGGING,false \ 15 | -define=CONFIG::FLASH_11_1,true" 16 | 17 | OPT_DEBUG_10_1="-use-network=false \ 18 | -optimize=true \ 19 | -define=CONFIG::LOGGING,true \ 20 | -define=CONFIG::FLASH_11_1,false" 21 | 22 | OPT_RELEASE_10_1="-use-network=false \ 23 | -optimize=true \ 24 | -define=CONFIG::LOGGING,false \ 25 | -define=CONFIG::FLASH_11_1,false" 26 | 27 | echo "Compiling bin/debug/flashls.swc" 28 | $FLEXPATH/bin/compc \ 29 | $OPT_DEBUG_10_1 \ 30 | -include-sources ../src/org/mangui/hls \ 31 | -output ../bin/debug/flashls.swc \ 32 | -target-player="10.1" 33 | 34 | echo "Compiling bin/release/flashls.swc" 35 | $FLEXPATH/bin/compc \ 36 | $OPT_RELEASE_10_1 \ 37 | -include-sources ../src/org/mangui/hls \ 38 | -output ../bin/release/flashls.swc \ 39 | -target-player="10.1" 40 | 41 | echo "Compiling bin/release/flashlsChromeless.swf" 42 | $FLEXPATH/bin/mxmlc ../src/org/mangui/chromeless/ChromelessPlayer.as \ 43 | -source-path ../src \ 44 | -o ../bin/release/flashlsChromeless.swf \ 45 | $OPT_RELEASE \ 46 | -library-path+=../lib/blooddy_crypto.swc \ 47 | -target-player="11.1" \ 48 | -default-size 480 270 \ 49 | -default-background-color=0x000000 50 | ./add-opt-in.py ../bin/release/flashlsChromeless.swf 51 | 52 | echo "Compiling bin/debug/flashlsChromeless.swf" 53 | $FLEXPATH/bin/mxmlc ../src/org/mangui/chromeless/ChromelessPlayer.as \ 54 | -source-path ../src \ 55 | -o ../bin/debug/flashlsChromeless.swf \ 56 | $OPT_DEBUG \ 57 | -library-path+=../lib/blooddy_crypto.swc \ 58 | -target-player="11.1" \ 59 | -default-size 480 270 \ 60 | -default-background-color=0x000000 61 | ./add-opt-in.py ../bin/debug/flashlsChromeless.swf 62 | 63 | #echo "Compiling flashlsBasic.swf" 64 | #$FLEXPATH/bin/mxmlc ../src/org/mangui/basic/Player.as \ 65 | # -source-path ../src \ 66 | # -o ../test/chromeless/flashlsBasic.swf \ 67 | # $COMMON_OPT \ 68 | # -target-player="11.1" \ 69 | # -default-size 640 480 \ 70 | # -default-background-color=0x000000 71 | 72 | echo "Compiling bin/release/flashlsFlowPlayer.swf" 73 | $FLEXPATH/bin/mxmlc ../src/org/mangui/flowplayer/HLSPluginFactory.as \ 74 | -source-path ../src -o ../bin/release/flashlsFlowPlayer.swf \ 75 | $OPT_RELEASE \ 76 | -library-path+=../lib/flowplayer \ 77 | -load-externs=../lib/flowplayer/flowplayer-classes.xml \ 78 | -target-player="11.1" 79 | ./add-opt-in.py ../bin/release/flashlsFlowPlayer.swf 80 | 81 | echo "Compiling bin/debug/flashlsFlowPlayer.swf" 82 | $FLEXPATH/bin/mxmlc ../src/org/mangui/flowplayer/HLSPluginFactory.as \ 83 | -source-path ../src -o ../bin/debug/flashlsFlowPlayer.swf \ 84 | $OPT_DEBUG \ 85 | -library-path+=../lib/flowplayer \ 86 | -load-externs=../lib/flowplayer/flowplayer-classes.xml \ 87 | -target-player="11.1" 88 | ./add-opt-in.py ../bin/debug/flashlsFlowPlayer.swf 89 | 90 | echo "Compiling bin/release/flashlsOSMF.swf" 91 | $FLEXPATH/bin/mxmlc ../src/org/mangui/osmf/plugins/HLSDynamicPlugin.as \ 92 | -source-path ../src \ 93 | -o ../bin/release/flashlsOSMF.swf \ 94 | $OPT_RELEASE_10_1 \ 95 | -library-path+=../lib/osmf \ 96 | -load-externs ../lib/osmf/exclude-sources.xml \ 97 | -target-player="10.1" #-compiler.verbose-stacktraces=true -link-report=../test/osmf/link-report.xml 98 | ./add-opt-in.py ../bin/release/flashlsOSMF.swf 99 | 100 | echo "Compiling bin/debug/flashlsOSMF.swf" 101 | $FLEXPATH/bin/mxmlc ../src/org/mangui/osmf/plugins/HLSDynamicPlugin.as \ 102 | -source-path ../src \ 103 | -o ../bin/debug/flashlsOSMF.swf \ 104 | $OPT_DEBUG_10_1 \ 105 | -library-path+=../lib/osmf \ 106 | -load-externs ../lib/osmf/exclude-sources.xml \ 107 | -target-player="10.1" #-compiler.verbose-stacktraces=true -link-report=../test/osmf/link-report.xml 108 | ./add-opt-in.py ../bin/debug/flashlsOSMF.swf 109 | 110 | echo "Compiling bin/release/flashlsOSMF.swc" 111 | $FLEXPATH/bin/compc -include-sources ../src/org/mangui/osmf \ 112 | -output ../bin/release/flashlsOSMF.swc \ 113 | $OPT_RELEASE_10_1 \ 114 | -library-path+=../bin/release/flashls.swc \ 115 | -library-path+=../lib/osmf \ 116 | -target-player="10.1" \ 117 | -debug=false \ 118 | -external-library-path+=../lib/osmf 119 | 120 | echo "Compiling bin/debug/flashlsOSMF.swc" 121 | $FLEXPATH/bin/compc -include-sources ../src/org/mangui/osmf \ 122 | -output ../bin/debug/flashlsOSMF.swc \ 123 | $OPT_DEBUG_10_1 \ 124 | -library-path+=../bin/debug/flashls.swc \ 125 | -library-path+=../lib/osmf \ 126 | -target-player="10.1" \ 127 | -debug=false \ 128 | -external-library-path+=../lib/osmf 129 | 130 | -------------------------------------------------------------------------------- /build/build_bemtv.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | FLEXPATH=~/airsdk/ 3 | 4 | OPT_DEBUG="-use-network=false \ 5 | -optimize=true \ 6 | -compress=true \ 7 | -strict=true \ 8 | -use-gpu=true \ 9 | -define=CONFIG::LOGGING,true \ 10 | -define=CONFIG::FLASH_11_1,true" 11 | 12 | OPT_RELEASE="-use-network=false \ 13 | -optimize=true \ 14 | -compress=true \ 15 | -strict=true \ 16 | -use-gpu=true \ 17 | -define=CONFIG::LOGGING,false \ 18 | -define=CONFIG::FLASH_11_1,true" 19 | 20 | 21 | echo "Compiling bin/debug/P2PHLSPlayer.swf" 22 | $FLEXPATH/bin/mxmlc ../src/tv/bem/BemTVPlayer.as \ 23 | -source-path ../src \ 24 | -o /Users/flavio.barbosa/Development/bemtv/clappr-p2phls-plugin/public/P2PHLSPlayer.swf \ 25 | $OPT_RELEASE \ 26 | -library-path+=../lib/blooddy_crypto.swc \ 27 | -target-player="11.5" \ 28 | -default-size 480 270 \ 29 | -default-background-color=0x000000 \ 30 | -default-frame-rate=60 31 | ./add-opt-in.py /Users/flavio.barbosa/Development/bemtv/clappr-p2phls-plugin/public/P2PHLSPlayer.swf 32 | 33 | 34 | -------------------------------------------------------------------------------- /build/build_globo.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | FLEXPATH=~/airsdk/ 3 | 4 | OPT_DEBUG=" 5 | -use-network=false \ 6 | -optimize=true \ 7 | -compress=true \ 8 | -strict=true \ 9 | -use-gpu=true \ 10 | -define=CONFIG::LOGGING,true \ 11 | -define=CONFIG::FLASH_11_1,true" 12 | 13 | OPT_RELEASE=" 14 | -use-network=false \ 15 | -optimize=true \ 16 | -define=CONFIG::LOGGING,false \ 17 | -define=CONFIG::FLASH_11_1,true" 18 | 19 | echo "Compiling bin/release/HLSPlayer.swf" 20 | $FLEXPATH/bin/mxmlc ../src/com/globo/Player.as \ 21 | -source-path ../src \ 22 | -o ../bin/release/HLSPlayer.swf \ 23 | $OPT_RELEASE \ 24 | -library-path+=../lib/blooddy_crypto.swc \ 25 | -target-player="14.0" \ 26 | -default-size 480 270 \ 27 | -default-background-color=0x000000 \ 28 | -default-frame-rate=60 29 | ./add-opt-in.py ../bin/release/HLSPlayer.swf 30 | 31 | echo "Compiling bin/debug/HLSPlayer.swf" 32 | $FLEXPATH/bin/mxmlc ../src/com/globo/Player.as \ 33 | -source-path ../src \ 34 | -o ../bin/debug/HLSPlayer.swf \ 35 | $OPT_DEBUG \ 36 | -library-path+=../lib/blooddy_crypto.swc \ 37 | -target-player="14.0" \ 38 | -default-size 480 270 \ 39 | -default-background-color=0x000000 \ 40 | -default-frame-rate=60 41 | ./add-opt-in.py ../bin/debug/HLSPlayer.swf 42 | 43 | 44 | -------------------------------------------------------------------------------- /examples/chromeless/example.html: -------------------------------------------------------------------------------- 1 | 2 | flashls/Chromeless Player sample page 3 | 4 | 5 | 6 | 7 | flashls/Chromeless sample page
8 | 9 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 26 | 27 | 28 | 29 | 30 | 146 |
147 | media position
148 | 149 | 150 |
151 |
152 |
Play
153 |
Pause
154 |
Resume
155 |
Stop
156 |
Seek
157 |
Volume
158 |
Level
159 |
160 | 161 |
seek to
162 |
set volume level to
163 |
force quality level to
164 |
Video status messages
165 | 166 | -------------------------------------------------------------------------------- /examples/flowplayer/flowplayer.controls.swf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bemtv/flashls/44ac635b7f2311fad65eefb0ea2229d2a94ecc10/examples/flowplayer/flowplayer.controls.swf -------------------------------------------------------------------------------- /examples/flowplayer/flowplayer.swf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bemtv/flashls/44ac635b7f2311fad65eefb0ea2229d2a94ecc10/examples/flowplayer/flowplayer.swf -------------------------------------------------------------------------------- /examples/flowplayer/index.html: -------------------------------------------------------------------------------- 1 | 2 | Flowplayer - flashls 3 | 4 | 5 | 6 | 7 | 8 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 |
27 | 28 |

flashls 29 | within Flowplayer 30 |

31 |
32 | 33 | 34 |
72 |

73 | This demonstrates a setup to play a VOD HLS stream within Flowplayer 74 | with a fallback to Html5 for Ipad. 75 |

76 | Examples 77 | 78 |

Basic example with a single bitrate

79 |
80 |

flowplayer("player", "flowplayer.swf", {
// configure the required plugins
plugins: {
httpstreaming: {
url: 'flashlsFlowPlayer.swf'
}
},
clip: {
url: "http://184.72.239.149/vod/smil:bigbuckbunnyiphone.smil/chunklist-b400000.m3u8",
ipadUrl: "http://184.72.239.149/vod/smil:bigbuckbunnyiphone.smil/chunklist-b400000.m3u8",
urlResolvers: ["httpstreaming","brselect"],
provider: "httpstreaming",
autoPlay: false
},
log: {
level: 'debug',
filter: 'org.osmf.*, org.electroteque.m3u8.*, org.flowplayer.bitrateselect.*'
}
}).ipad();
81 |
82 |
83 |
84 | -------------------------------------------------------------------------------- /examples/libs/ParsedQueryString.js: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * 3 | * ParsedQueryString version 1.0 4 | * Copyright 2007, Jeff Mott . All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms with or without 7 | * modification are permitted provided that the above copyright notice, 8 | * this condition, and the following disclaimer are retained. 9 | * 10 | * THIS SOFTWARE IS PROVIDED AS IS, AND ANY EXPRESS OR IMPLIED WARRANTIES, 11 | * INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 12 | * FITNESS FOR A PARTICULAR PURPOSE, ARE DISCLAIMED. IN NO EVENT SHALL THE 13 | * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 14 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING BUT NOT 15 | * LIMITED TO PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 16 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 17 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 18 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 19 | * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 20 | * 21 | ******************************************************************************/ 22 | 23 | function ParsedQueryString() { 24 | this._init(); 25 | } 26 | 27 | ParsedQueryString.version = '1.0'; 28 | 29 | ParsedQueryString.prototype = 30 | { 31 | _init: 32 | function () 33 | { 34 | this._parameters = {}; 35 | 36 | if (location.search.length <= 1) 37 | return; 38 | var pairs = location.search.substr(1).split(/[&;]/); 39 | for (var i = 0; i < pairs.length; i++) 40 | { 41 | var pair = pairs[i].split(/=/); 42 | var name = this._decodeURL(pair[0]); 43 | if (Boolean(pair[1])) 44 | { 45 | var value = this._decodeURL(pair[1]); 46 | if (Boolean(this._parameters[name])) 47 | this._parameters[name].push(value); 48 | else 49 | this._parameters[name] = [value]; 50 | } 51 | } 52 | }, 53 | 54 | _decodeURL: 55 | function (url) { 56 | return decodeURIComponent(url.replace(/\+/g, " ")); 57 | }, 58 | 59 | param: 60 | function (name) 61 | { 62 | if (Boolean(this._parameters[name])) 63 | return this._parameters[name][0]; 64 | else 65 | return ""; 66 | }, 67 | 68 | params: 69 | function (name) 70 | { 71 | if (Boolean(name)) 72 | { 73 | if (Boolean(this._parameters[name])) 74 | { 75 | var values = []; 76 | for (var i = 0; i < this._parameters[name].length; i++) 77 | values.push(this._parameters[name][i]); 78 | return values; 79 | } 80 | else 81 | return []; 82 | } 83 | else 84 | { 85 | var names = []; 86 | for (var name in this._parameters) 87 | names.push(name); 88 | return names; 89 | } 90 | } 91 | }; 92 | -------------------------------------------------------------------------------- /examples/osmf/GrindPlayer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | GrindPlayer 5 | 6 | 7 | 8 | 9 | 19 | 20 | 22 | 24 | 26 | 134 | 135 | 136 | 137 | 138 | 141 | 144 | 147 | 149 | 150 |
139 | 140 | 142 | GrindPlayer 143 | 145 | osmf logo 146 | 148 |
151 | 152 | 153 | 160 | 161 |
154 |
155 |

156 | Alternative content 157 |

158 |
159 |
162 | Check with your own Playlist ! beware of Cross Domain Policy
163 |
164 |

165 | 166 | 171 |

The following videos should stream correctly. Each time an 172 | issue is reported with a sample playlist, it will be added in the list 173 | and verified after bugfixing
174 |
    175 | 176 | 177 | -------------------------------------------------------------------------------- /examples/osmf/GrindPlayer.swf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bemtv/flashls/44ac635b7f2311fad65eefb0ea2229d2a94ecc10/examples/osmf/GrindPlayer.swf -------------------------------------------------------------------------------- /examples/osmf/StrobeMediaPlayback.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Strobe Media Playback 5 | 6 | 7 | 8 | 9 | 19 | 20 | 22 | 24 | 26 | 134 | 135 | 136 | 137 | 138 | 141 | 144 | 147 | 149 | 150 |
    139 | 140 | 142 | Strobe Media Playback 143 | 145 | osmf logo 146 | 148 |
    151 | 152 | 153 | 160 | 161 |
    154 |
    155 |

    156 | Alternative content 157 |

    158 |
    159 |
    162 | Check with your own Playlist ! beware of Cross Domain Policy
    163 |
    164 |

    165 | 166 | 171 |

    The following videos should stream correctly. Each time an 172 | issue is reported with a sample playlist, it will be added in the list 173 | and verified after bugfixing
    174 |
      175 | 176 | 177 | -------------------------------------------------------------------------------- /examples/osmf/StrobeMediaPlayback.swf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bemtv/flashls/44ac635b7f2311fad65eefb0ea2229d2a94ecc10/examples/osmf/StrobeMediaPlayback.swf -------------------------------------------------------------------------------- /examples/osmf/images/adobe-lq.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bemtv/flashls/44ac635b7f2311fad65eefb0ea2229d2a94ecc10/examples/osmf/images/adobe-lq.png -------------------------------------------------------------------------------- /examples/osmf/images/osmf_horizontal_red.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bemtv/flashls/44ac635b7f2311fad65eefb0ea2229d2a94ecc10/examples/osmf/images/osmf_horizontal_red.png -------------------------------------------------------------------------------- /examples/osmf/images/poster.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bemtv/flashls/44ac635b7f2311fad65eefb0ea2229d2a94ecc10/examples/osmf/images/poster.png -------------------------------------------------------------------------------- /lib/blooddy_crypto.swc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bemtv/flashls/44ac635b7f2311fad65eefb0ea2229d2a94ecc10/lib/blooddy_crypto.swc -------------------------------------------------------------------------------- /lib/flowplayer/flowplayer.swc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bemtv/flashls/44ac635b7f2311fad65eefb0ea2229d2a94ecc10/lib/flowplayer/flowplayer.swc -------------------------------------------------------------------------------- /lib/osmf/OSMF.swc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bemtv/flashls/44ac635b7f2311fad65eefb0ea2229d2a94ecc10/lib/osmf/OSMF.swc -------------------------------------------------------------------------------- /lib/osmf/exclude-sources.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /src/org/mangui/basic/Player.as: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | package org.mangui.basic { 6 | import org.mangui.hls.HLS; 7 | import org.mangui.hls.event.HLSEvent; 8 | 9 | import flash.display.Sprite; 10 | import flash.media.Video; 11 | 12 | 13 | public class Player extends Sprite { 14 | private var hls : HLS = null; 15 | private var video : Video = null; 16 | 17 | public function Player() { 18 | hls = new HLS(); 19 | 20 | video = new Video(640, 480); 21 | addChild(video); 22 | video.x = 0; 23 | video.y = 0; 24 | video.smoothing = true; 25 | video.attachNetStream(hls.stream); 26 | hls.addEventListener(HLSEvent.MANIFEST_LOADED, manifestHandler); 27 | hls.load("http://domain.com/hls/m1.m3u8"); 28 | } 29 | 30 | public function manifestHandler(event : HLSEvent) : void { 31 | hls.stream.play(); 32 | }; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/org/mangui/chromeless/JSURLStream.as: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | package org.mangui.chromeless { 5 | import flash.events.IOErrorEvent; 6 | 7 | import by.blooddy.crypto.Base64; 8 | 9 | import flash.events.TimerEvent; 10 | import flash.utils.Timer; 11 | import flash.external.ExternalInterface; 12 | import flash.events.Event; 13 | import flash.events.ProgressEvent; 14 | import flash.utils.ByteArray; 15 | import flash.net.URLStream; 16 | import flash.net.URLRequest; 17 | 18 | CONFIG::LOGGING { 19 | import org.mangui.hls.utils.Log; 20 | } 21 | 22 | public class JSURLStream extends URLStream { 23 | private var _connected : Boolean; 24 | private var _resource : ByteArray = new ByteArray(); 25 | /** Timer for decode packets **/ 26 | private var _timer : Timer; 27 | /** read position **/ 28 | private var _read_position : uint; 29 | /** read position **/ 30 | private var _base64_resource : String = ""; 31 | /** chunk size to avoid blocking **/ 32 | private static const CHUNK_SIZE : uint = 65536; 33 | private static var _instance_count : int = 0; 34 | private var _id : int; 35 | 36 | public function JSURLStream() { 37 | addEventListener(Event.OPEN, onOpen); 38 | super(); 39 | // Connect calls to JS. 40 | if (ExternalInterface.available) { 41 | _id = _instance_count; 42 | _instance_count++; 43 | CONFIG::LOGGING { 44 | Log.info("add callback resourceLoaded"); 45 | } 46 | ExternalInterface.addCallback("resourceLoaded" + _id, resourceLoaded); 47 | ExternalInterface.addCallback("resourceLoadingError" + _id, resourceLoadingError); 48 | } 49 | } 50 | 51 | override public function get connected() : Boolean { 52 | return _connected; 53 | } 54 | 55 | override public function get bytesAvailable() : uint { 56 | return _resource.bytesAvailable; 57 | } 58 | 59 | override public function readByte() : int { 60 | return _resource.readByte(); 61 | } 62 | 63 | override public function readUnsignedShort() : uint { 64 | return _resource.readUnsignedShort(); 65 | } 66 | 67 | override public function readBytes(bytes : ByteArray, offset : uint = 0, length : uint = 0) : void { 68 | _resource.readBytes(bytes, offset, length); 69 | } 70 | 71 | override public function close() : void { 72 | } 73 | 74 | override public function load(request : URLRequest) : void { 75 | CONFIG::LOGGING { 76 | Log.info("JSURLStream.load:" + request.url); 77 | } 78 | if (ExternalInterface.available) { 79 | ExternalInterface.call("onRequestResource" + _id, request.url); 80 | this.dispatchEvent(new Event(Event.OPEN)); 81 | } else { 82 | super.load(request); 83 | } 84 | } 85 | 86 | private function onOpen(event : Event) : void { 87 | _connected = true; 88 | } 89 | 90 | protected function resourceLoaded(base64Resource : String) : void { 91 | if (_base64_resource.length == 0) { 92 | _base64_resource = base64Resource; 93 | } else { 94 | _base64_resource += base64Resource; 95 | } 96 | } 97 | 98 | protected function startDecoding() : void { 99 | _resource = new ByteArray(); 100 | _read_position = 0; 101 | _timer = new Timer(0, 0); 102 | _timer.addEventListener(TimerEvent.TIMER, _decodeData); 103 | _timer.start(); 104 | } 105 | 106 | protected function resourceLoadingError() : void { 107 | CONFIG::LOGGING { 108 | Log.info("resourceLoadingError"); 109 | } 110 | _timer.stop(); 111 | _resource.position = 0; 112 | _base64_resource = ""; 113 | this.dispatchEvent(new IOErrorEvent(IOErrorEvent.IO_ERROR)); 114 | } 115 | 116 | protected function resourceLoadingSuccess() : void { 117 | _timer.stop(); 118 | _resource.position = 0; 119 | _base64_resource = ""; 120 | this.dispatchEvent(new ProgressEvent(ProgressEvent.PROGRESS, false, false, _resource.bytesAvailable, _resource.bytesAvailable)); 121 | this.dispatchEvent(new Event(Event.COMPLETE)); 122 | } 123 | 124 | /** decrypt a small chunk of packets each time to avoid blocking **/ 125 | private function _decodeData(e : Event) : void { 126 | var start_pos : uint = _read_position; 127 | var end_pos : uint; 128 | var decode_completed : Boolean; 129 | if (_base64_resource.length <= _read_position + CHUNK_SIZE) { 130 | end_pos = _base64_resource.length; 131 | decode_completed = true; 132 | } else { 133 | end_pos = _read_position + CHUNK_SIZE; 134 | } 135 | var tmpString : String = _base64_resource.substring(start_pos, end_pos); 136 | try { 137 | _resource.writeBytes(Base64.decode(tmpString)); 138 | } catch (error:Error) { 139 | resourceLoadingError(); 140 | } 141 | if (decode_completed) { 142 | resourceLoadingSuccess(); 143 | } else { 144 | _read_position = end_pos; 145 | } 146 | } 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /src/org/mangui/flowplayer/HLSPluginFactory.as: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | package org.mangui.flowplayer { 5 | import org.flowplayer.model.PluginFactory; 6 | 7 | import flash.display.Sprite; 8 | 9 | public class HLSPluginFactory extends Sprite implements PluginFactory { 10 | public function HLSPluginFactory() { 11 | } 12 | 13 | public function newPlugin() : Object { 14 | return new HLSStreamProvider(); 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /src/org/mangui/hls/HLS.as: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | package org.mangui.hls { 5 | import org.mangui.hls.model.AudioTrack; 6 | 7 | import flash.display.Stage; 8 | import flash.net.NetConnection; 9 | import flash.net.NetStream; 10 | import flash.net.URLStream; 11 | import flash.events.EventDispatcher; 12 | import flash.events.Event; 13 | 14 | import org.mangui.hls.model.Level; 15 | import org.mangui.hls.event.HLSEvent; 16 | import org.mangui.hls.playlist.AltAudioTrack; 17 | import org.mangui.hls.loader.ManifestLoader; 18 | import org.mangui.hls.controller.AudioTrackController; 19 | import org.mangui.hls.loader.FragmentLoader; 20 | import org.mangui.hls.stream.HLSNetStream; 21 | 22 | CONFIG::LOGGING { 23 | import org.mangui.hls.utils.Log; 24 | } 25 | 26 | /** Class that manages the streaming process. **/ 27 | public class HLS extends EventDispatcher { 28 | private var _fragmentLoader : FragmentLoader; 29 | private var _manifestLoader : ManifestLoader; 30 | private var _audioTrackController : AudioTrackController; 31 | /** HLS NetStream **/ 32 | private var _hlsNetStream : HLSNetStream; 33 | /** HLS URLStream **/ 34 | private var _hlsURLStream : Class; 35 | private var _client : Object = {}; 36 | private var _stage : Stage; 37 | 38 | /** Create and connect all components. **/ 39 | public function HLS() { 40 | var connection : NetConnection = new NetConnection(); 41 | connection.connect(null); 42 | _manifestLoader = new ManifestLoader(this); 43 | _audioTrackController = new AudioTrackController(this); 44 | _hlsURLStream = URLStream as Class; 45 | // default loader 46 | _fragmentLoader = new FragmentLoader(this, _audioTrackController); 47 | _hlsNetStream = new HLSNetStream(connection, this, _fragmentLoader); 48 | }; 49 | 50 | /** Forward internal errors. **/ 51 | override public function dispatchEvent(event : Event) : Boolean { 52 | if (event.type == HLSEvent.ERROR) { 53 | CONFIG::LOGGING { 54 | Log.error((event as HLSEvent).error); 55 | } 56 | _hlsNetStream.close(); 57 | } 58 | return super.dispatchEvent(event); 59 | }; 60 | 61 | public function dispose() : void { 62 | _fragmentLoader.dispose(); 63 | _manifestLoader.dispose(); 64 | _audioTrackController.dispose(); 65 | _hlsNetStream.dispose_(); 66 | _fragmentLoader = null; 67 | _manifestLoader = null; 68 | _audioTrackController = null; 69 | _hlsNetStream = null; 70 | _client = null; 71 | _stage = null; 72 | _hlsNetStream = null; 73 | } 74 | 75 | /** Return the quality level used when starting a fresh playback **/ 76 | public function get startlevel() : int { 77 | return _manifestLoader.startlevel; 78 | }; 79 | 80 | public function get delay() : Number { 81 | return _manifestLoader.delay; 82 | } 83 | 84 | /** Return the quality level used after a seek operation **/ 85 | public function get seeklevel() : int { 86 | return _manifestLoader.seeklevel; 87 | }; 88 | 89 | /** Return the quality level of the currently played fragment **/ 90 | public function get playbacklevel() : int { 91 | return _hlsNetStream.playbackLevel; 92 | }; 93 | 94 | /** Return the quality level of last loaded fragment **/ 95 | public function get level() : int { 96 | return _fragmentLoader.level; 97 | }; 98 | 99 | /* set quality level for next loaded fragment (-1 for automatic level selection) */ 100 | public function set level(level : int) : void { 101 | _fragmentLoader.level = level; 102 | }; 103 | 104 | public function removeLevel(pos:Number):void { 105 | _manifestLoader.removeLevel(pos); 106 | }; 107 | 108 | /* check if we are in automatic level selection mode */ 109 | public function get autolevel() : Boolean { 110 | return _fragmentLoader.autolevel; 111 | }; 112 | 113 | /** Return a Vector of quality level **/ 114 | public function get levels() : Vector. { 115 | return _manifestLoader.levels; 116 | }; 117 | 118 | /** Return the last program date **/ 119 | public function get lastProgramDate() : Number { 120 | return _manifestLoader.lastProgramDate; 121 | } 122 | 123 | /** Return the current playback position. **/ 124 | public function get position() : Number { 125 | return _hlsNetStream.position; 126 | }; 127 | 128 | /** Return the overall dropped frames **/ 129 | public function get droppedFrames() : int { 130 | return _hlsNetStream.droppedFrames; 131 | }; 132 | 133 | /** Return the current playback state. **/ 134 | public function get playbackState() : String { 135 | return _hlsNetStream.playbackState; 136 | }; 137 | 138 | /** Return the current seek state. **/ 139 | public function get seekState() : String { 140 | return _hlsNetStream.seekState; 141 | }; 142 | 143 | /** Return the type of stream (VOD/LIVE). **/ 144 | public function get type() : String { 145 | return _manifestLoader.type; 146 | }; 147 | 148 | /** Load and parse a new HLS URL **/ 149 | public function load(url : String) : void { 150 | _hlsNetStream.close(); 151 | _manifestLoader.load(url); 152 | }; 153 | 154 | /** return HLS NetStream **/ 155 | public function get stream() : NetStream { 156 | return _hlsNetStream; 157 | } 158 | 159 | public function get client() : Object { 160 | return _client; 161 | } 162 | 163 | public function set client(value : Object) : void { 164 | _client = value; 165 | } 166 | 167 | /** get current Buffer Length **/ 168 | public function get bufferLength() : Number { 169 | return _hlsNetStream.bufferLength; 170 | }; 171 | 172 | /** get audio tracks list**/ 173 | public function get audioTracks() : Vector. { 174 | return _audioTrackController.audioTracks; 175 | }; 176 | 177 | /** get alternate audio tracks list from playlist **/ 178 | public function get altAudioTracks() : Vector. { 179 | return _manifestLoader.altAudioTracks; 180 | }; 181 | 182 | /** get index of the selected audio track (index in audio track lists) **/ 183 | public function get audioTrack() : int { 184 | return _audioTrackController.audioTrack; 185 | }; 186 | 187 | /** select an audio track, based on its index in audio track lists**/ 188 | public function set audioTrack(val : int) : void { 189 | _audioTrackController.audioTrack = val; 190 | } 191 | 192 | /* set stage */ 193 | public function set stage(stage : Stage) : void { 194 | _stage = stage; 195 | } 196 | 197 | /* get stage */ 198 | public function get stage() : Stage { 199 | return _stage; 200 | } 201 | 202 | /* set URL stream loader */ 203 | public function set URLstream(urlstream : Class) : void { 204 | _hlsURLStream = urlstream; 205 | } 206 | 207 | /* retrieve URL stream loader */ 208 | public function get URLstream() : Class { 209 | return _hlsURLStream; 210 | } 211 | } 212 | ; 213 | } 214 | -------------------------------------------------------------------------------- /src/org/mangui/hls/constant/HLSMaxLevelCappingMode.as: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | package org.mangui.hls.constant { 5 | /** Max Level Capping Modes **/ 6 | public class HLSMaxLevelCappingMode { 7 | /** 8 | * max capped level should be the one with the dimensions equal or greater than the stage dimensions (so the video will be downscaled) 9 | */ 10 | public static const DOWNSCALE : String = "downscale"; 11 | 12 | /** 13 | * max capped level should be the one with the dimensions equal or lower than the stage dimensions (so the video will be upscaled) 14 | */ 15 | public static const UPSCALE : String = "upscale"; 16 | } 17 | } -------------------------------------------------------------------------------- /src/org/mangui/hls/constant/HLSPlayStates.as: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | package org.mangui.hls.constant { 5 | /** Identifiers for the different playback states. **/ 6 | public class HLSPlayStates { 7 | /** idle state. **/ 8 | public static const IDLE : String = "IDLE"; 9 | /** playing state. **/ 10 | public static const PLAYING : String = "PLAYING"; 11 | /** paused state. **/ 12 | public static const PAUSED : String = "PAUSED"; 13 | /** playing/buffering state (playback is paused and will restart automatically as soon as buffer will contain enough data) **/ 14 | public static const PLAYING_BUFFERING : String = "PLAYING_BUFFERING"; 15 | /** paused/buffering state (playback is paused, and buffer is in low condition) **/ 16 | public static const PAUSED_BUFFERING : String = "PAUSED_BUFFERING"; 17 | } 18 | } -------------------------------------------------------------------------------- /src/org/mangui/hls/constant/HLSSeekMode.as: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | package org.mangui.hls.constant { 5 | /** HLS Seek mode configuration **/ 6 | public class HLSSeekMode { 7 | /** seek on segment boundary **/ 8 | public static const SEGMENT_SEEK : String = "SEGMENT"; 9 | /** seek on keyframe boundary **/ 10 | public static const KEYFRAME_SEEK : String = "KEYFRAME"; 11 | /** accurate seeking **/ 12 | public static const ACCURATE_SEEK : String = "ACCURATE"; 13 | } 14 | } -------------------------------------------------------------------------------- /src/org/mangui/hls/constant/HLSSeekStates.as: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | package org.mangui.hls.constant { 5 | /** Identifiers for the different seeking states. **/ 6 | public class HLSSeekStates { 7 | /** idle state. **/ 8 | public static const IDLE : String = "IDLE"; 9 | /** seeking in progress state. **/ 10 | public static const SEEKING : String = "SEEKING"; 11 | /** seeked state. **/ 12 | public static const SEEKED : String = "SEEKED"; 13 | } 14 | } -------------------------------------------------------------------------------- /src/org/mangui/hls/constant/HLSTypes.as: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | package org.mangui.hls.constant { 5 | /** Identifiers for the different stream types. **/ 6 | public class HLSTypes { 7 | /** Identifier for live events. **/ 8 | public static const LIVE : String = "LIVE"; 9 | /** Identifier for on demand clips. **/ 10 | public static const VOD : String = "VOD"; 11 | } 12 | } -------------------------------------------------------------------------------- /src/org/mangui/hls/controller/AutoBufferController.as: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | package org.mangui.hls.controller { 5 | import org.mangui.hls.HLS; 6 | import org.mangui.hls.HLSSettings; 7 | import org.mangui.hls.event.HLSEvent; 8 | 9 | CONFIG::LOGGING { 10 | import org.mangui.hls.utils.Log; 11 | } 12 | 13 | /** Class that manages automatic min/low buffer len **/ 14 | public class AutoBufferController { 15 | /** Reference to the HLS controller. **/ 16 | private var _hls : HLS; 17 | // max nb of samples used for bw checking. the bigger it is, the more conservative it is. 18 | private static const MAX_SAMPLES : int = 30; 19 | private var _bw : Vector.; 20 | private var _nb_samples : uint; 21 | private var _targetduration : Number; 22 | private var _minBufferLength : Number; 23 | 24 | /** Create the loader. **/ 25 | public function AutoBufferController(hls : HLS) : void { 26 | _hls = hls; 27 | _hls.addEventListener(HLSEvent.MANIFEST_LOADED, _manifestLoadedHandler); 28 | _hls.addEventListener(HLSEvent.TAGS_LOADED, _fragmentLoadedHandler); 29 | _hls.addEventListener(HLSEvent.FRAGMENT_LOADED, _fragmentLoadedHandler); 30 | }; 31 | 32 | public function dispose() : void { 33 | _hls.removeEventListener(HLSEvent.MANIFEST_LOADED, _manifestLoadedHandler); 34 | _hls.removeEventListener(HLSEvent.TAGS_LOADED, _fragmentLoadedHandler); 35 | _hls.removeEventListener(HLSEvent.FRAGMENT_LOADED, _fragmentLoadedHandler); 36 | } 37 | 38 | public function get minBufferLength() : Number { 39 | return _minBufferLength; 40 | } 41 | 42 | private function _manifestLoadedHandler(event : HLSEvent) : void { 43 | _nb_samples = 0; 44 | _targetduration = event.levels[_hls.startlevel].targetduration; 45 | _bw = new Vector.(MAX_SAMPLES); 46 | _minBufferLength = _targetduration; 47 | }; 48 | 49 | private function _fragmentLoadedHandler(event : HLSEvent) : void { 50 | var cur_bw : Number = event.loadMetrics.bandwidth; 51 | _bw[_nb_samples % MAX_SAMPLES] = cur_bw; 52 | _nb_samples++; 53 | 54 | // compute min bw on MAX_SAMPLES 55 | var min_bw : Number = Number.POSITIVE_INFINITY; 56 | var samples_max : int = Math.min(_nb_samples, MAX_SAMPLES); 57 | for (var i : int = 0; i < samples_max; i++) { 58 | min_bw = Math.min(min_bw, _bw[i]); 59 | } 60 | 61 | // give more weight to current bandwidth 62 | var bw_ratio : Number = 2 * cur_bw / (min_bw + cur_bw); 63 | 64 | /* predict time to dl next segment using a conservative approach. 65 | * 66 | * heuristic is as follow : 67 | * 68 | * time to dl next segment = time to dl current segment * (playlist target duration / current segment duration) * bw_ratio 69 | * \---------------------------------------------------------------------------------/ 70 | * this part is a simple rule by 3, assuming we keep same dl bandwidth 71 | * bw ratio is the conservative factor, assuming that next segment will be downloaded with min bandwidth 72 | */ 73 | _minBufferLength = event.loadMetrics.frag_processing_time * (_targetduration / event.loadMetrics.frag_duration) * bw_ratio; 74 | // avoid min > max 75 | if (HLSSettings.maxBufferLength) { 76 | _minBufferLength = Math.min(HLSSettings.maxBufferLength, _minBufferLength); 77 | } 78 | CONFIG::LOGGING { 79 | Log.debug("AutoBufferManager:minBufferLength:" + _minBufferLength); 80 | } 81 | }; 82 | } 83 | } -------------------------------------------------------------------------------- /src/org/mangui/hls/demux/AVCC.as: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | package org.mangui.hls.demux { 5 | import flash.utils.ByteArray; 6 | CONFIG::LOGGING { 7 | import org.mangui.hls.utils.Log; 8 | import org.mangui.hls.HLSSettings; 9 | } 10 | public class AVCC { 11 | 12 | /** H264 profiles. **/ 13 | private static const PROFILES : Object = {'66':'H264 Baseline', '77':'H264 Main', '100':'H264 High'}; 14 | /** Get Avcc header from AVC stream 15 | See ISO 14496-15, 5.2.4.1 for the description of AVCDecoderConfigurationRecord 16 | **/ 17 | public static function getAVCC(sps : ByteArray, ppsVect : Vector.) : ByteArray { 18 | // Write startbyte 19 | var avcc : ByteArray = new ByteArray(); 20 | avcc.writeByte(0x01); 21 | // Write profile, compatibility and level. 22 | avcc.writeBytes(sps, 1, 3); 23 | // reserved (6 bits), NALU length size - 1 (2 bits) 24 | avcc.writeByte(0xFC | 3); 25 | // reserved (3 bits), num of SPS (5 bits) 26 | avcc.writeByte(0xE0 | 1); 27 | // 2 bytes for length of SPS 28 | avcc.writeShort(sps.length); 29 | // data of SPS 30 | avcc.writeBytes(sps, 0, sps.length); 31 | // Number of PPS 32 | avcc.writeByte(ppsVect.length); 33 | for each (var pps : ByteArray in ppsVect) { 34 | // 2 bytes for length of PPS 35 | avcc.writeShort(pps.length); 36 | // data of PPS 37 | avcc.writeBytes(pps, 0, pps.length); 38 | } 39 | CONFIG::LOGGING { 40 | if (HLSSettings.logDebug) { 41 | // Grab profile/level 42 | sps.position = 1; 43 | var prf : int = sps.readByte(); 44 | sps.position = 3; 45 | var lvl : int = sps.readByte(); 46 | Log.debug("AVC: " + PROFILES[prf] + ' level ' + lvl); 47 | } 48 | } 49 | avcc.position = 0; 50 | return avcc; 51 | } 52 | ; 53 | } 54 | } -------------------------------------------------------------------------------- /src/org/mangui/hls/demux/AudioFrame.as: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | package org.mangui.hls.demux { 5 | /** Audio Frame **/ 6 | public class AudioFrame { 7 | public var start : int; 8 | public var length : int; 9 | public var expected_length : int; 10 | public var rate : int; 11 | 12 | public function AudioFrame(start : int, length : int, expected_length : int, rate : int) { 13 | this.start = start; 14 | this.length = length; 15 | this.expected_length = expected_length; 16 | this.rate = rate; 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /src/org/mangui/hls/demux/DemuxHelper.as: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | package org.mangui.hls.demux { 5 | import org.mangui.hls.model.Level; 6 | 7 | import flash.display.DisplayObject; 8 | import flash.utils.ByteArray; 9 | 10 | CONFIG::LOGGING { 11 | import org.mangui.hls.utils.Log; 12 | } 13 | public class DemuxHelper { 14 | public static function probe(data : ByteArray, level : Level, displayObject : DisplayObject, audioselect : Function, progress : Function, complete : Function, videometadata : Function) : Demuxer { 15 | data.position = 0; 16 | CONFIG::LOGGING { 17 | Log.debug("probe fragment type"); 18 | } 19 | var aac_match : Boolean = AACDemuxer.probe(data); 20 | var mp3_match : Boolean = MP3Demuxer.probe(data); 21 | var ts_match : Boolean = TSDemuxer.probe(data); 22 | CONFIG::LOGGING { 23 | Log.debug("AAC/MP3/TS match:" + aac_match + "/" + mp3_match + "/" + ts_match); 24 | } 25 | /* prioritize level info : 26 | * if ts_match && codec_avc => TS demuxer 27 | * if aac_match && codec_aac => AAC demuxer 28 | * if mp3_match && codec_mp3 => MP3 demuxer 29 | * if no codec info in Manifest, use fallback order : AAC/MP3/TS 30 | */ 31 | if (ts_match && level.codec_h264) { 32 | CONFIG::LOGGING { 33 | Log.debug("TS match + H264 signaled in Manifest, use TS demuxer"); 34 | } 35 | return new TSDemuxer(displayObject, audioselect, progress, complete, videometadata); 36 | } else if (aac_match && level.codec_aac) { 37 | CONFIG::LOGGING { 38 | Log.debug("AAC match + AAC signaled in Manifest, use AAC demuxer"); 39 | } 40 | return new AACDemuxer(audioselect, progress, complete); 41 | } else if (mp3_match && level.codec_mp3) { 42 | CONFIG::LOGGING { 43 | Log.debug("MP3 match + MP3 signaled in Manifest, use MP3 demuxer"); 44 | } 45 | return new MP3Demuxer(audioselect, progress, complete); 46 | } else if (aac_match) { 47 | return new AACDemuxer(audioselect, progress, complete); 48 | } else if (mp3_match) { 49 | return new MP3Demuxer(audioselect, progress, complete); 50 | } else if (ts_match) { 51 | return new TSDemuxer(displayObject, audioselect, progress, complete, videometadata); 52 | } else { 53 | CONFIG::LOGGING { 54 | Log.debug("probe fails"); 55 | } 56 | return null; 57 | } 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/org/mangui/hls/demux/Demuxer.as: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | package org.mangui.hls.demux { 5 | import flash.utils.ByteArray; 6 | 7 | public interface Demuxer { 8 | function append(data : ByteArray) : void; 9 | 10 | function notifycomplete() : void; 11 | 12 | function cancel() : void; 13 | 14 | function audio_expected() : Boolean; 15 | 16 | function video_expected() : Boolean; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/org/mangui/hls/demux/ExpGolomb.as: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | package org.mangui.hls.demux { 5 | import flash.utils.ByteArray; 6 | public class ExpGolomb { 7 | private var _data : ByteArray; 8 | private var _bit : int; 9 | private var _curByte : uint; 10 | 11 | public function ExpGolomb(data : ByteArray) { 12 | _data = data; 13 | _bit = -1; 14 | } 15 | 16 | private function _readBit() : uint { 17 | var res : uint; 18 | if (_bit == -1) { 19 | // read next 20 | _curByte = _data.readByte(); 21 | _bit = 7; 22 | } 23 | res = _curByte & (1 << _bit) ? 1 : 0; 24 | _bit--; 25 | return res; 26 | } 27 | 28 | public function readBoolean() : Boolean { 29 | return (_readBit() == 1); 30 | } 31 | 32 | public function readBits(nbBits : uint) : int { 33 | var val : int = 0; 34 | for (var i : uint = 0; i < nbBits; ++i) 35 | val = (val << 1) + _readBit(); 36 | return val; 37 | } 38 | 39 | public function readUE() : uint { 40 | var nbZero : uint = 0; 41 | while (_readBit() == 0) 42 | ++nbZero; 43 | var x : uint = readBits(nbZero); 44 | return x + (1 << nbZero) - 1; 45 | } 46 | 47 | public function readSE() : uint { 48 | var value : int = readUE(); 49 | // the number is odd if the low order bit is set 50 | if (0x01 & value) { 51 | // add 1 to make it even, and divide by 2 52 | return (1 + value) >> 1; 53 | } else { 54 | // divide by two then make it negative 55 | return -1 * (value >> 1); 56 | } 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/org/mangui/hls/demux/ID3.as: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | package org.mangui.hls.demux { 5 | import flash.utils.ByteArray; 6 | 7 | CONFIG::LOGGING { 8 | import org.mangui.hls.utils.Log; 9 | } 10 | 11 | public class ID3 { 12 | public var len : int; 13 | public var hasTimestamp : Boolean = false; 14 | public var timestamp : Number; 15 | 16 | /* create ID3 object by parsing ByteArray, looking for ID3 tag length, and timestamp */ 17 | public function ID3(data : ByteArray) { 18 | var tagSize : uint = 0; 19 | try { 20 | var pos : uint = data.position; 21 | var header : String; 22 | do { 23 | header = data.readUTFBytes(3); 24 | if (header == 'ID3') { 25 | // skip 24 bits 26 | data.position += 3; 27 | // retrieve tag length 28 | var byte1 : uint = data.readUnsignedByte() & 0x7f; 29 | var byte2 : uint = data.readUnsignedByte() & 0x7f; 30 | var byte3 : uint = data.readUnsignedByte() & 0x7f; 31 | var byte4 : uint = data.readUnsignedByte() & 0x7f; 32 | tagSize = (byte1 << 21) + (byte2 << 14) + (byte3 << 7) + byte4; 33 | var end_pos : uint = data.position + tagSize; 34 | CONFIG::LOGGING { 35 | Log.debug2("ID3 tag found, size/end pos:" + tagSize + "/" + end_pos); 36 | } 37 | // read tag 38 | _parseFrame(data, end_pos); 39 | data.position = end_pos; 40 | } else if (header == '3DI') { 41 | // http://id3.org/id3v2.4.0-structure chapter 3.4. ID3v2 footer 42 | data.position += 7; 43 | CONFIG::LOGGING { 44 | Log.debug2("3DI footer found, end pos:" + data.position); 45 | } 46 | } else { 47 | data.position -= 3; 48 | len = data.position - pos; 49 | CONFIG::LOGGING { 50 | if (len) { 51 | Log.debug2("ID3 len:" + len); 52 | if (!hasTimestamp) { 53 | Log.warn("ID3 tag found, but no timestamp"); 54 | } 55 | } 56 | } 57 | return; 58 | } 59 | } while (true); 60 | } catch(e : Error) { 61 | } 62 | len = 0; 63 | return; 64 | }; 65 | 66 | /* Each Elementary Audio Stream segment MUST signal the timestamp of 67 | its first sample with an ID3 PRIV tag [ID3] at the beginning of 68 | the segment. The ID3 PRIV owner identifier MUST be 69 | "com.apple.streaming.transportStreamTimestamp". The ID3 payload 70 | MUST be a 33-bit MPEG-2 Program Elementary Stream timestamp 71 | expressed as a big-endian eight-octet number, with the upper 31 72 | bits set to zero. 73 | */ 74 | private function _parseFrame(data : ByteArray, end_pos : uint) : void { 75 | if (data.readUTFBytes(4) == "PRIV") { 76 | while (data.position + 53 <= end_pos) { 77 | // owner should be "com.apple.streaming.transportStreamTimestamp" 78 | if (data.readUTFBytes(44) == 'com.apple.streaming.transportStreamTimestamp') { 79 | // smelling even better ! we found the right descriptor 80 | // skip null character (string end) + 3 first bytes 81 | data.position += 4; 82 | // timestamp is 33 bit expressed as a big-endian eight-octet number, with the upper 31 bits set to zero. 83 | var pts_33_bit : int = data.readUnsignedByte() & 0x1; 84 | hasTimestamp = true; 85 | timestamp = (data.readUnsignedInt() / 90); 86 | if (pts_33_bit) { 87 | timestamp += 47721858.84; // 2^32 / 90 88 | } 89 | CONFIG::LOGGING { 90 | Log.debug("ID3 timestamp found:" + timestamp); 91 | } 92 | return; 93 | } else { 94 | // rewind 44 read bytes + move to next byte 95 | data.position -= 43; 96 | } 97 | } 98 | } 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/org/mangui/hls/demux/MP3Demuxer.as: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | package org.mangui.hls.demux { 5 | import flash.utils.ByteArray; 6 | 7 | import org.mangui.hls.model.AudioTrack; 8 | import org.mangui.hls.flv.FLVTag; 9 | 10 | CONFIG::LOGGING { 11 | import org.mangui.hls.utils.Log; 12 | } 13 | public class MP3Demuxer implements Demuxer { 14 | /* MPEG1-Layer3 syncword */ 15 | private static const SYNCWORD : uint = 0xFFFB; 16 | private static const RATES : Array = [44100, 48000, 32000]; 17 | private static const BIT_RATES : Array = [0, 32000, 40000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 160000, 192000, 224000, 256000, 320000, 0]; 18 | private static const SAMPLES_PER_FRAME : uint = 1152; 19 | /** Byte data to be read **/ 20 | private var _data : ByteArray; 21 | /* callback functions for audio selection, and parsing progress/complete */ 22 | private var _callback_audioselect : Function; 23 | private var _callback_progress : Function; 24 | private var _callback_complete : Function; 25 | 26 | /** append new data */ 27 | public function append(data : ByteArray) : void { 28 | if (_data == null) { 29 | _data = new ByteArray(); 30 | } 31 | _data.writeBytes(data); 32 | } 33 | 34 | /** cancel demux operation */ 35 | public function cancel() : void { 36 | _data = null; 37 | } 38 | 39 | public function audio_expected() : Boolean { 40 | return true; 41 | } 42 | 43 | public function video_expected() : Boolean { 44 | return false; 45 | } 46 | 47 | public function notifycomplete() : void { 48 | CONFIG::LOGGING { 49 | Log.debug("MP3: extracting MP3 tags"); 50 | } 51 | var audioTags : Vector. = new Vector.(); 52 | /* parse MP3, convert Elementary Streams to TAG */ 53 | _data.position = 0; 54 | var id3 : ID3 = new ID3(_data); 55 | // MP3 should contain ID3 tag filled with a timestamp 56 | var frames : Vector. = getFrames(_data, _data.position); 57 | var audioTag : FLVTag; 58 | var stamp : int; 59 | var i : int = 0; 60 | 61 | while (i < frames.length) { 62 | stamp = Math.round(id3.timestamp + i * 1024 * 1000 / frames[i].rate); 63 | audioTag = new FLVTag(FLVTag.MP3_RAW, stamp, stamp, false); 64 | if (i != frames.length - 1) { 65 | audioTag.push(_data, frames[i].start, frames[i].length); 66 | } else { 67 | audioTag.push(_data, frames[i].start, _data.length - frames[i].start); 68 | } 69 | audioTags.push(audioTag); 70 | i++; 71 | } 72 | var audiotracks : Vector. = new Vector.(); 73 | audiotracks.push(new AudioTrack('MP3 ES', AudioTrack.FROM_DEMUX, 0, true,false)); 74 | // report unique audio track. dont check return value as obviously the track will be selected 75 | _callback_audioselect(audiotracks); 76 | CONFIG::LOGGING { 77 | Log.debug("MP3: all tags extracted, callback demux"); 78 | } 79 | _data = null; 80 | _callback_progress(audioTags); 81 | _callback_complete(); 82 | } 83 | 84 | public function MP3Demuxer(callback_audioselect : Function, callback_progress : Function, callback_complete : Function) : void { 85 | _callback_audioselect = callback_audioselect; 86 | _callback_progress = callback_progress; 87 | _callback_complete = callback_complete; 88 | }; 89 | 90 | public static function probe(data : ByteArray) : Boolean { 91 | var pos : uint = data.position; 92 | var id3 : ID3 = new ID3(data); 93 | // MP3 should contain ID3 tag filled with a timestamp 94 | if (id3.hasTimestamp) { 95 | while (data.bytesAvailable > 1) { 96 | // Check for MP3 header 97 | var short : uint = data.readUnsignedShort(); 98 | if (short == SYNCWORD) { 99 | data.position = pos; 100 | return true; 101 | } else { 102 | data.position--; 103 | } 104 | } 105 | data.position = pos; 106 | } 107 | return false; 108 | } 109 | 110 | private static function getFrames(data : ByteArray, position : uint) : Vector. { 111 | var frames : Vector. = new Vector.(); 112 | var frame_start : uint; 113 | var frame_length : uint; 114 | var id3 : ID3 = new ID3(data); 115 | position += id3.len; 116 | // Get raw MP3 frames from audio stream. 117 | data.position = position; 118 | // we need at least 3 bytes, 2 for sync word, 1 for flags 119 | while (data.bytesAvailable > 3) { 120 | frame_start = data.position; 121 | // frame header described here : http://mpgedit.org/mpgedit/mpeg_format/MP3Format.html 122 | var short : uint = data.readUnsignedShort(); 123 | if (short == SYNCWORD) { 124 | var flag : uint = data.readByte(); 125 | // (15,12)=(&0xf0 >>4) Bitrate index 126 | var bitrate : uint = BIT_RATES[(flag & 0xf0) >> 4]; 127 | // (11,10)=(&0xc >> 2) Sampling rate frequency index (values are in Hz) 128 | var samplerate : uint = RATES[(flag & 0xc) >> 2]; 129 | // (9)=(&2 >>1) Padding bit 130 | var padbit : uint = (flag & 2) >> 1; 131 | frame_length = (SAMPLES_PER_FRAME / 8) * bitrate / samplerate + padbit; 132 | frame_length = Math.round(frame_length); 133 | data.position = data.position + (frame_length - 3); 134 | frames.push(new AudioFrame(frame_start, frame_length, frame_length, samplerate)); 135 | } else { 136 | data.position = data.position - 1; 137 | } 138 | } 139 | data.position = position; 140 | return frames; 141 | } 142 | } 143 | } -------------------------------------------------------------------------------- /src/org/mangui/hls/demux/Nalu.as: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | package org.mangui.hls.demux { 5 | 6 | import org.mangui.hls.HLSSettings; 7 | import flash.utils.ByteArray; 8 | CONFIG::LOGGING { 9 | import org.mangui.hls.utils.Log; 10 | } 11 | 12 | /** Constants and utilities for the H264 video format. **/ 13 | public class Nalu { 14 | /** H264 NAL unit names. **/ 15 | private static const NAMES : Array = ['Unspecified', // 0 16 | 'NDR', // 1 17 | 'Partition A', // 2 18 | 'Partition B', // 3 19 | 'Partition C', // 4 20 | 'IDR', // 5 21 | 'SEI', // 6 22 | 'SPS', // 7 23 | 'PPS', // 8 24 | 'AUD', // 9 25 | 'End of Sequence', // 10 26 | 'End of Stream', // 11 27 | 'Filler Data'// 12 28 | ]; 29 | 30 | /** Return an array with NAL delimiter indexes. **/ 31 | public static function getNALU(nalu : ByteArray, position : uint) : Vector. { 32 | var units : Vector. = new Vector.(); 33 | var unit_start : int; 34 | var unit_type : int; 35 | var unit_header : int; 36 | // Loop through data to find NAL startcodes. 37 | var window : uint = 0; 38 | nalu.position = position; 39 | while (nalu.bytesAvailable > 4) { 40 | window = nalu.readUnsignedInt(); 41 | // Match four-byte startcodes 42 | if ((window & 0xFFFFFFFF) == 0x01) { 43 | // push previous NAL unit if new start delimiter found, dont push unit with type = 0 44 | if (unit_start && unit_type) { 45 | units.push(new VideoFrame(unit_header, nalu.position - 4 - unit_start, unit_start, unit_type)); 46 | } 47 | unit_header = 4; 48 | unit_start = nalu.position; 49 | unit_type = nalu.readByte() & 0x1F; 50 | // NDR or IDR NAL unit 51 | if (unit_type == 1 || unit_type == 5) { 52 | break; 53 | } 54 | // Match three-byte startcodes 55 | } else if ((window & 0xFFFFFF00) == 0x100) { 56 | // push previous NAL unit if new start delimiter found, dont push unit with type = 0 57 | if (unit_start && unit_type) { 58 | units.push(new VideoFrame(unit_header, nalu.position - 4 - unit_start, unit_start, unit_type)); 59 | } 60 | nalu.position--; 61 | unit_header = 3; 62 | unit_start = nalu.position; 63 | unit_type = nalu.readByte() & 0x1F; 64 | // NDR or IDR NAL unit 65 | if (unit_type == 1 || unit_type == 5) { 66 | break; 67 | } 68 | } else { 69 | nalu.position -= 3; 70 | } 71 | } 72 | // Append the last NAL to the array. 73 | if (unit_start) { 74 | units.push(new VideoFrame(unit_header, nalu.length - unit_start, unit_start, unit_type)); 75 | } 76 | // Reset position and return results. 77 | CONFIG::LOGGING { 78 | if (HLSSettings.logDebug2) { 79 | if (units.length) { 80 | var txt : String = "AVC: "; 81 | for (var i : int = 0; i < units.length; i++) { 82 | txt += NAMES[units[i].type] + ", "; 83 | } 84 | Log.debug2(txt.substr(0, txt.length - 2) + " slices"); 85 | } else { 86 | Log.debug2('AVC: no NALU slices found'); 87 | } 88 | } 89 | } 90 | nalu.position = position; 91 | return units; 92 | }; 93 | } 94 | } -------------------------------------------------------------------------------- /src/org/mangui/hls/demux/PES.as: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | package org.mangui.hls.demux { 5 | import flash.utils.ByteArray; 6 | 7 | /** Representation of a Packetized Elementary Stream. **/ 8 | public class PES { 9 | /** Is it AAC audio or AVC video. **/ 10 | public var audio : Boolean; 11 | /** The PES data (including headers). **/ 12 | public var data : ByteArray; 13 | /** Start of the payload. **/ 14 | public var payload : uint; 15 | /** Timestamp from the PTS header. **/ 16 | public var pts : Number; 17 | /** Timestamp from the DTS header. **/ 18 | public var dts : Number; 19 | /** PES packet len **/ 20 | public var len : int; 21 | /** PES packet len **/ 22 | public var payload_len : int; 23 | 24 | /** Save the first chunk of PES data. **/ 25 | public function PES(dat : ByteArray, aud : Boolean) { 26 | data = dat; 27 | audio = aud; 28 | parse(); 29 | }; 30 | 31 | /** When all data is appended, parse the PES headers. **/ 32 | private function parse() : void { 33 | data.position = 0; 34 | // Start code prefix and packet ID. 35 | var prefix : uint = data.readUnsignedInt(); 36 | /*Audio streams (0x1C0-0x1DF) 37 | Video streams (0x1E0-0x1EF) 38 | 0x1BD is special case, could be audio or video (ffmpeg\libavformat\mpeg.c) 39 | */ 40 | if ((audio && (prefix > 0x1df || prefix < 0x1c0 && prefix != 0x1bd)) || (!audio && prefix != 0x1e0 && prefix != 0x1ea && prefix != 0x1bd)) { 41 | throw new Error("PES start code not found or not AAC/AVC: " + prefix); 42 | } 43 | // read len 44 | len = data.readUnsignedShort(); 45 | // Ignore marker bits. 46 | data.position += 1; 47 | // Check for PTS 48 | var flags : uint = (data.readUnsignedByte() & 192) >> 6; 49 | // Check PES header length 50 | var length : uint = data.readUnsignedByte(); 51 | 52 | if (flags == 2 || flags == 3) { 53 | // Grab the timestamp from PTS data (spread out over 5 bytes): 54 | // XXXX---X -------- -------X -------- -------X 55 | 56 | var _pts : Number = Number((data.readUnsignedByte() & 0x0e)) * Number(1 << 29) + Number((data.readUnsignedShort() >> 1) << 15) + Number((data.readUnsignedShort() >> 1)); 57 | // check if greater than 2^32 -1 58 | if (_pts > 4294967295) { 59 | // decrement 2^33 60 | _pts -= 8589934592; 61 | } 62 | length -= 5; 63 | var _dts : Number = _pts; 64 | if (flags == 3) { 65 | // Grab the DTS (like PTS) 66 | _dts = Number((data.readUnsignedByte() & 0x0e)) * Number(1 << 29) + Number((data.readUnsignedShort() >> 1) << 15) + Number((data.readUnsignedShort() >> 1)); 67 | // check if greater than 2^32 -1 68 | if (_dts > 4294967295) { 69 | // decrement 2^33 70 | _dts -= 8589934592; 71 | } 72 | length -= 5; 73 | } 74 | pts = Math.round(_pts / 90); 75 | dts = Math.round(_dts / 90); 76 | // CONFIG::LOGGING { 77 | // Log.info("pts/dts: " + pts + "/"+ dts); 78 | // } 79 | } 80 | // Skip other header data and parse payload. 81 | data.position += length; 82 | payload = data.position; 83 | if(len) { 84 | payload_len = len - data.position + 6; 85 | } else { 86 | payload_len = 0; 87 | } 88 | }; 89 | } 90 | } -------------------------------------------------------------------------------- /src/org/mangui/hls/demux/SPSInfo.as: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | package org.mangui.hls.demux { 5 | 6 | import flash.utils.ByteArray; 7 | 8 | /* inspired from https://github.com/aizvorski/h264bitstream/blob/master/h264_stream.c#L241-L342 */ 9 | 10 | public class SPSInfo { 11 | public var width : int; 12 | public var height : int; 13 | 14 | public function SPSInfo(sps : ByteArray) { 15 | var profile_idc : int; 16 | sps.position++; 17 | profile_idc = sps.readUnsignedByte(); 18 | var eg : ExpGolomb = new ExpGolomb(sps); 19 | // constraint_set[0-5]_flag, u(1), reserved_zero_2bits u(2), level_idc u(8) 20 | eg.readBits(16); 21 | // skip seq_parameter_set_id 22 | eg.readUE(); 23 | if (profile_idc == 100 || profile_idc == 110 || profile_idc == 122 || profile_idc == 144) { 24 | var chroma_format_idc : int = eg.readUE(); 25 | if (3 === chroma_format_idc) { 26 | // separate_colour_plane_flag 27 | eg.readBits(1); 28 | } 29 | // bit_depth_luma_minus8 30 | eg.readUE(); 31 | // bit_depth_chroma_minus8 32 | eg.readUE(); 33 | // qpprime_y_zero_transform_bypass_flag 34 | eg.readBits(1); 35 | // seq_scaling_matrix_present_flag 36 | var seq_scaling_matrix_present_flag : Boolean = eg.readBoolean(); 37 | if (seq_scaling_matrix_present_flag) { 38 | var imax : int = (chroma_format_idc != 3) ? 8 : 12; 39 | for (var i : int = 0; i < imax; ++i) { 40 | // seq_scaling_list_present_flag[ i ] 41 | if (eg.readBoolean()) { 42 | if (i < 6) { 43 | scaling_list(16, eg); 44 | } else { 45 | scaling_list(64, eg); 46 | } 47 | } 48 | } 49 | } 50 | } 51 | // log2_max_frame_num_minus4 52 | eg.readUE(); 53 | var pic_order_cnt_type : int = eg.readUE(); 54 | if ( 0 === pic_order_cnt_type ) { 55 | // log2_max_pic_order_cnt_lsb_minus4 56 | eg.readUE(); 57 | } else if ( 1 === pic_order_cnt_type ) { 58 | // delta_pic_order_always_zero_flag 59 | eg.readBits(1); 60 | // offset_for_non_ref_pic 61 | eg.readUE(); 62 | // offset_for_top_to_bottom_field 63 | eg.readUE(); 64 | var num_ref_frames_in_pic_order_cnt_cycle : int = eg.readUE(); 65 | for (i = 0; i < num_ref_frames_in_pic_order_cnt_cycle; ++i) { 66 | // offset_for_ref_frame[ i ] 67 | eg.readUE(); 68 | } 69 | } 70 | // max_num_ref_frames 71 | eg.readUE(); 72 | // gaps_in_frame_num_value_allowed_flag 73 | eg.readBits(1); 74 | var pic_width_in_mbs_minus1 : int = eg.readUE(); 75 | var pic_height_in_map_units_minus1 : int = eg.readUE(); 76 | var frame_mbs_only_flag : int = eg.readBits(1); 77 | if (0 === frame_mbs_only_flag) { 78 | // mb_adaptive_frame_field_flag 79 | eg.readBits(1); 80 | } 81 | // direct_8x8_inference_flag 82 | eg.readBits(1); 83 | var frame_cropping_flag : int = eg.readBits(1); 84 | if (frame_cropping_flag) { 85 | var frame_crop_left_offset : int = eg.readUE(); 86 | var frame_crop_right_offset : int = eg.readUE(); 87 | var frame_crop_top_offset : int = eg.readUE(); 88 | var frame_crop_bottom_offset : int = eg.readUE(); 89 | } 90 | width = ((pic_width_in_mbs_minus1 + 1) * 16) - frame_crop_left_offset * 2 - frame_crop_right_offset * 2; 91 | height = ((2 - frame_mbs_only_flag) * (pic_height_in_map_units_minus1 + 1) * 16) - (frame_crop_top_offset * 2) - (frame_crop_bottom_offset * 2); 92 | } 93 | 94 | private static function scaling_list(sizeOfScalingList : int, eg : ExpGolomb) : void { 95 | var lastScale : int = 8; 96 | var nextScale : int = 8; 97 | var delta_scale : int; 98 | for (var j : int = 0; j < sizeOfScalingList; ++j) { 99 | if (nextScale != 0) { 100 | delta_scale = eg.readSE(); 101 | nextScale = (lastScale + delta_scale + 256) % 256; 102 | } 103 | lastScale = (nextScale == 0) ? lastScale : nextScale; 104 | } 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/org/mangui/hls/demux/VideoFrame.as: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | package org.mangui.hls.demux { 5 | /** Video Frame **/ 6 | public class VideoFrame { 7 | public var header : int; 8 | public var start : int; 9 | public var length : int; 10 | public var type : int; 11 | 12 | public function VideoFrame(header : int, length : int, start : int, type : int) { 13 | this.header = header; 14 | this.start = start; 15 | this.length = length; 16 | this.type = type; 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /src/org/mangui/hls/event/HLSError.as: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | package org.mangui.hls.event { 5 | /** Error Identifier **/ 6 | public class HLSError { 7 | public static const OTHER_ERROR : int = 0; 8 | public static const MANIFEST_LOADING_CROSSDOMAIN_ERROR : int = 1; 9 | public static const MANIFEST_LOADING_IO_ERROR : int = 2; 10 | public static const MANIFEST_PARSING_ERROR : int = 3; 11 | public static const FRAGMENT_LOADING_CROSSDOMAIN_ERROR : int = 4; 12 | public static const FRAGMENT_LOADING_ERROR : int = 5; 13 | public static const FRAGMENT_PARSING_ERROR : int = 6; 14 | public static const KEY_LOADING_CROSSDOMAIN_ERROR : int = 7; 15 | public static const KEY_LOADING_ERROR : int = 8; 16 | public static const KEY_PARSING_ERROR : int = 9; 17 | public static const TAG_APPENDING_ERROR : int = 10; 18 | public static const FORBIDDEN: Number = 9; 19 | 20 | private var _code : int; 21 | private var _url : String; 22 | private var _msg : String; 23 | 24 | public function HLSError(code : int, url : String, msg : String) { 25 | _code = code; 26 | _url = url; 27 | _msg = msg; 28 | } 29 | 30 | public function get code() : int { 31 | return _code; 32 | } 33 | 34 | public function get msg() : String { 35 | return _msg; 36 | } 37 | 38 | public function get url() : String { 39 | return _url; 40 | } 41 | 42 | public function toString() : String { 43 | return "HLSError(code/url/msg)=" + _code + "/" + _url + "/" + _msg; 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /src/org/mangui/hls/event/HLSEvent.as: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | package org.mangui.hls.event { 5 | import org.mangui.hls.model.Level; 6 | 7 | import flash.events.Event; 8 | 9 | /** Event fired when an error prevents playback. **/ 10 | public class HLSEvent extends Event { 11 | /** Identifier for a manifest loading event, triggered after a call to hls.load(url) **/ 12 | public static const MANIFEST_LOADING : String = "hlsEventManifestLoading"; 13 | /** Identifier for a manifest parsed event, 14 | * triggered after main manifest has been retrieved and parsed. 15 | * hls playlist may not be playable yet, in case of adaptive streaming, start level playlist is not downloaded yet at that stage */ 16 | public static const MANIFEST_PARSED : String = "hlsEventManifestParsed"; 17 | /** Identifier for a manifest loaded event, when this event is received, main manifest and start level has been retrieved */ 18 | public static const MANIFEST_LOADED : String = "hlsEventManifestLoaded"; 19 | 20 | 21 | /** Identifier for a level loading event **/ 22 | public static const LEVEL_LOADING : String = "hlsEventLevelLoading"; 23 | /** Identifier for a level loaded event **/ 24 | public static const LEVEL_LOADED : String = "hlsEventLevelLoaded"; 25 | /** Identifier for a level switch event. **/ 26 | public static const LEVEL_SWITCH : String = "hlsEventLevelSwitch"; 27 | /** Identifier for a level ENDLIST event. **/ 28 | public static const LEVEL_ENDLIST : String = "hlsEventLevelEndList"; 29 | 30 | 31 | /** Identifier for a fragment loading event. **/ 32 | public static const FRAGMENT_LOADING : String = "hlsEventFragmentLoading"; 33 | /** Identifier for a fragment loaded event. **/ 34 | public static const FRAGMENT_LOADED : String = "hlsEventFragmentLoaded"; 35 | /** Identifier for a fragment playing event. **/ 36 | public static const FRAGMENT_PLAYING : String = "hlsEventFragmentPlaying"; 37 | 38 | /** Identifier for a audio tracks list change **/ 39 | public static const AUDIO_TRACKS_LIST_CHANGE : String = "audioTracksListChange"; 40 | /** Identifier for a audio track change **/ 41 | public static const AUDIO_TRACK_CHANGE : String = "audioTrackChange"; 42 | 43 | /** Identifier for audio/video TAGS loaded event. **/ 44 | public static const TAGS_LOADED : String = "hlsEventTagsLoaded"; 45 | /** Identifier when last fragment of playlist has been loaded **/ 46 | public static const LAST_VOD_FRAGMENT_LOADED : String = "hlsEventLastFragmentLoaded"; 47 | 48 | /** Identifier for a playback error event. **/ 49 | public static const ERROR : String = "hlsEventError"; 50 | 51 | /** Identifier for a playback media time change event. **/ 52 | public static const MEDIA_TIME : String = "hlsEventMediaTime"; 53 | /** Identifier for a playback state switch event. **/ 54 | public static const PLAYBACK_STATE : String = "hlsPlaybackState"; 55 | /** Identifier for a seek state switch event. **/ 56 | public static const SEEK_STATE : String = "hlsSeekState"; 57 | /** Identifier for a playback complete event. **/ 58 | public static const PLAYBACK_COMPLETE : String = "hlsEventPlayBackComplete"; 59 | /** Identifier for a Playlist Duration updated event **/ 60 | public static const PLAYLIST_DURATION_UPDATED : String = "hlsPlayListDurationUpdated"; 61 | /** Identifier for a ID3 updated event **/ 62 | public static const ID3_UPDATED : String = "hlsID3Updated"; 63 | 64 | /** The current url **/ 65 | public var url : String; 66 | /** The current quality level. **/ 67 | public var level : int; 68 | /** The current playlist duration. **/ 69 | public var duration : Number; 70 | /** The list with quality levels. **/ 71 | public var levels : Vector.; 72 | /** The error message. **/ 73 | public var error : HLSError; 74 | /** Load Metrics. **/ 75 | public var loadMetrics : HLSLoadMetrics; 76 | /** Play Metrics. **/ 77 | public var playMetrics : HLSPlayMetrics; 78 | /** The time position. **/ 79 | public var mediatime : HLSMediatime; 80 | /** The new playback state. **/ 81 | public var state : String; 82 | /** The current audio track **/ 83 | public var audioTrack : int; 84 | /** a complete ID3 payload from PES, as a hex dump **/ 85 | public var ID3Data:String; 86 | 87 | /** Assign event parameter and dispatch. **/ 88 | public function HLSEvent(type : String, parameter : *=null) { 89 | switch(type) { 90 | case HLSEvent.MANIFEST_LOADING: 91 | case HLSEvent.FRAGMENT_LOADING: 92 | url = parameter as String; 93 | break; 94 | case HLSEvent.ERROR: 95 | error = parameter as HLSError; 96 | break; 97 | case HLSEvent.TAGS_LOADED: 98 | case HLSEvent.FRAGMENT_LOADED: 99 | loadMetrics = parameter as HLSLoadMetrics; 100 | break; 101 | case HLSEvent.MANIFEST_PARSED: 102 | case HLSEvent.MANIFEST_LOADED: 103 | levels = parameter as Vector.; 104 | break; 105 | case HLSEvent.MEDIA_TIME: 106 | mediatime = parameter as HLSMediatime; 107 | break; 108 | case HLSEvent.PLAYBACK_STATE: 109 | case HLSEvent.SEEK_STATE: 110 | state = parameter as String; 111 | break; 112 | case HLSEvent.LEVEL_LOADING: 113 | case HLSEvent.LEVEL_LOADED: 114 | case HLSEvent.LEVEL_SWITCH: 115 | level = parameter as int; 116 | break; 117 | case HLSEvent.PLAYLIST_DURATION_UPDATED: 118 | duration = parameter as Number; 119 | break; 120 | case HLSEvent.ID3_UPDATED: 121 | ID3Data = parameter as String; 122 | break; 123 | case HLSEvent.FRAGMENT_PLAYING: 124 | playMetrics = parameter as HLSPlayMetrics; 125 | break; 126 | } 127 | super(type, false, false); 128 | }; 129 | } 130 | } -------------------------------------------------------------------------------- /src/org/mangui/hls/event/HLSLoadMetrics.as: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | package org.mangui.hls.event { 5 | /** Fragment Loading metrics **/ 6 | public class HLSLoadMetrics { 7 | private var _level : int; 8 | private var _bandwidth : Number; 9 | private var _frag_duration : Number; 10 | private var _frag_processing_time : Number; 11 | 12 | public function HLSLoadMetrics(level : int, bandwidth : Number, frag_duration : Number, frag_processing_time : Number) { 13 | _level = level; 14 | _bandwidth = bandwidth; 15 | _frag_duration = frag_duration; 16 | _frag_processing_time = frag_processing_time; 17 | } 18 | 19 | public function get level() : int { 20 | return _level; 21 | } 22 | 23 | public function get bandwidth() : Number { 24 | return _bandwidth; 25 | } 26 | 27 | public function get frag_duration() : Number { 28 | return _frag_duration; 29 | } 30 | 31 | public function get frag_processing_time() : Number { 32 | return _frag_processing_time; 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /src/org/mangui/hls/event/HLSMediatime.as: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | package org.mangui.hls.event { 5 | /** Identifiers for the different stream types. **/ 6 | public class HLSMediatime { 7 | /** playback position (in seconds), relative to current playlist start. 8 | * this value could be negative in case of live playlist sliding : 9 | * this can happen in case current playback position 10 | * is in a fragment that has been removed from the playlist 11 | */ 12 | public var position : Number; 13 | /** current playlist duration (in seconds) **/ 14 | public var duration : Number; 15 | /** live playlist sliding since previous seek() (in seconds)**/ 16 | public var live_sliding : Number; 17 | /** current buffer duration (in seconds) **/ 18 | public var buffer : Number; 19 | /** current date : meaningful is playlist contains date information */ 20 | public var program_date : Number; 21 | 22 | public function HLSMediatime(position : Number, duration : Number, buffer : Number, live_sliding : Number, program_date : Number) { 23 | this.position = position; 24 | this.duration = duration; 25 | this.buffer = buffer; 26 | this.live_sliding = live_sliding; 27 | this.program_date = program_date; 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /src/org/mangui/hls/event/HLSPlayMetrics.as: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | package org.mangui.hls.event { 5 | /** playback metrics, notified when playback of a given fragment starts **/ 6 | public class HLSPlayMetrics { 7 | public var level : int; 8 | public var seqnum : int; 9 | public var continuity_counter : int; 10 | public var audio_only : Boolean; 11 | public var video_width : int; 12 | public var video_height : int; 13 | public var tag_list : Array; 14 | 15 | public function HLSPlayMetrics(level : int, seqnum : int, cc : int, audio_only : Boolean, video_width : int, video_height : int, tag_list : Array) { 16 | this.level = level; 17 | this.seqnum = seqnum; 18 | this.continuity_counter = cc; 19 | this.audio_only = audio_only; 20 | this.video_width = video_width; 21 | this.video_height = video_height; 22 | this.tag_list = tag_list; 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /src/org/mangui/hls/flv/FLVTag.as: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | package org.mangui.hls.flv { 5 | import flash.utils.ByteArray; 6 | 7 | /** Metadata needed to build an FLV tag. **/ 8 | public class FLVTag { 9 | /** AAC Header Type ID. **/ 10 | public static const AAC_HEADER : int = 0; 11 | /** AAC Data Type ID. **/ 12 | public static const AAC_RAW : int = 1; 13 | /** AVC Header Type ID. **/ 14 | public static const AVC_HEADER : int = 2; 15 | /** AVC Data Type ID. **/ 16 | public static const AVC_NALU : int = 3; 17 | /** MP3 Data Type ID. **/ 18 | public static const MP3_RAW : int = 4; 19 | /** Discontinuity Data Type ID. **/ 20 | public static const DISCONTINUITY : int = 5; 21 | /** metadata Type ID. **/ 22 | public static const METADATA : int = 6; 23 | /* FLV TAG TYPE */ 24 | private static var TAG_TYPE_AUDIO : int = 8; 25 | private static var TAG_TYPE_VIDEO : int = 9; 26 | private static var TAG_TYPE_SCRIPT : int = 18; 27 | /** Is this a keyframe. **/ 28 | public var keyframe : Boolean; 29 | /** Array with data pointers. **/ 30 | private var pointers : Vector. = new Vector.(); 31 | /** PTS of this frame. **/ 32 | public var pts : Number; 33 | /** DTS of this frame. **/ 34 | public var dts : Number; 35 | /** Type of FLV tag.**/ 36 | public var type : int; 37 | 38 | /** Get the FLV file header. **/ 39 | public static function getHeader() : ByteArray { 40 | var flv : ByteArray = new ByteArray(); 41 | flv.length = 13; 42 | // "F" + "L" + "V". 43 | flv.writeByte(0x46); 44 | flv.writeByte(0x4C); 45 | flv.writeByte(0x56); 46 | // File version (1) 47 | flv.writeByte(1); 48 | // Audio + Video tags. 49 | flv.writeByte(1); 50 | // Length of the header. 51 | flv.writeUnsignedInt(9); 52 | // PreviousTagSize0 53 | flv.writeUnsignedInt(0); 54 | return flv; 55 | }; 56 | 57 | /** Get an FLV Tag header (11 bytes). **/ 58 | public static function getTagHeader(type : int, length : int, stamp : int) : ByteArray { 59 | var tag : ByteArray = new ByteArray(); 60 | tag.length = 11; 61 | tag.writeByte(type); 62 | 63 | // Size of the tag in bytes after StreamID. 64 | tag.writeByte(length >> 16); 65 | tag.writeByte(length >> 8); 66 | tag.writeByte(length); 67 | // Timestamp (lower 24 plus upper 8) 68 | tag.writeByte(stamp >> 16); 69 | tag.writeByte(stamp >> 8); 70 | tag.writeByte(stamp); 71 | tag.writeByte(stamp >> 24); 72 | // StreamID (3 empty bytes) 73 | tag.writeByte(0); 74 | tag.writeByte(0); 75 | tag.writeByte(0); 76 | // All done 77 | return tag; 78 | }; 79 | 80 | /** Save the frame data and parameters. **/ 81 | public function FLVTag(typ : int, stp_p : Number, stp_d : Number, key : Boolean) { 82 | type = typ; 83 | pts = stp_p; 84 | dts = stp_d; 85 | keyframe = key; 86 | } 87 | ; 88 | 89 | /** Returns the tag data. **/ 90 | public function get data() : ByteArray { 91 | var array : ByteArray; 92 | /* following specification http://download.macromedia.com/f4v/video_file_format_spec_v10_1.pdf */ 93 | 94 | // Render header data 95 | if (type == FLVTag.MP3_RAW) { 96 | array = getTagHeader(TAG_TYPE_AUDIO, length + 1, pts); 97 | // Presume MP3 is 44.1 stereo. 98 | array.writeByte(0x2F); 99 | } else if (type == AVC_HEADER || type == AVC_NALU) { 100 | array = getTagHeader(TAG_TYPE_VIDEO, length + 5, dts); 101 | // keyframe/interframe switch (0x10 / 0x20) + AVC (0x07) 102 | keyframe ? array.writeByte(0x17) : array.writeByte(0x27); 103 | /* AVC Packet Type : 104 | 0 = AVC sequence header 105 | 1 = AVC NALU 106 | 2 = AVC end of sequence (lower level NALU sequence ender is 107 | not required or supported) */ 108 | type == AVC_HEADER ? array.writeByte(0x00) : array.writeByte(0x01); 109 | // CompositionTime (in ms) 110 | // CONFIG::LOGGING { 111 | // Log.info("pts:"+pts+",dts:"+dts+",delta:"+compositionTime); 112 | // } 113 | var compositionTime : Number = (pts - dts); 114 | array.writeByte(compositionTime >> 16); 115 | array.writeByte(compositionTime >> 8); 116 | array.writeByte(compositionTime); 117 | } else if (type == DISCONTINUITY || type == METADATA) { 118 | array = getTagHeader(FLVTag.TAG_TYPE_SCRIPT, length, pts); 119 | } else { 120 | array = getTagHeader(TAG_TYPE_AUDIO, length + 2, pts); 121 | // SoundFormat, -Rate, -Size, Type and Header/Raw switch. 122 | array.writeByte(0xAF); 123 | type == AAC_HEADER ? array.writeByte(0x00) : array.writeByte(0x01); 124 | } 125 | for (var i : int = 0; i < pointers.length; i++) { 126 | if (type == AVC_NALU) { 127 | array.writeUnsignedInt(pointers[i].length); 128 | } 129 | array.writeBytes(pointers[i].array, pointers[i].start, pointers[i].length); 130 | } 131 | // Write previousTagSize and return data. 132 | array.writeUnsignedInt(array.length); 133 | return array; 134 | } 135 | 136 | /** Returns the bytesize of the frame. **/ 137 | private function get length() : int { 138 | var length : int = 0; 139 | for (var i : int = 0; i < pointers.length; i++) { 140 | length += pointers[i].length; 141 | // Account for NAL startcode length. 142 | if (type == AVC_NALU) { 143 | length += 4; 144 | } 145 | } 146 | return length; 147 | } 148 | ; 149 | 150 | /** push a data pointer into the frame. **/ 151 | public function push(array : ByteArray, start : int, length : int) : void { 152 | pointers.push(new TagData(array, start, length)); 153 | } 154 | ; 155 | 156 | /** Trace the contents of this tag. **/ 157 | public function toString() : String { 158 | return "TAG (type: " + type + ", pts:" + pts + ", dts:" + dts + ", length:" + length + ")"; 159 | } 160 | ; 161 | } 162 | } 163 | 164 | /** Tag Content **/ 165 | class TagData { 166 | import flash.utils.ByteArray; 167 | 168 | public var array : ByteArray; 169 | public var start : int; 170 | public var length : int; 171 | 172 | public function TagData(array : ByteArray, start : int, length : int) { 173 | this.array = array; 174 | this.start = start; 175 | this.length = length; 176 | } 177 | } -------------------------------------------------------------------------------- /src/org/mangui/hls/model/AudioTrack.as: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | package org.mangui.hls.model { 5 | /** Audio Track identifier **/ 6 | public class AudioTrack { 7 | public static const FROM_DEMUX : int = 0; 8 | public static const FROM_PLAYLIST : int = 1; 9 | public var title : String; 10 | public var id : int; 11 | public var source : int; 12 | public var isDefault : Boolean; 13 | public var isAAC : Boolean; 14 | 15 | public function AudioTrack(title : String, source : int, id : int, isDefault : Boolean, isAAC: Boolean) { 16 | this.title = title; 17 | this.source = source; 18 | this.id = id; 19 | this.isDefault = isDefault; 20 | this.isAAC = isAAC; 21 | } 22 | 23 | public function toString() : String { 24 | return "AudioTrack ID: " + id + " Title: " + title + " Source: " + source + " Default: " + isDefault + " AAC: " + isAAC; 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /src/org/mangui/hls/model/Fragment.as: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | package org.mangui.hls.model { 5 | import flash.utils.ByteArray; 6 | 7 | /** Fragment model **/ 8 | public class Fragment { 9 | /** Duration of this chunk. **/ 10 | public var duration : Number; 11 | /** Start time of this chunk. **/ 12 | public var start_time : Number; 13 | /** sequence number of this chunk. **/ 14 | public var seqnum : int; 15 | /** URL to this chunk. **/ 16 | public var url : String; 17 | /** continuity index of this chunk. **/ 18 | public var continuity : int; 19 | /** program date of this chunk. **/ 20 | public var program_date : Number; 21 | /** URL of the key used to decrypt content **/ 22 | public var decrypt_url : String; 23 | /** Initialization Vector to decrypt content **/ 24 | public var decrypt_iv : ByteArray; 25 | /** byte range start offset **/ 26 | public var byterange_start_offset : int; 27 | /** byte range offset **/ 28 | public var byterange_end_offset : int; 29 | /** data **/ 30 | public var data : FragmentData; 31 | /** metrics **/ 32 | public var metrics : FragmentMetrics; 33 | /** custom tags **/ 34 | public var tag_list : Vector.; 35 | 36 | /** Create the fragment. **/ 37 | public function Fragment(url : String, duration : Number, seqnum : int, start_time : Number, continuity : int, program_date : Number, decrypt_url : String, decrypt_iv : ByteArray, byterange_start_offset : int, byterange_end_offset : int , tag_list : Vector.) { 38 | this.url = url; 39 | this.duration = duration; 40 | this.seqnum = seqnum; 41 | this.start_time = start_time; 42 | this.continuity = continuity; 43 | this.program_date = program_date; 44 | this.decrypt_url = decrypt_url; 45 | this.decrypt_iv = decrypt_iv; 46 | this.byterange_start_offset = byterange_start_offset; 47 | this.byterange_end_offset = byterange_end_offset; 48 | this.tag_list = tag_list; 49 | data = new FragmentData(); 50 | metrics = new FragmentMetrics(); 51 | // CONFIG::LOGGING { 52 | // Log.info("Frag["+seqnum+"]:duration/start_time,cc="+duration+","+start_time+","+continuity); 53 | // } 54 | }; 55 | 56 | public function toString():String 57 | { 58 | return "Fragment (seqnum: " + seqnum + ", start_time:" + start_time + ", duration:" + duration + ")"; 59 | } 60 | } 61 | } -------------------------------------------------------------------------------- /src/org/mangui/hls/model/FragmentData.as: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | package org.mangui.hls.model { 5 | import org.mangui.hls.utils.AES; 6 | import org.mangui.hls.flv.FLVTag; 7 | 8 | import flash.utils.ByteArray; 9 | 10 | /** Fragment Data. **/ 11 | public class FragmentData { 12 | /** valid fragment **/ 13 | public var valid : Boolean; 14 | /** fragment byte array **/ 15 | public var bytes : ByteArray; 16 | /** bytes Loaded **/ 17 | public var bytesLoaded : int; 18 | /** AES decryption instance **/ 19 | public var decryptAES : AES; 20 | /** Start PTS of this chunk. **/ 21 | public var pts_start : Number; 22 | /** computed Start PTS of this chunk. **/ 23 | public var pts_start_computed : Number; 24 | /** min/max audio/video PTS of this chunk. **/ 25 | public var pts_min_audio : Number; 26 | public var pts_max_audio : Number; 27 | public var pts_min_video : Number; 28 | public var pts_max_video : Number; 29 | /** audio/video found ? */ 30 | public var audio_found : Boolean; 31 | public var video_found : Boolean; 32 | /** tag related stuff */ 33 | public var tags_pts_min_audio : Number; 34 | public var tags_pts_max_audio : Number; 35 | public var tags_pts_min_video : Number; 36 | public var tags_pts_max_video : Number; 37 | public var tags_audio_found : Boolean; 38 | public var tags_video_found : Boolean; 39 | public var tags : Vector.; 40 | /* video dimension */ 41 | public var video_width : int; 42 | public var video_height : int; 43 | 44 | /** Fragment metrics **/ 45 | public function FragmentData() { 46 | this.pts_start = NaN; 47 | this.pts_start_computed = NaN; 48 | this.valid = true; 49 | this.video_width = 0; 50 | this.video_height = 0; 51 | }; 52 | 53 | public function get pts_min() : Number { 54 | if (audio_found) { 55 | return pts_min_audio; 56 | } else { 57 | return pts_min_video; 58 | } 59 | } 60 | 61 | public function get pts_max() : Number { 62 | if (audio_found) { 63 | return pts_max_audio; 64 | } else { 65 | return pts_max_video; 66 | } 67 | } 68 | 69 | public function get tag_pts_min() : Number { 70 | if (audio_found) { 71 | return tags_pts_min_audio; 72 | } else { 73 | return tags_pts_min_video; 74 | } 75 | } 76 | 77 | public function get tag_pts_max() : Number { 78 | if (audio_found) { 79 | return tags_pts_max_audio; 80 | } else { 81 | return tags_pts_max_video; 82 | } 83 | } 84 | 85 | public function get tag_pts_start_offset() : Number { 86 | if (tags_audio_found) { 87 | return tags_pts_min_audio - pts_min_audio; 88 | } else { 89 | return tags_pts_min_video - pts_min_video; 90 | } 91 | } 92 | 93 | public function get tag_pts_end_offset() : Number { 94 | if (tags_audio_found) { 95 | return tags_pts_max_audio - pts_min_audio; 96 | } else { 97 | return tags_pts_max_video - pts_min_video; 98 | } 99 | } 100 | } 101 | } -------------------------------------------------------------------------------- /src/org/mangui/hls/model/FragmentMetrics.as: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | package org.mangui.hls.model { 5 | /** Fragment Metrics. **/ 6 | public class FragmentMetrics { 7 | /** fragment loading request/start/end time **/ 8 | public var loading_request_time : Number; 9 | public var loading_begin_time : Number; 10 | public var loading_end_time : Number; 11 | /** fragment decryption begin/end time **/ 12 | public var decryption_begin_time : Number; 13 | public var decryption_end_time : Number; 14 | /** fragment begin/end time */ 15 | public var parsing_begin_time : Number; 16 | public var parsing_end_time : Number; 17 | /** fragment size **/ 18 | public var size : int; 19 | 20 | /** Fragment metrics **/ 21 | public function FragmentMetrics() { 22 | }; 23 | 24 | public function get processing_duration() : Number { 25 | return (parsing_end_time - loading_request_time); 26 | } 27 | 28 | public function get rtt_duration() : Number { 29 | return (loading_begin_time - loading_request_time); 30 | } 31 | 32 | public function get bandwidth() : int { 33 | return(Math.round(size * 8000 / (parsing_end_time - loading_request_time))); 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /src/org/mangui/hls/playlist/AltAudioTrack.as: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | package org.mangui.hls.playlist { 5 | public class AltAudioTrack { 6 | public var group_id : String; 7 | public var lang : String; 8 | public var name : String; 9 | public var autoselect : Boolean; 10 | public var default_track : Boolean; 11 | public var url : String; 12 | 13 | /** Create the quality level. **/ 14 | public function AltAudioTrack(alt_group_id : String, alt_lang : String, alt_name : String, alt_autoselect : Boolean, alt_default : Boolean, alt_url : String) { 15 | group_id = alt_group_id; 16 | lang = alt_lang; 17 | name = alt_name; 18 | autoselect = alt_autoselect; 19 | default_track = alt_default; 20 | url = alt_url; 21 | }; 22 | 23 | public function toString() : String { 24 | return "AltAudioTrack url: " + url + " lang: " + lang + " name: " + name + ' default: ' + default_track; 25 | }; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/org/mangui/hls/playlist/DataUri.as: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | package org.mangui.hls.playlist { 5 | CONFIG::LOGGING { 6 | import org.mangui.hls.utils.Log; 7 | } 8 | 9 | /** 10 | * Facilitates extracting information from a data URI. 11 | */ 12 | public class DataUri { 13 | 14 | private static const DATA_PROTOCOL : String = "data:"; 15 | private static const BASE_64 : String = "base64"; 16 | 17 | private var _dataUri : String; 18 | 19 | public function DataUri(dataUri : String) { 20 | _dataUri = dataUri; 21 | } 22 | 23 | /** 24 | * @return Returns the data portion of the data URI if it is able extract the information, 25 | * null otherwise. 26 | */ 27 | public function extractData() : String { 28 | if (_dataUri == null) { 29 | return null; 30 | } 31 | 32 | var base64Index : int = _dataUri.indexOf(BASE_64 + ','); 33 | var dataIndex : int = _dataUri.indexOf(',') + 1; 34 | 35 | if (dataIndex > _dataUri.length) { 36 | return null; 37 | } 38 | 39 | var data : String = _dataUri.substr(dataIndex); 40 | return (base64Index === -1) ? _extractPlainData(data) : _extractBase64Data(data); 41 | } 42 | 43 | /** 44 | * Data URIs support base 64 encoding the data section. 45 | * This is not typically used for plain text files, which includes HLS manifests. 46 | * As such, decoded base 64 data sections is not currently (6/18/14) supported. 47 | * @param data 48 | * @return 49 | */ 50 | private function _extractBase64Data(data : String) : String { 51 | CONFIG::LOGGING { 52 | Log.warn("Base 64 encoded Data URIs are not supported."); 53 | } 54 | return null; 55 | } 56 | 57 | /** 58 | * @param data 59 | * @return The URL decoded data section from the data URI. 60 | */ 61 | private function _extractPlainData(data : String) : String { 62 | var decodedData : String = decodeURIComponent(data); 63 | CONFIG::LOGGING { 64 | Log.debug2("Decoded data from data URI into: " + decodedData); 65 | } 66 | return decodedData; 67 | } 68 | 69 | /** 70 | * @param dataUri 71 | * @return True if the provided string is a data URI, false otherwise. 72 | */ 73 | public static function isDataUri(dataUri : String) : Boolean { 74 | return dataUri != null && dataUri.indexOf(DATA_PROTOCOL) === 0; 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/org/mangui/hls/stream/HLSNetStreamClient.as: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | package org.mangui.hls.stream { 5 | import flash.utils.flash_proxy; 6 | import flash.utils.Proxy; 7 | 8 | /** Proxy that allows dispatching internal events fired by Netstream cues to 9 | * internal listeners as well as the traditional client object 10 | */ 11 | public class HLSNetStreamClient extends Proxy { 12 | private var _delegate : Object; 13 | private var _callbacks : Object = new Object(); 14 | 15 | public function HLSNetStreamClient() { 16 | } 17 | 18 | public function set delegate(client : Object) : void { 19 | this._delegate = client; 20 | } 21 | 22 | public function get delegate() : Object { 23 | return this._delegate; 24 | } 25 | 26 | public function registerCallback(name : String, callback : Function) : void { 27 | _callbacks[name] = callback; 28 | } 29 | 30 | override flash_proxy function callProperty(methodName : *, ... args) : * { 31 | var r : * = null; 32 | 33 | if (_callbacks && _callbacks.hasOwnProperty(methodName)) { 34 | r = _callbacks[methodName](args); 35 | } 36 | 37 | if (_delegate && _delegate.hasOwnProperty(methodName)) { 38 | r = _delegate[methodName](args); 39 | } 40 | 41 | return r; 42 | } 43 | 44 | override flash_proxy function getProperty(name : *) : * { 45 | var r : *; 46 | if (_callbacks && _callbacks.hasOwnProperty(name)) { 47 | r = _callbacks[name]; 48 | } 49 | 50 | if (_delegate && _delegate.hasOwnProperty(name)) { 51 | r = _delegate[name]; 52 | } 53 | 54 | return r; 55 | } 56 | 57 | override flash_proxy function hasProperty(name : *) : Boolean { 58 | return (_delegate && _delegate.hasOwnProperty(name)) || _callbacks.hasOwnProperty(name); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/org/mangui/hls/utils/AES.as: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | package org.mangui.hls.utils { 5 | import flash.utils.getTimer; 6 | import flash.display.DisplayObject; 7 | import flash.utils.ByteArray; 8 | import flash.events.Event; 9 | 10 | /** 11 | * Contains Utility functions for AES-128 CBC Decryption 12 | */ 13 | public class AES { 14 | private var _key : FastAESKey; 15 | //private var _keyArray : ByteArray; 16 | private var iv0 : uint; 17 | private var iv1 : uint; 18 | private var iv2 : uint; 19 | private var iv3 : uint; 20 | /* callback function upon decrypt progress */ 21 | private var _progress : Function; 22 | /* callback function upon decrypt complete */ 23 | private var _complete : Function; 24 | /** Byte data to be decrypt **/ 25 | private var _data : ByteArray; 26 | /** read position **/ 27 | private var _read_position : uint; 28 | /** write position **/ 29 | private var _write_position : uint; 30 | /** chunk size to avoid blocking **/ 31 | private static const CHUNK_SIZE : uint = 2048; 32 | /** is bytearray full ? **/ 33 | private var _data_complete : Boolean; 34 | /** display object used for ENTER_FRAME listener */ 35 | private var _displayObject : DisplayObject; 36 | 37 | public function AES(displayObject : DisplayObject, key : ByteArray, iv : ByteArray, notifyprogress : Function, notifycomplete : Function) { 38 | // _keyArray = key; 39 | _key = new FastAESKey(key); 40 | iv.position = 0; 41 | iv0 = iv.readUnsignedInt(); 42 | iv1 = iv.readUnsignedInt(); 43 | iv2 = iv.readUnsignedInt(); 44 | iv3 = iv.readUnsignedInt(); 45 | _data = new ByteArray(); 46 | _data_complete = false; 47 | _progress = notifyprogress; 48 | _complete = notifycomplete; 49 | _read_position = 0; 50 | _write_position = 0; 51 | _displayObject = displayObject; 52 | } 53 | 54 | public function append(data : ByteArray) : void { 55 | // CONFIG::LOGGING { 56 | // Log.info("notify append"); 57 | // } 58 | _data.position = _write_position; 59 | _data.writeBytes(data); 60 | if (_write_position == 0) { 61 | _displayObject.addEventListener(Event.ENTER_FRAME, _decryptTimer); 62 | } 63 | _write_position += data.length; 64 | } 65 | 66 | public function notifycomplete() : void { 67 | // CONFIG::LOGGING { 68 | // Log.info("notify complete"); 69 | // } 70 | _data_complete = true; 71 | } 72 | 73 | public function cancel() : void { 74 | _displayObject.removeEventListener(Event.ENTER_FRAME, _decryptTimer); 75 | } 76 | 77 | private function _decryptTimer(e : Event) : void { 78 | var start_time : int = getTimer(); 79 | var decrypted : Boolean; 80 | do { 81 | decrypted = _decryptChunk(); 82 | // dont spend more than 20 ms in the decrypt timer to avoid blocking/freezing video 83 | } while (decrypted && (getTimer() - start_time) < 20); 84 | } 85 | 86 | /** decrypt a small chunk of packets each time to avoid blocking **/ 87 | private function _decryptChunk() : Boolean { 88 | _data.position = _read_position; 89 | var decryptdata : ByteArray; 90 | if (_data.bytesAvailable) { 91 | if (_data.bytesAvailable <= CHUNK_SIZE) { 92 | if (_data_complete) { 93 | // CONFIG::LOGGING { 94 | // Log.info("data complete, last chunk"); 95 | // } 96 | _read_position += _data.bytesAvailable; 97 | decryptdata = _decryptCBC(_data, _data.bytesAvailable); 98 | unpad(decryptdata); 99 | } else { 100 | // data not complete, and available data less than chunk size, return 101 | return false; 102 | } 103 | } else { 104 | _read_position += CHUNK_SIZE; 105 | decryptdata = _decryptCBC(_data, CHUNK_SIZE); 106 | } 107 | _progress(decryptdata); 108 | return true; 109 | } else { 110 | if (_data_complete) { 111 | CONFIG::LOGGING { 112 | Log.debug("AES:data+decrypt completed, callback"); 113 | } 114 | // callback 115 | _complete(); 116 | _displayObject.removeEventListener(Event.ENTER_FRAME, _decryptTimer); 117 | } 118 | return false; 119 | } 120 | } 121 | 122 | /* Cypher Block Chaining Decryption, refer to 123 | * http://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher-block_chaining_ 124 | * for algorithm description 125 | */ 126 | private function _decryptCBC(crypt : ByteArray, len : uint) : ByteArray { 127 | var src : Vector. = new Vector.(4); 128 | var dst : Vector. = new Vector.(4); 129 | var decrypt : ByteArray = new ByteArray(); 130 | decrypt.length = len; 131 | 132 | for (var i : uint = 0; i < len / 16; i++) { 133 | // read src byte array 134 | src[0] = crypt.readUnsignedInt(); 135 | src[1] = crypt.readUnsignedInt(); 136 | src[2] = crypt.readUnsignedInt(); 137 | src[3] = crypt.readUnsignedInt(); 138 | 139 | // AES decrypt src vector into dst vector 140 | _key.decrypt128(src, dst); 141 | 142 | // CBC : write output = XOR(decrypted,IV) 143 | decrypt.writeUnsignedInt(dst[0] ^ iv0); 144 | decrypt.writeUnsignedInt(dst[1] ^ iv1); 145 | decrypt.writeUnsignedInt(dst[2] ^ iv2); 146 | decrypt.writeUnsignedInt(dst[3] ^ iv3); 147 | 148 | // CBC : next IV = (input) 149 | iv0 = src[0]; 150 | iv1 = src[1]; 151 | iv2 = src[2]; 152 | iv3 = src[3]; 153 | } 154 | decrypt.position = 0; 155 | return decrypt; 156 | } 157 | 158 | public function unpad(a : ByteArray) : void { 159 | var c : uint = a.length % 16; 160 | if (c != 0) throw new Error("PKCS#5::unpad: ByteArray.length isn't a multiple of the blockSize"); 161 | c = a[a.length - 1]; 162 | for (var i : uint = c; i > 0; i--) { 163 | var v : uint = a[a.length - 1]; 164 | a.length--; 165 | if (c != v) throw new Error("PKCS#5:unpad: Invalid padding value. expected [" + c + "], found [" + v + "]"); 166 | } 167 | } 168 | 169 | public function destroy() : void { 170 | _key.dispose(); 171 | // _key = null; 172 | } 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /src/org/mangui/hls/utils/Hex.as: -------------------------------------------------------------------------------- 1 | /** 2 | * Hex 3 | * 4 | * Utility class to convert Hex strings to ByteArray or String types. 5 | * Copyright (c) 2007 Henri Torgemane 6 | * 7 | * See LICENSE.txt for full license information. 8 | */ 9 | package org.mangui.hls.utils { 10 | import flash.utils.ByteArray; 11 | 12 | public class Hex { 13 | /** 14 | * Generates byte-array from given hexadecimal string 15 | * 16 | * Supports straight and colon-laced hex (that means 23:03:0e:f0, but *NOT* 23:3:e:f0) 17 | * The first nibble (hex digit) may be omitted. 18 | * Any whitespace characters are ignored. 19 | */ 20 | public static function toArray(hex : String) : ByteArray { 21 | hex = hex.replace(/^0x|\s|:/gm, ''); 22 | var a : ByteArray = new ByteArray; 23 | if ((hex.length & 1) == 1) hex = "0" + hex; 24 | for (var i : uint = 0; i < hex.length; i += 2) { 25 | a[i / 2] = parseInt(hex.substr(i, 2), 16); 26 | } 27 | return a; 28 | } 29 | 30 | /** 31 | * Generates lowercase hexadecimal string from given byte-array 32 | */ 33 | public static function fromArray(array : ByteArray, colons : Boolean = false) : String { 34 | var s : String = ""; 35 | for (var i : uint = 0; i < array.length; i++) { 36 | s += ("0" + array[i].toString(16)).substr(-2, 2); 37 | if (colons) { 38 | if (i < array.length - 1) s += ":"; 39 | } 40 | } 41 | return s; 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/org/mangui/hls/utils/Log.as: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | package org.mangui.hls.utils { 5 | import flash.external.ExternalInterface; 6 | 7 | import org.mangui.hls.HLSSettings; 8 | 9 | /** Class that sends log messages to browser console. **/ 10 | public class Log { 11 | private static const LEVEL_INFO : String = "INFO:"; 12 | private static const LEVEL_DEBUG : String = "DEBUG:"; 13 | private static const LEVEL_WARN : String = "WARN:"; 14 | private static const LEVEL_ERROR : String = "ERROR:"; 15 | 16 | public static function info(message : *) : void { 17 | if (HLSSettings.logInfo) 18 | outputlog(LEVEL_INFO, String(message)); 19 | }; 20 | 21 | public static function debug(message : *) : void { 22 | if (HLSSettings.logDebug) 23 | outputlog(LEVEL_DEBUG, String(message)); 24 | }; 25 | 26 | public static function debug2(message : *) : void { 27 | if (HLSSettings.logDebug2) 28 | outputlog(LEVEL_DEBUG, String(message)); 29 | }; 30 | 31 | public static function warn(message : *) : void { 32 | if (HLSSettings.logWarn) 33 | outputlog(LEVEL_WARN, String(message)); 34 | }; 35 | 36 | public static function error(message : *) : void { 37 | if (HLSSettings.logError) 38 | outputlog(LEVEL_ERROR, String(message)); 39 | }; 40 | 41 | /** Log a message to the console. **/ 42 | private static function outputlog(level : String, message : String) : void { 43 | if (ExternalInterface.available) 44 | ExternalInterface.call('console.log', level + message); 45 | else trace(level + message); 46 | } 47 | }; 48 | } -------------------------------------------------------------------------------- /src/org/mangui/hls/utils/PTS.as: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | package org.mangui.hls.utils { 5 | public class PTS { 6 | /* find PTS value nearest a given reference PTS value 7 | * 8 | * PTS retrieved from demux are within a range of 9 | * (+/-) 2^32/90 - 1 = (+/-) 47721858 10 | * when reaching upper limit, PTS will loop to lower limit 11 | * this cause some issues with fragment duration calculation 12 | * this method will normalize a given PTS value and output a result 13 | * that is closest to provided PTS reference value. 14 | * i.e it could output values bigger than the (+/-) 2^32/90. 15 | * this will avoid PTS looping issues. 16 | */ 17 | public static function normalize(reference : Number, value : Number) : Number { 18 | var offset : Number; 19 | if (reference < value) { 20 | // - 2^33/90 21 | offset = -95443717; 22 | } else { 23 | // + 2^33/90 24 | offset = 95443717; 25 | } 26 | // 2^32 / 90 27 | while (!isNaN(reference) && (Math.abs(value - reference) > 47721858)) { 28 | value += offset; 29 | } 30 | return value; 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/org/mangui/hls/utils/Params2Settings.as: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | package org.mangui.hls.utils { 5 | import org.mangui.hls.HLSSettings; 6 | 7 | import flash.utils.getQualifiedClassName; 8 | import flash.utils.getDefinitionByName; 9 | import flash.utils.Dictionary; 10 | 11 | /** 12 | * Params2Settings is an helper class that holds every legal external params names 13 | * which can be used to customize HLSSettings and maps them to the relevant HLSSettings values 14 | */ 15 | public class Params2Settings { 16 | /** 17 | * HLSSettings <-> params maping 18 | */ 19 | private static var _paramMap : Dictionary = new Dictionary(); 20 | _paramMap["minbufferlength"] = "minBufferLength"; 21 | _paramMap["maxbufferlength"] = "maxBufferLength"; 22 | _paramMap["lowbufferlength"] = "lowBufferLength"; 23 | _paramMap["seekmode"] = "seekMode"; 24 | _paramMap["startfromlevel"] = "startFromLevel"; 25 | _paramMap["seekfromlevel"] = "seekFromLevel"; 26 | _paramMap["live_flushurlcache"] = "flushLiveURLCache"; 27 | _paramMap["manifestloadmaxretry"] = "manifestLoadMaxRetry"; 28 | _paramMap["manifestloadmaxretrytimeout"] = "manifestLoadMaxRetryTimeout"; 29 | _paramMap["fragmentloadmaxretry"] = "fragmentLoadMaxRetry"; 30 | _paramMap["fragmentloadmaxretrytimeout"] = "fragmentLoadMaxRetryTimeout"; 31 | _paramMap["capleveltostage"] = "capLevelToStage"; 32 | _paramMap["maxlevelcappingmode"] = "maxLevelCappingMode"; 33 | _paramMap["usehardwarevideodecoder"] = "useHardwareVideoDecoder"; 34 | _paramMap["info"] = "logInfo"; 35 | _paramMap["debug"] = "logDebug"; 36 | _paramMap["debug2"] = "logDebug2"; 37 | _paramMap["warn"] = "logWarn"; 38 | _paramMap["error"] = "logError"; 39 | public static function set(key : String, value : Object) : void { 40 | var param : String = _paramMap[key]; 41 | if (param) { 42 | // try to assign value with proper object type 43 | try { 44 | var cName : String = getQualifiedClassName(HLSSettings[param]); 45 | // AS3 bug: "getDefinitionByName" considers var value, not type, and wrongly (e.g. 3.0 >> "int"; 3.1 >> "Number"). 46 | var c : Class = cName === "int" ? Number : getDefinitionByName(cName) as Class; 47 | // get HLSSetting type 48 | HLSSettings[param] = c(value); 49 | CONFIG::LOGGING { 50 | Log.info("HLSSettings." + param + " = " + HLSSettings[param]); 51 | } 52 | } catch(error : Error) { 53 | CONFIG::LOGGING { 54 | Log.warn("Can't set HLSSettings." + param); 55 | } 56 | } 57 | } 58 | } 59 | } 60 | } -------------------------------------------------------------------------------- /src/org/mangui/hls/utils/ScaleVideo.as: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | package org.mangui.hls.utils { 5 | import flash.geom.Rectangle; 6 | 7 | public class ScaleVideo { 8 | public static function resizeRectangle(videoWidth : int, videoHeight : int, containerWidth : int, containerHeight : int) : Rectangle { 9 | var rect : Rectangle = new Rectangle(); 10 | var xscale : Number = containerWidth / videoWidth; 11 | var yscale : Number = containerHeight / videoHeight; 12 | if (xscale >= yscale) { 13 | rect.width = Math.min(videoWidth * yscale, containerWidth); 14 | rect.height = videoHeight * yscale; 15 | } else { 16 | rect.width = Math.min(videoWidth * xscale, containerWidth); 17 | rect.height = videoHeight * xscale; 18 | } 19 | rect.width = Math.ceil(rect.width); 20 | rect.height = Math.ceil(rect.height); 21 | rect.x = Math.round((containerWidth - rect.width) / 2); 22 | rect.y = Math.round((containerHeight - rect.height) / 2); 23 | CONFIG::LOGGING { 24 | Log.debug("width:" + rect.width); 25 | Log.debug("height:" + rect.height); 26 | Log.debug("x:" + rect.x); 27 | Log.debug("y:" + rect.y); 28 | } 29 | return rect; 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/org/mangui/osmf/plugins/HLSDynamicPlugin.as: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | package org.mangui.osmf.plugins { 5 | import flash.display.Sprite; 6 | import flash.system.Security; 7 | 8 | import org.osmf.media.PluginInfo; 9 | 10 | public class HLSDynamicPlugin extends Sprite { 11 | private var _pluginInfo : PluginInfo; 12 | 13 | public function HLSDynamicPlugin() { 14 | super(); 15 | Security.allowDomain("*"); 16 | _pluginInfo = new HLSPlugin(); 17 | } 18 | 19 | public function get pluginInfo() : PluginInfo { 20 | return _pluginInfo; 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /src/org/mangui/osmf/plugins/HLSMediaElement.as: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | package org.mangui.osmf.plugins { 5 | import flash.media.Video; 6 | import flash.net.NetStream; 7 | 8 | import org.mangui.hls.HLS; 9 | import org.mangui.hls.event.HLSEvent; 10 | import org.mangui.osmf.plugins.loader.HLSNetLoader; 11 | import org.mangui.osmf.plugins.traits.*; 12 | import org.mangui.osmf.plugins.utils.ErrorManager; 13 | 14 | import org.osmf.media.LoadableElementBase; 15 | import org.osmf.media.MediaElement; 16 | import org.osmf.media.MediaResourceBase; 17 | import org.osmf.media.videoClasses.VideoSurface; 18 | import org.osmf.net.NetStreamAudioTrait; 19 | import org.osmf.net.StreamType; 20 | import org.osmf.net.StreamingURLResource; 21 | import org.osmf.traits.AudioTrait; 22 | import org.osmf.traits.BufferTrait; 23 | import org.osmf.traits.DVRTrait; 24 | import org.osmf.traits.LoadTrait; 25 | import org.osmf.traits.LoaderBase; 26 | import org.osmf.traits.MediaTraitType; 27 | import org.osmf.traits.PlayTrait; 28 | import org.osmf.traits.SeekTrait; 29 | import org.osmf.traits.TimeTrait; 30 | import org.osmf.utils.OSMFSettings; 31 | import org.osmf.events.MediaError; 32 | import org.osmf.events.MediaErrorEvent; 33 | 34 | CONFIG::LOGGING { 35 | import org.mangui.hls.utils.Log; 36 | } 37 | 38 | public class HLSMediaElement extends LoadableElementBase { 39 | private var _hls : HLS; 40 | private var _stream : NetStream; 41 | private var _defaultduration : Number; 42 | private var videoSurface : VideoSurface; 43 | private var _smoothing : Boolean; 44 | 45 | public function HLSMediaElement(resource : MediaResourceBase, hls : HLS, duration : Number) { 46 | _hls = hls; 47 | _defaultduration = duration; 48 | super(resource, new HLSNetLoader(hls)); 49 | _hls.addEventListener(HLSEvent.ERROR, _errorHandler); 50 | } 51 | 52 | protected function createVideo() : Video { 53 | return new Video(); 54 | } 55 | 56 | override protected function createLoadTrait(resource : MediaResourceBase, loader : LoaderBase) : LoadTrait { 57 | return new HLSNetStreamLoadTrait(_hls, _defaultduration, loader, resource); 58 | } 59 | 60 | override protected function processLoadingState() : void { 61 | CONFIG::LOGGING { 62 | Log.debug("HLSMediaElement:processLoadingState"); 63 | } 64 | } 65 | 66 | override protected function processReadyState() : void { 67 | CONFIG::LOGGING { 68 | Log.debug("HLSMediaElement:processReadyState"); 69 | } 70 | initTraits(); 71 | } 72 | 73 | override protected function processUnloadingState() : void { 74 | CONFIG::LOGGING { 75 | Log.debug("HLSMediaElement:processUnloadingState"); 76 | } 77 | removeTrait(MediaTraitType.AUDIO); 78 | removeTrait(MediaTraitType.BUFFER); 79 | removeTrait(MediaTraitType.TIME); 80 | removeTrait(MediaTraitType.DISPLAY_OBJECT); 81 | removeTrait(MediaTraitType.PLAY); 82 | removeTrait(MediaTraitType.SEEK); 83 | removeTrait(MediaTraitType.DYNAMIC_STREAM); 84 | removeTrait(MediaTraitType.DVR); 85 | removeTrait(MediaTraitType.ALTERNATIVE_AUDIO); 86 | if (videoSurface != null) { 87 | videoSurface.attachNetStream(null); 88 | videoSurface = null; 89 | } 90 | } 91 | 92 | /** 93 | * Specifies whether the video should be smoothed (interpolated) when it is scaled. 94 | * For smoothing to work, the runtime must be in high-quality mode (the default). 95 | * The default value is false (no smoothing). Set this property to true to take 96 | * advantage of mipmapping image optimization. 97 | * 98 | * @see flash.media.Video 99 | * 100 | * @langversion 3.0 101 | * @playerversion Flash 10 102 | * @playerversion AIR 1.5 103 | * @productversion OSMF 1.0 104 | **/ 105 | public function get smoothing() : Boolean { 106 | return _smoothing; 107 | } 108 | 109 | public function set smoothing(value : Boolean) : void { 110 | _smoothing = value; 111 | if (videoSurface != null) { 112 | videoSurface.smoothing = value; 113 | } 114 | } 115 | 116 | private function initTraits() : void { 117 | _stream = _hls.stream; 118 | 119 | // Set the video's dimensions so that it doesn't appear at the wrong size. 120 | // We'll set the correct dimensions once the metadata is loaded. (FM-206) 121 | videoSurface = new VideoSurface(OSMFSettings.enableStageVideo && OSMFSettings.supportsStageVideo, createVideo); 122 | videoSurface.smoothing = true; 123 | videoSurface.deblocking = 1; 124 | videoSurface.width = videoSurface.height = 0; 125 | videoSurface.attachNetStream(_stream); 126 | 127 | var audioTrait : AudioTrait = new NetStreamAudioTrait(_stream); 128 | addTrait(MediaTraitType.AUDIO, audioTrait); 129 | 130 | var bufferTrait : BufferTrait = new HLSBufferTrait(_hls); 131 | addTrait(MediaTraitType.BUFFER, bufferTrait); 132 | 133 | var timeTrait : TimeTrait = new HLSTimeTrait(_hls, _defaultduration); 134 | addTrait(MediaTraitType.TIME, timeTrait); 135 | 136 | var displayObjectTrait : HLSDisplayObjectTrait = new HLSDisplayObjectTrait(_hls,videoSurface, NaN, NaN); 137 | addTrait(MediaTraitType.DISPLAY_OBJECT, displayObjectTrait); 138 | 139 | var playTrait : PlayTrait = new HLSPlayTrait(_hls); 140 | addTrait(MediaTraitType.PLAY, playTrait); 141 | 142 | // setup seek trait 143 | var seekTrait : SeekTrait = new HLSSeekTrait(_hls, timeTrait); 144 | addTrait(MediaTraitType.SEEK, seekTrait); 145 | 146 | if (_hls.levels.length > 1) { 147 | // setup dynamic stream trait 148 | var dsTrait : HLSDynamicStreamTrait = new HLSDynamicStreamTrait(_hls); 149 | addTrait(MediaTraitType.DYNAMIC_STREAM, dsTrait); 150 | } 151 | 152 | // retrieve stream type 153 | var streamType : String = (resource as StreamingURLResource).streamType; 154 | if (streamType == StreamType.DVR) { 155 | // add DvrTrait 156 | var dvrTrait : DVRTrait = new DVRTrait(true); 157 | addTrait(MediaTraitType.DVR, dvrTrait); 158 | } 159 | 160 | // setup drm trait 161 | // addTrait(MediaTraitType.DRM, drmTrait); 162 | 163 | // setup alternative audio trait 164 | var alternateAudioTrait : HLSAlternativeAudioTrait = new HLSAlternativeAudioTrait(_hls, this as MediaElement); 165 | addTrait(MediaTraitType.ALTERNATIVE_AUDIO, alternateAudioTrait); 166 | } 167 | 168 | private function _errorHandler(event : HLSEvent) : void { 169 | var errorCode : int = ErrorManager.getMediaErrorCode(event); 170 | var errorMsg : String = ErrorManager.getMediaErrorMessage(event); 171 | CONFIG::LOGGING { 172 | Log.warn("HLS Error event received, dispatching MediaError " + errorCode + "," + errorMsg); 173 | } 174 | dispatchEvent(new MediaErrorEvent(MediaErrorEvent.MEDIA_ERROR, false, false, new MediaError(errorCode, errorMsg))); 175 | } 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /src/org/mangui/osmf/plugins/HLSPlugin.as: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | package org.mangui.osmf.plugins { 5 | import org.mangui.hls.utils.Params2Settings; 6 | import org.mangui.osmf.plugins.loader.HLSLoaderBase; 7 | import org.mangui.osmf.plugins.loader.HLSLoadFromDocumentElement; 8 | import org.osmf.media.MediaElement; 9 | import org.osmf.media.MediaFactoryItem; 10 | import org.osmf.media.MediaFactoryItemType; 11 | import org.osmf.media.MediaResourceBase; 12 | import org.osmf.media.PluginInfo; 13 | 14 | CONFIG::LOGGING { 15 | import org.mangui.hls.utils.Log; 16 | } 17 | 18 | public class HLSPlugin extends PluginInfo { 19 | public function HLSPlugin(items : Vector.=null, elementCreatedNotification : Function = null) { 20 | items = new Vector.(); 21 | items.push(new MediaFactoryItem('org.mangui.osmf.plugins.HLSPlugin', canHandleResource, createMediaElement, MediaFactoryItemType.STANDARD)); 22 | 23 | super(items, elementCreatedNotification); 24 | } 25 | 26 | /** 27 | * Called from super class when plugin has been initialized with the MediaFactory from which it was loaded. 28 | * Used for customize HLSSettings with values provided in resource metadata (that was set eg. in flash vars) 29 | * 30 | * @param resource Provides acces to the resource used to load the plugin and any associated metadata 31 | * 32 | */ 33 | override public function initializePlugin(resource : MediaResourceBase) : void { 34 | CONFIG::LOGGING { 35 | Log.debug("OSMF HLSPlugin init"); 36 | } 37 | metadataParamsToHLSSettings(resource); 38 | } 39 | 40 | private function canHandleResource(resource : MediaResourceBase) : Boolean { 41 | return HLSLoaderBase.canHandle(resource); 42 | } 43 | 44 | private function createMediaElement() : MediaElement { 45 | return new HLSLoadFromDocumentElement(null, new HLSLoaderBase()); 46 | } 47 | 48 | private function metadataParamsToHLSSettings(resource : MediaResourceBase) : void { 49 | if (resource == null) { 50 | return; 51 | } 52 | 53 | var metadataNamespaceURLs : Vector. = resource.metadataNamespaceURLs; 54 | 55 | // set all legal params values to HLSSetings properties 56 | for each (var key : String in metadataNamespaceURLs) { 57 | Params2Settings.set(key, resource.getMetadataValue(key)); 58 | } 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/org/mangui/osmf/plugins/loader/HLSLoadFromDocumentElement.as: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | package org.mangui.osmf.plugins.loader { 6 | import org.osmf.elements.proxyClasses.LoadFromDocumentLoadTrait; 7 | import org.osmf.events.LoadEvent; 8 | import org.osmf.media.MediaResourceBase; 9 | import org.osmf.traits.LoadState; 10 | import org.osmf.traits.LoadTrait; 11 | import org.osmf.traits.LoaderBase; 12 | import org.osmf.traits.MediaTraitType; 13 | import org.osmf.utils.OSMFStrings; 14 | import org.osmf.elements.ProxyElement; 15 | 16 | /** 17 | * LoadFromDocumentElement is the base class for MediaElements that load documents 18 | * that contain information about the real MediaElement to expose. For example, the 19 | * contents of a SMIL document can be represented as a MediaElement, but until the 20 | * SMIL document is loaded, it's impossible to know what MediaElement the SMIL 21 | * document represents. 22 | * 23 | *

      Because of the dynamic nature of this operation, a LoadFromDocumentElement extends 24 | * ProxyElement. When the document has been loaded, this class will set the 25 | * proxiedElement property to the MediaElement that was generated from 26 | * the document.

      27 | * 28 | *

      This is an abstract base class, and must be subclassed.

      29 | * 30 | *

      Note: It is simplest to use the MediaPlayer class in conjunction with subclasses of 31 | * the LoadFromDocumentElement. If you work directly with a LoadFromDocumentElement, then 32 | * it's important to listen for events related to traits being added and removed. If you 33 | * use the MediaPlayer class with a LoadFromDocumentElement, then the MediaPlayer will 34 | * automatically listen for these events for you.

      35 | * 36 | * @see org.osmf.elements.ProxyElement 37 | * @see org.osmf.media.MediaElement 38 | * @see org.osmf.media.MediaPlayer 39 | * 40 | * @langversion 3.0 41 | * @playerversion Flash 10 42 | * @playerversion AIR 1.5 43 | * @productversion OSMF 1.0 44 | */ 45 | public class HLSLoadFromDocumentElement extends ProxyElement { 46 | /** 47 | * Constructor. 48 | * 49 | * @param resource The resource associated with this element. 50 | * @param loader The LoaderBase used to load the resource. Cannot be null. 51 | * 52 | * @throws ArgumentError If loader is null. 53 | * 54 | * @langversion 3.0 55 | * @playerversion Flash 10 56 | * @playerversion AIR 1.5 57 | * @productversion OSMF 1.0 58 | */ 59 | public function HLSLoadFromDocumentElement(resource : MediaResourceBase = null, loader : LoaderBase = null) { 60 | super(null); 61 | 62 | this.loader = loader; 63 | this.resource = resource; 64 | 65 | if (loader == null) { 66 | throw new ArgumentError(OSMFStrings.getString(OSMFStrings.NULL_PARAM)); 67 | } 68 | } 69 | 70 | /** 71 | * @private 72 | * 73 | * Overriding is necessary because there is a null proxiedElement. 74 | */ 75 | override public function set resource(value : MediaResourceBase) : void { 76 | if (_resource != value && value != null) { 77 | _resource = value; 78 | loadTrait = new LoadFromDocumentLoadTrait(loader, resource); 79 | loadTrait.addEventListener(LoadEvent.LOAD_STATE_CHANGE, onLoadStateChange, false, int.MAX_VALUE); 80 | 81 | if (super.getTrait(MediaTraitType.LOAD) != null) { 82 | super.removeTrait(MediaTraitType.LOAD); 83 | } 84 | super.addTrait(MediaTraitType.LOAD, loadTrait); 85 | } 86 | } 87 | 88 | /** 89 | * @private 90 | */ 91 | override public function get resource() : MediaResourceBase { 92 | return _resource; 93 | } 94 | 95 | private function onLoadStateChange(event : LoadEvent) : void { 96 | if (event.loadState == LoadState.READY) { 97 | event.stopImmediatePropagation(); 98 | 99 | // Remove the temporary LoadTrait. 100 | removeTrait(MediaTraitType.LOAD); 101 | 102 | // Tell the soon-to-be proxied element to load itself. 103 | // Note that we do this before setting it as the proxied 104 | // element, so as to avoid dispatching a second LOADING 105 | // event. 106 | 107 | // Set up a listener so that we can prevent the dispatch 108 | // of a second LOADING event. 109 | var proxiedLoadTrait : LoadTrait = loadTrait.mediaElement.getTrait(MediaTraitType.LOAD) as LoadTrait; 110 | proxiedLoadTrait.addEventListener(LoadEvent.LOAD_STATE_CHANGE, onProxiedElementLoadStateChange, false, int.MAX_VALUE); 111 | 112 | // Expose the proxied element. 113 | proxiedElement = loadTrait.mediaElement; 114 | 115 | // If our proxied element hasn't started loading yet, we should 116 | // initiate the load. 117 | if (proxiedLoadTrait.loadState == LoadState.UNINITIALIZED) { 118 | proxiedLoadTrait.load(); 119 | } 120 | 121 | function onProxiedElementLoadStateChange(event : LoadEvent) : void { 122 | if (event.loadState == LoadState.LOADING) { 123 | event.stopImmediatePropagation(); 124 | } else { 125 | proxiedLoadTrait.removeEventListener(LoadEvent.LOAD_STATE_CHANGE, onProxiedElementLoadStateChange); 126 | } 127 | } 128 | } 129 | } 130 | 131 | private var _resource : MediaResourceBase; 132 | private var loadTrait : LoadFromDocumentLoadTrait; 133 | private var loader : LoaderBase; 134 | } 135 | } -------------------------------------------------------------------------------- /src/org/mangui/osmf/plugins/loader/HLSLoaderBase.as: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | package org.mangui.osmf.plugins.loader { 5 | import org.mangui.hls.HLS; 6 | import org.mangui.hls.event.HLSEvent; 7 | import org.mangui.hls.model.Level; 8 | import org.mangui.hls.constant.HLSTypes; 9 | import org.mangui.osmf.plugins.HLSMediaElement; 10 | import org.mangui.osmf.plugins.utils.ErrorManager; 11 | import org.osmf.elements.proxyClasses.LoadFromDocumentLoadTrait; 12 | import org.osmf.events.MediaError; 13 | import org.osmf.events.MediaErrorEvent; 14 | import org.osmf.media.MediaElement; 15 | import org.osmf.media.MediaResourceBase; 16 | import org.osmf.media.URLResource; 17 | import org.osmf.net.DynamicStreamingItem; 18 | import org.osmf.net.DynamicStreamingResource; 19 | import org.osmf.net.StreamType; 20 | import org.osmf.net.StreamingURLResource; 21 | import org.osmf.traits.LoadState; 22 | import org.osmf.traits.LoadTrait; 23 | import org.osmf.traits.LoaderBase; 24 | 25 | CONFIG::LOGGING { 26 | import org.mangui.hls.utils.Log; 27 | } 28 | 29 | /** 30 | * Loader for .m3u8 playlist file. 31 | * Works like a F4MLoader 32 | */ 33 | public class HLSLoaderBase extends LoaderBase { 34 | private var _loadTrait : LoadTrait; 35 | /** Reference to the framework. **/ 36 | private var _hls : HLS = null; 37 | 38 | public function HLSLoaderBase() { 39 | super(); 40 | } 41 | 42 | public static function canHandle(resource : MediaResourceBase) : Boolean { 43 | if (resource !== null && resource is URLResource) { 44 | var urlResource : URLResource = URLResource(resource); 45 | // check for m3u/m3u8 46 | if (urlResource.url.search(/(https?|file)\:\/\/.*?\m3u(\?.*)?/i) !== -1) { 47 | return true; 48 | } 49 | 50 | var contentType : Object = urlResource.getMetadataValue("content-type"); 51 | if (contentType && contentType is String) { 52 | // If the filename doesn't include a .m3u or m3u8 extension, but 53 | // explicit content-type metadata is found on the 54 | // URLResource, we can handle it. Must be either of: 55 | // - "application/x-mpegURL" 56 | // - "vnd.apple.mpegURL" 57 | if ((contentType as String).search(/(application\/x-mpegURL|vnd.apple.mpegURL)/i) !== -1) { 58 | return true; 59 | } 60 | } 61 | } 62 | return false; 63 | } 64 | 65 | override public function canHandleResource(resource : MediaResourceBase) : Boolean { 66 | return canHandle(resource); 67 | } 68 | 69 | override protected function executeLoad(loadTrait : LoadTrait) : void { 70 | _loadTrait = loadTrait; 71 | updateLoadTrait(loadTrait, LoadState.LOADING); 72 | 73 | if (_hls != null) { 74 | _hls.removeEventListener(HLSEvent.MANIFEST_LOADED, _manifestHandler); 75 | _hls.removeEventListener(HLSEvent.ERROR, _errorHandler); 76 | _hls.dispose(); 77 | _hls = null; 78 | } 79 | _hls = new HLS(); 80 | _hls.addEventListener(HLSEvent.MANIFEST_LOADED, _manifestHandler); 81 | _hls.addEventListener(HLSEvent.ERROR, _errorHandler); 82 | /* load playlist */ 83 | _hls.load(URLResource(loadTrait.resource).url); 84 | } 85 | 86 | override protected function executeUnload(loadTrait : LoadTrait) : void { 87 | updateLoadTrait(loadTrait, LoadState.UNINITIALIZED); 88 | } 89 | 90 | /** Update video A/R on manifest load. **/ 91 | private function _manifestHandler(event : HLSEvent) : void { 92 | var resource : MediaResourceBase = URLResource(_loadTrait.resource); 93 | 94 | // retrieve stream type 95 | var streamType : String = (resource as StreamingURLResource).streamType; 96 | if (streamType == null || streamType == StreamType.LIVE_OR_RECORDED) { 97 | if (_hls.type == HLSTypes.LIVE) { 98 | streamType = StreamType.LIVE; 99 | } else { 100 | streamType = StreamType.RECORDED; 101 | } 102 | } 103 | 104 | var levels : Vector. = _hls.levels; 105 | var nbLevel : int = levels.length; 106 | var urlRes : URLResource = resource as URLResource; 107 | var dynamicRes : DynamicStreamingResource = new DynamicStreamingResource(urlRes.url); 108 | var streamItems : Vector. = new Vector.(); 109 | 110 | for (var i : int = 0; i < nbLevel; i++) { 111 | if (levels[i].width) { 112 | streamItems.push(new DynamicStreamingItem(level2label(levels[i]), levels[i].bitrate / 1024, levels[i].width, levels[i].height)); 113 | } else { 114 | streamItems.push(new DynamicStreamingItem(level2label(levels[i]), levels[i].bitrate / 1024)); 115 | } 116 | } 117 | dynamicRes.streamItems = streamItems; 118 | dynamicRes.initialIndex = _hls.startlevel; 119 | resource = dynamicRes; 120 | // set Stream Type 121 | var streamUrlRes : StreamingURLResource = resource as StreamingURLResource; 122 | streamUrlRes.streamType = streamType; 123 | try { 124 | var loadedElem : MediaElement = new HLSMediaElement(resource, _hls, event.levels[_hls.startlevel].duration); 125 | LoadFromDocumentLoadTrait(_loadTrait).mediaElement = loadedElem; 126 | updateLoadTrait(_loadTrait, LoadState.READY); 127 | } catch(e : Error) { 128 | updateLoadTrait(_loadTrait, LoadState.LOAD_ERROR); 129 | _loadTrait.dispatchEvent(new MediaErrorEvent(MediaErrorEvent.MEDIA_ERROR, false, false, new MediaError(e.errorID, e.message))); 130 | } 131 | }; 132 | 133 | private function level2label(level : Level) : String { 134 | if (level.name) { 135 | return level.name; 136 | } else { 137 | if (level.height) { 138 | return(level.height + 'p / ' + Math.round(level.bitrate / 1024) + 'kb'); 139 | } else { 140 | return(Math.round(level.bitrate / 1024) + 'kb'); 141 | } 142 | } 143 | } 144 | 145 | private function _errorHandler(event : HLSEvent) : void { 146 | var errorCode : int = ErrorManager.getMediaErrorCode(event); 147 | var errorMsg : String = ErrorManager.getMediaErrorMessage(event); 148 | CONFIG::LOGGING { 149 | Log.warn("HLS Error event received, dispatching MediaError " + errorCode + "," + errorMsg); 150 | } 151 | updateLoadTrait(_loadTrait, LoadState.LOAD_ERROR); 152 | _loadTrait.dispatchEvent(new MediaErrorEvent(MediaErrorEvent.MEDIA_ERROR, false, false, new MediaError(errorCode, errorMsg))); 153 | } 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /src/org/mangui/osmf/plugins/loader/HLSNetLoader.as: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | package org.mangui.osmf.plugins.loader { 5 | import flash.net.NetStream; 6 | import flash.net.NetConnection; 7 | 8 | import org.osmf.net.NetLoader; 9 | import org.osmf.media.URLResource; 10 | import org.mangui.hls.HLS; 11 | 12 | public class HLSNetLoader extends NetLoader { 13 | private var _hls : HLS; 14 | private var _connection : NetConnection; 15 | private var _resource : URLResource; 16 | 17 | public function HLSNetLoader(hls : HLS) { 18 | _hls = hls; 19 | super(); 20 | } 21 | 22 | override protected function createNetStream(connection : NetConnection, resource : URLResource) : NetStream { 23 | _connection = connection; 24 | _resource = resource; 25 | return _hls.stream; 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /src/org/mangui/osmf/plugins/traits/HLSAlternativeAudioTrait.as: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | package org.mangui.osmf.plugins.traits { 5 | import org.mangui.hls.HLS; 6 | import org.mangui.hls.model.AudioTrack; 7 | import org.mangui.hls.event.HLSEvent; 8 | import org.osmf.events.AlternativeAudioEvent; 9 | import org.osmf.media.MediaElement; 10 | import org.osmf.net.StreamingItem; 11 | import org.osmf.traits.AlternativeAudioTrait; 12 | import org.osmf.utils.OSMFStrings; 13 | 14 | CONFIG::LOGGING { 15 | import org.mangui.hls.utils.Log; 16 | } 17 | 18 | public class HLSAlternativeAudioTrait extends AlternativeAudioTrait { 19 | private var _hls : HLS; 20 | private var _media : MediaElement; 21 | private var _audioTrackList : Vector.; 22 | private var _numAlternativeAudioStreams : int; 23 | private var _transitionInProgress : Boolean = false; 24 | private var _activeTransitionIndex : int = DEFAULT_TRANSITION_INDEX; 25 | private var _lastTransitionIndex : int = INVALID_TRANSITION_INDEX; 26 | 27 | public function HLSAlternativeAudioTrait(hls : HLS, media : MediaElement) { 28 | CONFIG::LOGGING { 29 | Log.debug("HLSAlternativeAudioTrait()"); 30 | } 31 | super(0); 32 | _numAlternativeAudioStreams = 0; 33 | _hls = hls; 34 | _media = media; 35 | _hls.addEventListener(HLSEvent.AUDIO_TRACK_CHANGE, _audioTrackChangedHandler); 36 | _hls.addEventListener(HLSEvent.AUDIO_TRACKS_LIST_CHANGE, _audioTrackListChangedHandler); 37 | } 38 | 39 | override public function dispose() : void { 40 | CONFIG::LOGGING { 41 | Log.debug("HLSAlternativeAudioTrait:dispose"); 42 | } 43 | _hls.removeEventListener(HLSEvent.AUDIO_TRACK_CHANGE, _audioTrackChangedHandler); 44 | _hls.removeEventListener(HLSEvent.AUDIO_TRACKS_LIST_CHANGE, _audioTrackListChangedHandler); 45 | super.dispose(); 46 | } 47 | 48 | override public function get numAlternativeAudioStreams() : int { 49 | CONFIG::LOGGING { 50 | Log.debug("HLSAlternativeAudioTrait:numAlternativeAudioStreams:" + _numAlternativeAudioStreams); 51 | } 52 | return _numAlternativeAudioStreams; 53 | } 54 | 55 | override public function getItemForIndex(index : int) : StreamingItem { 56 | CONFIG::LOGGING { 57 | Log.debug("HLSDynamicStreamTrait:getItemForIndex(" + index + ")"); 58 | } 59 | if (index <= INVALID_TRANSITION_INDEX || index >= numAlternativeAudioStreams) { 60 | throw new RangeError(OSMFStrings.getString(OSMFStrings.ALTERNATIVEAUDIO_INVALID_INDEX)); 61 | } 62 | 63 | if (index == DEFAULT_TRANSITION_INDEX) { 64 | return null; 65 | } 66 | var name : String = _audioTrackList[index + 1].title; 67 | var streamItem : StreamingItem = new StreamingItem("AUDIO", name); 68 | streamItem.info.label = name; 69 | return streamItem; 70 | } 71 | 72 | override protected function endSwitching(index : int) : void { 73 | CONFIG::LOGGING { 74 | Log.debug("HLSDynamicStreamTrait:endSwitching(" + index + ")"); 75 | } 76 | if (switching) { 77 | executeSwitching(_indexToSwitchTo); 78 | } 79 | super.endSwitching(index); 80 | } 81 | 82 | protected function executeSwitching(indexToSwitchTo : int) : void { 83 | CONFIG::LOGGING { 84 | Log.debug("HLSDynamicStreamTrait:executeSwitching(" + indexToSwitchTo + ")"); 85 | } 86 | if (_lastTransitionIndex != indexToSwitchTo) { 87 | _activeTransitionIndex = indexToSwitchTo; 88 | _transitionInProgress = true; 89 | _hls.audioTrack = indexToSwitchTo + 1; 90 | } 91 | } 92 | 93 | private function _audioTrackChangedHandler(event : HLSEvent) : void { 94 | CONFIG::LOGGING { 95 | Log.debug("HLSDynamicStreamTrait:_audioTrackChangedHandler"); 96 | } 97 | _transitionInProgress = false; 98 | setSwitching(false, _lastTransitionIndex); 99 | } 100 | 101 | private function _audioTrackListChangedHandler(event : HLSEvent) : void { 102 | CONFIG::LOGGING { 103 | Log.debug("HLSDynamicStreamTrait:_audioTrackListChangedHandler"); 104 | } 105 | _audioTrackList = _hls.audioTracks; 106 | if (_audioTrackList.length > 0) { 107 | // try to change default Audio Track Title for GrindPlayer ... 108 | if (_audioTrackList[0].title.indexOf("TS/") == -1) { 109 | CONFIG::LOGGING { 110 | Log.debug("default audio track title:" + _audioTrackList[0].title); 111 | } 112 | _media.resource.addMetadataValue("defaultAudioLabel", _audioTrackList[0].title); 113 | } 114 | } 115 | _numAlternativeAudioStreams = _audioTrackList.length - 1; 116 | if (_numAlternativeAudioStreams > 0) { 117 | dispatchEvent(new AlternativeAudioEvent(AlternativeAudioEvent.NUM_ALTERNATIVE_AUDIO_STREAMS_CHANGE, false, false, false)); 118 | } 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/org/mangui/osmf/plugins/traits/HLSBufferTrait.as: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | package org.mangui.osmf.plugins.traits { 5 | import org.mangui.hls.HLS; 6 | import org.mangui.hls.event.HLSEvent; 7 | import org.mangui.hls.constant.HLSPlayStates; 8 | import org.osmf.traits.BufferTrait; 9 | 10 | CONFIG::LOGGING { 11 | import org.mangui.hls.utils.Log; 12 | } 13 | 14 | public class HLSBufferTrait extends BufferTrait { 15 | private var _hls : HLS; 16 | 17 | public function HLSBufferTrait(hls : HLS) { 18 | CONFIG::LOGGING { 19 | Log.debug("HLSBufferTrait()"); 20 | } 21 | super(); 22 | _hls = hls; 23 | _hls.addEventListener(HLSEvent.PLAYBACK_STATE, _stateChangedHandler); 24 | } 25 | 26 | override public function dispose() : void { 27 | CONFIG::LOGGING { 28 | Log.debug("HLSBufferTrait:dispose"); 29 | } 30 | _hls.removeEventListener(HLSEvent.PLAYBACK_STATE, _stateChangedHandler); 31 | super.dispose(); 32 | } 33 | 34 | override public function get bufferLength() : Number { 35 | return _hls.stream.bufferLength; 36 | } 37 | 38 | /** state changed handler **/ 39 | private function _stateChangedHandler(event : HLSEvent) : void { 40 | switch(event.state) { 41 | case HLSPlayStates.PLAYING_BUFFERING: 42 | case HLSPlayStates.PAUSED_BUFFERING: 43 | CONFIG::LOGGING { 44 | Log.debug("HLSBufferTrait:_stateChangedHandler:setBuffering(true)"); 45 | } 46 | setBuffering(true); 47 | break; 48 | default: 49 | CONFIG::LOGGING { 50 | Log.debug("HLSBufferTrait:_stateChangedHandler:setBuffering(false)"); 51 | } 52 | setBuffering(false); 53 | break; 54 | } 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/org/mangui/osmf/plugins/traits/HLSDisplayObjectTrait.as: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | package org.mangui.osmf.plugins.traits { 5 | 6 | import flash.display.DisplayObject; 7 | import flash.display.Stage; 8 | import flash.events.Event; 9 | 10 | import org.mangui.hls.HLS; 11 | import org.osmf.media.videoClasses.VideoSurface; 12 | import org.osmf.traits.DisplayObjectTrait; 13 | 14 | CONFIG::LOGGING { 15 | import org.mangui.hls.utils.Log; 16 | } 17 | 18 | public class HLSDisplayObjectTrait extends DisplayObjectTrait { 19 | private var videoSurface : VideoSurface; 20 | private var _hls : HLS; 21 | 22 | public function HLSDisplayObjectTrait(hls : HLS, videoSurface : DisplayObject, mediaWidth : int = 0, mediaHeight : int = 0) { 23 | CONFIG::LOGGING { 24 | Log.debug("HLSDisplayObjectTrait()"); 25 | } 26 | _hls = hls; 27 | super(videoSurface, mediaWidth, mediaHeight); 28 | this.videoSurface = videoSurface as VideoSurface; 29 | 30 | if (this.videoSurface is VideoSurface) 31 | this.videoSurface.addEventListener(Event.ADDED_TO_STAGE, onStage); 32 | } 33 | 34 | override public function dispose() : void { 35 | CONFIG::LOGGING { 36 | Log.debug("HLSDisplayObjectTrait:dispose"); 37 | } 38 | videoSurface.removeEventListener(Event.ENTER_FRAME, onFrame); 39 | super.dispose(); 40 | } 41 | 42 | private function onStage(event : Event) : void { 43 | _hls.stage = event.target.stage as Stage; 44 | videoSurface.removeEventListener(Event.ADDED_TO_STAGE, onStage); 45 | videoSurface.addEventListener(Event.ENTER_FRAME, onFrame); 46 | } 47 | 48 | private function onFrame(event : Event) : void { 49 | var newWidth : int = videoSurface.videoWidth; 50 | var newHeight : int = videoSurface.videoHeight; 51 | if (newWidth != 0 && newHeight != 0 && (newWidth != mediaWidth || newHeight != mediaHeight)) { 52 | // If there is no layout, set as no scale. 53 | if (videoSurface.width == 0 && videoSurface.height == 0) { 54 | videoSurface.width = newWidth; 55 | videoSurface.height = newHeight; 56 | } 57 | CONFIG::LOGGING { 58 | Log.info("HLSDisplayObjectTrait:setMediaSize(" + newWidth + "," + newHeight + ")"); 59 | } 60 | setMediaSize(newWidth, newHeight); 61 | } 62 | // videoSurface.removeEventListener(Event.ENTER_FRAME, onFrame); 63 | } 64 | } 65 | } -------------------------------------------------------------------------------- /src/org/mangui/osmf/plugins/traits/HLSDynamicStreamTrait.as: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | package org.mangui.osmf.plugins.traits { 5 | import org.mangui.hls.HLS; 6 | import org.mangui.hls.event.HLSEvent; 7 | import org.osmf.traits.DynamicStreamTrait; 8 | import org.osmf.utils.OSMFStrings; 9 | 10 | CONFIG::LOGGING { 11 | import org.mangui.hls.utils.Log; 12 | } 13 | 14 | public class HLSDynamicStreamTrait extends DynamicStreamTrait { 15 | private var _hls : HLS; 16 | 17 | public function HLSDynamicStreamTrait(hls : HLS) { 18 | CONFIG::LOGGING { 19 | Log.debug("HLSDynamicStreamTrait()"); 20 | } 21 | _hls = hls; 22 | _hls.addEventListener(HLSEvent.LEVEL_SWITCH, _levelSwitchHandler); 23 | super(true, _hls.startlevel, hls.levels.length); 24 | } 25 | 26 | override public function dispose() : void { 27 | CONFIG::LOGGING { 28 | Log.debug("HLSDynamicStreamTrait:dispose"); 29 | } 30 | _hls.removeEventListener(HLSEvent.LEVEL_SWITCH, _levelSwitchHandler); 31 | super.dispose(); 32 | } 33 | 34 | override public function getBitrateForIndex(index : int) : Number { 35 | if (index > numDynamicStreams - 1 || index < 0) { 36 | throw new RangeError(OSMFStrings.getString(OSMFStrings.STREAMSWITCH_INVALID_INDEX)); 37 | } 38 | var bitrate : Number = _hls.levels[index].bitrate / 1000; 39 | CONFIG::LOGGING { 40 | Log.debug("HLSDynamicStreamTrait:getBitrateForIndex(" + index + ")=" + bitrate); 41 | } 42 | return bitrate; 43 | } 44 | 45 | override public function switchTo(index : int) : void { 46 | CONFIG::LOGGING { 47 | Log.debug("HLSDynamicStreamTrait:switchTo(" + index + ")/max:" + maxAllowedIndex); 48 | } 49 | if (index < 0 || index > maxAllowedIndex) { 50 | throw new RangeError(OSMFStrings.getString(OSMFStrings.STREAMSWITCH_INVALID_INDEX)); 51 | } 52 | autoSwitch = false; 53 | if (!switching) { 54 | setSwitching(true, index); 55 | } 56 | } 57 | 58 | override protected function autoSwitchChangeStart(value : Boolean) : void { 59 | CONFIG::LOGGING { 60 | Log.debug("HLSDynamicStreamTrait:autoSwitchChangeStart:" + value); 61 | } 62 | if (value == true && _hls.autolevel == false) { 63 | _hls.level = -1; 64 | // only seek if position is set 65 | if (!isNaN(_hls.position)) { 66 | _hls.stream.seek(_hls.position); 67 | } 68 | } 69 | } 70 | 71 | override protected function switchingChangeStart(newSwitching : Boolean, index : int) : void { 72 | CONFIG::LOGGING { 73 | Log.debug("HLSDynamicStreamTrait:switchingChangeStart(newSwitching/index):" + newSwitching + "/" + index); 74 | } 75 | if (newSwitching) { 76 | _hls.level = index; 77 | } 78 | } 79 | 80 | /** Update playback position/duration **/ 81 | private function _levelSwitchHandler(event : HLSEvent) : void { 82 | var newLevel : int = event.level; 83 | CONFIG::LOGGING { 84 | Log.debug("HLSDynamicStreamTrait:_qualitySwitchHandler:" + newLevel); 85 | } 86 | setCurrentIndex(newLevel); 87 | setSwitching(false, newLevel); 88 | }; 89 | } 90 | } -------------------------------------------------------------------------------- /src/org/mangui/osmf/plugins/traits/HLSNetStreamLoadTrait.as: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | package org.mangui.osmf.plugins.traits { 5 | import org.mangui.hls.HLS; 6 | import org.mangui.hls.event.HLSEvent; 7 | import org.osmf.media.MediaResourceBase; 8 | import org.osmf.net.NetStreamLoadTrait; 9 | import org.osmf.traits.LoaderBase; 10 | 11 | CONFIG::LOGGING { 12 | import org.mangui.hls.utils.Log; 13 | } 14 | 15 | public class HLSNetStreamLoadTrait extends NetStreamLoadTrait { 16 | private var _hls : HLS; 17 | private var _time_loaded : Number; 18 | private var _time_total : Number; 19 | 20 | public function HLSNetStreamLoadTrait(hls : HLS, duration : Number, loader : LoaderBase, resource : MediaResourceBase) { 21 | CONFIG::LOGGING { 22 | Log.debug("HLSNetStreamLoadTrait()"); 23 | } 24 | super(loader, resource); 25 | _hls = hls; 26 | _time_loaded = 0; 27 | _time_total = duration; 28 | super.netStream = _hls.stream; 29 | _hls.addEventListener(HLSEvent.MEDIA_TIME, _mediaTimeHandler); 30 | } 31 | 32 | override public function dispose() : void { 33 | CONFIG::LOGGING { 34 | Log.debug("HLSNetStreamLoadTrait:dispose"); 35 | } 36 | _hls.removeEventListener(HLSEvent.MEDIA_TIME, _mediaTimeHandler); 37 | super.dispose(); 38 | } 39 | 40 | override public function get bytesLoaded() : Number { 41 | return _time_loaded; 42 | } 43 | 44 | override public function get bytesTotal() : Number { 45 | return _time_total; 46 | } 47 | 48 | /** **/ 49 | private function _mediaTimeHandler(event : HLSEvent) : void { 50 | var time_total : Number = Math.round(10 * event.mediatime.duration) / 10; 51 | var time_loaded : Number = Math.round(10 * (event.mediatime.position + event.mediatime.buffer)) / 10; 52 | 53 | if (_time_total != time_total) { 54 | if (time_total < _time_loaded || time_total < 0) { 55 | time_total = NaN; 56 | } 57 | _time_total = time_total; 58 | setBytesTotal(time_total); 59 | } 60 | 61 | if (_time_loaded != time_loaded && time_loaded <= time_total) { 62 | _time_loaded = time_loaded; 63 | setBytesLoaded(time_loaded); 64 | } 65 | }; 66 | } 67 | } -------------------------------------------------------------------------------- /src/org/mangui/osmf/plugins/traits/HLSPlayTrait.as: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | package org.mangui.osmf.plugins.traits { 5 | import org.mangui.hls.HLS; 6 | import org.mangui.hls.event.HLSEvent; 7 | import org.osmf.traits.PlayState; 8 | import org.osmf.traits.PlayTrait; 9 | 10 | CONFIG::LOGGING { 11 | import org.mangui.hls.utils.Log; 12 | } 13 | 14 | public class HLSPlayTrait extends PlayTrait { 15 | private var _hls : HLS; 16 | private var streamStarted : Boolean = false; 17 | 18 | public function HLSPlayTrait(hls : HLS) { 19 | CONFIG::LOGGING { 20 | Log.debug("HLSPlayTrait()"); 21 | } 22 | super(); 23 | _hls = hls; 24 | _hls.addEventListener(HLSEvent.PLAYBACK_COMPLETE, _playbackComplete); 25 | } 26 | 27 | override public function dispose() : void { 28 | CONFIG::LOGGING { 29 | Log.debug("HLSPlayTrait:dispose"); 30 | } 31 | _hls.removeEventListener(HLSEvent.PLAYBACK_COMPLETE, _playbackComplete); 32 | super.dispose(); 33 | } 34 | 35 | override protected function playStateChangeStart(newPlayState : String) : void { 36 | CONFIG::LOGGING { 37 | Log.info("HLSPlayTrait:playStateChangeStart:" + newPlayState); 38 | } 39 | switch(newPlayState) { 40 | case PlayState.PLAYING: 41 | if (!streamStarted) { 42 | _hls.stream.play(); 43 | streamStarted = true; 44 | } else { 45 | _hls.stream.resume(); 46 | } 47 | break; 48 | case PlayState.PAUSED: 49 | _hls.stream.pause(); 50 | break; 51 | case PlayState.STOPPED: 52 | streamStarted = false; 53 | _hls.stream.close(); 54 | break; 55 | } 56 | } 57 | 58 | /** playback complete handler **/ 59 | private function _playbackComplete(event : HLSEvent) : void { 60 | stop(); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/org/mangui/osmf/plugins/traits/HLSSeekTrait.as: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | package org.mangui.osmf.plugins.traits { 5 | import org.mangui.hls.HLS; 6 | import org.mangui.hls.event.HLSEvent; 7 | import org.mangui.hls.constant.HLSSeekStates; 8 | import org.osmf.traits.SeekTrait; 9 | import org.osmf.traits.TimeTrait; 10 | 11 | CONFIG::LOGGING { 12 | import org.mangui.hls.utils.Log; 13 | } 14 | 15 | public class HLSSeekTrait extends SeekTrait { 16 | private var _hls : HLS; 17 | 18 | public function HLSSeekTrait(hls : HLS, timeTrait : TimeTrait) { 19 | CONFIG::LOGGING { 20 | Log.debug("HLSSeekTrait()"); 21 | } 22 | super(timeTrait); 23 | _hls = hls; 24 | _hls.addEventListener(HLSEvent.SEEK_STATE, _stateChangedHandler); 25 | } 26 | 27 | override public function dispose() : void { 28 | CONFIG::LOGGING { 29 | Log.debug("HLSSeekTrait:dispose"); 30 | } 31 | _hls.removeEventListener(HLSEvent.SEEK_STATE, _stateChangedHandler); 32 | super.dispose(); 33 | } 34 | 35 | /** 36 | * @private 37 | * Communicates a seeking change to the media through the NetStream. 38 | * @param newSeeking New seeking value. 39 | * @param time Time to seek to, in seconds. 40 | */ 41 | override protected function seekingChangeStart(newSeeking : Boolean, time : Number) : void { 42 | if (newSeeking) { 43 | CONFIG::LOGGING { 44 | Log.info("HLSSeekTrait:seekingChangeStart(newSeeking/time):(" + newSeeking + "/" + time + ")"); 45 | } 46 | _hls.stream.seek(time); 47 | } 48 | super.seekingChangeStart(newSeeking, time); 49 | } 50 | 51 | /** state changed handler **/ 52 | private function _stateChangedHandler(event : HLSEvent) : void { 53 | if (seeking && event.state != HLSSeekStates.SEEKING) { 54 | CONFIG::LOGGING { 55 | Log.debug("HLSSeekTrait:setSeeking(false);"); 56 | } 57 | setSeeking(false, timeTrait.currentTime); 58 | } 59 | } 60 | } 61 | } -------------------------------------------------------------------------------- /src/org/mangui/osmf/plugins/traits/HLSTimeTrait.as: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | package org.mangui.osmf.plugins.traits { 5 | import org.mangui.hls.HLS; 6 | import org.mangui.hls.event.HLSEvent; 7 | import org.osmf.traits.TimeTrait; 8 | 9 | CONFIG::LOGGING { 10 | import org.mangui.hls.utils.Log; 11 | } 12 | 13 | public class HLSTimeTrait extends TimeTrait { 14 | private var _hls : HLS; 15 | 16 | public function HLSTimeTrait(hls : HLS, duration : Number = 0) { 17 | CONFIG::LOGGING { 18 | Log.debug("HLSTimeTrait()"); 19 | } 20 | super(duration); 21 | setCurrentTime(0); 22 | _hls = hls; 23 | _hls.addEventListener(HLSEvent.MEDIA_TIME, _mediaTimeHandler); 24 | _hls.addEventListener(HLSEvent.PLAYBACK_COMPLETE, _playbackComplete); 25 | } 26 | 27 | override public function dispose() : void { 28 | CONFIG::LOGGING { 29 | Log.debug("HLSTimeTrait:dispose"); 30 | } 31 | _hls.removeEventListener(HLSEvent.MEDIA_TIME, _mediaTimeHandler); 32 | _hls.removeEventListener(HLSEvent.PLAYBACK_COMPLETE, _playbackComplete); 33 | super.dispose(); 34 | } 35 | 36 | /** Update playback position/duration **/ 37 | private function _mediaTimeHandler(event : HLSEvent) : void { 38 | var new_duration : Number = event.mediatime.duration; 39 | var new_position : Number = Math.max(0, event.mediatime.position); 40 | setDuration(new_duration); 41 | setCurrentTime(new_position); 42 | }; 43 | 44 | /** playback complete handler **/ 45 | private function _playbackComplete(event : HLSEvent) : void { 46 | signalComplete(); 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /src/org/mangui/osmf/plugins/utils/ErrorManager.as: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | package org.mangui.osmf.plugins.utils { 5 | import org.mangui.hls.event.HLSError; 6 | import org.mangui.hls.event.HLSEvent; 7 | import org.osmf.events.MediaErrorCodes; 8 | 9 | public class ErrorManager { 10 | 11 | public static function getMediaErrorCode(event : HLSEvent) : int { 12 | var errorCode : int = MediaErrorCodes.NETSTREAM_PLAY_FAILED; 13 | if (event && event.error) { 14 | switch (event.error.code) { 15 | case HLSError.FRAGMENT_LOADING_ERROR: 16 | case HLSError.FRAGMENT_LOADING_CROSSDOMAIN_ERROR: 17 | case HLSError.KEY_LOADING_ERROR: 18 | case HLSError.KEY_LOADING_CROSSDOMAIN_ERROR: 19 | case HLSError.MANIFEST_LOADING_CROSSDOMAIN_ERROR: 20 | case HLSError.MANIFEST_LOADING_IO_ERROR: 21 | errorCode = MediaErrorCodes.IO_ERROR; 22 | break; 23 | case org.mangui.hls.event.HLSError.FRAGMENT_PARSING_ERROR: 24 | case org.mangui.hls.event.HLSError.KEY_PARSING_ERROR: 25 | case org.mangui.hls.event.HLSError.MANIFEST_PARSING_ERROR: 26 | errorCode = MediaErrorCodes.NETSTREAM_FILE_STRUCTURE_INVALID; 27 | break; 28 | case org.mangui.hls.event.HLSError.TAG_APPENDING_ERROR: 29 | errorCode = MediaErrorCodes.ARGUMENT_ERROR; 30 | break; 31 | } 32 | } 33 | return errorCode; 34 | }; 35 | 36 | public static function getMediaErrorMessage(event : HLSEvent) : String { 37 | return (event && event.error) ? event.error.msg : "Unknown error"; 38 | }; 39 | }; 40 | } -------------------------------------------------------------------------------- /src/tv/bem/BemTVPlayer.as: -------------------------------------------------------------------------------- 1 | package tv.bem { 2 | import flash.external.ExternalInterface; 3 | import flash.display.*; 4 | import flash.utils.setTimeout; 5 | import flash.events.*; 6 | import org.mangui.hls.utils.*; 7 | import flash.geom.Rectangle; 8 | import flash.system.Security; 9 | 10 | import com.globo.Player; 11 | import tv.bem.BemTVURLStream; 12 | import tv.bem.PlaybackIdHolder; 13 | 14 | public class BemTVPlayer extends Player { 15 | private var idHolder:PlaybackIdHolder; 16 | 17 | public function BemTVPlayer() { 18 | super(); 19 | ExternalInterface.call("console.log", "BemTV Player (0.0.12)"); 20 | idHolder = PlaybackIdHolder.getInstance(); 21 | idHolder.playbackId = LoaderInfo(this.root.loaderInfo).parameters.playbackId; 22 | } 23 | 24 | override protected function _setupExternalGetters():void { 25 | super._setupExternalGetters(); 26 | ExternalInterface.addCallback("getmaxBufferLength", _getmaxBufferLength); 27 | ExternalInterface.addCallback("getminBufferLength", _getminBufferLength); 28 | ExternalInterface.addCallback("getlowBufferLength", _getlowBufferLength); 29 | ExternalInterface.addCallback("getDelay", _getDelay); 30 | } 31 | 32 | override protected function _setupExternalCallers():void { 33 | super._setupExternalCallers(); 34 | ExternalInterface.addCallback("playerSetmaxBufferLength", _setmaxBufferLength); 35 | ExternalInterface.addCallback("playerSetminBufferLength", _setminBufferLength); 36 | ExternalInterface.addCallback("playerSetlowBufferLength", _setlowBufferLength); 37 | } 38 | 39 | override protected function _onStageVideoState(event : StageVideoAvailabilityEvent) : void { 40 | super._onStageVideoState(event); 41 | _hls.URLstream = BemTVURLStream as Class; 42 | } 43 | 44 | private function _getDelay():Number { 45 | return _hls.delay; 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/tv/bem/BemTVURLStream.as: -------------------------------------------------------------------------------- 1 | package tv.bem { 2 | import flash.net.URLRequest; 3 | import flash.events.Event; 4 | import flash.external.ExternalInterface; 5 | import org.mangui.chromeless.JSURLStream; 6 | import tv.bem.PlaybackIdHolder; 7 | 8 | public class BemTVURLStream extends JSURLStream { 9 | private var idHolder:PlaybackIdHolder; 10 | private var playbackId:String; 11 | 12 | public function BemTVURLStream() { 13 | super(); 14 | idHolder = PlaybackIdHolder.getInstance(); 15 | playbackId = idHolder.playbackId; 16 | ExternalInterface.addCallback("resourceLoaded", resourceLoaded); 17 | ExternalInterface.addCallback("startDecoding", startDecoding); 18 | } 19 | 20 | override public function load(request:URLRequest):void { 21 | _triggerEvent("requestresource", request.url); 22 | dispatchEvent(new Event(Event.OPEN)); 23 | } 24 | 25 | private function _triggerEvent(eventName: String, param:String=null):void { 26 | var event:String = playbackId + ":" + eventName; 27 | ExternalInterface.call('Clappr.Mediator.trigger', event, param); 28 | } 29 | 30 | override protected function resourceLoadingError() : void { 31 | super.resourceLoadingError(); 32 | _triggerEvent("decodeerror"); 33 | } 34 | 35 | override protected function resourceLoadingSuccess() : void { 36 | super.resourceLoadingSuccess(); 37 | _triggerEvent("decodesuccess"); 38 | } 39 | 40 | } 41 | } 42 | 43 | -------------------------------------------------------------------------------- /src/tv/bem/PlaybackIdHolder.as: -------------------------------------------------------------------------------- 1 | package tv.bem { 2 | import flash.external.ExternalInterface; 3 | 4 | public class PlaybackIdHolder { 5 | private static var instance:PlaybackIdHolder; 6 | private var _playbackId:String; 7 | 8 | public function PlaybackIdHolder() { 9 | if (instance) { 10 | throw new Error("use getInstance()"); 11 | } 12 | instance = this; 13 | } 14 | 15 | public static function getInstance():PlaybackIdHolder { 16 | if (!instance) { 17 | new PlaybackIdHolder(); 18 | } 19 | return instance; 20 | } 21 | 22 | public function set playbackId(id:String):void { 23 | _playbackId = id; 24 | } 25 | 26 | public function get playbackId():String { 27 | return _playbackId; 28 | } 29 | } 30 | } 31 | --------------------------------------------------------------------------------