├── .gitignore ├── web ├── ulex.png ├── FlashPhone.swf ├── history │ ├── history.css │ ├── historyFrame.html │ └── history.js ├── FlashPhone.html ├── index.html ├── custom_phone.php ├── AC_OETags.js ├── look.html └── swfobject.js ├── src ├── chan_rtmp.c ├── rtmp.conf ├── Makefile ├── flvtools.h ├── rtmp.h ├── keys.h ├── dhgroups.h ├── rtmpe.h └── flvtools.c ├── doc ├── rtmp_specification_1.0.pdf ├── video_file_format_spec_v10.pdf └── rtmp_specification_license_1.0.pdf ├── README.md └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | *.d 2 | *.o 3 | src/chan_rtmp.so -------------------------------------------------------------------------------- /web/ulex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voximal/asterisk-rtmp/HEAD/web/ulex.png -------------------------------------------------------------------------------- /src/chan_rtmp.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voximal/asterisk-rtmp/HEAD/src/chan_rtmp.c -------------------------------------------------------------------------------- /web/FlashPhone.swf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voximal/asterisk-rtmp/HEAD/web/FlashPhone.swf -------------------------------------------------------------------------------- /doc/rtmp_specification_1.0.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voximal/asterisk-rtmp/HEAD/doc/rtmp_specification_1.0.pdf -------------------------------------------------------------------------------- /doc/video_file_format_spec_v10.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voximal/asterisk-rtmp/HEAD/doc/video_file_format_spec_v10.pdf -------------------------------------------------------------------------------- /doc/rtmp_specification_license_1.0.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voximal/asterisk-rtmp/HEAD/doc/rtmp_specification_license_1.0.pdf -------------------------------------------------------------------------------- /src/rtmp.conf: -------------------------------------------------------------------------------- 1 | ; 2 | ; RTMP Driver Configuration File 3 | ; 4 | 5 | [general] 6 | bindaddr=0.0.0.0 7 | bindport=1935 8 | application=asterisk 9 | videosupport=yes 10 | textsupport=yes 11 | autousers=yes 12 | multipleusers=yes 13 | 14 | [user1] 15 | type=user 16 | secret=1234 17 | context=default 18 | 19 | 20 | [user2] 21 | type=user 22 | secret=1234 23 | -------------------------------------------------------------------------------- /web/history/history.css: -------------------------------------------------------------------------------- 1 | /* This CSS stylesheet defines styles used by required elements in a flex application page that supports browser history */ 2 | 3 | #ie_historyFrame { width: 0px; height: 0px; display:none } 4 | #firefox_anchorDiv { width: 0px; height: 0px; display:none } 5 | #safari_formDiv { width: 0px; height: 0px; display:none } 6 | #safari_rememberDiv { width: 0px; height: 0px; display:none } 7 | -------------------------------------------------------------------------------- /web/history/historyFrame.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 27 | Hidden frame for Browser History support. 28 | 29 | 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | chan_rtmp 2 | ========= 3 | 4 | --- 5 | The RTMP Asterisk module allows to place audio (and video) calls from a web browser using the FlashPlayer from Adobe(R). 6 | 7 | We offer a free FlashPhone to connect to the Asterisk using the RTMP module. 8 | 9 | Main features 10 | ------------- 11 | 12 | * Writen in C using asterisk-macros. 13 | * Asterisk 1.6 to Asterisk 11.(help requested to port it to Asterisk 13/14) 14 | * This module supports realtime and static peers. 15 | * Text/Chat features 16 | * Audio and Video 17 | * Codecs supported : Speex, a/ulaw , PCM 16 bits, Video Sorenson 18 | * Geo localisation (with IP) 19 | * Works with Vconference (Video / Switch module), Transcode (video transcoder) 20 | * configuration file (rtmp.conf) 21 | * realtime configuration 22 | 23 | Installation 24 | ------------ 25 | 26 | ```sh 27 | export ASTERISKMACROSDIR=[Asterisk macros Git Voximal directory] 28 | export ASTERISKDIR=[Asterisk sources directory] 29 | export LINUX_BUILD=[x86-64 or i686 or armv6l] 30 | export LIBGEOIPDIR=[GeoIP sources directory]/libGeoIP/ 31 | 32 | git clone https://github.com/voximal/asterisk-rtmp chan_rtmp 33 | cd chan_rtmp/src 34 | make 35 | make install 36 | ``` 37 | 38 | Client 39 | ------ 40 | 41 | The client over FlashPlayer allows to set differents skins. 42 | (Chrome seems to need to host your HTML pages in a HTTS server) 43 | 44 | An Android SDK for smartphone/webtv is available to create video call applications. 45 | 46 | 47 | Demo 48 | ---- 49 | 50 | - default : https://rtmp.ulex.fr:44129/webphone/ 51 | - no https : http://rtmp.ulex.fr/webphone/ 52 | - more looks : http://rtmp.ulex.fr/webphone/look.html 53 | 54 | 55 | 56 | Contact 57 | ------- 58 | 59 | Contact us with the Ulex web site : http://www.ulex.fr 60 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Asterisk -- A telephony toolkit for Linux. 3 | # 4 | # Makefile for channel drivers 5 | # 6 | # Copyright (C) 1999-2006, Digium, Inc. 7 | # 8 | # This program is free software, distributed under the terms of 9 | # the GNU General Public License 10 | # 11 | 12 | ASTTOPDIR=$(ASTERISKDIR) 13 | FFMPEGDIR=$(FFMPEGOLDDIR) 14 | ASTMACDIR=$(ASTERISKMACROSDIR)/include 15 | 16 | include ${ASTTOPDIR}/menuselect.makeopts ${ASTTOPDIR}/menuselect.makedeps 17 | 18 | INSTALL_PREFIX := /usr 19 | INSTALL_MODULES_DIR := $(INSTALL_PREFIX)/lib/asterisk/modules 20 | MODULES_DIR := ${INSTALL_MODULES_DIR} 21 | 22 | #LIBS := -L$(FFMPEGDIR)/libavcodec -lavcodec -lssl -lcrypto -L$(LIBGEOIPDIR)/.libs -lGeoIP -ljpeg 23 | LIBS := -lssl -lcrypto -L$(LIBGEOIPDIR)/.libs -lGeoIP 24 | RPATH := -Wl,-rpath=/usr/lib/asteriskrtmp:/usr/lib/openvxi 25 | 26 | MENUSELECT_CATEGORY=CHANNELS 27 | MENUSELECT_DESCRIPTION=Channel Drivers 28 | 29 | ALL_C_MODS:=$(patsubst %.c,%,$(wildcard chan_*.c)) 30 | ALL_CC_MODS:=$(patsubst %.cc,%,$(wildcard chan_*.cc)) 31 | 32 | C_MODS:=$(filter-out $(MENUSELECT_CHANNELS),$(ALL_C_MODS)) 33 | CC_MODS:=$(filter-out $(MENUSELECT_CHANNELS),$(ALL_CC_MODS)) 34 | 35 | LOADABLE_MODS:=$(C_MODS) $(CC_MODS) 36 | 37 | ifneq ($(findstring channels,$(MENUSELECT_EMBED)),) 38 | EMBEDDED_MODS:=$(LOADABLE_MODS) 39 | LOADABLE_MODS:= 40 | endif 41 | 42 | RTMPOBJS=chan_rtmp.o flvtools.o 43 | SHAREDOS=chan_rtmp.so 44 | 45 | 46 | CFLAGS += -ggdb -march=$(LINUX_BUILD) -fPIC -DAST_MODULE=\"chan_rtmp\" -pipe -Wall -Wmissing-prototypes -Wmissing-declarations $(DEBUG) $(INCLUDE) -D_REENTRANT -D_GNU_SOURCE -Dulex 47 | RTMPCFLAGS = -I./ -I$(ASTTOPDIR)/include -I$(ASTMACDIR) -I$(LIBGEOIPDIR) 48 | 49 | # Options 50 | ifneq ($(FFMPEGDIR), "") 51 | #LIBS += -L$(FFMPEGDIR)/libavcodec -lavcodec 52 | #RTMPCFLAGS += -DRTMP_FFMPEG -I$(FFMPEGDIR) 53 | endif 54 | 55 | all: _all 56 | 57 | include $(ASTTOPDIR)/Makefile.moddir_rules 58 | 59 | clean:: 60 | rm -f $(addprefix $(RTMPOBJS).,$(ALL_C_MODS) $(ALL_CC_MODS)) 61 | 62 | chan_rtmp.o: ASTCFLAGS+=$(RTMPCFLAGS) $(CFLAGS) 63 | 64 | flvtools.o: ASTCFLAGS+=$(RTMPCFLAGS) $(CFLAGS) 65 | 66 | chan_rtmp.so : $(RTMPOBJS) 67 | $(CC) -pg -shared -Xlinker -x -o $@ $(RTMPOBJS) $(LIBS) $(RPATH) 68 | 69 | $(if $(filter chan_rtmp,$(EMBEDDED_MODS)),modules.link,chan_rtmp.so): ASTCFLAGS+=$(RTMPCFLAGS) $(CFLAGS) 70 | $(if $(filter chan_rtmp,$(EMBEDDED_MODS)),modules.link,chan_rtmp.so): chan_rtmp.o 71 | 72 | install2: all 73 | for x in $(SHAREDOS); do $(INSTALL) -m 755 $$x $(INSTALL_MODULES_DIR) ; done 74 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Asterisk is distributed under the GNU General Public License version 2 2 | and is also available under alternative licenses negotiated directly 3 | with Digium, Inc. If you obtained Asterisk under the GPL, then the GPL 4 | applies to all loadable Asterisk modules used on your system as well, 5 | except as defined below. The GPL (version 2) is included in this 6 | source tree in the file COPYING. 7 | 8 | This package also includes various components that are not part of 9 | Asterisk itself; these components are in the 'contrib' directory 10 | and its subdirectories. These components are also distributed under the 11 | GPL version 2 as well. 12 | 13 | Digium, Inc. (formerly Linux Support Services) holds copyright 14 | and/or sufficient licenses to all components of the Asterisk 15 | package, and therefore can grant, at its sole discretion, the ability 16 | for companies, individuals, or organizations to create proprietary or 17 | Open Source (even if not GPL) modules which may be dynamically linked at 18 | runtime with the portions of Asterisk which fall under our 19 | copyright/license umbrella, or are distributed under more flexible 20 | licenses than GPL. 21 | 22 | If you wish to use our code in other GPL programs, don't worry -- 23 | there is no requirement that you provide the same exception in your 24 | GPL'd products (although if you've written a module for Asterisk we 25 | would strongly encourage you to make the same exception that we do). 26 | 27 | Specific permission is also granted to link Asterisk with OpenSSL, OpenH323 28 | UniMRCP, and/or the UW IMAP Toolkit and distribute the resulting binary files. 29 | 30 | In addition, Asterisk implements several management/control protocols. 31 | This includes the Asterisk Manager Interface (AMI), the Asterisk Gateway 32 | Interface (AGI), and the Asterisk REST Interface (ARI). It is our belief 33 | that applications using these protocols to manage or control an Asterisk 34 | instance do not have to be licensed under the GPL or a compatible license, 35 | as we believe these protocols do not create a 'derivative work' as referred 36 | to in the GPL. However, should any court or other judiciary body find that 37 | these protocols do fall under the terms of the GPL, then we hereby grant you a 38 | license to use these protocols in combination with Asterisk in external 39 | applications licensed under any license you wish. 40 | 41 | The 'Asterisk' name and logos are trademarks owned by Digium, Inc., 42 | and use of them is subject to our trademark licensing policies. If you 43 | wish to use these trademarks for purposes other than simple 44 | redistribution of Asterisk source code obtained from Digium, you 45 | should contact our licensing department to determine the necessary 46 | steps you must take. For more information on this policy, please read: 47 | 48 | http://www.digium.com/en/company/profile/trademarkpolicy.php 49 | 50 | If you have any questions regarding our licensing policy, please 51 | contact us: 52 | 53 | +1.877.344.4861 (via telephone in the USA) 54 | +1.256.428.6000 (via telephone outside the USA) 55 | +1.256.864.0464 (via FAX inside or outside the USA) 56 | IAX2/pbx.digium.com (via IAX2) 57 | licensing@digium.com (via email) 58 | 59 | Digium, Inc. 60 | 445 Jan Davis Drive 61 | Huntsville, AL 35806 62 | USA 63 | -------------------------------------------------------------------------------- /web/FlashPhone.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 13 | 14 | 15 | 16 | 17 | 23 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 61 | 62 | 63 | 67 |
68 |

69 | To view this page ensure that Adobe Flash Player version 70 | 0.0.0 or greater is installed. 71 |

72 | 77 |
78 | 79 | 107 | 108 | 109 | -------------------------------------------------------------------------------- /src/flvtools.h: -------------------------------------------------------------------------------- 1 | /*! \file 2 | * 3 | * \brief Tools to manage FLV format file managment 4 | * 5 | * \author JYG 6 | * 7 | */ 8 | 9 | 10 | 11 | 12 | #ifndef _FLVTOOLS_H_ 13 | #define _FLVTOOLS_H_ 14 | 15 | #include "asterisk.h" 16 | #include "asterisk/frame.h" 17 | #include "asterisk/utils.h" 18 | #include "asterisk/paths.h" 19 | 20 | /*** Marcos Rev02 ***/ 21 | #define AST_4 10400 22 | #define AST_6 10600 23 | #define AST_6_1 10601 24 | #define AST_8 10800 25 | #define AST_11 110000 26 | #define AST_12 120000 27 | 28 | /********************/ 29 | 30 | /* 31 | * Define for FLV file structure 32 | */ 33 | #define FLV_HEADER_SIZE 9 34 | #define FLV_HEADER_TAG_SIZE 11 35 | #define FLV_SIZE_DOUBLE 9 // type+64bits 36 | 37 | /* 38 | * FLV define 39 | */ 40 | 41 | #define FLV_TYPE_AUDIO 0x08 42 | #define FLV_TYPE_VIDEO 0x09 43 | #define FLV_TYPE_META 0x12 44 | 45 | #define FLV_SAMPLESIZE_8BITS 8 46 | #define FLV_SAMPLESIZE_16BITS 16 47 | 48 | 49 | /* offsets for packed values */ 50 | #define FLV_AUDIO_SAMPLESSIZE_OFFSET 1 51 | #define FLV_AUDIO_SAMPLERATE_OFFSET 2 52 | #define FLV_AUDIO_CODECID_OFFSET 4 53 | 54 | #define FLV_VIDEO_FRAMETYPE_OFFSET 4 55 | 56 | /* bitmasks to isolate specific values */ 57 | #define FLV_AUDIO_CHANNEL_MASK 0x01 58 | #define FLV_AUDIO_SAMPLESIZE_MASK 0x02 59 | #define FLV_AUDIO_SAMPLERATE_MASK 0x0c 60 | #define FLV_AUDIO_CODECID_MASK 0xf0 61 | 62 | #define FLV_VIDEO_CODECID_MASK 0x0f 63 | #define FLV_VIDEO_FRAMETYPE_MASK 0xf0 64 | 65 | 66 | 67 | enum { 68 | FLV_HEADER_FLAG_HASVIDEO = 1, 69 | FLV_HEADER_FLAG_HASAUDIO = 4, 70 | }; 71 | 72 | 73 | enum { 74 | FLV_MONO = 0, 75 | FLV_STEREO = 1, 76 | }; 77 | 78 | enum { 79 | FLV_SAMPLESSIZE_8BIT = 0, 80 | FLV_SAMPLESSIZE_16BIT = 1 << FLV_AUDIO_SAMPLESSIZE_OFFSET, 81 | }; 82 | 83 | enum { 84 | FLV_SAMPLERATE_SPECIAL = 0, /**< signifies 5512Hz and 8000Hz in the case of NELLYMOSER */ 85 | FLV_SAMPLERATE_11025HZ = 1 << FLV_AUDIO_SAMPLERATE_OFFSET, 86 | FLV_SAMPLERATE_22050HZ = 2 << FLV_AUDIO_SAMPLERATE_OFFSET, 87 | FLV_SAMPLERATE_44100HZ = 3 << FLV_AUDIO_SAMPLERATE_OFFSET, 88 | }; 89 | 90 | enum { 91 | FLV_CODECID_PCM = 0, 92 | FLV_CODECID_ADPCM = 1 << FLV_AUDIO_CODECID_OFFSET, 93 | FLV_CODECID_MP3 = 2 << FLV_AUDIO_CODECID_OFFSET, 94 | FLV_CODECID_PCM_LE = 3 << FLV_AUDIO_CODECID_OFFSET, 95 | FLV_CODECID_NELLYMOSER_16KHZ_MONO = 4 << FLV_AUDIO_CODECID_OFFSET, 96 | FLV_CODECID_NELLYMOSER_8KHZ_MONO = 5 << FLV_AUDIO_CODECID_OFFSET, 97 | FLV_CODECID_NELLYMOSER = 6 << FLV_AUDIO_CODECID_OFFSET, 98 | FLV_CODECID_G711_ALAW = 7 << FLV_AUDIO_CODECID_OFFSET, 99 | FLV_CODECID_G711_ULAW = 8 << FLV_AUDIO_CODECID_OFFSET, 100 | FLV_CODECID_RESERVED = 9 << FLV_AUDIO_CODECID_OFFSET, 101 | FLV_CODECID_AAC = 10<< FLV_AUDIO_CODECID_OFFSET, 102 | FLV_CODECID_SPEEX = 11<< FLV_AUDIO_CODECID_OFFSET, 103 | }; 104 | 105 | enum { 106 | FLV_CODECID_H263 = 2, 107 | FLV_CODECID_SCREEN = 3, 108 | FLV_CODECID_VP6 = 4, 109 | FLV_CODECID_VP6A = 5, 110 | FLV_CODECID_SCREEN2 = 6, 111 | FLV_CODECID_H264 = 7, 112 | }; 113 | 114 | enum { 115 | FLV_FRAME_KEY = 1 << FLV_VIDEO_FRAMETYPE_OFFSET, 116 | FLV_FRAME_INTER = 2 << FLV_VIDEO_FRAMETYPE_OFFSET, 117 | FLV_FRAME_DISP_INTER = 3 << FLV_VIDEO_FRAMETYPE_OFFSET, 118 | }; 119 | enum { 120 | FLV_FRAMETYPE_KEY = 1, // keyframe (for AVC, a seekable frame) 121 | FLV_FRAMETYPE_INTER, // inter frame (for AVC, a nonseekable frame) 122 | FLV_FRAMETYPE_DISP_INTER, // disposable inter frame (H.263 only) 123 | FLV_FRAMETYPE_GENERATED_KEY, // generated keyframe (reserved for server use only) 124 | FLV_FRAMETYPE_VIDEOINFO, // video info/command frame 125 | }; 126 | 127 | 128 | typedef enum { 129 | AMF_DATA_TYPE_NUMBER = 0x00, 130 | AMF_DATA_TYPE_BOOL = 0x01, 131 | AMF_DATA_TYPE_STRING = 0x02, 132 | AMF_DATA_TYPE_OBJECT = 0x03, 133 | AMF_DATA_TYPE_NULL = 0x05, 134 | AMF_DATA_TYPE_UNDEFINED = 0x06, 135 | AMF_DATA_TYPE_REFERENCE = 0x07, 136 | AMF_DATA_TYPE_MIXEDARRAY = 0x08, 137 | AMF_DATA_TYPE_OBJECT_END = 0x09, 138 | AMF_DATA_TYPE_ARRAY = 0x0a, 139 | AMF_DATA_TYPE_DATE = 0x0b, 140 | AMF_DATA_TYPE_LONG_STRING = 0x0c, 141 | AMF_DATA_TYPE_UNSUPPORTED = 0x0d, 142 | } AMFDataType; 143 | 144 | 145 | // Return code 146 | typedef enum { 147 | FLV_ERROR_FD = -100, 148 | FLV_ERROR_WRITING, 149 | FLV_ERROR_WRITING_HDR, 150 | FLV_ERROR_DATA, 151 | FLV_ERROR_BAD_TYPE, 152 | FLV_ERROR_BAD_AUDIO_CODECID, 153 | FLV_ERROR_BAD_SAMPLERATE, 154 | FLV_ERROR_FILENAME, 155 | 156 | FLV_ERROR = -1, 157 | FLV_OK = 0, 158 | FLV_ERROR_LAST 159 | } eFlvReturnCode; 160 | 161 | 162 | /* 163 | * Data structure needed by FLV module 164 | */ 165 | typedef struct { 166 | int fd; // file descriptor for file 167 | uint8_t audio_tag; 168 | uint8_t video_tag; 169 | uint8_t meta_tag; 170 | int fileSizeOffset; 171 | int filesize; 172 | int durationOffset; 173 | int audioDataSize; 174 | int sampleRate; 175 | int sampleSize; 176 | int vcodec; 177 | int acodec; 178 | int stereo; 179 | int pictureSizeGetted; 180 | int widthOffset; 181 | int heigthOffset; 182 | } stFLV_data; 183 | 184 | #ifdef RTMP_FFMPEG 185 | /* 186 | * Prototyp definition 187 | */ 188 | #if ASTERISK_VERSION_NUM < AST_11 189 | eFlvReturnCode FLV_init(stFLV_data *pCtx, int channels, int sampleSize, int sampleRate, int codec, 190 | int videoFrameType, char *pFilename); 191 | #else 192 | eFlvReturnCode FLV_init(stFLV_data *pCtx, int channels, int sampleSize, int sampleRate, struct ast_format codec, 193 | int videoFrameType, char *pFilename); 194 | #endif 195 | eFlvReturnCode FLV_close(stFLV_data *pCtx, long iDuration, int iWidth, int iHeigth); 196 | eFlvReturnCode FLV_writeHeader(stFLV_data *pCtx); 197 | eFlvReturnCode FLV_writePkt(stFLV_data *pCtx, int type, int timestamp, int len, uint8_t *pData); 198 | eFlvReturnCode FLV_getPictureSize(int *pWidth, int *pHeight, uint8_t *pData) ; 199 | int FLV_getPictureType(uint8_t *pData) ; 200 | 201 | int writeBmpImage(FILE *fp, unsigned char *buff, int len, int width, int height) ; 202 | void put_jpeg_yuv420p_file(FILE *fp, unsigned char *image[3], int width, int height, int quality); 203 | #endif 204 | 205 | #endif // _FLVTOOLS_H_ 206 | 207 | -------------------------------------------------------------------------------- /web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 13 | 14 | 15 | 16 | 17 | 23 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 115 | 116 | 117 | 121 |
122 |

123 | To view this page ensure that Adobe Flash Player version 124 | 0.0.0 or greater is installed. 125 |

126 | 131 |
132 | 133 | 156 | 157 | 158 | -------------------------------------------------------------------------------- /web/custom_phone.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 31 | 44 | 45 | 46 | 47 | 48 | 199 | 200 | 201 | 202 | -------------------------------------------------------------------------------- /web/AC_OETags.js: -------------------------------------------------------------------------------- 1 | // Flash Player Version Detection - Rev 1.6 2 | // Detect Client Browser type 3 | // Copyright(c) 2005-2006 Adobe Macromedia Software, LLC. All rights reserved. 4 | var isIE = (navigator.appVersion.indexOf("MSIE") != -1) ? true : false; 5 | var isWin = (navigator.appVersion.toLowerCase().indexOf("win") != -1) ? true : false; 6 | var isOpera = (navigator.userAgent.indexOf("Opera") != -1) ? true : false; 7 | 8 | function ControlVersion() 9 | { 10 | var version; 11 | var axo; 12 | var e; 13 | 14 | // NOTE : new ActiveXObject(strFoo) throws an exception if strFoo isn't in the registry 15 | 16 | try { 17 | // version will be set for 7.X or greater players 18 | axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.7"); 19 | version = axo.GetVariable("$version"); 20 | } catch (e) { 21 | } 22 | 23 | if (!version) 24 | { 25 | try { 26 | // version will be set for 6.X players only 27 | axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.6"); 28 | 29 | // installed player is some revision of 6.0 30 | // GetVariable("$version") crashes for versions 6.0.22 through 6.0.29, 31 | // so we have to be careful. 32 | 33 | // default to the first public version 34 | version = "WIN 6,0,21,0"; 35 | 36 | // throws if AllowScripAccess does not exist (introduced in 6.0r47) 37 | axo.AllowScriptAccess = "always"; 38 | 39 | // safe to call for 6.0r47 or greater 40 | version = axo.GetVariable("$version"); 41 | 42 | } catch (e) { 43 | } 44 | } 45 | 46 | if (!version) 47 | { 48 | try { 49 | // version will be set for 4.X or 5.X player 50 | axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.3"); 51 | version = axo.GetVariable("$version"); 52 | } catch (e) { 53 | } 54 | } 55 | 56 | if (!version) 57 | { 58 | try { 59 | // version will be set for 3.X player 60 | axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.3"); 61 | version = "WIN 3,0,18,0"; 62 | } catch (e) { 63 | } 64 | } 65 | 66 | if (!version) 67 | { 68 | try { 69 | // version will be set for 2.X player 70 | axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash"); 71 | version = "WIN 2,0,0,11"; 72 | } catch (e) { 73 | version = -1; 74 | } 75 | } 76 | 77 | return version; 78 | } 79 | 80 | // JavaScript helper required to detect Flash Player PlugIn version information 81 | function GetSwfVer(){ 82 | // NS/Opera version >= 3 check for Flash plugin in plugin array 83 | var flashVer = -1; 84 | 85 | if (navigator.plugins != null && navigator.plugins.length > 0) { 86 | if (navigator.plugins["Shockwave Flash 2.0"] || navigator.plugins["Shockwave Flash"]) { 87 | var swVer2 = navigator.plugins["Shockwave Flash 2.0"] ? " 2.0" : ""; 88 | var flashDescription = navigator.plugins["Shockwave Flash" + swVer2].description; 89 | var descArray = flashDescription.split(" "); 90 | var tempArrayMajor = descArray[2].split("."); 91 | var versionMajor = tempArrayMajor[0]; 92 | var versionMinor = tempArrayMajor[1]; 93 | var versionRevision = descArray[3]; 94 | if (versionRevision == "") { 95 | versionRevision = descArray[4]; 96 | } 97 | if (versionRevision[0] == "d") { 98 | versionRevision = versionRevision.substring(1); 99 | } else if (versionRevision[0] == "r") { 100 | versionRevision = versionRevision.substring(1); 101 | if (versionRevision.indexOf("d") > 0) { 102 | versionRevision = versionRevision.substring(0, versionRevision.indexOf("d")); 103 | } 104 | } else if (versionRevision[0] == "b") { 105 | versionRevision = versionRevision.substring(1); 106 | } 107 | var flashVer = versionMajor + "." + versionMinor + "." + versionRevision; 108 | } 109 | } 110 | // MSN/WebTV 2.6 supports Flash 4 111 | else if (navigator.userAgent.toLowerCase().indexOf("webtv/2.6") != -1) flashVer = 4; 112 | // WebTV 2.5 supports Flash 3 113 | else if (navigator.userAgent.toLowerCase().indexOf("webtv/2.5") != -1) flashVer = 3; 114 | // older WebTV supports Flash 2 115 | else if (navigator.userAgent.toLowerCase().indexOf("webtv") != -1) flashVer = 2; 116 | else if ( isIE && isWin && !isOpera ) { 117 | flashVer = ControlVersion(); 118 | } 119 | return flashVer; 120 | } 121 | 122 | // When called with reqMajorVer, reqMinorVer, reqRevision returns true if that version or greater is available 123 | function DetectFlashVer(reqMajorVer, reqMinorVer, reqRevision) 124 | { 125 | versionStr = GetSwfVer(); 126 | if (versionStr == -1 ) { 127 | return false; 128 | } else if (versionStr != 0) { 129 | if(isIE && isWin && !isOpera) { 130 | // Given "WIN 2,0,0,11" 131 | tempArray = versionStr.split(" "); // ["WIN", "2,0,0,11"] 132 | tempString = tempArray[1]; // "2,0,0,11" 133 | versionArray = tempString.split(","); // ['2', '0', '0', '11'] 134 | } else { 135 | versionArray = versionStr.split("."); 136 | } 137 | var versionMajor = versionArray[0]; 138 | var versionMinor = versionArray[1]; 139 | var versionRevision = versionArray[2]; 140 | 141 | // is the major.revision >= requested major.revision AND the minor version >= requested minor 142 | if (versionMajor > parseFloat(reqMajorVer)) { 143 | return true; 144 | } else if (versionMajor == parseFloat(reqMajorVer)) { 145 | if (versionMinor > parseFloat(reqMinorVer)) 146 | return true; 147 | else if (versionMinor == parseFloat(reqMinorVer)) { 148 | if (versionRevision >= parseFloat(reqRevision)) 149 | return true; 150 | } 151 | } 152 | return false; 153 | } 154 | } 155 | 156 | function AC_AddExtension(src, ext) 157 | { 158 | var qIndex = src.indexOf('?'); 159 | if ( qIndex != -1) 160 | { 161 | // Add the extention (if needed) before the query params 162 | var path = src.substring(0, qIndex); 163 | if (path.length >= ext.length && path.lastIndexOf(ext) == (path.length - ext.length)) 164 | return src; 165 | else 166 | return src.replace(/\?/, ext+'?'); 167 | } 168 | else 169 | { 170 | // Add the extension (if needed) to the end of the URL 171 | if (src.length >= ext.length && src.lastIndexOf(ext) == (src.length - ext.length)) 172 | return src; // Already have extension 173 | else 174 | return src + ext; 175 | } 176 | } 177 | 178 | function AC_Generateobj(objAttrs, params, embedAttrs) 179 | { 180 | var str = ''; 181 | if (isIE && isWin && !isOpera) 182 | { 183 | str += ' '; 189 | str += ''; 190 | } else { 191 | str += ' 8 | * 9 | * See http://www.asterisk.org for more information about 10 | * the Asterisk project. Please do not directly contact 11 | * any of the maintainers of this project for assistance; 12 | * the project provides a web site, mailing lists and IRC 13 | * channels for your use. 14 | * 15 | * This program is free software, distributed under the terms of 16 | * the GNU General Public License Version 2. See the LICENSE file 17 | * at the top of the source tree. 18 | */ 19 | 20 | /*! \file 21 | * 22 | * \brief RTMP (Adobe's Flash client) support 23 | * 24 | * \author Philippe Sultan 25 | * 26 | * \ingroup channel_drivers 27 | */ 28 | 29 | 30 | #define CLIENT_TYPE_FLASH 1 31 | #define CLIENT_TYPE_ANDROID 2 32 | #define CLIENT_TYPE_IOS 3 33 | 34 | 35 | 36 | #define JITTER_SIZE 160 37 | 38 | #define RTMP_FEATURE_HTTP 0x01 39 | #define RTMP_FEATURE_ENC 0x02 40 | #define RTMP_FEATURE_SSL 0x04 41 | #define RTMP_FEATURE_MFP 0x08 42 | #define RTMP_FEATURE_WRITE 0x10 43 | #define RTMP_FEATURE_HTTP2 0x20 44 | 45 | #define RTMP_INCOMING 0 46 | #define RTMP_OUTGOING 1 47 | 48 | #define RTMPBUFSIZE 512 49 | #define RTMP_BLOCK_SIZE 1536 50 | //#define RTMP_RECV_BUFSIZE 2*RTMP_BLOCK_SIZE + 1 /* should match with RTMP server */ 51 | #define RTMP_RECV_BUFSIZE 271360 52 | #define RTMP_DEFAULT_PORT 1935 53 | #define RTMP_WINDOW_SIZE 131072 54 | #define RTMP_CHUNK_SIZE 128 55 | //#define RTMP_MAX_BODYSIZE 16777215 /* 0xFFFFFF */ 56 | //#define RTMP_MAX_BODYSIZE 65535 /* 0xFFFF */ 57 | //#define RTMP_MAX_BODYSIZE 1048575 /* 0xFFFFF */ 58 | #define RTMP_MAX_BODYSIZE 200 /* 0xFFFFF */ 59 | #define RTMP_MAX_CHANNELS 64 60 | #define RTMP_STREAM_CHANNEL_RANGE 5 61 | #define RTMP_MAX_STREAMS 12 62 | 63 | #define RTMP_EXTENDEDTIMESTAMP_SIZE 4 64 | 65 | #define RTMP_CHANNEL_SYSTEM 2 66 | #define RTMP_CHANNEL_CONNECT 3 67 | #define RTMP_CHANNEL_DATA 0 68 | #define RTMP_CHANNEL_PUBLISH 4 69 | #define RTMP_CHANNEL_LOCAL 5 70 | #define RTMP_CHANNEL_VIDEO 6 71 | #define RTMP_CHANNEL_UNKNOWN 3 72 | #define RTMP_CHANNEL_CONTROL 4 73 | 74 | #define RTMP_PING_DEFAULTBODYSIZE 6 75 | #define RTMP_PING_TYPE_CLEAR 0x01 /* clear stream */ 76 | #define RTMP_PING_TYPE_PLAY 0x02 /* clear playing buffer */ 77 | #define RTMP_PING_TYPE_TIME 0x03 /* buffer time in milliseconds */ 78 | #define RTMP_PING_TYPE_RESET 0x04 /* reset stream */ 79 | #define RTMP_PING_TYPE_PING 0x06 80 | #define RTMP_PING_TYPE_PONG 0x07 81 | 82 | 83 | #define RTMP_TYPE_CHUNK_SIZE 0x01 84 | #define RTMP_TYPE_BYTES_READ 0x03 85 | #define RTMP_TYPE_PING 0x04 86 | #define RTMP_TYPE_SERVER_BANDWIDTH 0x05 87 | #define RTMP_TYPE_CLIENT_BANDWIDTH 0x06 88 | #define RTMP_TYPE_AUDIO_DATA 0x08 89 | #define RTMP_TYPE_VIDEO_DATA 0x09 90 | #define RTMP_TYPE_FLEX_STREAM_SEND 0x0F 91 | #define RTMP_TYPE_FLEX_SHARED_OBJECT 0x10 92 | #define RTMP_TYPE_FLEX_MESSAGE 0x11 93 | #define RTMP_TYPE_NOTIFY 0x12 94 | #define RTMP_TYPE_SHARED_OBJECT 0x13 95 | #define RTMP_TYPE_INVOKE 0x14 96 | #define RTMP_TYPE_UNKNOWN 0x15 97 | 98 | 99 | #define AMF_TYPE_NUMBER 0x00 100 | #define AMF_TYPE_BOOLEAN 0x01 101 | #define AMF_TYPE_STRING 0x02 102 | #define AMF_TYPE_OBJECT 0x03 103 | #define AMF_TYPE_MOVIECLIP 0x04 104 | #define AMF_TYPE_NULL 0x05 105 | #define AMF_TYPE_UNDEFINED 0x06 106 | #define AMF_TYPE_REFERENCE 0x07 107 | #define AMF_TYPE_MIXED_ARRAY 0x08 108 | #define AMF_TYPE_OBJECT_END 0x09 109 | #define AMF_TYPE_ARRAY 0x0A 110 | #define AMF_TYPE_DATE 0x0B 111 | #define AMF_TYPE_LONG_STRING 0x0C 112 | #define AMF_TYPE_UNSUPPORTED 0x0D 113 | #define AMF_TYPE_RECORDSET 0x0E 114 | #define AMF_TYPE_XML 0x0F 115 | #define AMF_TYPE_CLASS_OBJECT 0x10 116 | #define AMF_TYPE_AMF3_OBJECT 0x11 117 | 118 | #define AMF_BOOLEAN_FALSE "\0" 119 | #define AMF_BOOLEAN_TRUE "\1" 120 | 121 | #define RTMP_AUDIO_CODEC_LINEAR 0x1 122 | #define RTMP_AUDIO_CODEC_ALAW 0x80 123 | #define RTMP_AUDIO_CODEC_MULAW 0x100 124 | #define RTMP_AUDIO_CODEC_SPEEX 0x800 125 | #define RTMP_AUDIO_CODEC_SUPPORTED (RTMP_AUDIO_CODEC_LINEAR | RTMP_AUDIO_CODEC_ALAW | RTMP_AUDIO_CODEC_MULAW | RTMP_AUDIO_CODEC_SPEEX) 126 | 127 | #define RTMP_VIDEO_CODEC_SORENSON 0x4 128 | #define RTMP_VIDEO_CODEC_H264 0x80 129 | #define RTMP_VIDEO_CODEC_SUPPORTED RTMP_VIDEO_CODEC_SORENSON | RTMP_VIDEO_CODEC_H264 130 | 131 | 132 | #define FLV_AUDIO_CODEC_ADPCM 1 133 | #define FLV_AUDIO_CODEC_MP3 2 134 | #define FLV_AUDIO_CODEC_PCMS16le 3 135 | #define FLV_AUDIO_CODEC_NELLYMOSER_16KHZ_MONO 4 136 | #define FLV_AUDIO_CODEC_NELLYMOSER_8KHZ_MONO 5 137 | #define FLV_AUDIO_CODEC_NELLYMOSER 6 138 | #define FLV_AUDIO_CODEC_G711_ALAW 7 139 | #define FLV_AUDIO_CODEC_G711_MULAW 8 140 | #define FLV_AUDIO_CODEC_ASTERISK_SLIN 9 141 | #define FLV_AUDIO_CODEC_AAC 10 142 | #define FLV_AUDIO_CODEC_SPEEX 11 143 | #define FLV_AUDIO_CODEC_MP3_8KHZ 14 144 | #define FLV_AUDIO_CODEC_DEVICE_SPECIFIC 15 145 | 146 | 147 | #define ADMIN_CMD_GETQOS "getqos:" 148 | #define ADMIN_CMD_GETQOS_LG 7 149 | #define ADMIN_CMD_GETVERSION "getversion:" 150 | #define ADMIN_CMD_GETVERSION_LG 11 151 | #define ADMIN_CMD_PING "ping:" 152 | #define ADMIN_CMD_PING_LG 5 153 | 154 | #define ADMIN_RESP_GETQOS "respGetQos:" 155 | #define ADMIN_RESP_GETQOS_LG 11 156 | #define ADMIN_RESP_GETCFG "respGetCfg:" 157 | #define ADMIN_RESP_GETCFG_LG 11 158 | #define ADMIN_RESP_GETVERSION "respGetVersion:" 159 | #define ADMIN_RESP_GETVERSION_LG 15 160 | #define ADMIN_RESP_PONG "pong:" 161 | #define ADMIN_RESP_PONG_LG 5 162 | 163 | 164 | struct rtmp_channel { 165 | uint8_t channelid; /* the RTMP channel id (64 bits long) */ 166 | uint8_t hdrlen[2]; 167 | uint32_t timestamp[2]; 168 | uint32_t bodylen[2]; 169 | uint8_t type[2]; 170 | uint32_t streamid[2]; /* the stream this channel is part of */ 171 | 172 | int isactive; /* is this channel active? */ 173 | }; 174 | 175 | struct rtmp_message { 176 | uint8_t hdrlen; /* 1,2,4 or 12 */ 177 | uint8_t channelid; /* less than 64 */ 178 | uint32_t timestamp; /* three bytes long */ 179 | uint32_t timestampdelta; /* three bytes long */ 180 | uint32_t bodyalloc; /* three bytes long */ 181 | uint32_t bodysize; /* three bytes long */ 182 | uint8_t type; 183 | uint32_t streamid; /* four bytes long */ 184 | void *body; /* bytes after header */ 185 | 186 | int bytesread; /* the number of parsed bytes */ 187 | }; 188 | 189 | struct amf_hdr { 190 | uint8_t hdrlen; /* 1,2,4 or 12 */ 191 | uint8_t type; 192 | uint8_t objectid; /* less than 64 */ 193 | }; 194 | 195 | struct amf_basic_object { 196 | uint8_t type; 197 | uint16_t length; /* used if the object is a string */ 198 | char *property; 199 | void *value; 200 | struct amf_basic_object *next; 201 | }; 202 | 203 | struct amf_object { 204 | struct amf_basic_object *bobject; 205 | unsigned int size; 206 | }; 207 | 208 | enum rtmp_state { 209 | RTMP_DISCONNECTING, 210 | RTMP_DISCONNECTED, 211 | RTMP_CONNECTING, 212 | RTMP_HANDSHAKE_OK, 213 | RTMP_CONNECTED 214 | }; 215 | 216 | enum rtmp_reply { 217 | RTMP_REPLY_RESULT, 218 | 219 | RTMP_REPLY_CONNECT, 220 | RTMP_REPLY_INITSTREAM, 221 | RTMP_REPLY_RELEASESTREAM, 222 | RTMP_REPLY_CREATESTREAM, 223 | RTMP_REPLY_DELETESTREAM, 224 | RTMP_REPLY_INVITE, 225 | RTMP_REPLY_ACCEPT, 226 | RTMP_REPLY_REJECT, 227 | RTMP_REPLY_BYE, 228 | 229 | RTMP_REPLY_PUBLISH, 230 | RTMP_REPLY_PLAY, 231 | RTMP_REPLY_CLOSESTREAM, 232 | 233 | RTMP_REPLY_DTMF, 234 | RTMP_REPLY_TEXT, 235 | 236 | RTMP_NOREPLY, 237 | }; 238 | 239 | enum rtmp_pipe { 240 | RTMP_PIPE_NULL, 241 | RTMP_PIPE_MARK, 242 | RTMP_PIPE_AUDIO_NELLYMOSER, 243 | RTMP_PIPE_AUDIO_SPEEX, 244 | RTMP_PIPE_AUDIO_SLINEAR, 245 | RTMP_PIPE_AUDIO_ULAW, 246 | RTMP_PIPE_AUDIO_ALAW, 247 | RTMP_PIPE_VIDEO_SORENSON, 248 | RTMP_PIPE_VIDEO_SORENSON_MARK, 249 | RTMP_PIPE_VIDEO_H264, 250 | RTMP_PIPE_VIDEO_H264_MARK, 251 | RTMP_PIPE_DTMF, 252 | RTMP_PIPE_TEXT, 253 | RTMP_PIPE_EVENT, 254 | }; 255 | 256 | enum amf_parser { 257 | AMF_PARSE_TYPE, 258 | AMF_PARSE_STRINGLEN, 259 | AMF_PARSE_STRINGVAL, 260 | AMF_PARSE_DOUBLE_RESULT, 261 | AMF_PARSE_PROPERTY_LEVEL, 262 | AMF_PARSE_PROPERTY_CODE, 263 | AMF_PARSE_PROPERTY_DESCRIPTION, 264 | AMF_PARSE_STRING_VALUE, 265 | AMF_PARSE_NUMBER, 266 | AMF_PARSE_BOOLEAN, 267 | 268 | AMF_PARSE_ID, 269 | AMF_PARSE_APP, 270 | AMF_PARSE_FLASHVER, 271 | AMF_PARSE_SWFURL, 272 | AMF_PARSE_TCURL, 273 | AMF_PARSE_FPAD, 274 | AMF_PARSE_CAPABILITIES, 275 | AMF_PARSE_AUDIOCODECS, 276 | AMF_PARSE_VIDEOCODECS, 277 | AMF_PARSE_VIDEOFUNCTION, 278 | AMF_PARSE_PAGEURL, 279 | AMF_PARSE_OBJECTENCODING, 280 | 281 | AMF_PARSE_RESULT, 282 | 283 | AMF_PARSE_CREATESTREAM, 284 | AMF_PARSE_DELETESTREAM, 285 | 286 | AMF_PARSE_INVITE, 287 | AMF_PARSE_ACCEPT, 288 | AMF_PARSE_REJECT, 289 | AMF_PARSE_BYE, 290 | 291 | AMF_PARSE_PUBLISH, 292 | AMF_PARSE_PLAY, 293 | AMF_PARSE_CLOSESTREAM, 294 | }; 295 | -------------------------------------------------------------------------------- /src/keys.h: -------------------------------------------------------------------------------- 1 | /* RTMPDump - Diffie-Hellmann Key Exchange 2 | * Copyright (C) 2009 Andrej Stepanchuk 3 | * Copyright (C) 2009-2010 Howard Chu 4 | * 5 | * This file is part of librtmp. 6 | * 7 | * librtmp is free software; you can redistribute it and/or modify 8 | * it under the terms of the GNU Lesser General Public License as 9 | * published by the Free Software Foundation; either version 2.1, 10 | * or (at your option) any later version. 11 | * 12 | * librtmp is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Lesser General Public License 18 | * along with librtmp see the file COPYING. If not, write to 19 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 20 | * Boston, MA 02110-1301, USA. 21 | * http://www.gnu.org/copyleft/lgpl.html 22 | */ 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | 31 | #ifdef USE_POLARSSL 32 | 33 | #include 34 | typedef mpi * MP_t; 35 | #define MP_new(m) m = malloc(sizeof(mpi)); mpi_init(m, NULL) 36 | #define MP_set_w(mpi, w) mpi_lset(mpi, w) 37 | #define MP_cmp(u, v) mpi_cmp_mpi(u, v) 38 | #define MP_set(u, v) mpi_copy(u, v) 39 | #define MP_sub_w(mpi, w) mpi_sub_int(mpi, mpi, w) 40 | #define MP_cmp_1(mpi) mpi_cmp_int(mpi, 1) 41 | #define MP_modexp(r, y, q, p) mpi_exp_mod(r, y, q, p, NULL) 42 | #define MP_free(mpi) mpi_free(mpi, NULL); free(mpi) 43 | #define MP_gethex(u, hex, res) MP_new(u); res = mpi_read_string(u, 16, hex) == 0 44 | #define MP_bytes(u) mpi_size(u) 45 | #define MP_setbin(u,buf,len) mpi_write_binary(u,buf,len) 46 | #define MP_getbin(u,buf,len) MP_new(u); mpi_read_binary(u,buf,len) 47 | 48 | typedef struct MDH { 49 | MP_t p; 50 | MP_t g; 51 | MP_t pub_key; 52 | MP_t priv_key; 53 | long length; 54 | dhm_context ctx; 55 | } MDH; 56 | 57 | #define MDH_new() calloc(1,sizeof(MDH)) 58 | #define MDH_free(vp) {MDH *dh = vp; dhm_free(&dh->ctx); MP_free(dh->p); MP_free(dh->g); MP_free(dh->pub_key); MP_free(dh->priv_key); free(dh);} 59 | 60 | static int MDH_generate_key(MDH *dh) 61 | { 62 | unsigned char out[2]; 63 | MP_set(&dh->ctx.P, dh->p); 64 | MP_set(&dh->ctx.G, dh->g); 65 | dh->ctx.len = 128; 66 | dhm_make_public(&dh->ctx, 1024, out, 1, havege_rand, &RTMP_TLS_ctx->hs); 67 | MP_new(dh->pub_key); 68 | MP_new(dh->priv_key); 69 | MP_set(dh->pub_key, &dh->ctx.GX); 70 | MP_set(dh->priv_key, &dh->ctx.X); 71 | return 1; 72 | } 73 | 74 | static int MDH_compute_key(uint8_t *secret, size_t len, MP_t pub, MDH *dh) 75 | { 76 | int n = len; 77 | MP_set(&dh->ctx.GY, pub); 78 | dhm_calc_secret(&dh->ctx, secret, &n); 79 | return 0; 80 | } 81 | 82 | #elif defined(USE_GNUTLS) 83 | 84 | #include 85 | typedef gcry_mpi_t MP_t; 86 | #define MP_new(m) m = gcry_mpi_new(1) 87 | #define MP_set_w(mpi, w) gcry_mpi_set_ui(mpi, w) 88 | #define MP_cmp(u, v) gcry_mpi_cmp(u, v) 89 | #define MP_set(u, v) gcry_mpi_set(u, v) 90 | #define MP_sub_w(mpi, w) gcry_mpi_sub_ui(mpi, mpi, w) 91 | #define MP_cmp_1(mpi) gcry_mpi_cmp_ui(mpi, 1) 92 | #define MP_modexp(r, y, q, p) gcry_mpi_powm(r, y, q, p) 93 | #define MP_free(mpi) gcry_mpi_release(mpi) 94 | #define MP_gethex(u, hex, res) res = (gcry_mpi_scan(&u, GCRYMPI_FMT_HEX, hex, 0, 0) == 0) 95 | #define MP_bytes(u) (gcry_mpi_get_nbits(u) + 7) / 8 96 | #define MP_setbin(u,buf,len) gcry_mpi_print(GCRYMPI_FMT_USG,buf,len,NULL,u) 97 | #define MP_getbin(u,buf,len) gcry_mpi_scan(&u,GCRYMPI_FMT_USG,buf,len,NULL) 98 | 99 | typedef struct MDH { 100 | MP_t p; 101 | MP_t g; 102 | MP_t pub_key; 103 | MP_t priv_key; 104 | long length; 105 | } MDH; 106 | 107 | #define MDH_new() calloc(1,sizeof(MDH)) 108 | #define MDH_free(dh) do {MP_free(((MDH*)(dh))->p); MP_free(((MDH*)(dh))->g); MP_free(((MDH*)(dh))->pub_key); MP_free(((MDH*)(dh))->priv_key); free(dh);} while(0) 109 | 110 | extern MP_t gnutls_calc_dh_secret(MP_t *priv, MP_t g, MP_t p); 111 | extern MP_t gnutls_calc_dh_key(MP_t y, MP_t x, MP_t p); 112 | 113 | #define MDH_generate_key(dh) (dh->pub_key = gnutls_calc_dh_secret(&dh->priv_key, dh->g, dh->p)) 114 | static int MDH_compute_key(uint8_t *secret, size_t len, MP_t pub, MDH *dh) 115 | { 116 | MP_t sec = gnutls_calc_dh_key(pub, dh->priv_key, dh->p); 117 | if (sec) 118 | { 119 | MP_setbin(sec, secret, len); 120 | MP_free(sec); 121 | return 0; 122 | } 123 | else 124 | return -1; 125 | } 126 | 127 | #else /* USE_OPENSSL */ 128 | 129 | #pragma "Compile with SSL" 130 | #include 131 | #include 132 | 133 | typedef BIGNUM * MP_t; 134 | #define MP_new(m) m = BN_new() 135 | #define MP_set_w(mpi, w) BN_set_word(mpi, w) 136 | #define MP_cmp(u, v) BN_cmp(u, v) 137 | #define MP_set(u, v) BN_copy(u, v) 138 | #define MP_sub_w(mpi, w) BN_sub_word(mpi, w) 139 | #define MP_cmp_1(mpi) BN_cmp(mpi, BN_value_one()) 140 | #define MP_modexp(r, y, q, p) do {BN_CTX *ctx = BN_CTX_new(); BN_mod_exp(r, y, q, p, ctx); BN_CTX_free(ctx);} while(0) 141 | #define MP_free(mpi) BN_free(mpi) 142 | #define MP_gethex(u, hex, res) res = BN_hex2bn(&u, hex) 143 | #define MP_bytes(u) BN_num_bytes(u) 144 | #define MP_setbin(u,buf,len) BN_bn2bin(u,buf) 145 | #define MP_getbin(u,buf,len) u = BN_bin2bn(buf,len,0) 146 | 147 | #define MDH DH 148 | #define MDH_new() DH_new() 149 | #define MDH_free(dh) DH_free(dh) 150 | #define MDH_generate_key(dh) DH_generate_key(dh) 151 | #define MDH_compute_key(secret, seclen, pub, dh) DH_compute_key(secret, pub, dh) 152 | 153 | 154 | #endif 155 | 156 | #include "dhgroups.h" 157 | 158 | extern int DHGenerateKey(MDH *dh); 159 | 160 | #define TRUE 1 161 | #define FALSE 0 162 | 163 | 164 | /* RFC 2631, Section 2.1.5, http://www.ietf.org/rfc/rfc2631.txt */ 165 | static int isValidPublicKey(MP_t y, MP_t p, MP_t q) 166 | { 167 | int ret = TRUE; 168 | MP_t bn; 169 | assert(y); 170 | 171 | MP_new(bn); 172 | assert(bn); 173 | 174 | /* y must lie in [2,p-1] */ 175 | MP_set_w(bn, 1); 176 | if (MP_cmp(y, bn) < 0) 177 | { 178 | ast_debug(8, "DH public key must be at least 2"); 179 | ret = FALSE; 180 | goto failed; 181 | } 182 | 183 | /* bn = p-2 */ 184 | MP_set(bn, p); 185 | MP_sub_w(bn, 1); 186 | if (MP_cmp(y, bn) > 0) 187 | { 188 | ast_debug(8, "DH public key must be at most p-2"); 189 | ret = FALSE; 190 | goto failed; 191 | } 192 | 193 | /* Verify with Sophie-Germain prime 194 | * 195 | * This is a nice test to make sure the public key position is calculated 196 | * correctly. This test will fail in about 50% of the cases if applied to 197 | * random data. 198 | */ 199 | if (q) 200 | { 201 | /* y must fulfill y^q mod p = 1 */ 202 | MP_modexp(bn, y, q, p); 203 | 204 | if (MP_cmp_1(bn) != 0) { 205 | ast_debug(0, "DH public key does not fulfill y^q mod p = 1"); 206 | } 207 | } 208 | 209 | failed: 210 | MP_free(bn); 211 | return ret; 212 | } 213 | 214 | static MDH *DHInit(int nKeyBits) 215 | { 216 | size_t res; 217 | MDH *dh = MDH_new(); 218 | 219 | if (!dh) { 220 | ast_verbose("DHInit: failed to get DH ctx!\n"); 221 | goto failed; 222 | } 223 | 224 | MP_new(dh->g); 225 | 226 | if (!dh->g){ 227 | ast_verbose("DHInit: failed to get DH G ctx!\n"); 228 | goto failed; 229 | } 230 | 231 | MP_gethex(dh->p, P1024, res); /* prime P1024, see dhgroups.h */ 232 | if (!res) 233 | { 234 | ast_verbose("DHInit: get P1024 failed %d\n", res); 235 | goto failed; 236 | } 237 | 238 | MP_set_w(dh->g, 2); /* base 2 */ 239 | 240 | dh->length = nKeyBits; 241 | return dh; 242 | 243 | failed: 244 | if (dh) 245 | MDH_free(dh); 246 | 247 | return 0; 248 | } 249 | 250 | int DHGenerateKey(MDH *dh) 251 | { 252 | size_t res = 0; 253 | if (!dh) 254 | return 0; 255 | 256 | while (!res) 257 | { 258 | MP_t q1 = NULL; 259 | 260 | if (!MDH_generate_key(dh)) 261 | return 0; 262 | 263 | MP_gethex(q1, Q1024, res); 264 | assert(res); 265 | 266 | res = isValidPublicKey(dh->pub_key, dh->p, q1); 267 | if (!res) 268 | { 269 | MP_free(dh->pub_key); 270 | MP_free(dh->priv_key); 271 | dh->pub_key = dh->priv_key = 0; 272 | } 273 | 274 | MP_free(q1); 275 | } 276 | return 1; 277 | } 278 | 279 | /* fill pubkey with the public key in BIG ENDIAN order 280 | * 00 00 00 00 00 x1 x2 x3 ..... 281 | */ 282 | 283 | static int DHGetPublicKey(MDH *dh, uint8_t *pubkey, size_t nPubkeyLen) 284 | { 285 | int len; 286 | if (!dh || !dh->pub_key) 287 | return 0; 288 | 289 | len = MP_bytes(dh->pub_key); 290 | if (len <= 0 || len > (int) nPubkeyLen) 291 | return 0; 292 | 293 | memset(pubkey, 0, nPubkeyLen); 294 | MP_setbin(dh->pub_key, pubkey + (nPubkeyLen - len), len); 295 | return 1; 296 | } 297 | 298 | 299 | /* computes the shared secret key from the private MDH value and the 300 | * other party's public key (pubkey) 301 | */ 302 | static int DHComputeSharedSecretKey(MDH *dh, uint8_t *pubkey, size_t nPubkeyLen, 303 | uint8_t *secret) 304 | { 305 | MP_t q1 = NULL, pubkeyBn = NULL; 306 | size_t len; 307 | int res; 308 | 309 | if (!dh || !secret || nPubkeyLen >= INT_MAX) 310 | return -1; 311 | 312 | MP_getbin(pubkeyBn, pubkey, nPubkeyLen); 313 | if (!pubkeyBn) 314 | return -1; 315 | 316 | MP_gethex(q1, Q1024, len); 317 | assert(len); 318 | 319 | if (isValidPublicKey(pubkeyBn, dh->p, q1)) 320 | res = MDH_compute_key(secret, nPubkeyLen, pubkeyBn, dh); 321 | else 322 | res = -1; 323 | 324 | MP_free(q1); 325 | MP_free(pubkeyBn); 326 | 327 | return res; 328 | } 329 | -------------------------------------------------------------------------------- /src/dhgroups.h: -------------------------------------------------------------------------------- 1 | /* librtmp - Diffie-Hellmann Key Exchange 2 | * Copyright (C) 2009 Andrej Stepanchuk 3 | * 4 | * This file is part of librtmp. 5 | * 6 | * librtmp is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU Lesser General Public License as 8 | * published by the Free Software Foundation; either version 2.1, 9 | * or (at your option) any later version. 10 | * 11 | * librtmp is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License 17 | * along with librtmp see the file COPYING. If not, write to 18 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 19 | * Boston, MA 02110-1301, USA. 20 | * http://www.gnu.org/copyleft/lgpl.html 21 | */ 22 | 23 | /* from RFC 3526, see http://www.ietf.org/rfc/rfc3526.txt */ 24 | 25 | /* 2^768 - 2 ^704 - 1 + 2^64 * { [2^638 pi] + 149686 } */ 26 | #define P768 \ 27 | "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \ 28 | "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \ 29 | "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \ 30 | "E485B576625E7EC6F44C42E9A63A3620FFFFFFFFFFFFFFFF" 31 | 32 | /* 2^1024 - 2^960 - 1 + 2^64 * { [2^894 pi] + 129093 } */ 33 | #define P1024 \ 34 | "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \ 35 | "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \ 36 | "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \ 37 | "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \ 38 | "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381" \ 39 | "FFFFFFFFFFFFFFFF" 40 | 41 | /* Group morder largest prime factor: */ 42 | #define Q1024 \ 43 | "7FFFFFFFFFFFFFFFE487ED5110B4611A62633145C06E0E68" \ 44 | "948127044533E63A0105DF531D89CD9128A5043CC71A026E" \ 45 | "F7CA8CD9E69D218D98158536F92F8A1BA7F09AB6B6A8E122" \ 46 | "F242DABB312F3F637A262174D31BF6B585FFAE5B7A035BF6" \ 47 | "F71C35FDAD44CFD2D74F9208BE258FF324943328F67329C0" \ 48 | "FFFFFFFFFFFFFFFF" 49 | 50 | /* 2^1536 - 2^1472 - 1 + 2^64 * { [2^1406 pi] + 741804 } */ 51 | #define P1536 \ 52 | "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \ 53 | "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \ 54 | "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \ 55 | "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \ 56 | "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" \ 57 | "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" \ 58 | "83655D23DCA3AD961C62F356208552BB9ED529077096966D" \ 59 | "670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF" 60 | 61 | /* 2^2048 - 2^1984 - 1 + 2^64 * { [2^1918 pi] + 124476 } */ 62 | #define P2048 \ 63 | "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \ 64 | "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \ 65 | "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \ 66 | "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \ 67 | "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" \ 68 | "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" \ 69 | "83655D23DCA3AD961C62F356208552BB9ED529077096966D" \ 70 | "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" \ 71 | "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" \ 72 | "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" \ 73 | "15728E5A8AACAA68FFFFFFFFFFFFFFFF" 74 | 75 | /* 2^3072 - 2^3008 - 1 + 2^64 * { [2^2942 pi] + 1690314 } */ 76 | #define P3072 \ 77 | "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \ 78 | "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \ 79 | "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \ 80 | "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \ 81 | "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" \ 82 | "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" \ 83 | "83655D23DCA3AD961C62F356208552BB9ED529077096966D" \ 84 | "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" \ 85 | "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" \ 86 | "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" \ 87 | "15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64" \ 88 | "ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" \ 89 | "ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B" \ 90 | "F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" \ 91 | "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31" \ 92 | "43DB5BFCE0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF" 93 | 94 | /* 2^4096 - 2^4032 - 1 + 2^64 * { [2^3966 pi] + 240904 } */ 95 | #define P4096 \ 96 | "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \ 97 | "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \ 98 | "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \ 99 | "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \ 100 | "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" \ 101 | "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" \ 102 | "83655D23DCA3AD961C62F356208552BB9ED529077096966D" \ 103 | "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" \ 104 | "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" \ 105 | "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" \ 106 | "15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64" \ 107 | "ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" \ 108 | "ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B" \ 109 | "F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" \ 110 | "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31" \ 111 | "43DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7" \ 112 | "88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA" \ 113 | "2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6" \ 114 | "287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED" \ 115 | "1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9" \ 116 | "93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199" \ 117 | "FFFFFFFFFFFFFFFF" 118 | 119 | /* 2^6144 - 2^6080 - 1 + 2^64 * { [2^6014 pi] + 929484 } */ 120 | #define P6144 \ 121 | "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \ 122 | "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \ 123 | "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \ 124 | "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \ 125 | "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" \ 126 | "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" \ 127 | "83655D23DCA3AD961C62F356208552BB9ED529077096966D" \ 128 | "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" \ 129 | "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" \ 130 | "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" \ 131 | "15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64" \ 132 | "ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" \ 133 | "ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B" \ 134 | "F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" \ 135 | "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31" \ 136 | "43DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7" \ 137 | "88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA" \ 138 | "2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6" \ 139 | "287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED" \ 140 | "1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9" \ 141 | "93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492" \ 142 | "36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BD" \ 143 | "F8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831" \ 144 | "179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1B" \ 145 | "DB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF" \ 146 | "5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6" \ 147 | "D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F3" \ 148 | "23A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA" \ 149 | "CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE328" \ 150 | "06A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55C" \ 151 | "DA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE" \ 152 | "12BF2D5B0B7474D6E694F91E6DCC4024FFFFFFFFFFFFFFFF" 153 | 154 | /* 2^8192 - 2^8128 - 1 + 2^64 * { [2^8062 pi] + 4743158 } */ 155 | #define P8192 \ 156 | "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \ 157 | "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \ 158 | "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \ 159 | "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \ 160 | "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" \ 161 | "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" \ 162 | "83655D23DCA3AD961C62F356208552BB9ED529077096966D" \ 163 | "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" \ 164 | "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" \ 165 | "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" \ 166 | "15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64" \ 167 | "ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" \ 168 | "ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B" \ 169 | "F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" \ 170 | "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31" \ 171 | "43DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7" \ 172 | "88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA" \ 173 | "2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6" \ 174 | "287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED" \ 175 | "1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9" \ 176 | "93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492" \ 177 | "36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BD" \ 178 | "F8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831" \ 179 | "179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1B" \ 180 | "DB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF" \ 181 | "5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6" \ 182 | "D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F3" \ 183 | "23A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA" \ 184 | "CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE328" \ 185 | "06A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55C" \ 186 | "DA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE" \ 187 | "12BF2D5B0B7474D6E694F91E6DBE115974A3926F12FEE5E4" \ 188 | "38777CB6A932DF8CD8BEC4D073B931BA3BC832B68D9DD300" \ 189 | "741FA7BF8AFC47ED2576F6936BA424663AAB639C5AE4F568" \ 190 | "3423B4742BF1C978238F16CBE39D652DE3FDB8BEFC848AD9" \ 191 | "22222E04A4037C0713EB57A81A23F0C73473FC646CEA306B" \ 192 | "4BCBC8862F8385DDFA9D4B7FA2C087E879683303ED5BDD3A" \ 193 | "062B3CF5B3A278A66D2A13F83F44F82DDF310EE074AB6A36" \ 194 | "4597E899A0255DC164F31CC50846851DF9AB48195DED7EA1" \ 195 | "B1D510BD7EE74D73FAF36BC31ECFA268359046F4EB879F92" \ 196 | "4009438B481C6CD7889A002ED5EE382BC9190DA6FC026E47" \ 197 | "9558E4475677E9AA9E3050E2765694DFC81F56E880B96E71" \ 198 | "60C980DD98EDD3DFFFFFFFFFFFFFFFFF" 199 | 200 | -------------------------------------------------------------------------------- /web/look.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | 13 | 14 | 15 | 58 | 59 | 60 | 61 | 65 | 66 | 67 | 72 | 73 | 74 | 244 | 245 |
62 |



63 |

64 |
68 |

69 | FlashPhone main parameters

70 |

71 |
75 |
76 | 77 | 78 | 79 | 82 | 83 | 84 | 85 | 107 | 108 | 109 | 110 | 116 | 117 | 118 | 119 | 122 | 123 | 124 | 125 | 131 | 132 | 133 | 134 | 140 | 141 | 142 | 143 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 166 | 167 | 168 | 169 | 175 | 176 | 177 | 178 | 185 | 186 | 187 | 188 | 195 | 196 | 197 | 198 | 205 | 206 | 207 | 208 | 215 | 216 | 217 | 218 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 |
Select the URL RTMP (or @IP) : 80 | 81 |
GUI Look : 86 | 106 |
Autologin : 111 | 115 |
Login : 120 | 121 |
Default camera use : 126 | 130 |
Auto answer : 135 | 139 |
VideoSize : 144 | 148 |
Video quality :
Video bandwith :
Auto call : 161 | 165 |
Auto Recall : 170 | 174 |
Show DTMF : 179 | 184 |
Show Called number : 189 | 194 |
Show Call duration : 199 | 204 |
Show Hangup/Call image : 209 | 214 |
Save settings : 219 | 224 |
Called number for auto call :
Test :
Timeout between calls (bench) :
242 |
243 |
246 |

247 | 248 | 249 | -------------------------------------------------------------------------------- /src/rtmpe.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef __SECURITY_H__ 3 | #define __SECURITY_H__ 4 | 5 | #ifdef USE_POLARSSL 6 | #include 7 | #include 8 | #ifndef SHA256_DIGEST_LENGTH 9 | #define SHA256_DIGEST_LENGTH 32 10 | #endif 11 | #define HMAC_CTX sha2_context 12 | #define HMAC_setup(ctx, key, len) sha2_hmac_starts(&ctx, (unsigned char *)key, len, 0) 13 | #define HMAC_crunch(ctx, buf, len) sha2_hmac_update(&ctx, buf, len) 14 | #define HMAC_finish(ctx, dig, dlen) dlen = SHA256_DIGEST_LENGTH; sha2_hmac_finish(&ctx, dig) 15 | 16 | typedef arc4_context * RC4_handle; 17 | #define RC4_alloc(h) *h = malloc(sizeof(arc4_context)) 18 | #define RC4_setkey(h,l,k) arc4_setup(h,k,l) 19 | #define RC4_encrypt(h,l,d) arc4_crypt(h,l,(unsigned char *)d,(unsigned char *)d) 20 | #define RC4_encrypt2(h,l,s,d) arc4_crypt(h,l,(unsigned char *)s,(unsigned char *)d) 21 | #define RC4_free(h) free(h) 22 | 23 | #elif defined(USE_GNUTLS) 24 | #include 25 | #ifndef SHA256_DIGEST_LENGTH 26 | #define SHA256_DIGEST_LENGTH 32 27 | #endif 28 | #define HMAC_CTX gcry_md_hd_t 29 | #define HMAC_setup(ctx, key, len) gcry_md_open(&ctx, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC); gcry_md_setkey(ctx, key, len) 30 | #define HMAC_crunch(ctx, buf, len) gcry_md_write(ctx, buf, len) 31 | #define HMAC_finish(ctx, dig, dlen) dlen = SHA256_DIGEST_LENGTH; memcpy(dig, gcry_md_read(ctx, 0), dlen); gcry_md_close(ctx) 32 | 33 | typedef gcry_cipher_hd_t RC4_handle; 34 | #define RC4_alloc(h) gcry_cipher_open(h, GCRY_CIPHER_ARCFOUR, GCRY_CIPHER_MODE_STREAM, 0) 35 | #define RC4_setkey(h,l,k) gcry_cipher_setkey(h,k,l) 36 | #define RC4_encrypt(h,l,d) gcry_cipher_encrypt(h,(void *)d,l,NULL,0) 37 | #define RC4_encrypt2(h,l,s,d) gcry_cipher_encrypt(h,(void *)d,l,(void *)s,l) 38 | #define RC4_free(h) gcry_cipher_close(h) 39 | 40 | #else /* USE_OPENSSL */ 41 | #include 42 | #include 43 | #include 44 | #if OPENSSL_VERSION_NUMBER < 0x0090800 || !defined(SHA256_DIGEST_LENGTH) 45 | #error Your OpenSSL is too old, need 0.9.8 or newer with SHA256 46 | #endif 47 | #define HMAC_setup(ctx, key, len) HMAC_CTX_init(&ctx); HMAC_Init_ex(&ctx, key, len, EVP_sha256(), 0) 48 | #define HMAC_crunch(ctx, buf, len) HMAC_Update(&ctx, buf, len) 49 | #define HMAC_finish(ctx, dig, dlen) HMAC_Final(&ctx, dig, &dlen); HMAC_CTX_cleanup(&ctx) 50 | 51 | typedef RC4_KEY * RC4_handle; 52 | #define RC4_alloc(h) *h = malloc(sizeof(RC4_KEY)) 53 | #define RC4_setkey(h,l,k) RC4_set_key(h,l,k) 54 | #define RC4_encrypt(h,l,d) RC4(h,l,(uint8_t *)d,(uint8_t *)d) 55 | #define RC4_encrypt2(h,l,s,d) RC4(h,l,(uint8_t *)s,(uint8_t *)d) 56 | #define RC4_free(h) free(h) 57 | #endif 58 | 59 | #define FP10 60 | 61 | #include "keys.h" 62 | 63 | 64 | extern unsigned int getDigestPos(int offalgo, uint8_t *handshake, unsigned int len); 65 | extern unsigned int getDhPos(int offalgo, uint8_t *handshake, unsigned int len); 66 | extern void verify_HMAC(const uint8_t *message, size_t messageLen, const uint8_t *key, int keyType, size_t keylen, uint8_t *digest); 67 | extern int verify_digest(int offalgo, uint8_t *handshakeMessage); 68 | extern void calculate_digest(unsigned int digestPos, uint8_t *handshakeMessage, 69 | int keyType , size_t keyLen, uint8_t *digest); 70 | 71 | extern void InitRC4Encryption(uint8_t * secretKey, 72 | uint8_t * pubKeyIn, 73 | uint8_t * pubKeyOut, RC4_handle *rc4keyIn, RC4_handle *rc4keyOut) ; 74 | 75 | 76 | #define GENUINE_FMSKEY 1 77 | #define GENUINE_FPSKEY 2 78 | 79 | static const uint8_t GenuineFMSKey[] = { 80 | 0x47, 0x65, 0x6e, 0x75, 0x69, 0x6e, 0x65, 0x20, 0x41, 0x64, 0x6f, 0x62, 81 | 0x65, 0x20, 0x46, 0x6c, 82 | 0x61, 0x73, 0x68, 0x20, 0x4d, 0x65, 0x64, 0x69, 0x61, 0x20, 0x53, 0x65, 83 | 0x72, 0x76, 0x65, 0x72, 84 | 0x20, 0x30, 0x30, 0x31, /* Genuine Adobe Flash Media Server 001 */ 85 | 86 | 0xf0, 0xee, 0xc2, 0x4a, 0x80, 0x68, 0xbe, 0xe8, 0x2e, 0x00, 0xd0, 0xd1, 87 | 0x02, 0x9e, 0x7e, 0x57, 0x6e, 0xec, 0x5d, 0x2d, 0x29, 0x80, 0x6f, 0xab, 88 | 0x93, 0xb8, 0xe6, 0x36, 89 | 0xcf, 0xeb, 0x31, 0xae 90 | }; /* 68 */ 91 | 92 | static const uint8_t GenuineFPKey[] = { 93 | 0x47, 0x65, 0x6E, 0x75, 0x69, 0x6E, 0x65, 0x20, 0x41, 0x64, 0x6F, 0x62, 94 | 0x65, 0x20, 0x46, 0x6C, 95 | 0x61, 0x73, 0x68, 0x20, 0x50, 0x6C, 0x61, 0x79, 0x65, 0x72, 0x20, 0x30, 96 | 0x30, 0x31, /* Genuine Adobe Flash Player 001 */ 97 | 0xF0, 0xEE, 98 | 0xC2, 0x4A, 0x80, 0x68, 0xBE, 0xE8, 0x2E, 0x00, 0xD0, 0xD1, 0x02, 0x9E, 99 | 0x7E, 0x57, 0x6E, 0xEC, 100 | 0x5D, 0x2D, 0x29, 0x80, 0x6F, 0xAB, 0x93, 0xB8, 0xE6, 0x36, 0xCF, 0xEB, 101 | 0x31, 0xAE 102 | }; /* 62 */ 103 | 104 | void InitRC4Encryption(uint8_t * secretKey, 105 | uint8_t * pubKeyIn, 106 | uint8_t * pubKeyOut, RC4_handle *rc4keyIn, RC4_handle *rc4keyOut) 107 | { 108 | uint8_t digest[SHA256_DIGEST_LENGTH]; 109 | unsigned int digestLen = 0; 110 | HMAC_CTX ctx; 111 | 112 | RC4_alloc(rc4keyIn); 113 | RC4_alloc(rc4keyOut); 114 | 115 | HMAC_setup(ctx, secretKey, 128); 116 | HMAC_crunch(ctx, pubKeyIn, 128); 117 | HMAC_finish(ctx, digest, digestLen); 118 | 119 | ast_debug(8, "RC4 Out Key\n"); 120 | 121 | RC4_setkey(*rc4keyOut, 16, digest); 122 | 123 | HMAC_setup(ctx, secretKey, 128); 124 | HMAC_crunch(ctx, pubKeyOut, 128); 125 | HMAC_finish(ctx, digest, digestLen); 126 | 127 | ast_debug(8, "RC4 In Key\n"); 128 | 129 | RC4_setkey(*rc4keyIn, 16, digest); 130 | } 131 | 132 | typedef unsigned int (getoff)(uint8_t *buf, unsigned int len); 133 | 134 | static unsigned int GetDHOffset2(uint8_t *handshake, unsigned int len) 135 | { 136 | unsigned int offset = 0; 137 | uint8_t *ptr = handshake + 768; 138 | unsigned int res; 139 | 140 | //// assert(RTMP_BLOCK_SIZE <= len); 141 | 142 | offset += (*ptr); 143 | ptr++; 144 | offset += (*ptr); 145 | ptr++; 146 | offset += (*ptr); 147 | ptr++; 148 | offset += (*ptr); 149 | 150 | res = (offset % 632) + 8; 151 | 152 | if (res + 128 > 767) 153 | { 154 | ast_verbose( "%s: Couldn't calculate correct DH offset (got %d), exiting!", 155 | __FUNCTION__, res); 156 | return -1; 157 | } 158 | return res; 159 | } 160 | 161 | static unsigned int GetDigestOffset2(uint8_t *handshake, unsigned int len) 162 | { 163 | unsigned int offset = 0; 164 | uint8_t *ptr = handshake + 772; 165 | unsigned int res; 166 | 167 | offset += (*ptr); 168 | ptr++; 169 | offset += (*ptr); 170 | ptr++; 171 | offset += (*ptr); 172 | ptr++; 173 | offset += (*ptr); 174 | 175 | res = (offset % 728) + 776; 176 | 177 | if (res + 32 > 1535) 178 | { 179 | ast_verbose("%s: Couldn't calculate correct digest offset (got %d), exiting", 180 | __FUNCTION__, res); 181 | return -1; 182 | } 183 | return res; 184 | } 185 | 186 | static unsigned int GetDHOffset1(uint8_t *handshake, unsigned int len) 187 | { 188 | unsigned int offset = 0; 189 | uint8_t *ptr = handshake + 1532; 190 | unsigned int res; 191 | 192 | ///// assert(RTMP_BLOCK_SIZE <= len); 193 | 194 | offset += (*ptr); 195 | ptr++; 196 | offset += (*ptr); 197 | ptr++; 198 | offset += (*ptr); 199 | ptr++; 200 | offset += (*ptr); 201 | 202 | res = (offset % 632) + 772; 203 | 204 | if (res + 128 > 1531) 205 | { 206 | ast_verbose( "%s: Couldn't calculate DH offset (got %d), exiting!", 207 | __FUNCTION__, res); 208 | return -1; 209 | } 210 | 211 | return res; 212 | } 213 | 214 | static unsigned int GetDigestOffset1(uint8_t *handshake, unsigned int len) 215 | { 216 | unsigned int offset = 0; 217 | uint8_t *ptr = handshake + 8; 218 | unsigned int res; 219 | 220 | /// assert(12 <= len); 221 | 222 | offset += (*ptr); 223 | ptr++; 224 | offset += (*ptr); 225 | ptr++; 226 | offset += (*ptr); 227 | ptr++; 228 | offset += (*ptr); 229 | 230 | res = (offset % 728) + 12; 231 | 232 | if (res + 32 > 771) 233 | { 234 | ast_verbose("%s: Couldn't calculate digest offset (got %d), exiting!", 235 | __FUNCTION__, res); 236 | return -1; 237 | } 238 | 239 | return res; 240 | } 241 | 242 | 243 | static void HMACsha256(const uint8_t *message, size_t messageLen, const uint8_t *key, 244 | size_t keylen, uint8_t *digest) 245 | { 246 | unsigned int digestLen; 247 | HMAC_CTX ctx; 248 | 249 | HMAC_setup(ctx, key, keylen); 250 | HMAC_crunch(ctx, message, messageLen); 251 | HMAC_finish(ctx, digest, digestLen); 252 | 253 | ///assert(digestLen == 32); 254 | } 255 | 256 | static void CalculateDigest(unsigned int digestPos, uint8_t *handshakeMessage, 257 | const uint8_t *key , size_t keyLen, uint8_t *digest) 258 | { 259 | const int messageLen = RTMP_BLOCK_SIZE - SHA256_DIGEST_LENGTH; 260 | uint8_t message[RTMP_BLOCK_SIZE - SHA256_DIGEST_LENGTH]; 261 | 262 | memcpy(message, handshakeMessage, digestPos); 263 | memcpy(message + digestPos, 264 | &handshakeMessage[digestPos + SHA256_DIGEST_LENGTH], 265 | messageLen - digestPos); 266 | 267 | HMACsha256(message, messageLen, key, keyLen, digest); 268 | } 269 | 270 | static int VerifyDigest(unsigned int digestPos, uint8_t *handshakeMessage, const uint8_t *key, 271 | size_t keyLen) 272 | { 273 | uint8_t calcDigest[SHA256_DIGEST_LENGTH]; 274 | 275 | CalculateDigest(digestPos, handshakeMessage, key, keyLen, calcDigest); 276 | 277 | return memcmp(&handshakeMessage[digestPos], calcDigest, 278 | SHA256_DIGEST_LENGTH) == 0; 279 | } 280 | 281 | /* handshake 282 | * 283 | * Type = [1 bytes] plain: 0x03, encrypted: 0x06, 0x08, 0x09 284 | * -------------------------------------------------------------------- [1536 bytes] 285 | * Uptime = [4 bytes] big endian unsigned number, uptime 286 | * Version = [4 bytes] each byte represents a version number, e.g. 9.0.124.0 287 | * ... 288 | * 289 | */ 290 | 291 | static const uint32_t rtmpe8_keys[16][4] = { 292 | {0xbff034b2, 0x11d9081f, 0xccdfb795, 0x748de732}, 293 | {0x086a5eb6, 0x1743090e, 0x6ef05ab8, 0xfe5a39e2}, 294 | {0x7b10956f, 0x76ce0521, 0x2388a73a, 0x440149a1}, 295 | {0xa943f317, 0xebf11bb2, 0xa691a5ee, 0x17f36339}, 296 | {0x7a30e00a, 0xb529e22c, 0xa087aea5, 0xc0cb79ac}, 297 | {0xbdce0c23, 0x2febdeff, 0x1cfaae16, 0x1123239d}, 298 | {0x55dd3f7b, 0x77e7e62e, 0x9bb8c499, 0xc9481ee4}, 299 | {0x407bb6b4, 0x71e89136, 0xa7aebf55, 0xca33b839}, 300 | {0xfcf6bdc3, 0xb63c3697, 0x7ce4f825, 0x04d959b2}, 301 | {0x28e091fd, 0x41954c4c, 0x7fb7db00, 0xe3a066f8}, 302 | {0x57845b76, 0x4f251b03, 0x46d45bcd, 0xa2c30d29}, 303 | {0x0acceef8, 0xda55b546, 0x03473452, 0x5863713b}, 304 | {0xb82075dc, 0xa75f1fee, 0xd84268e8, 0xa72a44cc}, 305 | {0x07cf6e9e, 0xa16d7b25, 0x9fa7ae6c, 0xd92f5629}, 306 | {0xfeb1eae4, 0x8c8c3ce1, 0x4e0064a7, 0x6a387c2a}, 307 | {0x893a9427, 0xcc3013a2, 0xf106385b, 0xa829f927} 308 | }; 309 | 310 | /* RTMPE type 8 uses XTEA on the regular signature 311 | * http://en.wikipedia.org/wiki/XTEA 312 | */ 313 | static void rtmpe8_sig(uint8_t *in, uint8_t *out, int keyid) 314 | { 315 | unsigned int i, num_rounds = 32; 316 | uint32_t v0, v1, sum=0, delta=0x9E3779B9; 317 | uint32_t const *k; 318 | 319 | v0 = in[0] | (in[1] << 8) | (in[2] << 16) | (in[3] << 24); 320 | v1 = in[4] | (in[5] << 8) | (in[6] << 16) | (in[7] << 24); 321 | k = rtmpe8_keys[keyid]; 322 | 323 | for (i=0; i < num_rounds; i++) { 324 | v0 += (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + k[sum & 3]); 325 | sum += delta; 326 | v1 += (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + k[(sum>>11) & 3]); 327 | } 328 | 329 | out[0] = v0; v0 >>= 8; 330 | out[1] = v0; v0 >>= 8; 331 | out[2] = v0; v0 >>= 8; 332 | out[3] = v0; 333 | 334 | out[4] = v1; v1 >>= 8; 335 | out[5] = v1; v1 >>= 8; 336 | out[6] = v1; v1 >>= 8; 337 | out[7] = v1; 338 | } 339 | 340 | 341 | static getoff *digoff[] = {GetDigestOffset1, GetDigestOffset2}; 342 | static getoff *dhoff[] = {GetDHOffset1, GetDHOffset2}; 343 | 344 | unsigned int getDigestPos(int offalgo, uint8_t *handshake, unsigned int len) 345 | { 346 | getoff *getdig = NULL; 347 | getdig = digoff[offalgo]; 348 | return getdig(handshake, len); 349 | } 350 | 351 | unsigned int getDhPos(int offalgo, uint8_t *handshake, unsigned int len) 352 | { 353 | getoff *getdh = NULL; 354 | getdh = dhoff[offalgo]; 355 | return getdh(handshake, len); 356 | } 357 | 358 | void verify_HMAC(const uint8_t *message, size_t messageLen, const uint8_t *key, int keyType, size_t keylen, uint8_t *digest) 359 | { 360 | if (key == NULL) { 361 | if (keyType == GENUINE_FMSKEY) { 362 | ast_debug(9, "verify_HMAC with GenuineFMSKey\n"); 363 | HMACsha256(message, messageLen, GenuineFMSKey, sizeof(GenuineFMSKey) ,digest); 364 | } 365 | else { 366 | ast_debug(9, "verify_HMAC with GenuineFPSKey\n"); 367 | HMACsha256(message, messageLen, GenuineFPKey, sizeof(GenuineFPKey) ,digest); 368 | } 369 | } 370 | else { 371 | ast_debug(9, "verify_HMAC with buffer\n"); 372 | HMACsha256(message, messageLen, key, keylen ,digest); 373 | } 374 | } 375 | 376 | 377 | int verify_digest(int offalgo, uint8_t *handshakeMessage) 378 | { 379 | /* we have to use this signature now to find the correct algorithms for getting the digest and DH positions */ 380 | int digestPosClient = getDigestPos(offalgo, handshakeMessage, RTMP_BLOCK_SIZE); 381 | 382 | if (!VerifyDigest(digestPosClient, handshakeMessage, GenuineFPKey, 30)) 383 | { 384 | ast_debug(4, "handshake: Trying different position for client digest!\n"); 385 | offalgo ^= 1; 386 | 387 | digestPosClient = getDigestPos(offalgo, handshakeMessage, RTMP_BLOCK_SIZE); 388 | 389 | if (!VerifyDigest(digestPosClient, handshakeMessage, GenuineFPKey, 30)) 390 | { 391 | ast_log(LOG_ERROR, "Couldn't verify the client digest\n"); 392 | return -1; 393 | } 394 | } 395 | return digestPosClient; 396 | } 397 | 398 | void calculate_digest(unsigned int digestPos, uint8_t *handshakeMessage, 399 | int keyType , size_t keyLen, uint8_t *digest) 400 | { 401 | const uint8_t *key; 402 | 403 | if (keyType == GENUINE_FMSKEY) { 404 | key = GenuineFMSKey; 405 | } 406 | else { 407 | key = GenuineFPKey; 408 | } 409 | CalculateDigest(digestPos, handshakeMessage, key, keyLen,digest); 410 | 411 | return; 412 | } 413 | 414 | 415 | #endif 416 | 417 | 418 | -------------------------------------------------------------------------------- /web/history/history.js: -------------------------------------------------------------------------------- 1 | BrowserHistoryUtils = { 2 | addEvent: function(elm, evType, fn, useCapture) { 3 | useCapture = useCapture || false; 4 | if (elm.addEventListener) { 5 | elm.addEventListener(evType, fn, useCapture); 6 | return true; 7 | } 8 | else if (elm.attachEvent) { 9 | var r = elm.attachEvent('on' + evType, fn); 10 | return r; 11 | } 12 | else { 13 | elm['on' + evType] = fn; 14 | } 15 | } 16 | } 17 | 18 | BrowserHistory = (function() { 19 | // type of browser 20 | var browser = { 21 | ie: false, 22 | ie8: false, 23 | firefox: false, 24 | safari: false, 25 | opera: false, 26 | version: -1 27 | }; 28 | 29 | // Default app state URL to use when no fragment ID present 30 | var defaultHash = ''; 31 | 32 | // Last-known app state URL 33 | var currentHref = document.location.href; 34 | 35 | // Initial URL (used only by IE) 36 | var initialHref = document.location.href; 37 | 38 | // Initial URL (used only by IE) 39 | var initialHash = document.location.hash; 40 | 41 | // History frame source URL prefix (used only by IE) 42 | var historyFrameSourcePrefix = 'history/historyFrame.html?'; 43 | 44 | // History maintenance (used only by Safari) 45 | var currentHistoryLength = -1; 46 | 47 | // Flag to denote the existence of onhashchange 48 | var browserHasHashChange = false; 49 | 50 | var historyHash = []; 51 | 52 | var initialState = createState(initialHref, initialHref + '#' + initialHash, initialHash); 53 | 54 | var backStack = []; 55 | var forwardStack = []; 56 | 57 | var currentObjectId = null; 58 | 59 | //UserAgent detection 60 | var useragent = navigator.userAgent.toLowerCase(); 61 | 62 | if (useragent.indexOf("opera") != -1) { 63 | browser.opera = true; 64 | } else if (useragent.indexOf("msie") != -1) { 65 | browser.ie = true; 66 | browser.version = parseFloat(useragent.substring(useragent.indexOf('msie') + 4)); 67 | if (browser.version == 8) 68 | { 69 | browser.ie = false; 70 | browser.ie8 = true; 71 | } 72 | } else if (useragent.indexOf("safari") != -1) { 73 | browser.safari = true; 74 | browser.version = parseFloat(useragent.substring(useragent.indexOf('safari') + 7)); 75 | } else if (useragent.indexOf("gecko") != -1) { 76 | browser.firefox = true; 77 | } 78 | 79 | if (browser.ie == true && browser.version == 7) { 80 | window["_ie_firstload"] = false; 81 | } 82 | 83 | function hashChangeHandler() 84 | { 85 | currentHref = document.location.href; 86 | var flexAppUrl = getHash(); 87 | //ADR: to fix multiple 88 | if (typeof BrowserHistory_multiple != "undefined" && BrowserHistory_multiple == true) { 89 | var pl = getPlayers(); 90 | for (var i = 0; i < pl.length; i++) { 91 | pl[i].browserURLChange(flexAppUrl); 92 | } 93 | } else { 94 | getPlayer().browserURLChange(flexAppUrl); 95 | } 96 | } 97 | 98 | // Accessor functions for obtaining specific elements of the page. 99 | function getHistoryFrame() 100 | { 101 | return document.getElementById('ie_historyFrame'); 102 | } 103 | 104 | function getFormElement() 105 | { 106 | return document.getElementById('safari_formDiv'); 107 | } 108 | 109 | function getRememberElement() 110 | { 111 | return document.getElementById("safari_remember_field"); 112 | } 113 | 114 | // Get the Flash player object for performing ExternalInterface callbacks. 115 | // Updated for changes to SWFObject2. 116 | function getPlayer(id) { 117 | var i; 118 | 119 | if (id && document.getElementById(id)) { 120 | var r = document.getElementById(id); 121 | if (typeof r.SetVariable != "undefined") { 122 | return r; 123 | } 124 | else { 125 | var o = r.getElementsByTagName("object"); 126 | var e = r.getElementsByTagName("embed"); 127 | for (i = 0; i < o.length; i++) { 128 | if (typeof o[i].browserURLChange != "undefined") 129 | return o[i]; 130 | } 131 | for (i = 0; i < e.length; i++) { 132 | if (typeof e[i].browserURLChange != "undefined") 133 | return e[i]; 134 | } 135 | } 136 | } 137 | else { 138 | var o = document.getElementsByTagName("object"); 139 | var e = document.getElementsByTagName("embed"); 140 | for (i = 0; i < e.length; i++) { 141 | if (typeof e[i].browserURLChange != "undefined") 142 | { 143 | return e[i]; 144 | } 145 | } 146 | for (i = 0; i < o.length; i++) { 147 | if (typeof o[i].browserURLChange != "undefined") 148 | { 149 | return o[i]; 150 | } 151 | } 152 | } 153 | return undefined; 154 | } 155 | 156 | function getPlayers() { 157 | var i; 158 | var players = []; 159 | if (players.length == 0) { 160 | var tmp = document.getElementsByTagName('object'); 161 | for (i = 0; i < tmp.length; i++) 162 | { 163 | if (typeof tmp[i].browserURLChange != "undefined") 164 | players.push(tmp[i]); 165 | } 166 | } 167 | if (players.length == 0 || players[0].object == null) { 168 | var tmp = document.getElementsByTagName('embed'); 169 | for (i = 0; i < tmp.length; i++) 170 | { 171 | if (typeof tmp[i].browserURLChange != "undefined") 172 | players.push(tmp[i]); 173 | } 174 | } 175 | return players; 176 | } 177 | 178 | function getIframeHash() { 179 | var doc = getHistoryFrame().contentWindow.document; 180 | var hash = String(doc.location.search); 181 | if (hash.length == 1 && hash.charAt(0) == "?") { 182 | hash = ""; 183 | } 184 | else if (hash.length >= 2 && hash.charAt(0) == "?") { 185 | hash = hash.substring(1); 186 | } 187 | return hash; 188 | } 189 | 190 | /* Get the current location hash excluding the '#' symbol. */ 191 | function getHash() { 192 | // It would be nice if we could use document.location.hash here, 193 | // but it's faulty sometimes. 194 | var idx = document.location.href.indexOf('#'); 195 | return (idx >= 0) ? document.location.href.substr(idx+1) : ''; 196 | } 197 | 198 | /* Get the current location hash excluding the '#' symbol. */ 199 | function setHash(hash) { 200 | // It would be nice if we could use document.location.hash here, 201 | // but it's faulty sometimes. 202 | if (hash == '') hash = '#' 203 | document.location.hash = hash; 204 | } 205 | 206 | function createState(baseUrl, newUrl, flexAppUrl) { 207 | return { 'baseUrl': baseUrl, 'newUrl': newUrl, 'flexAppUrl': flexAppUrl, 'title': null }; 208 | } 209 | 210 | /* Add a history entry to the browser. 211 | * baseUrl: the portion of the location prior to the '#' 212 | * newUrl: the entire new URL, including '#' and following fragment 213 | * flexAppUrl: the portion of the location following the '#' only 214 | */ 215 | function addHistoryEntry(baseUrl, newUrl, flexAppUrl) { 216 | 217 | //delete all the history entries 218 | forwardStack = []; 219 | 220 | if (browser.ie) { 221 | //Check to see if we are being asked to do a navigate for the first 222 | //history entry, and if so ignore, because it's coming from the creation 223 | //of the history iframe 224 | if (flexAppUrl == defaultHash && document.location.href == initialHref && window['_ie_firstload']) { 225 | currentHref = initialHref; 226 | return; 227 | } 228 | if ((!flexAppUrl || flexAppUrl == defaultHash) && window['_ie_firstload']) { 229 | newUrl = baseUrl + '#' + defaultHash; 230 | flexAppUrl = defaultHash; 231 | } else { 232 | // for IE, tell the history frame to go somewhere without a '#' 233 | // in order to get this entry into the browser history. 234 | getHistoryFrame().src = historyFrameSourcePrefix + flexAppUrl; 235 | } 236 | setHash(flexAppUrl); 237 | } else { 238 | 239 | //ADR 240 | if (backStack.length == 0 && initialState.flexAppUrl == flexAppUrl) { 241 | initialState = createState(baseUrl, newUrl, flexAppUrl); 242 | } else if(backStack.length > 0 && backStack[backStack.length - 1].flexAppUrl == flexAppUrl) { 243 | backStack[backStack.length - 1] = createState(baseUrl, newUrl, flexAppUrl); 244 | } 245 | 246 | if (browser.safari && !browserHasHashChange) { 247 | // for Safari, submit a form whose action points to the desired URL 248 | if (browser.version <= 419.3) { 249 | var file = window.location.pathname.toString(); 250 | file = file.substring(file.lastIndexOf("/")+1); 251 | getFormElement().innerHTML = '
'; 252 | //get the current elements and add them to the form 253 | var qs = window.location.search.substring(1); 254 | var qs_arr = qs.split("&"); 255 | for (var i = 0; i < qs_arr.length; i++) { 256 | var tmp = qs_arr[i].split("="); 257 | var elem = document.createElement("input"); 258 | elem.type = "hidden"; 259 | elem.name = tmp[0]; 260 | elem.value = tmp[1]; 261 | document.forms.historyForm.appendChild(elem); 262 | } 263 | document.forms.historyForm.submit(); 264 | } else { 265 | top.location.hash = flexAppUrl; 266 | } 267 | // We also have to maintain the history by hand for Safari 268 | historyHash[history.length] = flexAppUrl; 269 | _storeStates(); 270 | } else { 271 | // Otherwise, just tell the browser to go there 272 | setHash(flexAppUrl); 273 | } 274 | } 275 | backStack.push(createState(baseUrl, newUrl, flexAppUrl)); 276 | } 277 | 278 | function _storeStates() { 279 | if (browser.safari) { 280 | getRememberElement().value = historyHash.join(","); 281 | } 282 | } 283 | 284 | function handleBackButton() { 285 | //The "current" page is always at the top of the history stack. 286 | var current = backStack.pop(); 287 | if (!current) { return; } 288 | var last = backStack[backStack.length - 1]; 289 | if (!last && backStack.length == 0){ 290 | last = initialState; 291 | } 292 | forwardStack.push(current); 293 | } 294 | 295 | function handleForwardButton() { 296 | //summary: private method. Do not call this directly. 297 | 298 | var last = forwardStack.pop(); 299 | if (!last) { return; } 300 | backStack.push(last); 301 | } 302 | 303 | function handleArbitraryUrl() { 304 | //delete all the history entries 305 | forwardStack = []; 306 | } 307 | 308 | /* Called periodically to poll to see if we need to detect navigation that has occurred */ 309 | function checkForUrlChange() { 310 | 311 | if (browser.ie) { 312 | if (currentHref != document.location.href && currentHref + '#' != document.location.href) { 313 | //This occurs when the user has navigated to a specific URL 314 | //within the app, and didn't use browser back/forward 315 | //IE seems to have a bug where it stops updating the URL it 316 | //shows the end-user at this point, but programatically it 317 | //appears to be correct. Do a full app reload to get around 318 | //this issue. 319 | if (browser.version < 7) { 320 | currentHref = document.location.href; 321 | document.location.reload(); 322 | } else { 323 | if (getHash() != getIframeHash()) { 324 | // this.iframe.src = this.blankURL + hash; 325 | var sourceToSet = historyFrameSourcePrefix + getHash(); 326 | getHistoryFrame().src = sourceToSet; 327 | currentHref = document.location.href; 328 | } 329 | } 330 | } 331 | } 332 | 333 | if (browser.safari && !browserHasHashChange) { 334 | // For Safari, we have to check to see if history.length changed. 335 | if (currentHistoryLength >= 0 && history.length != currentHistoryLength) { 336 | //alert("did change: " + history.length + ", " + historyHash.length + "|" + historyHash[history.length] + "|>" + historyHash.join("|")); 337 | var flexAppUrl = getHash(); 338 | if (browser.version < 528.16 /* Anything earlier than Safari 4.0 */) 339 | { 340 | // If it did change and we're running Safari 3.x or earlier, 341 | // then we have to look the old state up in our hand-maintained 342 | // array since document.location.hash won't have changed, 343 | // then call back into BrowserManager. 344 | currentHistoryLength = history.length; 345 | flexAppUrl = historyHash[currentHistoryLength]; 346 | } 347 | 348 | //ADR: to fix multiple 349 | if (typeof BrowserHistory_multiple != "undefined" && BrowserHistory_multiple == true) { 350 | var pl = getPlayers(); 351 | for (var i = 0; i < pl.length; i++) { 352 | pl[i].browserURLChange(flexAppUrl); 353 | } 354 | } else { 355 | getPlayer().browserURLChange(flexAppUrl); 356 | } 357 | _storeStates(); 358 | } 359 | } 360 | if (browser.firefox && !browserHasHashChange) { 361 | if (currentHref != document.location.href) { 362 | var bsl = backStack.length; 363 | 364 | var urlActions = { 365 | back: false, 366 | forward: false, 367 | set: false 368 | } 369 | 370 | if ((window.location.hash == initialHash || window.location.href == initialHref) && (bsl == 1)) { 371 | urlActions.back = true; 372 | // FIXME: could this ever be a forward button? 373 | // we can't clear it because we still need to check for forwards. Ugg. 374 | // clearInterval(this.locationTimer); 375 | handleBackButton(); 376 | } 377 | 378 | // first check to see if we could have gone forward. We always halt on 379 | // a no-hash item. 380 | if (forwardStack.length > 0) { 381 | if (forwardStack[forwardStack.length-1].flexAppUrl == getHash()) { 382 | urlActions.forward = true; 383 | handleForwardButton(); 384 | } 385 | } 386 | 387 | // ok, that didn't work, try someplace back in the history stack 388 | if ((bsl >= 2) && (backStack[bsl - 2])) { 389 | if (backStack[bsl - 2].flexAppUrl == getHash()) { 390 | urlActions.back = true; 391 | handleBackButton(); 392 | } 393 | } 394 | 395 | if (!urlActions.back && !urlActions.forward) { 396 | var foundInStacks = { 397 | back: -1, 398 | forward: -1 399 | } 400 | 401 | for (var i = 0; i < backStack.length; i++) { 402 | if (backStack[i].flexAppUrl == getHash() && i != (bsl - 2)) { 403 | arbitraryUrl = true; 404 | foundInStacks.back = i; 405 | } 406 | } 407 | for (var i = 0; i < forwardStack.length; i++) { 408 | if (forwardStack[i].flexAppUrl == getHash() && i != (bsl - 2)) { 409 | arbitraryUrl = true; 410 | foundInStacks.forward = i; 411 | } 412 | } 413 | handleArbitraryUrl(); 414 | } 415 | 416 | // Firefox changed; do a callback into BrowserManager to tell it. 417 | currentHref = document.location.href; 418 | var flexAppUrl = getHash(); 419 | //ADR: to fix multiple 420 | if (typeof BrowserHistory_multiple != "undefined" && BrowserHistory_multiple == true) { 421 | var pl = getPlayers(); 422 | for (var i = 0; i < pl.length; i++) { 423 | pl[i].browserURLChange(flexAppUrl); 424 | } 425 | } else { 426 | getPlayer().browserURLChange(flexAppUrl); 427 | } 428 | } 429 | } 430 | } 431 | 432 | var _initialize = function () { 433 | 434 | browserHasHashChange = ("onhashchange" in document.body); 435 | 436 | if (browser.ie) 437 | { 438 | var scripts = document.getElementsByTagName('script'); 439 | for (var i = 0, s; s = scripts[i]; i++) { 440 | if (s.src.indexOf("history.js") > -1) { 441 | var iframe_location = (new String(s.src)).replace("history.js", "historyFrame.html"); 442 | } 443 | } 444 | historyFrameSourcePrefix = iframe_location + "?"; 445 | var src = historyFrameSourcePrefix; 446 | 447 | var iframe = document.createElement("iframe"); 448 | iframe.id = 'ie_historyFrame'; 449 | iframe.name = 'ie_historyFrame'; 450 | iframe.src = 'javascript:false;'; 451 | 452 | try { 453 | document.body.appendChild(iframe); 454 | } catch(e) { 455 | setTimeout(function() { 456 | document.body.appendChild(iframe); 457 | }, 0); 458 | } 459 | } 460 | 461 | if (browser.safari && !browserHasHashChange) 462 | { 463 | var rememberDiv = document.createElement("div"); 464 | rememberDiv.id = 'safari_rememberDiv'; 465 | document.body.appendChild(rememberDiv); 466 | rememberDiv.innerHTML = ''; 467 | 468 | var formDiv = document.createElement("div"); 469 | formDiv.id = 'safari_formDiv'; 470 | document.body.appendChild(formDiv); 471 | 472 | var reloader_content = document.createElement('div'); 473 | reloader_content.id = 'safarireloader'; 474 | var scripts = document.getElementsByTagName('script'); 475 | for (var i = 0, s; s = scripts[i]; i++) { 476 | if (s.src.indexOf("history.js") > -1) { 477 | html = (new String(s.src)).replace(".js", ".html"); 478 | } 479 | } 480 | reloader_content.innerHTML = ''; 481 | document.body.appendChild(reloader_content); 482 | reloader_content.style.position = 'absolute'; 483 | reloader_content.style.left = reloader_content.style.top = '-9999px'; 484 | iframe = reloader_content.getElementsByTagName('iframe')[0]; 485 | 486 | if (document.getElementById("safari_remember_field").value != "" ) { 487 | historyHash = document.getElementById("safari_remember_field").value.split(","); 488 | } 489 | } 490 | 491 | if (browserHasHashChange) 492 | document.body.onhashchange = hashChangeHandler; 493 | } 494 | 495 | return { 496 | historyHash: historyHash, 497 | backStack: function() { return backStack; }, 498 | forwardStack: function() { return forwardStack }, 499 | getPlayer: getPlayer, 500 | initialize: function(src) { 501 | _initialize(src); 502 | }, 503 | setURL: function(url) { 504 | document.location.href = url; 505 | }, 506 | getURL: function() { 507 | return document.location.href; 508 | }, 509 | getTitle: function() { 510 | return document.title; 511 | }, 512 | setTitle: function(title) { 513 | try { 514 | backStack[backStack.length - 1].title = title; 515 | } catch(e) { } 516 | //if on safari, set the title to be the empty string. 517 | if (browser.safari) { 518 | if (title == "") { 519 | try { 520 | var tmp = window.location.href.toString(); 521 | title = tmp.substring((tmp.lastIndexOf("/")+1), tmp.lastIndexOf("#")); 522 | } catch(e) { 523 | title = ""; 524 | } 525 | } 526 | } 527 | document.title = title; 528 | }, 529 | setDefaultURL: function(def) 530 | { 531 | defaultHash = def; 532 | def = getHash(); 533 | //trailing ? is important else an extra frame gets added to the history 534 | //when navigating back to the first page. Alternatively could check 535 | //in history frame navigation to compare # and ?. 536 | if (browser.ie) 537 | { 538 | window['_ie_firstload'] = true; 539 | var sourceToSet = historyFrameSourcePrefix + def; 540 | var func = function() { 541 | getHistoryFrame().src = sourceToSet; 542 | window.location.replace("#" + def); 543 | setInterval(checkForUrlChange, 50); 544 | } 545 | try { 546 | func(); 547 | } catch(e) { 548 | window.setTimeout(function() { func(); }, 0); 549 | } 550 | } 551 | 552 | if (browser.safari) 553 | { 554 | currentHistoryLength = history.length; 555 | if (historyHash.length == 0) { 556 | historyHash[currentHistoryLength] = def; 557 | var newloc = "#" + def; 558 | window.location.replace(newloc); 559 | } else { 560 | //alert(historyHash[historyHash.length-1]); 561 | } 562 | setInterval(checkForUrlChange, 50); 563 | } 564 | 565 | 566 | if (browser.firefox || browser.opera) 567 | { 568 | var reg = new RegExp("#" + def + "$"); 569 | if (window.location.toString().match(reg)) { 570 | } else { 571 | var newloc ="#" + def; 572 | window.location.replace(newloc); 573 | } 574 | setInterval(checkForUrlChange, 50); 575 | } 576 | 577 | }, 578 | 579 | /* Set the current browser URL; called from inside BrowserManager to propagate 580 | * the application state out to the container. 581 | */ 582 | setBrowserURL: function(flexAppUrl, objectId) { 583 | if (browser.ie && typeof objectId != "undefined") { 584 | currentObjectId = objectId; 585 | } 586 | //fromIframe = fromIframe || false; 587 | //fromFlex = fromFlex || false; 588 | //alert("setBrowserURL: " + flexAppUrl); 589 | //flexAppUrl = (flexAppUrl == "") ? defaultHash : flexAppUrl ; 590 | 591 | var pos = document.location.href.indexOf('#'); 592 | var baseUrl = pos != -1 ? document.location.href.substr(0, pos) : document.location.href; 593 | var newUrl = baseUrl + '#' + flexAppUrl; 594 | 595 | if (document.location.href != newUrl && document.location.href + '#' != newUrl) { 596 | currentHref = newUrl; 597 | addHistoryEntry(baseUrl, newUrl, flexAppUrl); 598 | currentHistoryLength = history.length; 599 | } 600 | }, 601 | 602 | browserURLChange: function(flexAppUrl) { 603 | var objectId = null; 604 | if (browser.ie && currentObjectId != null) { 605 | objectId = currentObjectId; 606 | } 607 | 608 | if (typeof BrowserHistory_multiple != "undefined" && BrowserHistory_multiple == true) { 609 | var pl = getPlayers(); 610 | for (var i = 0; i < pl.length; i++) { 611 | try { 612 | pl[i].browserURLChange(flexAppUrl); 613 | } catch(e) { } 614 | } 615 | } else { 616 | try { 617 | getPlayer(objectId).browserURLChange(flexAppUrl); 618 | } catch(e) { } 619 | } 620 | 621 | currentObjectId = null; 622 | }, 623 | getUserAgent: function() { 624 | return navigator.userAgent; 625 | }, 626 | getPlatform: function() { 627 | return navigator.platform; 628 | } 629 | 630 | } 631 | 632 | })(); 633 | 634 | // Initialization 635 | 636 | // Automated unit testing and other diagnostics 637 | 638 | function setURL(url) 639 | { 640 | document.location.href = url; 641 | } 642 | 643 | function backButton() 644 | { 645 | history.back(); 646 | } 647 | 648 | function forwardButton() 649 | { 650 | history.forward(); 651 | } 652 | 653 | function goForwardOrBackInHistory(step) 654 | { 655 | history.go(step); 656 | } 657 | 658 | //BrowserHistoryUtils.addEvent(window, "load", function() { BrowserHistory.initialize(); }); 659 | (function(i) { 660 | var u =navigator.userAgent;var e=/*@cc_on!@*/false; 661 | var st = setTimeout; 662 | if(/webkit/i.test(u)){ 663 | st(function(){ 664 | var dr=document.readyState; 665 | if(dr=="loaded"||dr=="complete"){i()} 666 | else{st(arguments.callee,10);}},10); 667 | } else if((/mozilla/i.test(u)&&!/(compati)/.test(u)) || (/opera/i.test(u))){ 668 | document.addEventListener("DOMContentLoaded",i,false); 669 | } else if(e){ 670 | (function(){ 671 | var t=document.createElement('doc:rdy'); 672 | try{t.doScroll('left'); 673 | i();t=null; 674 | }catch(e){st(arguments.callee,0);}})(); 675 | } else{ 676 | window.onload=i; 677 | } 678 | })( function() {BrowserHistory.initialize();} ); 679 | -------------------------------------------------------------------------------- /web/swfobject.js: -------------------------------------------------------------------------------- 1 | /*! SWFObject v2.2 2 | is released under the MIT License 3 | */ 4 | 5 | var swfobject = function() { 6 | 7 | var UNDEF = "undefined", 8 | OBJECT = "object", 9 | SHOCKWAVE_FLASH = "Shockwave Flash", 10 | SHOCKWAVE_FLASH_AX = "ShockwaveFlash.ShockwaveFlash", 11 | FLASH_MIME_TYPE = "application/x-shockwave-flash", 12 | EXPRESS_INSTALL_ID = "SWFObjectExprInst", 13 | ON_READY_STATE_CHANGE = "onreadystatechange", 14 | 15 | win = window, 16 | doc = document, 17 | nav = navigator, 18 | 19 | plugin = false, 20 | domLoadFnArr = [main], 21 | regObjArr = [], 22 | objIdArr = [], 23 | listenersArr = [], 24 | storedAltContent, 25 | storedAltContentId, 26 | storedCallbackFn, 27 | storedCallbackObj, 28 | isDomLoaded = false, 29 | isExpressInstallActive = false, 30 | dynamicStylesheet, 31 | dynamicStylesheetMedia, 32 | autoHideShow = true, 33 | 34 | /* Centralized function for browser feature detection 35 | - User agent string detection is only used when no good alternative is possible 36 | - Is executed directly for optimal performance 37 | */ 38 | ua = function() { 39 | var w3cdom = typeof doc.getElementById != UNDEF && typeof doc.getElementsByTagName != UNDEF && typeof doc.createElement != UNDEF, 40 | u = nav.userAgent.toLowerCase(), 41 | p = nav.platform.toLowerCase(), 42 | windows = p ? /win/.test(p) : /win/.test(u), 43 | mac = p ? /mac/.test(p) : /mac/.test(u), 44 | webkit = /webkit/.test(u) ? parseFloat(u.replace(/^.*webkit\/(\d+(\.\d+)?).*$/, "$1")) : false, // returns either the webkit version or false if not webkit 45 | ie = !+"\v1", // feature detection based on Andrea Giammarchi's solution: http://webreflection.blogspot.com/2009/01/32-bytes-to-know-if-your-browser-is-ie.html 46 | playerVersion = [0,0,0], 47 | d = null; 48 | if (typeof nav.plugins != UNDEF && typeof nav.plugins[SHOCKWAVE_FLASH] == OBJECT) { 49 | d = nav.plugins[SHOCKWAVE_FLASH].description; 50 | if (d && !(typeof nav.mimeTypes != UNDEF && nav.mimeTypes[FLASH_MIME_TYPE] && !nav.mimeTypes[FLASH_MIME_TYPE].enabledPlugin)) { // navigator.mimeTypes["application/x-shockwave-flash"].enabledPlugin indicates whether plug-ins are enabled or disabled in Safari 3+ 51 | plugin = true; 52 | ie = false; // cascaded feature detection for Internet Explorer 53 | d = d.replace(/^.*\s+(\S+\s+\S+$)/, "$1"); 54 | playerVersion[0] = parseInt(d.replace(/^(.*)\..*$/, "$1"), 10); 55 | playerVersion[1] = parseInt(d.replace(/^.*\.(.*)\s.*$/, "$1"), 10); 56 | playerVersion[2] = /[a-zA-Z]/.test(d) ? parseInt(d.replace(/^.*[a-zA-Z]+(.*)$/, "$1"), 10) : 0; 57 | } 58 | } 59 | else if (typeof win.ActiveXObject != UNDEF) { 60 | try { 61 | var a = new ActiveXObject(SHOCKWAVE_FLASH_AX); 62 | if (a) { // a will return null when ActiveX is disabled 63 | d = a.GetVariable("$version"); 64 | if (d) { 65 | ie = true; // cascaded feature detection for Internet Explorer 66 | d = d.split(" ")[1].split(","); 67 | playerVersion = [parseInt(d[0], 10), parseInt(d[1], 10), parseInt(d[2], 10)]; 68 | } 69 | } 70 | } 71 | catch(e) {} 72 | } 73 | return { w3:w3cdom, pv:playerVersion, wk:webkit, ie:ie, win:windows, mac:mac }; 74 | }(), 75 | 76 | /* Cross-browser onDomLoad 77 | - Will fire an event as soon as the DOM of a web page is loaded 78 | - Internet Explorer workaround based on Diego Perini's solution: http://javascript.nwbox.com/IEContentLoaded/ 79 | - Regular onload serves as fallback 80 | */ 81 | onDomLoad = function() { 82 | if (!ua.w3) { return; } 83 | if ((typeof doc.readyState != UNDEF && doc.readyState == "complete") || (typeof doc.readyState == UNDEF && (doc.getElementsByTagName("body")[0] || doc.body))) { // function is fired after onload, e.g. when script is inserted dynamically 84 | callDomLoadFunctions(); 85 | } 86 | if (!isDomLoaded) { 87 | if (typeof doc.addEventListener != UNDEF) { 88 | doc.addEventListener("DOMContentLoaded", callDomLoadFunctions, false); 89 | } 90 | if (ua.ie && ua.win) { 91 | doc.attachEvent(ON_READY_STATE_CHANGE, function() { 92 | if (doc.readyState == "complete") { 93 | doc.detachEvent(ON_READY_STATE_CHANGE, arguments.callee); 94 | callDomLoadFunctions(); 95 | } 96 | }); 97 | if (win == top) { // if not inside an iframe 98 | (function(){ 99 | if (isDomLoaded) { return; } 100 | try { 101 | doc.documentElement.doScroll("left"); 102 | } 103 | catch(e) { 104 | setTimeout(arguments.callee, 0); 105 | return; 106 | } 107 | callDomLoadFunctions(); 108 | })(); 109 | } 110 | } 111 | if (ua.wk) { 112 | (function(){ 113 | if (isDomLoaded) { return; } 114 | if (!/loaded|complete/.test(doc.readyState)) { 115 | setTimeout(arguments.callee, 0); 116 | return; 117 | } 118 | callDomLoadFunctions(); 119 | })(); 120 | } 121 | addLoadEvent(callDomLoadFunctions); 122 | } 123 | }(); 124 | 125 | function callDomLoadFunctions() { 126 | if (isDomLoaded) { return; } 127 | try { // test if we can really add/remove elements to/from the DOM; we don't want to fire it too early 128 | var t = doc.getElementsByTagName("body")[0].appendChild(createElement("span")); 129 | t.parentNode.removeChild(t); 130 | } 131 | catch (e) { return; } 132 | isDomLoaded = true; 133 | var dl = domLoadFnArr.length; 134 | for (var i = 0; i < dl; i++) { 135 | domLoadFnArr[i](); 136 | } 137 | } 138 | 139 | function addDomLoadEvent(fn) { 140 | if (isDomLoaded) { 141 | fn(); 142 | } 143 | else { 144 | domLoadFnArr[domLoadFnArr.length] = fn; // Array.push() is only available in IE5.5+ 145 | } 146 | } 147 | 148 | /* Cross-browser onload 149 | - Based on James Edwards' solution: http://brothercake.com/site/resources/scripts/onload/ 150 | - Will fire an event as soon as a web page including all of its assets are loaded 151 | */ 152 | function addLoadEvent(fn) { 153 | if (typeof win.addEventListener != UNDEF) { 154 | win.addEventListener("load", fn, false); 155 | } 156 | else if (typeof doc.addEventListener != UNDEF) { 157 | doc.addEventListener("load", fn, false); 158 | } 159 | else if (typeof win.attachEvent != UNDEF) { 160 | addListener(win, "onload", fn); 161 | } 162 | else if (typeof win.onload == "function") { 163 | var fnOld = win.onload; 164 | win.onload = function() { 165 | fnOld(); 166 | fn(); 167 | }; 168 | } 169 | else { 170 | win.onload = fn; 171 | } 172 | } 173 | 174 | /* Main function 175 | - Will preferably execute onDomLoad, otherwise onload (as a fallback) 176 | */ 177 | function main() { 178 | if (plugin) { 179 | testPlayerVersion(); 180 | } 181 | else { 182 | matchVersions(); 183 | } 184 | } 185 | 186 | /* Detect the Flash Player version for non-Internet Explorer browsers 187 | - Detecting the plug-in version via the object element is more precise than using the plugins collection item's description: 188 | a. Both release and build numbers can be detected 189 | b. Avoid wrong descriptions by corrupt installers provided by Adobe 190 | c. Avoid wrong descriptions by multiple Flash Player entries in the plugin Array, caused by incorrect browser imports 191 | - Disadvantage of this method is that it depends on the availability of the DOM, while the plugins collection is immediately available 192 | */ 193 | function testPlayerVersion() { 194 | var b = doc.getElementsByTagName("body")[0]; 195 | var o = createElement(OBJECT); 196 | o.setAttribute("type", FLASH_MIME_TYPE); 197 | var t = b.appendChild(o); 198 | if (t) { 199 | var counter = 0; 200 | (function(){ 201 | if (typeof t.GetVariable != UNDEF) { 202 | var d = t.GetVariable("$version"); 203 | if (d) { 204 | d = d.split(" ")[1].split(","); 205 | ua.pv = [parseInt(d[0], 10), parseInt(d[1], 10), parseInt(d[2], 10)]; 206 | } 207 | } 208 | else if (counter < 10) { 209 | counter++; 210 | setTimeout(arguments.callee, 10); 211 | return; 212 | } 213 | b.removeChild(o); 214 | t = null; 215 | matchVersions(); 216 | })(); 217 | } 218 | else { 219 | matchVersions(); 220 | } 221 | } 222 | 223 | /* Perform Flash Player and SWF version matching; static publishing only 224 | */ 225 | function matchVersions() { 226 | var rl = regObjArr.length; 227 | if (rl > 0) { 228 | for (var i = 0; i < rl; i++) { // for each registered object element 229 | var id = regObjArr[i].id; 230 | var cb = regObjArr[i].callbackFn; 231 | var cbObj = {success:false, id:id}; 232 | if (ua.pv[0] > 0) { 233 | var obj = getElementById(id); 234 | if (obj) { 235 | if (hasPlayerVersion(regObjArr[i].swfVersion) && !(ua.wk && ua.wk < 312)) { // Flash Player version >= published SWF version: Houston, we have a match! 236 | setVisibility(id, true); 237 | if (cb) { 238 | cbObj.success = true; 239 | cbObj.ref = getObjectById(id); 240 | cb(cbObj); 241 | } 242 | } 243 | else if (regObjArr[i].expressInstall && canExpressInstall()) { // show the Adobe Express Install dialog if set by the web page author and if supported 244 | var att = {}; 245 | att.data = regObjArr[i].expressInstall; 246 | att.width = obj.getAttribute("width") || "0"; 247 | att.height = obj.getAttribute("height") || "0"; 248 | if (obj.getAttribute("class")) { att.styleclass = obj.getAttribute("class"); } 249 | if (obj.getAttribute("align")) { att.align = obj.getAttribute("align"); } 250 | // parse HTML object param element's name-value pairs 251 | var par = {}; 252 | var p = obj.getElementsByTagName("param"); 253 | var pl = p.length; 254 | for (var j = 0; j < pl; j++) { 255 | if (p[j].getAttribute("name").toLowerCase() != "movie") { 256 | par[p[j].getAttribute("name")] = p[j].getAttribute("value"); 257 | } 258 | } 259 | showExpressInstall(att, par, id, cb); 260 | } 261 | else { // Flash Player and SWF version mismatch or an older Webkit engine that ignores the HTML object element's nested param elements: display alternative content instead of SWF 262 | displayAltContent(obj); 263 | if (cb) { cb(cbObj); } 264 | } 265 | } 266 | } 267 | else { // if no Flash Player is installed or the fp version cannot be detected we let the HTML object element do its job (either show a SWF or alternative content) 268 | setVisibility(id, true); 269 | if (cb) { 270 | var o = getObjectById(id); // test whether there is an HTML object element or not 271 | if (o && typeof o.SetVariable != UNDEF) { 272 | cbObj.success = true; 273 | cbObj.ref = o; 274 | } 275 | cb(cbObj); 276 | } 277 | } 278 | } 279 | } 280 | } 281 | 282 | function getObjectById(objectIdStr) { 283 | var r = null; 284 | var o = getElementById(objectIdStr); 285 | if (o && o.nodeName == "OBJECT") { 286 | if (typeof o.SetVariable != UNDEF) { 287 | r = o; 288 | } 289 | else { 290 | var n = o.getElementsByTagName(OBJECT)[0]; 291 | if (n) { 292 | r = n; 293 | } 294 | } 295 | } 296 | return r; 297 | } 298 | 299 | /* Requirements for Adobe Express Install 300 | - only one instance can be active at a time 301 | - fp 6.0.65 or higher 302 | - Win/Mac OS only 303 | - no Webkit engines older than version 312 304 | */ 305 | function canExpressInstall() { 306 | return !isExpressInstallActive && hasPlayerVersion("6.0.65") && (ua.win || ua.mac) && !(ua.wk && ua.wk < 312); 307 | } 308 | 309 | /* Show the Adobe Express Install dialog 310 | - Reference: http://www.adobe.com/cfusion/knowledgebase/index.cfm?id=6a253b75 311 | */ 312 | function showExpressInstall(att, par, replaceElemIdStr, callbackFn) { 313 | isExpressInstallActive = true; 314 | storedCallbackFn = callbackFn || null; 315 | storedCallbackObj = {success:false, id:replaceElemIdStr}; 316 | var obj = getElementById(replaceElemIdStr); 317 | if (obj) { 318 | if (obj.nodeName == "OBJECT") { // static publishing 319 | storedAltContent = abstractAltContent(obj); 320 | storedAltContentId = null; 321 | } 322 | else { // dynamic publishing 323 | storedAltContent = obj; 324 | storedAltContentId = replaceElemIdStr; 325 | } 326 | att.id = EXPRESS_INSTALL_ID; 327 | if (typeof att.width == UNDEF || (!/%$/.test(att.width) && parseInt(att.width, 10) < 310)) { att.width = "310"; } 328 | if (typeof att.height == UNDEF || (!/%$/.test(att.height) && parseInt(att.height, 10) < 137)) { att.height = "137"; } 329 | doc.title = doc.title.slice(0, 47) + " - Flash Player Installation"; 330 | var pt = ua.ie && ua.win ? "ActiveX" : "PlugIn", 331 | fv = "MMredirectURL=" + encodeURI(window.location).toString().replace(/&/g,"%26") + "&MMplayerType=" + pt + "&MMdoctitle=" + doc.title; 332 | if (typeof par.flashvars != UNDEF) { 333 | par.flashvars += "&" + fv; 334 | } 335 | else { 336 | par.flashvars = fv; 337 | } 338 | // IE only: when a SWF is loading (AND: not available in cache) wait for the readyState of the object element to become 4 before removing it, 339 | // because you cannot properly cancel a loading SWF file without breaking browser load references, also obj.onreadystatechange doesn't work 340 | if (ua.ie && ua.win && obj.readyState != 4) { 341 | var newObj = createElement("div"); 342 | replaceElemIdStr += "SWFObjectNew"; 343 | newObj.setAttribute("id", replaceElemIdStr); 344 | obj.parentNode.insertBefore(newObj, obj); // insert placeholder div that will be replaced by the object element that loads expressinstall.swf 345 | obj.style.display = "none"; 346 | (function(){ 347 | if (obj.readyState == 4) { 348 | obj.parentNode.removeChild(obj); 349 | } 350 | else { 351 | setTimeout(arguments.callee, 10); 352 | } 353 | })(); 354 | } 355 | createSWF(att, par, replaceElemIdStr); 356 | } 357 | } 358 | 359 | /* Functions to abstract and display alternative content 360 | */ 361 | function displayAltContent(obj) { 362 | if (ua.ie && ua.win && obj.readyState != 4) { 363 | // IE only: when a SWF is loading (AND: not available in cache) wait for the readyState of the object element to become 4 before removing it, 364 | // because you cannot properly cancel a loading SWF file without breaking browser load references, also obj.onreadystatechange doesn't work 365 | var el = createElement("div"); 366 | obj.parentNode.insertBefore(el, obj); // insert placeholder div that will be replaced by the alternative content 367 | el.parentNode.replaceChild(abstractAltContent(obj), el); 368 | obj.style.display = "none"; 369 | (function(){ 370 | if (obj.readyState == 4) { 371 | obj.parentNode.removeChild(obj); 372 | } 373 | else { 374 | setTimeout(arguments.callee, 10); 375 | } 376 | })(); 377 | } 378 | else { 379 | obj.parentNode.replaceChild(abstractAltContent(obj), obj); 380 | } 381 | } 382 | 383 | function abstractAltContent(obj) { 384 | var ac = createElement("div"); 385 | if (ua.win && ua.ie) { 386 | ac.innerHTML = obj.innerHTML; 387 | } 388 | else { 389 | var nestedObj = obj.getElementsByTagName(OBJECT)[0]; 390 | if (nestedObj) { 391 | var c = nestedObj.childNodes; 392 | if (c) { 393 | var cl = c.length; 394 | for (var i = 0; i < cl; i++) { 395 | if (!(c[i].nodeType == 1 && c[i].nodeName == "PARAM") && !(c[i].nodeType == 8)) { 396 | ac.appendChild(c[i].cloneNode(true)); 397 | } 398 | } 399 | } 400 | } 401 | } 402 | return ac; 403 | } 404 | 405 | /* Cross-browser dynamic SWF creation 406 | */ 407 | function createSWF(attObj, parObj, id) { 408 | var r, el = getElementById(id); 409 | if (ua.wk && ua.wk < 312) { return r; } 410 | if (el) { 411 | if (typeof attObj.id == UNDEF) { // if no 'id' is defined for the object element, it will inherit the 'id' from the alternative content 412 | attObj.id = id; 413 | } 414 | if (ua.ie && ua.win) { // Internet Explorer + the HTML object element + W3C DOM methods do not combine: fall back to outerHTML 415 | var att = ""; 416 | for (var i in attObj) { 417 | if (attObj[i] != Object.prototype[i]) { // filter out prototype additions from other potential libraries 418 | if (i.toLowerCase() == "data") { 419 | parObj.movie = attObj[i]; 420 | } 421 | else if (i.toLowerCase() == "styleclass") { // 'class' is an ECMA4 reserved keyword 422 | att += ' class="' + attObj[i] + '"'; 423 | } 424 | else if (i.toLowerCase() != "classid") { 425 | att += ' ' + i + '="' + attObj[i] + '"'; 426 | } 427 | } 428 | } 429 | var par = ""; 430 | for (var j in parObj) { 431 | if (parObj[j] != Object.prototype[j]) { // filter out prototype additions from other potential libraries 432 | par += ''; 433 | } 434 | } 435 | el.outerHTML = '' + par + ''; 436 | objIdArr[objIdArr.length] = attObj.id; // stored to fix object 'leaks' on unload (dynamic publishing only) 437 | r = getElementById(attObj.id); 438 | } 439 | else { // well-behaving browsers 440 | var o = createElement(OBJECT); 441 | o.setAttribute("type", FLASH_MIME_TYPE); 442 | for (var m in attObj) { 443 | if (attObj[m] != Object.prototype[m]) { // filter out prototype additions from other potential libraries 444 | if (m.toLowerCase() == "styleclass") { // 'class' is an ECMA4 reserved keyword 445 | o.setAttribute("class", attObj[m]); 446 | } 447 | else if (m.toLowerCase() != "classid") { // filter out IE specific attribute 448 | o.setAttribute(m, attObj[m]); 449 | } 450 | } 451 | } 452 | for (var n in parObj) { 453 | if (parObj[n] != Object.prototype[n] && n.toLowerCase() != "movie") { // filter out prototype additions from other potential libraries and IE specific param element 454 | createObjParam(o, n, parObj[n]); 455 | } 456 | } 457 | el.parentNode.replaceChild(o, el); 458 | r = o; 459 | } 460 | } 461 | return r; 462 | } 463 | 464 | function createObjParam(el, pName, pValue) { 465 | var p = createElement("param"); 466 | p.setAttribute("name", pName); 467 | p.setAttribute("value", pValue); 468 | el.appendChild(p); 469 | } 470 | 471 | /* Cross-browser SWF removal 472 | - Especially needed to safely and completely remove a SWF in Internet Explorer 473 | */ 474 | function removeSWF(id) { 475 | var obj = getElementById(id); 476 | if (obj && obj.nodeName == "OBJECT") { 477 | if (ua.ie && ua.win) { 478 | obj.style.display = "none"; 479 | (function(){ 480 | if (obj.readyState == 4) { 481 | removeObjectInIE(id); 482 | } 483 | else { 484 | setTimeout(arguments.callee, 10); 485 | } 486 | })(); 487 | } 488 | else { 489 | obj.parentNode.removeChild(obj); 490 | } 491 | } 492 | } 493 | 494 | function removeObjectInIE(id) { 495 | var obj = getElementById(id); 496 | if (obj) { 497 | for (var i in obj) { 498 | if (typeof obj[i] == "function") { 499 | obj[i] = null; 500 | } 501 | } 502 | obj.parentNode.removeChild(obj); 503 | } 504 | } 505 | 506 | /* Functions to optimize JavaScript compression 507 | */ 508 | function getElementById(id) { 509 | var el = null; 510 | try { 511 | el = doc.getElementById(id); 512 | } 513 | catch (e) {} 514 | return el; 515 | } 516 | 517 | function createElement(el) { 518 | return doc.createElement(el); 519 | } 520 | 521 | /* Updated attachEvent function for Internet Explorer 522 | - Stores attachEvent information in an Array, so on unload the detachEvent functions can be called to avoid memory leaks 523 | */ 524 | function addListener(target, eventType, fn) { 525 | target.attachEvent(eventType, fn); 526 | listenersArr[listenersArr.length] = [target, eventType, fn]; 527 | } 528 | 529 | /* Flash Player and SWF content version matching 530 | */ 531 | function hasPlayerVersion(rv) { 532 | var pv = ua.pv, v = rv.split("."); 533 | v[0] = parseInt(v[0], 10); 534 | v[1] = parseInt(v[1], 10) || 0; // supports short notation, e.g. "9" instead of "9.0.0" 535 | v[2] = parseInt(v[2], 10) || 0; 536 | return (pv[0] > v[0] || (pv[0] == v[0] && pv[1] > v[1]) || (pv[0] == v[0] && pv[1] == v[1] && pv[2] >= v[2])) ? true : false; 537 | } 538 | 539 | /* Cross-browser dynamic CSS creation 540 | - Based on Bobby van der Sluis' solution: http://www.bobbyvandersluis.com/articles/dynamicCSS.php 541 | */ 542 | function createCSS(sel, decl, media, newStyle) { 543 | if (ua.ie && ua.mac) { return; } 544 | var h = doc.getElementsByTagName("head")[0]; 545 | if (!h) { return; } // to also support badly authored HTML pages that lack a head element 546 | var m = (media && typeof media == "string") ? media : "screen"; 547 | if (newStyle) { 548 | dynamicStylesheet = null; 549 | dynamicStylesheetMedia = null; 550 | } 551 | if (!dynamicStylesheet || dynamicStylesheetMedia != m) { 552 | // create dynamic stylesheet + get a global reference to it 553 | var s = createElement("style"); 554 | s.setAttribute("type", "text/css"); 555 | s.setAttribute("media", m); 556 | dynamicStylesheet = h.appendChild(s); 557 | if (ua.ie && ua.win && typeof doc.styleSheets != UNDEF && doc.styleSheets.length > 0) { 558 | dynamicStylesheet = doc.styleSheets[doc.styleSheets.length - 1]; 559 | } 560 | dynamicStylesheetMedia = m; 561 | } 562 | // add style rule 563 | if (ua.ie && ua.win) { 564 | if (dynamicStylesheet && typeof dynamicStylesheet.addRule == OBJECT) { 565 | dynamicStylesheet.addRule(sel, decl); 566 | } 567 | } 568 | else { 569 | if (dynamicStylesheet && typeof doc.createTextNode != UNDEF) { 570 | dynamicStylesheet.appendChild(doc.createTextNode(sel + " {" + decl + "}")); 571 | } 572 | } 573 | } 574 | 575 | function setVisibility(id, isVisible) { 576 | if (!autoHideShow) { return; } 577 | var v = isVisible ? "visible" : "hidden"; 578 | if (isDomLoaded && getElementById(id)) { 579 | getElementById(id).style.visibility = v; 580 | } 581 | else { 582 | createCSS("#" + id, "visibility:" + v); 583 | } 584 | } 585 | 586 | /* Filter to avoid XSS attacks 587 | */ 588 | function urlEncodeIfNecessary(s) { 589 | var regex = /[\\\"<>\.;]/; 590 | var hasBadChars = regex.exec(s) != null; 591 | return hasBadChars && typeof encodeURIComponent != UNDEF ? encodeURIComponent(s) : s; 592 | } 593 | 594 | /* Release memory to avoid memory leaks caused by closures, fix hanging audio/video threads and force open sockets/NetConnections to disconnect (Internet Explorer only) 595 | */ 596 | var cleanup = function() { 597 | if (ua.ie && ua.win) { 598 | window.attachEvent("onunload", function() { 599 | // remove listeners to avoid memory leaks 600 | var ll = listenersArr.length; 601 | for (var i = 0; i < ll; i++) { 602 | listenersArr[i][0].detachEvent(listenersArr[i][1], listenersArr[i][2]); 603 | } 604 | // cleanup dynamically embedded objects to fix audio/video threads and force open sockets and NetConnections to disconnect 605 | var il = objIdArr.length; 606 | for (var j = 0; j < il; j++) { 607 | removeSWF(objIdArr[j]); 608 | } 609 | // cleanup library's main closures to avoid memory leaks 610 | for (var k in ua) { 611 | ua[k] = null; 612 | } 613 | ua = null; 614 | for (var l in swfobject) { 615 | swfobject[l] = null; 616 | } 617 | swfobject = null; 618 | }); 619 | } 620 | }(); 621 | 622 | return { 623 | /* Public API 624 | - Reference: http://code.google.com/p/swfobject/wiki/documentation 625 | */ 626 | registerObject: function(objectIdStr, swfVersionStr, xiSwfUrlStr, callbackFn) { 627 | if (ua.w3 && objectIdStr && swfVersionStr) { 628 | var regObj = {}; 629 | regObj.id = objectIdStr; 630 | regObj.swfVersion = swfVersionStr; 631 | regObj.expressInstall = xiSwfUrlStr; 632 | regObj.callbackFn = callbackFn; 633 | regObjArr[regObjArr.length] = regObj; 634 | setVisibility(objectIdStr, false); 635 | } 636 | else if (callbackFn) { 637 | callbackFn({success:false, id:objectIdStr}); 638 | } 639 | }, 640 | 641 | getObjectById: function(objectIdStr) { 642 | if (ua.w3) { 643 | return getObjectById(objectIdStr); 644 | } 645 | }, 646 | 647 | embedSWF: function(swfUrlStr, replaceElemIdStr, widthStr, heightStr, swfVersionStr, xiSwfUrlStr, flashvarsObj, parObj, attObj, callbackFn) { 648 | var callbackObj = {success:false, id:replaceElemIdStr}; 649 | if (ua.w3 && !(ua.wk && ua.wk < 312) && swfUrlStr && replaceElemIdStr && widthStr && heightStr && swfVersionStr) { 650 | setVisibility(replaceElemIdStr, false); 651 | addDomLoadEvent(function() { 652 | widthStr += ""; // auto-convert to string 653 | heightStr += ""; 654 | var att = {}; 655 | if (attObj && typeof attObj === OBJECT) { 656 | for (var i in attObj) { // copy object to avoid the use of references, because web authors often reuse attObj for multiple SWFs 657 | att[i] = attObj[i]; 658 | } 659 | } 660 | att.data = swfUrlStr; 661 | att.width = widthStr; 662 | att.height = heightStr; 663 | var par = {}; 664 | if (parObj && typeof parObj === OBJECT) { 665 | for (var j in parObj) { // copy object to avoid the use of references, because web authors often reuse parObj for multiple SWFs 666 | par[j] = parObj[j]; 667 | } 668 | } 669 | if (flashvarsObj && typeof flashvarsObj === OBJECT) { 670 | for (var k in flashvarsObj) { // copy object to avoid the use of references, because web authors often reuse flashvarsObj for multiple SWFs 671 | if (typeof par.flashvars != UNDEF) { 672 | par.flashvars += "&" + k + "=" + flashvarsObj[k]; 673 | } 674 | else { 675 | par.flashvars = k + "=" + flashvarsObj[k]; 676 | } 677 | } 678 | } 679 | if (hasPlayerVersion(swfVersionStr)) { // create SWF 680 | var obj = createSWF(att, par, replaceElemIdStr); 681 | if (att.id == replaceElemIdStr) { 682 | setVisibility(replaceElemIdStr, true); 683 | } 684 | callbackObj.success = true; 685 | callbackObj.ref = obj; 686 | } 687 | else if (xiSwfUrlStr && canExpressInstall()) { // show Adobe Express Install 688 | att.data = xiSwfUrlStr; 689 | showExpressInstall(att, par, replaceElemIdStr, callbackFn); 690 | return; 691 | } 692 | else { // show alternative content 693 | setVisibility(replaceElemIdStr, true); 694 | } 695 | if (callbackFn) { callbackFn(callbackObj); } 696 | }); 697 | } 698 | else if (callbackFn) { callbackFn(callbackObj); } 699 | }, 700 | 701 | switchOffAutoHideShow: function() { 702 | autoHideShow = false; 703 | }, 704 | 705 | ua: ua, 706 | 707 | getFlashPlayerVersion: function() { 708 | return { major:ua.pv[0], minor:ua.pv[1], release:ua.pv[2] }; 709 | }, 710 | 711 | hasFlashPlayerVersion: hasPlayerVersion, 712 | 713 | createSWF: function(attObj, parObj, replaceElemIdStr) { 714 | if (ua.w3) { 715 | return createSWF(attObj, parObj, replaceElemIdStr); 716 | } 717 | else { 718 | return undefined; 719 | } 720 | }, 721 | 722 | showExpressInstall: function(att, par, replaceElemIdStr, callbackFn) { 723 | if (ua.w3 && canExpressInstall()) { 724 | showExpressInstall(att, par, replaceElemIdStr, callbackFn); 725 | } 726 | }, 727 | 728 | removeSWF: function(objElemIdStr) { 729 | if (ua.w3) { 730 | removeSWF(objElemIdStr); 731 | } 732 | }, 733 | 734 | createCSS: function(selStr, declStr, mediaStr, newStyleBoolean) { 735 | if (ua.w3) { 736 | createCSS(selStr, declStr, mediaStr, newStyleBoolean); 737 | } 738 | }, 739 | 740 | addDomLoadEvent: addDomLoadEvent, 741 | 742 | addLoadEvent: addLoadEvent, 743 | 744 | getQueryParamValue: function(param) { 745 | var q = doc.location.search || doc.location.hash; 746 | if (q) { 747 | if (/\?/.test(q)) { q = q.split("?")[1]; } // strip question mark 748 | if (param == null) { 749 | return urlEncodeIfNecessary(q); 750 | } 751 | var pairs = q.split("&"); 752 | for (var i = 0; i < pairs.length; i++) { 753 | if (pairs[i].substring(0, pairs[i].indexOf("=")) == param) { 754 | return urlEncodeIfNecessary(pairs[i].substring((pairs[i].indexOf("=") + 1))); 755 | } 756 | } 757 | } 758 | return ""; 759 | }, 760 | 761 | // For internal usage only 762 | expressInstallCallback: function() { 763 | if (isExpressInstallActive) { 764 | var obj = getElementById(EXPRESS_INSTALL_ID); 765 | if (obj && storedAltContent) { 766 | obj.parentNode.replaceChild(storedAltContent, obj); 767 | if (storedAltContentId) { 768 | setVisibility(storedAltContentId, true); 769 | if (ua.ie && ua.win) { storedAltContent.style.display = "block"; } 770 | } 771 | if (storedCallbackFn) { storedCallbackFn(storedCallbackObj); } 772 | } 773 | isExpressInstallActive = false; 774 | } 775 | } 776 | }; 777 | }(); 778 | -------------------------------------------------------------------------------- /src/flvtools.c: -------------------------------------------------------------------------------- 1 | /*! \file 2 | * 3 | * \brief Tools to manage FLV format file managment 4 | * 5 | * \author JYG 6 | * 7 | */ 8 | 9 | #ifdef RTMP_FFMPEG 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | //#include 22 | //#include 23 | 24 | #include "flvtools.h" 25 | 26 | /*** Marcos Rev02 ***/ 27 | #define AST_4 10400 28 | #define AST_6 10600 29 | #define AST_6_1 10601 30 | #define AST_8 10800 31 | #define AST_11 110000 32 | #define AST_12 120000 33 | 34 | /********************/ 35 | 36 | 37 | 38 | /* 39 | * Static Global variable 40 | */ 41 | 42 | 43 | 44 | /* 45 | * Static prototype 46 | */ 47 | #if ASTERISK_VERSION_NUM < AST_11 48 | static uint8_t getVideoFlags(stFLV_data *pCtx, int codecid, int frameType); 49 | static uint8_t getAudioFlags(stFLV_data *pCtx, int channels, int sampleSize, int sampleRate, int codecid); 50 | #else 51 | static uint8_t getVideoFlags(stFLV_data *pCtx, struct ast_format codecid, int frameType); 52 | static uint8_t getAudioFlags(stFLV_data *pCtx, int channels, int sampleSize, int sampleRate, struct ast_format codecid); 53 | #endif 54 | static uint8_t getTag(stFLV_data *pCtx, int type); 55 | //static int64_t av_dbl2int(double d); 56 | static void FLV_writeHeaderMetaData(stFLV_data *pCtx); 57 | 58 | 59 | 60 | 61 | 62 | /* 63 | * Macro defines 64 | */ 65 | #define WR_8(p, val) *(p)++ = val; 66 | #define WR_16(p, val) {WR_8(p, (val >> 8));WR_8(p, val);} 67 | #define WR_24(p, val) {WR_16(p, (val >> 8));WR_8(p, val);} 68 | #define WR_32(p, val) {WR_8(p, (val >> 24)); \ 69 | WR_8(p, (val >> 16)); \ 70 | WR_8(p, (val >> 8));\ 71 | WR_8(p, (val));} 72 | 73 | #define WR_64(p, val) WR_32(p, (uint32_t)(val >> 32)); WR_32(p, (uint32_t)(val & 0xffffffff)); 74 | 75 | 76 | #define AMF_STRING(p, string) {\ 77 | size_t len = strlen(string);\ 78 | WR_16(p, len);\ 79 | memcpy(p, string, len);\ 80 | p+=len;\ 81 | } 82 | 83 | #define AMF_DOUBLE(p, value) {\ 84 | WR_8(p, AMF_DATA_TYPE_NUMBER);\ 85 | WR_64(p, av_dbl2int(value));\ 86 | } 87 | 88 | #define AMF_BOOL(p, value) {\ 89 | WR_8(p, AMF_DATA_TYPE_BOOL);\ 90 | WR_8(p, !!value);\ 91 | } 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | /*! \fn int FLV_init(int channels, int sampleSize, int sampleRate, int codec) 100 | * \brief save packet info to create header tags 101 | * \param channels channels number 0=mono 1=stereo 102 | * \param sampleSize sample size 8 or 16 bits 103 | * \param sampleRate sample rate in Hz 104 | * \param codec audio and video codec used 105 | * \param videoFrameType video frame type keyframe, inter frame, disposable inter frame 106 | * \param pFilename record filename 107 | */ 108 | // Marcos Rev95: Difference header 109 | #if ASTERISK_VERSION_NUM < AST_11 110 | eFlvReturnCode FLV_init(stFLV_data *pCtx, 111 | int channels, int sampleSize, int sampleRate, int codec, int videoFrameType, 112 | char *pFilename) 113 | #else 114 | eFlvReturnCode FLV_init(stFLV_data *pCtx, 115 | int channels, int sampleSize, int sampleRate, struct ast_format codec, int videoFrameType, 116 | char *pFilename) 117 | #endif 118 | { 119 | if (pCtx == NULL){ 120 | ast_log(LOG_ERROR, "FLV ctx NULL!\n"); 121 | return FLV_ERROR_FD; 122 | } 123 | 124 | if (pFilename == NULL) 125 | { 126 | ast_log(LOG_ERROR, "filename NULL!\n"); 127 | return FLV_ERROR_FILENAME; 128 | } 129 | 130 | if (pCtx->fd != 0) 131 | { 132 | ast_log(LOG_ERROR, "WARNING: fd not null!\n"); 133 | } 134 | 135 | ast_debug(6, " ch %d splSize %d splRate %d codec %s frmType %d file %s\n", 136 | channels, sampleSize, sampleRate, GET_FORMAT_NAME(codec), videoFrameType, pFilename); // Marcos Rev96: show format name instead of hexadecimal value 137 | 138 | // Open file 139 | if ( (pCtx->fd = open(pFilename, O_CREAT|O_WRONLY|O_TRUNC, 0664)) <= 0) { 140 | ast_log(LOG_ERROR, "Cannot create file %s : %s\n", pFilename, strerror(errno)); 141 | return FLV_ERROR_WRITING_HDR; 142 | } 143 | else { 144 | ast_debug(3, "Record FLV file %s fd %d\n", pFilename, pCtx->fd); 145 | 146 | // Initialize context 147 | if ((sampleSize == 0) || (sampleRate == 0)) 148 | { 149 | if (COMPARE_VARFORMAT_IDFORMAT(codec, AST_FORMAT_SPEEX)) { // Marcos Rev97, Original codec & AST_FORMAT_SPEEX 150 | if (!sampleSize) 151 | sampleSize = FLV_SAMPLESIZE_16BITS; 152 | if (!sampleRate) 153 | sampleRate = 11025; 154 | } 155 | else { 156 | if (!sampleSize) 157 | sampleSize = FLV_SAMPLESIZE_8BITS; 158 | if (!sampleRate) 159 | sampleRate = 8000; 160 | } 161 | ast_debug(3, "Force sample size %d sample rate %d\n", sampleSize, sampleRate); 162 | } 163 | pCtx->filesize = 0; 164 | pCtx->audioDataSize = 0; 165 | pCtx->sampleRate = sampleRate; 166 | pCtx->sampleSize = sampleSize; 167 | pCtx->stereo = channels==2?1:0; 168 | pCtx->pictureSizeGetted = 0; 169 | 170 | if ((pCtx->audio_tag = getAudioFlags(pCtx, channels, sampleSize, sampleRate, codec)) < 0) 171 | return pCtx->audio_tag; 172 | 173 | if ((pCtx->video_tag = getVideoFlags(pCtx, codec, videoFrameType)) < 0) 174 | return pCtx->video_tag; 175 | 176 | // Create header 177 | if (FLV_writeHeader(pCtx) != FLV_OK){ 178 | ast_log(LOG_ERROR, "Failed to write header flv file %s\n", pFilename); 179 | close(pCtx->fd); 180 | pCtx->fd = 0; 181 | return FLV_ERROR_WRITING_HDR; 182 | } 183 | else { 184 | ast_debug(3, "FLV: tag audio 0x%X video 0x%02X\n", pCtx->audio_tag, pCtx->video_tag); 185 | return FLV_OK; 186 | } 187 | } 188 | 189 | 190 | 191 | } 192 | 193 | /*! \fn int FLV_close() 194 | * \brief ended file 195 | */ 196 | eFlvReturnCode FLV_close(stFLV_data *pCtx, long iDuration, int iWidth, int iHeigth) 197 | { 198 | if ((pCtx == NULL) || (pCtx->fd <= 0)) { 199 | ast_log(LOG_ERROR, "bad ctx\n"); 200 | return FLV_ERROR_FD; 201 | } 202 | 203 | // Write file size if non null 204 | if (pCtx->filesize > 0) 205 | { 206 | char buffer[10]; 207 | char *p=buffer; 208 | 209 | lseek(pCtx->fd, pCtx->fileSizeOffset, SEEK_SET); 210 | AMF_DOUBLE(p, pCtx->filesize); 211 | if (write(pCtx->fd, buffer, FLV_SIZE_DOUBLE) < 0) { 212 | ast_log(LOG_ERROR, "FLV_close: Failed to write filesize %s\n", strerror(errno)); 213 | return FLV_ERROR_WRITING; 214 | } 215 | ast_debug(3, "FLV: filesize %d written\n", pCtx->filesize); 216 | } 217 | 218 | if (pCtx->audioDataSize > 0) 219 | { 220 | char buffer[10]; 221 | char *p=buffer; 222 | int duration=0; 223 | 224 | if (iDuration != 0) // pCtx->sampleRate != 0) 225 | { 226 | //duration = pCtx->audioDataSize / pCtx->sampleRate; 227 | //duration = ast_tvdiff_ms(ast_tvnow(), client->timestart) / 1000; 228 | duration = (iDuration/1000)+1; 229 | lseek(pCtx->fd, pCtx->durationOffset, SEEK_SET); 230 | AMF_DOUBLE(p, duration); 231 | if (write(pCtx->fd, buffer, FLV_SIZE_DOUBLE) < 0) { 232 | ast_log(LOG_ERROR, "FLV_close: Failed to write filesize %s\n", strerror(errno)); 233 | return FLV_ERROR_WRITING; 234 | } 235 | ast_debug(3, "FLV: duration %d written\n", duration); 236 | } 237 | } 238 | if (iWidth != 0) 239 | { 240 | char buffer[10]; 241 | char *p=buffer; 242 | 243 | lseek(pCtx->fd, pCtx->widthOffset, SEEK_SET); 244 | AMF_DOUBLE(p, iWidth); 245 | if (write(pCtx->fd, buffer, FLV_SIZE_DOUBLE) < 0) { 246 | ast_log(LOG_ERROR, "FLV_close: Failed to write width %s\n", strerror(errno)); 247 | return FLV_ERROR_WRITING; 248 | } 249 | } 250 | if (iHeigth != 0) 251 | { 252 | char buffer[10]; 253 | char *p=buffer; 254 | 255 | lseek(pCtx->fd, pCtx->heigthOffset, SEEK_SET); 256 | AMF_DOUBLE(p, iHeigth); 257 | if (write(pCtx->fd, buffer, FLV_SIZE_DOUBLE) < 0) { 258 | ast_log(LOG_ERROR, "FLV_close: Failed to write heigth %s\n", strerror(errno)); 259 | return FLV_ERROR_WRITING; 260 | } 261 | } 262 | ast_debug(3, "FLV: picture size %dx%d written\n", iWidth, iHeigth); 263 | 264 | close(pCtx->fd); 265 | pCtx->fd = 0; 266 | pCtx->pictureSizeGetted = 0; 267 | 268 | 269 | return FLV_OK; 270 | } 271 | 272 | 273 | /*! \fn int FLV_writeHeader(stFLV_data *pCtx) 274 | * \brief Writes header info in FLV file with filedescriptor \a pCtx->fd. 275 | * \param pCtx->fd The descriptor to write to. 276 | */ 277 | eFlvReturnCode FLV_writeHeader(stFLV_data *pCtx) 278 | { 279 | char header[FLV_HEADER_SIZE] = {0}; 280 | uint32_t size = 0; 281 | 282 | if ((pCtx == NULL) || (pCtx->fd <= 0)) { 283 | ast_log(LOG_ERROR, "FLV_writeHeader: bad ctx\n"); 284 | return FLV_ERROR_FD; 285 | } 286 | 287 | /* 288 | * FLV header is compose like this : 289 | * 3 bytes : signature "FLV" 290 | * uint8 : Version = 1 291 | * uint8 : Flag : bitmask: 4 is audio, 1 is video (5=audio+video) 292 | * uint32 : offset : size of header => 9 293 | */ 294 | header[0] = 'F'; 295 | header[1] = 'L'; 296 | header[2] = 'V'; 297 | header[3] = 0x01; // Version 298 | header[4] = 0x05; // We always create audio & video stream 299 | header[8] = 0x09; 300 | 301 | // Write header 302 | if (write(pCtx->fd, header, FLV_HEADER_SIZE) < 0) { 303 | ast_log(LOG_ERROR, "FLV_writeHeader: Failed to write header %s\n", strerror(errno)); 304 | return FLV_ERROR_WRITING; 305 | } 306 | pCtx->filesize += FLV_HEADER_SIZE; 307 | 308 | // Write first tag size to 0 309 | size = 0; 310 | if (write(pCtx->fd, (char*)&size, sizeof(uint32_t)) < 0) { 311 | ast_log(LOG_ERROR, "FLV_writeHeader: Failed to write size in header %s\n", strerror(errno)); 312 | return FLV_ERROR_WRITING; 313 | } 314 | pCtx->filesize += sizeof(uint32_t); 315 | 316 | // Need to add META-DATA for file info 317 | FLV_writeHeaderMetaData(pCtx); 318 | 319 | return FLV_OK; 320 | } 321 | 322 | /*! \fn int FLV_writePkt(stFLV_data *pCtx, int type, int len, char *pData) 323 | * \brief Writes packet data in FLV file with filedescriptor \a pCtx->fd. 324 | * \param pCtx->fd The descriptor to write to. 325 | * \param type stream type of data. 326 | * \param len data len. 327 | * \param pData packet data. 328 | */ 329 | eFlvReturnCode FLV_writePkt(stFLV_data *pCtx, int type, int timestamp, int len, uint8_t *pData) 330 | { 331 | uint8_t tagHdr[FLV_HEADER_TAG_SIZE+1];// +1 for first pkt byte 332 | uint8_t *p = tagHdr; 333 | int hdrSize=0; 334 | 335 | if ((pCtx == NULL) || (pCtx->fd <= 0)) { 336 | ast_log(LOG_ERROR, "FLV_writePkt: bad ctx\n"); 337 | return FLV_ERROR_FD; 338 | } 339 | if (len <= 0) { 340 | ast_log(LOG_ERROR, "FLV_writePkt: bad data length\n"); 341 | return FLV_ERROR_DATA; 342 | } 343 | if ((type != FLV_TYPE_AUDIO) && (type != FLV_TYPE_VIDEO) && (type != FLV_TYPE_META)) { 344 | ast_log(LOG_ERROR, "FLV_writePkt: bad type %d \n", type); 345 | return FLV_ERROR_BAD_TYPE; 346 | } 347 | 348 | 349 | 350 | /* 351 | * Create TAG 352 | */ 353 | WR_8 (p, type); 354 | if ((type == FLV_TYPE_AUDIO) || (type == FLV_TYPE_VIDEO) ){ 355 | WR_24(p, (len+1)); // length + tag 356 | } 357 | else { 358 | WR_24(p, len); 359 | } 360 | WR_24(p, timestamp); // timestamp 361 | if (timestamp > 0xFFFFFF) { 362 | WR_8(p, (timestamp & 0xFF000000)>>24); 363 | } 364 | else { 365 | WR_8(p, 0); 366 | } 367 | WR_24(p, 0); // streamid 368 | 369 | // The first byte of an audio&video tag is specific 370 | if ((type == FLV_TYPE_AUDIO) || (type == FLV_TYPE_VIDEO) ) 371 | { 372 | WR_8(p, getTag(pCtx, type)); // first byte 373 | hdrSize = FLV_HEADER_TAG_SIZE+1; 374 | } 375 | else 376 | hdrSize = FLV_HEADER_TAG_SIZE; 377 | 378 | 379 | // Write header tag + specific tag for audio&video 380 | if (write(pCtx->fd, tagHdr, hdrSize) < 0) { 381 | ast_log(LOG_ERROR, "Failed to write tag %s\n", strerror(errno)); 382 | return FLV_ERROR_WRITING; 383 | } 384 | pCtx->filesize += hdrSize; 385 | //ast_debug(6, " wrote %d bytes for header tag %s\n", FLV_HEADER_TAG_SIZE+1, type==FLV_TYPE_AUDIO?"audio":"video"); 386 | 387 | // Write data 388 | if (write(pCtx->fd, pData, len) < 0) { 389 | ast_log(LOG_ERROR, "Failed to write data %s\n", strerror(errno)); 390 | return FLV_ERROR_WRITING; 391 | } 392 | pCtx->filesize += len; 393 | if (type == FLV_TYPE_VIDEO) 394 | pCtx->audioDataSize += len; 395 | ///ast_debug(6, " wrote %d data len for data %s\n", len, type==FLV_TYPE_AUDIO?"audio":"video"); 396 | 397 | // write previous size 398 | p = tagHdr; 399 | WR_32(p, (len+hdrSize)); 400 | 401 | if (write(pCtx->fd, tagHdr, 4) < 0) { 402 | ast_log(LOG_ERROR, "Failed to size %s\n", strerror(errno)); 403 | return FLV_ERROR_WRITING; 404 | } 405 | pCtx->filesize += 4; 406 | //ast_debug(6, " wrote %d datasize for tag %s\n", (len+FLV_HEADER_TAG_SIZE+1), type==FLV_TYPE_AUDIO?"audio":"video"); 407 | 408 | ast_debug(3, "FLV: %d bytes %s written timestamp %d (tag 0x%02X size %d) in fd %d\n", 409 | len, type==FLV_TYPE_AUDIO?"audio":"video", timestamp, getTag(pCtx, type), 410 | len+FLV_HEADER_TAG_SIZE, pCtx->fd); 411 | 412 | return FLV_OK; 413 | } 414 | 415 | 416 | 417 | 418 | 419 | 420 | /*! \fn int FLV_writeHeaderMetaData(int fd) 421 | * \brief add meta-data in file to add file inforamtions 422 | * \param fd fiel descriptor 423 | */ 424 | static void FLV_writeHeaderMetaData(stFLV_data *pCtx) 425 | { 426 | char buffer[2048]; 427 | char *ptr = buffer; 428 | char *pBegin = buffer; 429 | char *pLgTag = buffer + 1; // point on tag data len 430 | char *pStartData; // begining of data 431 | int sizeWr = 0; 432 | 433 | 434 | /* write meta_tag */ 435 | WR_8(ptr, FLV_TYPE_META); // tag type META 436 | WR_24(ptr, 0); // write size at the end 437 | WR_32(ptr, 0); // time stamp 438 | WR_24(ptr, 0); // stream id 439 | 440 | /* now data of data_size size */ 441 | pStartData = ptr; 442 | 443 | /* first event name as a string */ 444 | WR_8(ptr, AMF_DATA_TYPE_STRING); 445 | AMF_STRING(ptr, "onMetaData"); // 12 bytes 446 | 447 | /* mixed array (hash) with size and string/type/data tuples */ 448 | WR_8(ptr, AMF_DATA_TYPE_MIXEDARRAY); 449 | WR_32(ptr, 12); // 12 elt: duration+width+height+video 450 | 451 | AMF_STRING(ptr, "duration"); 452 | // Get current position & offset to save duration at the end 453 | pCtx->durationOffset = lseek(pCtx->fd, 0, SEEK_CUR) + ptr-pBegin; 454 | AMF_DOUBLE(ptr, 0.0); 455 | 456 | AMF_STRING(ptr, "width"); 457 | // Get current position & offset to save width 458 | pCtx->widthOffset = lseek(pCtx->fd, 0, SEEK_CUR) + ptr-pBegin; 459 | AMF_DOUBLE(ptr, 176.0); 460 | 461 | AMF_STRING(ptr, "height"); 462 | // Get current position & offset to save width 463 | pCtx->heigthOffset = lseek(pCtx->fd, 0, SEEK_CUR) + ptr-pBegin; 464 | AMF_DOUBLE(ptr, 144.0); 465 | ast_debug(8, "FLV: write header: default picture size 176x144\n"); 466 | 467 | AMF_STRING(ptr, "videodatarate"); 468 | AMF_DOUBLE(ptr, 0.0); 469 | 470 | AMF_STRING(ptr, "framerate"); 471 | AMF_DOUBLE(ptr, 0.0); 472 | 473 | AMF_STRING(ptr, "videocodecid"); 474 | AMF_DOUBLE(ptr, pCtx->vcodec); // 2); 475 | 476 | AMF_STRING(ptr, "audiodatarate"); 477 | AMF_DOUBLE(ptr, 0.0); 478 | 479 | AMF_STRING(ptr, "audiosamplerate"); 480 | AMF_DOUBLE(ptr, pCtx->sampleRate); //11025); 481 | 482 | AMF_STRING(ptr, "audiosamplesize"); 483 | AMF_DOUBLE(ptr, pCtx->sampleSize); //8); 484 | 485 | AMF_STRING(ptr, "stereo"); 486 | AMF_BOOL(ptr, pCtx->stereo);// 0); 487 | 488 | 489 | AMF_STRING(ptr, "audiocodecid"); 490 | AMF_DOUBLE(ptr, pCtx->acodec); //11); 491 | 492 | ast_debug(8, "FLV: write header: videodatarate=framerate=audiodatarate=0.0\n"); 493 | ast_debug(8, "FLV: write header: audiocodecid=%d(%Xh) videocodecid=%d(%Xh)\n", pCtx->acodec, pCtx->acodec, pCtx->vcodec, pCtx->vcodec); 494 | ast_debug(8, "FLV: write header: sampleRate=%d(%Xh) sampleSize=%d(%Xh) stereo=%d\n", pCtx->sampleRate, pCtx->sampleSize, pCtx->stereo); 495 | 496 | AMF_STRING(ptr, "filesize"); 497 | // Get current position to save filesize offset. 498 | pCtx->fileSizeOffset = lseek(pCtx->fd, 0, SEEK_CUR) + ptr-pBegin; 499 | AMF_DOUBLE(ptr, 0.0); // delayed write 500 | 501 | AMF_STRING(ptr, ""); 502 | WR_8(ptr, AMF_DATA_TYPE_OBJECT_END); 503 | 504 | // write tag data len 505 | WR_24(pLgTag, (ptr-pStartData)); 506 | 507 | 508 | // Calcul and write tag size 509 | sizeWr = ptr-pBegin; 510 | if (sizeWr > 0) { 511 | WR_32(ptr, sizeWr); 512 | } 513 | else{ 514 | sizeWr = 0; 515 | WR_32(ptr, 0); 516 | } 517 | 518 | /*ast_log(LOG_ERROR, "-->dbgJYG: splSize %d splRate %d stereo %d acodec 0x%X vcodec 0x%X\n", 519 | pCtx->sampleSize, pCtx->sampleRate, pCtx->stereo, pCtx->acodec, pCtx->vcodec);*/ 520 | 521 | ast_debug(3, " onMetaData %d bytes written\n", sizeWr ); 522 | // write data + 4( = tagsize for previous tag) 523 | if (write(pCtx->fd, (char*)buffer, sizeWr+4) < 0) { 524 | ast_log(LOG_ERROR, "Failed to write metaData for header %s\n", strerror(errno)); 525 | } 526 | pCtx->filesize += sizeWr; 527 | 528 | 529 | } 530 | 531 | #if 0 532 | /*! \fn int av_dbl2int(double d) 533 | * \brief convert a double to int 534 | * \param d double to convert 535 | */ 536 | static int64_t av_dbl2int(double d){ 537 | int e; 538 | if ( !d) return 0; 539 | else if(d-d) { 540 | return 0x7FF0000000000000LL + ((int64_t)(d<0)<<63) + (d!=d); 541 | } 542 | d= frexp(d, &e); 543 | return (int64_t)(d<0)<<63 | (e+1022LL)<<52 | (int64_t)((fabs(d)-0.5)*(1LL<<53)); 544 | } 545 | #endif 546 | 547 | /*! \fn int getTag(int type) 548 | * \brief return tag type to use 549 | * \param type packet type 550 | */ 551 | static uint8_t getTag(stFLV_data *pCtx, int type) 552 | { 553 | switch(type) 554 | { 555 | case FLV_TYPE_AUDIO: 556 | return pCtx->audio_tag; 557 | break; 558 | case FLV_TYPE_VIDEO: 559 | return pCtx->video_tag; 560 | break; 561 | case FLV_TYPE_META: 562 | return pCtx->meta_tag; 563 | break; 564 | } 565 | return FLV_ERROR; 566 | } 567 | 568 | 569 | /*! \fn int getAudioFlags(int channels, int sampleSize, int sampleRate, int codec) 570 | * \brief return audio flag to use in tag type 571 | * \param channels channels number 0=mono 1=stereo 572 | * \param sampleSize sample size 8 or 16 bits 573 | * \param sampleRate sample rate in Hz 574 | * \param codec codec used 575 | */ 576 | #if ASTERISK_VERSION_NUM < AST_11 577 | static uint8_t getAudioFlags(stFLV_data *pCtx, int channels, int sampleSize, int sampleRate, int codecid) 578 | #else 579 | static uint8_t getAudioFlags(stFLV_data *pCtx, int channels, int sampleSize, int sampleRate, struct ast_format codecid) 580 | #endif 581 | { 582 | uint8_t flags = 0; 583 | 584 | if (sampleSize == FLV_SAMPLESIZE_16BITS) { 585 | flags |= FLV_SAMPLESSIZE_16BIT; 586 | } 587 | else 588 | flags |= FLV_SAMPLESSIZE_8BIT; 589 | 590 | switch (sampleRate) { 591 | case 44100: 592 | flags |= FLV_SAMPLERATE_44100HZ; 593 | break; 594 | case 22050: 595 | flags |= FLV_SAMPLERATE_22050HZ; 596 | break; 597 | case 11025: 598 | flags |= FLV_SAMPLERATE_11025HZ; 599 | break; 600 | case 8000: //nellymoser only 601 | case 5512: //not mp3 602 | flags |= FLV_SAMPLERATE_SPECIAL; 603 | break; 604 | /*if(enc->codec_id != CODEC_ID_MP3){ 605 | flags |= FLV_SAMPLERATE_SPECIAL; 606 | break; 607 | } */ 608 | default: 609 | ast_debug(1, "unsupported sample rate %d. Force speex 11kHz\n", sampleRate); 610 | flags |= FLV_SAMPLERATE_11025HZ; 611 | } 612 | 613 | if (channels > 1) { 614 | flags |= FLV_STEREO; 615 | } 616 | 617 | // Marcos Rev89 618 | /* After realise that in the variable client->audiocodec (received here as "codecid") we are just 619 | going to store one format (the audio format) there's no sense to do the audio masking 620 | Original: 621 | int codec = codecid & AST_FORMAT_AUDIO_MASK; 622 | switch(codec){ 623 | */ 624 | switch(FORMAT_VAR_TO_ID(codecid)){ 625 | // 626 | case AST_FORMAT_SPEEX: 627 | flags |= FLV_CODECID_SPEEX; 628 | pCtx->acodec = FLV_CODECID_SPEEX >> FLV_AUDIO_CODECID_OFFSET; 629 | break; 630 | case AST_FORMAT_SLINEAR: 631 | //flags |= FLV_CODECID_PCM | FLV_SAMPLESSIZE_16BIT; 632 | flags |= FLV_CODECID_PCM | FLV_SAMPLESSIZE_8BIT; 633 | pCtx->acodec = FLV_CODECID_PCM >> FLV_AUDIO_CODECID_OFFSET; 634 | break; 635 | case AST_FORMAT_ALAW: 636 | flags |= FLV_CODECID_G711_ALAW | FLV_SAMPLESSIZE_16BIT; 637 | pCtx->acodec = FLV_CODECID_G711_ALAW >> FLV_AUDIO_CODECID_OFFSET; 638 | break; 639 | case AST_FORMAT_ULAW: 640 | flags |= FLV_CODECID_G711_ULAW | FLV_SAMPLESSIZE_16BIT; 641 | pCtx->acodec = FLV_CODECID_G711_ULAW >> FLV_AUDIO_CODECID_OFFSET; 642 | break; 643 | 644 | /* 645 | case CODEC_ID_NELLYMOSER: 646 | if (sampleRate == 8000) { 647 | flags |= FLV_CODECID_NELLYMOSER_8KHZ_MONO | FLV_SAMPLESSIZE_16BIT; 648 | } else { 649 | flags |= FLV_CODECID_NELLYMOSER | FLV_SAMPLESSIZE_16BIT; 650 | } 651 | break; 652 | */ 653 | 654 | /* 655 | case CODEC_ID_ADPCM_SWF: 656 | flags |= FLV_CODECID_ADPCM | FLV_SAMPLESSIZE_16BIT; 657 | break; 658 | case CODEC_ID_PCM_S16LE: 659 | flags |= FLV_CODECID_RESERVED | FLV_SAMPLESSIZE_16BIT; 660 | break; 661 | case CODEC_ID_MP3: 662 | flags |= FLV_CODECID_MP3 | FLV_SAMPLESSIZE_16BIT; 663 | break; 664 | case 0: 665 | flags |= enc->codec_tag<<4; 666 | break; 667 | */ 668 | default: 669 | // Marcos Rev91 670 | // Original: ast_debug(1, "unsupported audio codec id %d from 0x%X. Force SPEEX\n", codec, codecid); 671 | ast_debug(1, "unsupported audio codec %s. Force SPEEX\n", GET_FORMAT_NAME(codecid)); 672 | flags |= FLV_CODECID_SPEEX; 673 | pCtx->acodec = FLV_CODECID_SPEEX >> FLV_AUDIO_CODECID_OFFSET; 674 | } 675 | 676 | ast_debug(7, " FLV audio tag 0x%X from ch %d splSize %d splRate %d codecid %s\n", 677 | flags, channels, sampleSize, sampleRate, GET_FORMAT_NAME(codecid) ); 678 | return (flags); 679 | } 680 | 681 | /*! \fn int getVideoFlags(int codec, int frameType) 682 | * \brief return audio flag to use in tag type 683 | * \param type of frame 684 | * \param codec codec used 685 | * codec id : 686 | * FLV_CODECID_H263 687 | * FLV_CODECID_SCREEN 688 | * FLV_CODECID_VP6 689 | * FLV_CODECID_VP6A 690 | * FLV_CODECID_SCREEN2 691 | * FLV_CODECID_H264 692 | * frame type : 693 | * FLV_FRAME_KEY 694 | * FLV_FRAME_INTER 695 | * FLV_FRAME_DISP_INTER 696 | */ 697 | #if ASTERISK_VERSION_NUM < AST_11 698 | static uint8_t getVideoFlags(stFLV_data *pCtx, int codecid, int frameType) 699 | #else 700 | static uint8_t getVideoFlags(stFLV_data *pCtx, struct ast_format codecid, int frameType) 701 | #endif 702 | { 703 | uint8_t flags = 0; 704 | // Marcos Rev90 705 | /* Since we realised that client->audiocodec (received here as "codecid") just stores the audio codec of the client, 706 | there's no video format inside this variable and it always goes to the default case, the switch has no sense. 707 | Original: 708 | int codec = codecid & AST_FORMAT_VIDEO_MASK; 709 | switch (codec) 710 | { 711 | case AST_FORMAT_H263: 712 | case AST_FORMAT_H263_PLUS: 713 | flags = FLV_CODECID_H263; 714 | pCtx->vcodec = FLV_CODECID_H263; 715 | break; 716 | case AST_FORMAT_H264: 717 | flags = FLV_CODECID_H264 | frameType; 718 | pCtx->vcodec = FLV_CODECID_H264; 719 | break; 720 | default: 721 | ast_debug(1, " unsupported video codec id %d from 0x%X. Force H263\n", codec, codecid); 722 | flags = FLV_CODECID_H263 | frameType; 723 | pCtx->vcodec = FLV_CODECID_H263; 724 | } 725 | */ 726 | flags = FLV_CODECID_H263 | frameType; 727 | pCtx->vcodec = FLV_CODECID_H263; 728 | // 729 | 730 | if ((frameType != FLV_FRAME_KEY) && (frameType != FLV_FRAME_INTER) && (frameType != FLV_FRAME_DISP_INTER)) 731 | { 732 | ast_debug(1, " unsupported video frame type 0x%02X. Force FRAME KEY\n",frameType); 733 | } 734 | flags |= frameType; 735 | ast_debug(7, " FLV video tag 0x%X from codecid %s frameType %d\n", 736 | flags, GET_FORMAT_NAME(codecid), frameType ); 737 | return (flags); 738 | 739 | } 740 | 741 | 742 | /*! \fn int getPictureSize(int codec, int frameType) 743 | * \brief return audio flag to use in tag type 744 | * \param pCtx FLV context 745 | * \param pData data video frame 746 | * 747 | * From flvdec.c of ffmpeg 748 | 749 | picture header 750 | if (get_bits_long(&s->gb, 17) != 1) { 751 | av_log(s->avctx, AV_LOG_ERROR, "Bad picture start code\n"); 752 | return -1; 753 | } 754 | format = get_bits(&s->gb, 5); 755 | if (format != 0 && format != 1) { 756 | av_log(s->avctx, AV_LOG_ERROR, "Bad picture format\n"); 757 | return -1; 758 | } 759 | s->h263_flv = format+1; 760 | s->picture_number = get_bits(&s->gb, 8); 761 | 762 | s->pict_type = AV_PICTURE_TYPE_I + get_bits(&s->gb, 2); 763 | 764 | // Get last 2bits of first 32 bits 765 | p32 = (uint32_t *)pData; 766 | p8 = pData+1; 767 | */ 768 | eFlvReturnCode FLV_getPictureSize(int *pWidth, int *pHeight, uint8_t *pData) 769 | { 770 | int format, width=0, height=0; 771 | uint8_t *p8; 772 | uint16_t *p16; 773 | 774 | // Get last 2bits of first 32 bits 775 | p8 = pData+3; 776 | format = (((*p8) & 0x03) << 1) | ( ((*(p8+1)) & 0x80) >> 7); 777 | 778 | /* 779 | ast_log(LOG_ERROR, " -->dbgJYG: format=%d pData=0x%X p8=0x%X\n", format, pData, p8); 780 | ast_log(LOG_ERROR, " %02X %02X %02X %02X %02X %02X %02X %02X \n", 781 | pData[0], pData[1], pData[2], pData[3], 782 | pData[4], pData[5], pData[6], pData[7]); 783 | */ 784 | switch (format) { 785 | case 0: 786 | // Width is the fisrt 8bits and heigth next 8bits 787 | width = ((*p8 & 0xEF) << 1) | ( (*(p8+1) & 0x80) >> 7); 788 | p8++; 789 | height = ((*p8 & 0xEF) << 1) | ( (*(p8+1) & 0x80) >> 7); 790 | break; 791 | case 1: 792 | // Width is the fisrt 16bits and heigth next 16bits 793 | p16 = (uint16_t*)p8; 794 | p8 = (uint8_t*) (p16+1); 795 | 796 | width = ((*p16 & 0xEFFF) << 1) | (uint16_t) ( ( (*(p8+1) & 0x80) >> 7)); 797 | p16++; 798 | p8 = (uint8_t*) (p16+1); 799 | height = width = ((*p16 & 0xEFFF) << 1) | (uint16_t) ( ( (*(p8+1) & 0x80) >> 7)); 800 | break; 801 | case 2: 802 | width = 352; 803 | height = 288; 804 | break; 805 | case 3: 806 | width = 176; 807 | height = 144; 808 | break; 809 | case 4: 810 | width = 128; 811 | height = 96; 812 | break; 813 | case 5: 814 | width = 320; 815 | height = 240; 816 | break; 817 | case 6: 818 | width = 160; 819 | height = 120; 820 | break; 821 | default: 822 | width = height = 0; 823 | break; 824 | } 825 | 826 | 827 | *pWidth = width; 828 | *pHeight = height; 829 | return 0; 830 | } 831 | 832 | int FLV_getPictureType(uint8_t *pData) 833 | { 834 | int format, width=0, height=0; 835 | uint8_t *p8; 836 | uint16_t *p16; 837 | uint16_t typefr; 838 | 839 | p8 = pData+3; 840 | /*format = (((*p8) & 0x03) << 1) | ( ((*(p8+1)) & 0x80) >> 7); 841 | 842 | switch (format) { 843 | case 0: 844 | // Width is the fisrt 8bits and heigth next 8bits 845 | width = ((*p8 & 0xEF) << 1) | ( (*(p8+1) & 0x80) >> 7); 846 | p8++; 847 | height = ((*p8 & 0xEF) << 1) | ( (*(p8+1) & 0x80) >> 7); 848 | break; 849 | case 1: 850 | // Width is the fisrt 16bits and heigth next 16bits 851 | p16 = (uint16_t*)p8; 852 | p8 = (uint8_t*) (p16+1); 853 | 854 | width = ((*p16 & 0xEFFF) << 1) | (uint16_t) ( ( (*(p8+1) & 0x80) >> 7)); 855 | p16++; 856 | p8 = (uint8_t*) (p16+1); 857 | height = width = ((*p16 & 0xEFFF) << 1) | (uint16_t) ( ( (*(p8+1) & 0x80) >> 7)); 858 | break; 859 | case 2: 860 | width = 352; 861 | height = 288; 862 | break; 863 | case 3: 864 | width = 176; 865 | height = 144; 866 | break; 867 | case 4: 868 | width = 128; 869 | height = 96; 870 | break; 871 | case 5: 872 | width = 320; 873 | height = 240; 874 | break; 875 | case 6: 876 | width = 160; 877 | height = 120; 878 | break; 879 | default: 880 | width = height = 0; 881 | break; 882 | } */ 883 | 884 | 885 | typefr = (uint16_t) ( ( (*(p8+1) & 0x60) >> 5)); 886 | //ast_log(LOG_ERROR, " -->dbgJYG: format=%d typeframe=%d pData=0x%X p8=0x%X: %02X %02X\n", 887 | // format, typefr, pData, p8, p8[0], p8[1]); 888 | return 1+typefr; 889 | } 890 | 891 | 892 | 893 | /* 894 | * Essai de fonction pour créer des fichiers images bases sur les images pleine h263 895 | * Non utilisé pour l'instant 896 | * 897 | * En attendant on enregistre un fichier flv avec juste une image pleine! 898 | */ 899 | #if 0 900 | // !! NOT WORKING! 901 | void decodeYUVtoRGB(int *rgba, unsigned char *yuv420sp, int width, int height) 902 | { 903 | int frameSize = width * height; 904 | int i,j,yp; 905 | 906 | ast_debug(6, "YUV->RGB: %dx%d size %d\n", width, height, frameSize); 907 | 908 | for (j = 0, yp = 0; j < height; j++) 909 | { 910 | int uvp = frameSize + (j >> 1) * width, u = 0, v = 0; 911 | for (i = 0; i < width; i++, yp++) { 912 | int y = (0xff & ((int) yuv420sp[yp])) - 16; 913 | if (y < 0) 914 | y = 0; 915 | if ((i & 1) == 0) { 916 | v = (0xff & yuv420sp[uvp++]) - 128; 917 | u = (0xff & yuv420sp[uvp++]) - 128; 918 | } 919 | 920 | int y1192 = 1192 * y; 921 | int r = (y1192 + 1634 * v); 922 | int g = (y1192 - 833 * v - 400 * u); 923 | int b = (y1192 + 2066 * u); 924 | 925 | if (r < 0) 926 | r = 0; 927 | else if (r > 262143) 928 | r = 262143; 929 | if (g < 0) 930 | g = 0; 931 | else if (g > 262143) 932 | g = 262143; 933 | if (b < 0) 934 | b = 0; 935 | else if (b > 262143) 936 | b = 262143; 937 | 938 | // rgb[yp] = 0xff000000 | ((r << 6) & 0xff0000) | ((g >> 2) & 939 | // 0xff00) | ((b >> 10) & 0xff); 940 | // rgba, divide 2^10 ( >> 10) 941 | rgba[yp] = ((r << 14) & 0xff000000) | ((g << 6) & 0xff0000) 942 | | ((b >> 2) | 0xff00); 943 | } 944 | } 945 | } 946 | 947 | 948 | int writeBmpImage(FILE *fp, unsigned char *buff, int len, int width, int height) 949 | { 950 | char head[14] ={0}; 951 | int *ptr; 952 | /*int *intArray; 953 | 954 | intArray = malloc(width * height); 955 | decodeYUVtoRGB(intArray, buff, width, height); 956 | 957 | len=width*height*sizeof(int); 958 | */ 959 | // Create header 960 | head[0] = 'B'; 961 | head[1] = 'M'; 962 | 963 | ptr = (int*)&(head[2]); 964 | *ptr = len + 14; // write len 965 | 966 | ptr = (int*)&(head[10]); 967 | *ptr = 15; // write offset of data 968 | 969 | 970 | if (fwrite( head, 1, 14, fp ) <= 0) 971 | { 972 | ast_log(LOG_ERROR, "Failed to write header bmp file %s\n", strerror(errno)); 973 | //free(intArray); 974 | return -1; 975 | } 976 | 977 | // write data 978 | if (fwrite( buff /*(char*)intArray*/, 1, len, fp ) <= 0) 979 | { 980 | ast_log(LOG_ERROR, "Failed to write %d bytes to bmp file: %s\n", len, strerror(errno)); 981 | //free(intArray); 982 | return -1; 983 | } 984 | ast_debug(6, "BmpFile: %d bytes wrote\n", len); 985 | fflush(fp); 986 | //free(intArray); 987 | return 0; 988 | } 989 | 990 | 991 | #include 992 | #include 993 | 994 | /* put_jpeg_yuv420p_file converts an YUV420P coded image to a jpeg image and writes 995 | * it to an already open file. 996 | * Inputs: 997 | * - image is the image in YUV420P format. 998 | * - width and height are the dimensions of the image 999 | * - quality is the jpeg encoding quality 0-100% 1000 | * Output: 1001 | * - The jpeg is written directly to the file given by the file pointer fp 1002 | * Returns nothing 1003 | */ 1004 | void put_jpeg_yuv420p_file(FILE *fp, unsigned char *image[3], int width, int height, int quality) 1005 | { 1006 | int i,j; 1007 | 1008 | JSAMPROW y[16],cb[16],cr[16]; // y[2][5] = color sample of row 2 and pixel column 5; (one plane) 1009 | JSAMPARRAY data[3]; // t[0][2][5] = color sample 0 of row 2 and column 5 1010 | 1011 | struct jpeg_compress_struct cinfo; 1012 | struct jpeg_error_mgr jerr; 1013 | 1014 | ast_debug(6, "jpeg create %dx%d img file\n", width, height); 1015 | 1016 | 1017 | data[0] = y; 1018 | data[1] = cb; 1019 | data[2] = cr; 1020 | 1021 | cinfo.err = jpeg_std_error(&jerr); // errors get written to stderr 1022 | 1023 | jpeg_create_compress(&cinfo); 1024 | cinfo.image_width = width; 1025 | cinfo.image_height = height; 1026 | cinfo.input_components = 3; 1027 | jpeg_set_defaults(&cinfo); 1028 | 1029 | jpeg_set_colorspace(&cinfo, JCS_YCbCr); 1030 | 1031 | cinfo.raw_data_in = TRUE; // supply downsampled data 1032 | #if JPEG_LIB_VERSION >= 70 1033 | #warning using JPEG_LIB_VERSION >= 70 1034 | cinfo.do_fancy_downsampling = FALSE; // Fix segfault with v7 1035 | #endif 1036 | cinfo.comp_info[0].h_samp_factor = 2; 1037 | cinfo.comp_info[0].v_samp_factor = 2; 1038 | cinfo.comp_info[1].h_samp_factor = 1; 1039 | cinfo.comp_info[1].v_samp_factor = 1; 1040 | cinfo.comp_info[2].h_samp_factor = 1; 1041 | cinfo.comp_info[2].v_samp_factor = 1; 1042 | 1043 | jpeg_set_quality(&cinfo, quality, TRUE); 1044 | cinfo.dct_method = JDCT_FASTEST; 1045 | 1046 | jpeg_stdio_dest(&cinfo, fp); // data written to file 1047 | jpeg_start_compress(&cinfo, TRUE); 1048 | 1049 | 1050 | for (j = 0; j < height; j += 16) { 1051 | for (i = 0; i < 16; i++) { 1052 | y[i] = image[0] + cinfo.image_width * (i + j); 1053 | // need to handle other chroma subsampling 1054 | if (i % 2 == 0) { 1055 | cb[i / 2] = image[1] + width*height + width/2*((i+j)/2); 1056 | cr[i / 2] = image[2] + width*height + width*height/4 + width/2*((i+j)/2); 1057 | } 1058 | } 1059 | ast_debug(9, "jpeg: treat %d line\n",j); 1060 | jpeg_write_raw_data(&cinfo, data, 16); 1061 | } 1062 | 1063 | /* 1064 | for (j=0;j YYYYUUVV 1095 | /-*pTmp = client->buffer + 1; 1096 | yuv_data[0] = pTmp; 1097 | pTmp += client->pictureOut_width; 1098 | yuv_data[1] = pTmp; 1099 | pTmp += client->pictureOut_width/2; 1100 | yuv_data[2] = pTmp; 1101 | 1102 | //(unsigned char *) (client->buffer + 1) 1103 | put_jpeg_yuv420p_file(fpJpeg, yuv_data , 1104 | client->pictureOut_width, client->pictureOut_heigth, 100); 1105 | *-/ 1106 | writeBmpImage(fpJpeg, (unsigned char *) (client->buffer + 1), client->bufferLen,client->pictureOut_width, client->pictureOut_heigth) ; 1107 | fclose(fpJpeg); 1108 | } 1109 | else 1110 | { 1111 | RTMP_VERBOSE(client, "failed to open %s\n", filename); 1112 | } 1113 | */ 1114 | 1115 | 1116 | #endif // if 0 essai de fonction 1117 | 1118 | 1119 | 1120 | 1121 | #if 0 1122 | 1123 | On ne peut pas utiliser les fct get_bits() parce que la compil genere une erreur 1124 | un des includes inclus config.h hors ce fichier est genere a la compil de ffmpeg 1125 | donc il n existe pas qd dans l export de la machine d integration !!! 1126 | 1127 | 1128 | /*! \fn int getPictureSize(int codec, int frameType) 1129 | * \brief return audio flag to use in tag type 1130 | * \param pCtx FLV context 1131 | * \param pData data video frame 1132 | * 1133 | * From flvdec.c of ffmpeg 1134 | 1135 | picture header 1136 | if (get_bits_long(&s->gb, 17) != 1) { 1137 | av_log(s->avctx, AV_LOG_ERROR, "Bad picture start code\n"); 1138 | return -1; 1139 | } 1140 | format = get_bits(&s->gb, 5); 1141 | if (format != 0 && format != 1) { 1142 | av_log(s->avctx, AV_LOG_ERROR, "Bad picture format\n"); 1143 | return -1; 1144 | } 1145 | s->h263_flv = format+1; 1146 | s->picture_number = get_bits(&s->gb, 8); 1147 | 1148 | // Get last 2bits of first 32 bits 1149 | p32 = (uint32_t *)pData; 1150 | p8 = pData+1; 1151 | 1152 | format = (((*p32) & 0x3) << 1) | ( ((*p8) & 0x80) >> 7); 1153 | */ 1154 | eFlvReturnCode FLV_getPictureSize(int *pWidth, int *pHeight, uint8_t *pData) 1155 | { 1156 | int format, width=0, height=0; 1157 | int tmp1, tmp2; 1158 | //uint32_t *p32; 1159 | //uint8_t *p8; 1160 | GetBitContext bitsCtx; 1161 | 1162 | // init bits ctx size max read for picture size is 30+3+ 2x16 => 96 bits 1163 | init_get_bits(&bitsCtx, pData, 96); 1164 | 1165 | // Zap 30 bits 1166 | tmp1 = get_bits(&bitsCtx, 30); 1167 | format = get_bits(&bitsCtx, 3); 1168 | 1169 | switch (format) { 1170 | case 0: 1171 | width = get_bits(&bitsCtx, 8); 1172 | height = get_bits(&bitsCtx, 8); 1173 | break; 1174 | case 1: 1175 | width = get_bits(&bitsCtx, 16); 1176 | height = get_bits(&bitsCtx, 16); 1177 | break; 1178 | case 2: 1179 | width = 352; 1180 | height = 288; 1181 | break; 1182 | case 3: 1183 | width = 176; 1184 | height = 144; 1185 | break; 1186 | case 4: 1187 | width = 128; 1188 | height = 96; 1189 | break; 1190 | case 5: 1191 | width = 320; 1192 | height = 240; 1193 | break; 1194 | case 6: 1195 | width = 160; 1196 | height = 120; 1197 | break; 1198 | default: 1199 | width = height = 0; 1200 | break; 1201 | } 1202 | 1203 | *pWidth = width; 1204 | *pHeight = height; 1205 | 1206 | 1207 | return 0; 1208 | } 1209 | 1210 | 1211 | /*! \fn int getPictureSize(int codec, int frameType) 1212 | * \brief return audio flag to use in tag type 1213 | * \param pCtx FLV context 1214 | * \param pData data video frame 1215 | */ 1216 | static int getPictureSize(stFLV_data *pCtx, int dataLen, uint8_t *pData) 1217 | { 1218 | AVCodec *decoder; 1219 | AVCodecContext *decoderCtx; 1220 | AVFrame *decoderPic; 1221 | 1222 | decoderCtx = avcodec_alloc_context(); 1223 | decoder = avcodec_find_decoder(CODEC_ID_FLV1); 1224 | 1225 | if (avcodec_open(decoderCtx, decoder) < 0){ 1226 | return FLV_ERROR; 1227 | } 1228 | 1229 | // Decode frame to get size 1230 | { 1231 | AVPacket avpkt; 1232 | int got_picture; 1233 | 1234 | decoderPic = avcodec_alloc_frame(); 1235 | av_init_packet(&avpkt); 1236 | avpkt.data = pData; 1237 | avpkt.size = dataLen; 1238 | 1239 | avpkt.flags = AV_PKT_FLAG_KEY; 1240 | avcodec_decode_video2(decoderCtx, decoderPic, &got_picture, &avpkt); 1241 | 1242 | /* Check size */ 1243 | ast_debug(7, " Frame size %dx%d\n", decoderCtx->width, decoderCtx->height); 1244 | pCtx->pictureSizeGetted = 1; 1245 | } 1246 | 1247 | return FLV_OK; 1248 | } 1249 | #endif 1250 | 1251 | #endif 1252 | 1253 | --------------------------------------------------------------------------------