From a721e68dfc0eb7ce49b5d83bdbfb4ba3b43e318a Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Sat, 11 Feb 2012 18:45:02 +0100 Subject: [PATCH] Imported Upstream version 0.4.5+svn3912~dfsg0 --- Makefile | 3 +- applications/GPAX/GPAX.cpp | 76 - applications/GPAX/GPAX.def | 9 - applications/GPAX/GPAX.dsp | 173 -- applications/GPAX/GPAX.h | 495 --- applications/GPAX/GPAX.idl | 84 - applications/GPAX/GPAX.rc | 149 - applications/GPAX/GPAX.rgs | 175 -- applications/GPAX/GPAXPlugin.cpp | 635 ---- applications/GPAX/GPAXPlugin.h | 258 -- applications/GPAX/GPAX_i.c | 178 -- applications/GPAX/GPAX_p.c | 602 ---- applications/GPAX/GPAXps.def | 11 - applications/GPAX/GPAXps.mk | 16 - applications/GPAX/StdAfx.cpp | 12 - applications/GPAX/StdAfx.h | 34 - applications/GPAX/dlldata.c | 38 - applications/GPAX/gpax.bmp | Bin 246 -> 0 bytes applications/GPAX/resource.h | 24 - applications/Makefile | 6 +- applications/generators/MPEG4/main.c | 8 +- applications/hbbtvplayer/README.txt | 15 + applications/hbbtvplayer/REQUIREMENT | 10 + applications/hbbtvplayer/doc/avancement.txt | 17 + applications/hbbtvplayer/doc/introduction.txt | 5 + .../hbbtvplayer/doc/projectmanagement.txt | 16 + applications/hbbtvplayer/getsources.sh | 44 + .../hbbtvplayer/hbbtvbrowserplugin/README | 14 + .../hbbtvplayer/hbbtvbrowserplugin/autogen.sh | 5 + .../hbbtvbrowserplugin/configure.ac | 19 + .../hbbtvBrowserPlugin-NPAPI-20111108.pptx | Bin 0 -> 651985 bytes .../doc/listeOIPFProfile.xlsx | Bin 0 -> 18967 bytes .../hbbtvbrowserplugin.pc.in | 12 + .../hbbtvbrowserplugin.vcproj | 311 ++ .../hbbtvbrowserplugin/makefile.am | 7 + .../hbbtvbrowserplugin/makefile.old | 110 + .../hbbtvbrowserplugin/hbbtvbrowserplugin.cbp | 92 + .../hbbtvbrowserplugin.layout | 106 + .../releases/hbbtvbrowserplugin-0.01.tar.gz | Bin 0 -> 11234 bytes .../hbbtvbrowserplugin/src/applicationclass.c | 265 ++ .../hbbtvbrowserplugin/src/applicationclass.h | 59 + .../src/applicationprivatedataclass.c | 219 ++ .../src/applicationprivatedataclass.h | 54 + .../src/configurationclass.c | 182 ++ .../src/configurationclass.h | 43 + .../hbbtvbrowserplugin/src/downloadclass.c | 209 ++ .../hbbtvbrowserplugin/src/downloadclass.h | 42 + .../src/downloadcollectionclass.c | 194 ++ .../src/downloadcollectionclass.h | 43 + .../src/drmcontrolinfocollectionclass.c | 20 + .../src/drmcontrolinfocollectionclass.h | 26 + .../src/drmcontrolinformationclass.c | 20 + .../src/drmcontrolinformationclass.h | 26 + .../src/hbbtvbrowserplugin.c | 335 ++ .../src/hbbtvbrowserplugin.h | 82 + .../src/hbbtvbrowserpluginapi.c | 45 + .../src/hbbtvbrowserpluginapi.h | 32 + .../hbbtvbrowserplugin/src/keysetclass.c | 330 ++ .../hbbtvbrowserplugin/src/keysetclass.h | 51 + .../hbbtvbrowserplugin/src/makefile.am | 47 + .../src/oipfapplicationmanager.c | 203 ++ .../src/oipfapplicationmanager.h | 59 + .../src/oipfconfiguration.c | 182 ++ .../src/oipfconfiguration.h | 42 + .../src/oipfdownloadmanager.c | 233 ++ .../src/oipfdownloadmanager.h | 48 + .../src/oipfdownloadtrigger.c | 205 ++ .../src/oipfdownloadtrigger.h | 46 + .../hbbtvbrowserplugin/src/videobroadcast.c | 431 +++ .../hbbtvbrowserplugin/src/videobroadcast.h | 70 + .../src/webkit-plugin-header/npapi.h | 901 ++++++ .../src/webkit-plugin-header/npfunctions.h | 223 ++ .../src/webkit-plugin-header/npruntime.h | 393 +++ .../src/webkit-plugin-header/nptypes.h | 122 + .../hbbtvplayer/hbbtvterminal/autogen.sh | 6 + .../hbbtvplayer/hbbtvterminal/configure.ac | 40 + .../hbbtvterminal/hbbtv_terminal.vcproj | 219 ++ .../hbbtvplayer/hbbtvterminal/makefile.am | 17 + .../hbbtvplayer/hbbtvterminal/makefile.old | 81 + .../hbbtvterminal/HbbtvTerminal.cbp | 35 + .../hbbtvterminal/HbbtvTerminal.layout | 28 + .../hbbtvterminal/src/hbbtv_channel.cpp | 253 ++ .../hbbtvterminal/src/hbbtv_demux.cpp | 388 +++ .../hbbtvterminal/src/hbbtv_keycontrol.cpp | 354 +++ .../hbbtvterminal/src/hbbtv_tools.cpp | 51 + .../hbbtvterminal/src/hbbtvterminal.cpp | 1081 +++++++ .../hbbtvterminal/src/hbbtvterminal.h | 350 +++ .../hbbtvplayer/hbbtvterminal/src/makefile.am | 25 + applications/hbbtvplayer/install.sh | 78 + applications/hbbtvplayer/listdependencies | 79 + .../codeblocks/hbbtvplayer.workspace | 7 + applications/m3u82mpd/m3u82mpd.vcproj | 183 ++ applications/m3u82mpd/main.c | 160 +- applications/mp42avi/main.c | 16 +- applications/mp42ts/main.c | 80 +- applications/mp42ts/mp42ts.dsp | 99 - applications/mp42ts/mp42ts.vcproj | 231 -- applications/mp4box/filedump.c | 477 ++- applications/mp4box/fileimport.c | 75 +- applications/mp4box/live.c | 26 +- applications/mp4box/main.c | 344 +- applications/mp4client/extract.c | 14 +- applications/mp4client/main.c | 139 +- applications/osmo4_ios/Resources/icon.png | Bin 0 -> 7682 bytes applications/osmo4_ios/Resources/osmo.icns | Bin 0 -> 147235 bytes applications/osmo4_ios/extract.c | 702 +++++ applications/osmo4_ios/main.c | 2117 +++++++++++++ applications/osmo4_ios/osmo4ios-Info.plist | 24 + applications/osmo4_wx/Darwin.Info.plist | 32 + .../osmo4_wx/Darwin.InfoPlist.strings | Bin 0 -> 456 bytes applications/osmo4_wx/Darwin.Osmo.icns | Bin 0 -> 38570 bytes applications/osmo4_wx/Makefile | 94 + applications/osmo4_wx/Playlist.cpp | 826 +++++ applications/osmo4_wx/Playlist.h | 134 + applications/osmo4_wx/fileprops.cpp | 608 ++++ applications/osmo4_wx/fileprops.h | 83 + applications/osmo4_wx/menubtn.cpp | 863 +++++ applications/osmo4_wx/menubtn.h | 314 ++ .../osmo4_wx/osmo4.ico | Bin applications/osmo4_wx/osmo4.xpm | 301 ++ applications/osmo4_wx/playlist.xpm | 158 + applications/osmo4_wx/resource.h | 16 + applications/osmo4_wx/toolbar.xpm | 254 ++ applications/osmo4_wx/wxGPACControl.cpp | 1031 ++++++ applications/osmo4_wx/wxGPACControl.h | 137 + applications/osmo4_wx/wxOsmo4.cpp | 2347 ++++++++++++++ applications/osmo4_wx/wxOsmo4.h | 390 +++ applications/osmo4_wx/wxOsmo4.rc | 72 + applications/osmozilla/Makefile | 112 - applications/osmozilla/nsIOsmozilla.h | 125 - applications/osmozilla/nsIOsmozilla.idl | 9 - applications/osmozilla/nsIOsmozilla.xpt_linux | Bin 180 -> 0 bytes applications/osmozilla/nsIOsmozilla.xpt_w32 | Bin 180 -> 0 bytes applications/osmozilla/osmo_npapi.cpp | 773 ----- applications/osmozilla/osmo_npapi.h | 133 - applications/osmozilla/osmozilla.cpp | 499 --- applications/osmozilla/osmozilla.def | 6 - applications/osmozilla/osmozilla.h | 71 - applications/osmozilla/osmozilla.png | Bin 121239 -> 0 bytes applications/osmozilla/osmozilla.rc | 128 - applications/osmozilla/readme.txt | 8 - applications/osmozilla/resource.h | 21 - applications/testapps/broadcaster/Makefile | 5 +- .../testapps/broadcaster/broadcaster.c | 6 +- .../testapps/broadcaster/sdp_generator.c | 20 +- applications/testapps/dmbrs/main.c | 6 +- applications/testapps/mpedemux/main.c | 3 - applications/testapps/mpeg2ts/main.c | 2 +- applications/udptsseg/main.c | 4 +- bin/w32_rel/nsis_install/gpac_installer.nsi | 738 ----- .../release/nsis_install/gpac_installer.nsi | 1 + configure | 742 +++-- doc/configuration.html | Bin 44316 -> 95134 bytes doc/man/mp4box.1 | 3 + doc/man/mp4client.1 | 55 + .../include/OpenSVCDecoder/ControlLayer.h | 45 + extra_lib/include/OpenSVCDecoder/ParseAU.h | 39 + .../OpenSVCDecoder/SVCDecoder_ietr_api.h | 128 + .../include/OpenSVCDecoder/SvcInterface.h | 59 + extra_lib/include/avcap/CaptureDevice.h | 189 ++ extra_lib/include/avcap/CaptureHandler.h | 58 + extra_lib/include/avcap/CaptureManager.h | 132 + extra_lib/include/avcap/Connector.h | 117 + extra_lib/include/avcap/ConnectorManager.h | 151 + extra_lib/include/avcap/ControlManager.h | 82 + extra_lib/include/avcap/Control_avcap.h | 206 ++ extra_lib/include/avcap/DeviceCollector.h | 131 + extra_lib/include/avcap/DeviceDescriptor.h | 196 ++ extra_lib/include/avcap/FormatManager.h | 310 ++ extra_lib/include/avcap/IOBuffer.h | 135 + extra_lib/include/avcap/Interval.h | 56 + extra_lib/include/avcap/Manager.h | 67 + extra_lib/include/avcap/ProbeValues.h | 51 + extra_lib/include/avcap/Tuner_avcap.h | 188 ++ extra_lib/include/avcap/avcap-export.h | 43 + extra_lib/include/avcap/avcap.h | 45 + .../avcap/linux/AVC_ConnectorManager.h | 61 + .../include/avcap/linux/AVC_ControlManager.h | 59 + extra_lib/include/avcap/linux/AVC_Device.h | 103 + .../avcap/linux/AVC_DeviceDescriptor.h | 103 + .../include/avcap/linux/AVC_FormatManager.h | 69 + extra_lib/include/avcap/linux/AVC_Reader.h | 74 + .../include/avcap/linux/AVC_VidCapManager.h | 102 + .../include/avcap/linux/V4L1_Connector.h | 57 + .../avcap/linux/V4L1_ConnectorManager.h | 60 + extra_lib/include/avcap/linux/V4L1_Control.h | 88 + .../include/avcap/linux/V4L1_ControlManager.h | 49 + extra_lib/include/avcap/linux/V4L1_Device.h | 83 + .../avcap/linux/V4L1_DeviceDescriptor.h | 132 + .../include/avcap/linux/V4L1_FormatManager.h | 107 + .../include/avcap/linux/V4L1_VidCapManager.h | 111 + .../include/avcap/linux/V4L2_BoolControl.h | 68 + .../include/avcap/linux/V4L2_ButtonControl.h | 72 + .../include/avcap/linux/V4L2_Connector.h | 62 + .../avcap/linux/V4L2_ConnectorManager.h | 69 + .../include/avcap/linux/V4L2_ControlBase.h | 82 + .../include/avcap/linux/V4L2_ControlManager.h | 55 + .../avcap/linux/V4L2_CtrlClassControl.h | 68 + extra_lib/include/avcap/linux/V4L2_Device.h | 84 + .../avcap/linux/V4L2_DeviceDescriptor.h | 112 + .../include/avcap/linux/V4L2_FormatManager.h | 85 + .../include/avcap/linux/V4L2_IntControl.h | 75 + .../include/avcap/linux/V4L2_MenuControl.h | 77 + extra_lib/include/avcap/linux/V4L2_Tuner.h | 120 + .../include/avcap/linux/V4L2_VidCapManager.h | 125 + extra_lib/include/avcap/linux/error.h | 44 + extra_lib/include/avcap/linux/frame.h | 161 + extra_lib/include/avcap/linux/ieee1394io.h | 188 ++ extra_lib/include/avcap/linux/ivtv.h | 397 +++ extra_lib/include/avcap/linux/pwc-ioctl.h | 329 ++ extra_lib/include/avcap/linux/raw1394util.h | 25 + extra_lib/include/avcap/linux/uvc_compat.h | 129 + extra_lib/include/avcap/log.h | 44 + .../include/avcap/osx/QT_ConnectorManager.h | 53 + extra_lib/include/avcap/osx/QT_Control.h | 89 + .../include/avcap/osx/QT_ControlManager.h | 53 + extra_lib/include/avcap/osx/QT_Device.h | 83 + .../include/avcap/osx/QT_DeviceDescriptor.h | 103 + .../include/avcap/osx/QT_DeviceEnumerator.h | 58 + .../include/avcap/osx/QT_FormatManager.h | 92 + .../include/avcap/osx/QT_VidCapManager.h | 122 + extra_lib/include/avcap/osx/avcap-config.h | 264 ++ extra_lib/include/avcap/singleton.h | 57 + extra_lib/include/avcap/windows/Crossbar.h | 123 + .../include/avcap/windows/DS_Connector.h | 74 + .../avcap/windows/DS_ConnectorManager.h | 91 + extra_lib/include/avcap/windows/DS_Control.h | 185 ++ .../include/avcap/windows/DS_ControlManager.h | 80 + extra_lib/include/avcap/windows/DS_Device.h | 82 + .../avcap/windows/DS_DeviceDescriptor.h | 131 + .../include/avcap/windows/DS_FormatManager.h | 101 + extra_lib/include/avcap/windows/DS_Tuner.h | 134 + .../include/avcap/windows/DS_VidCapManager.h | 98 + extra_lib/include/avcap/windows/FormatNames.h | 356 +++ extra_lib/include/avcap/windows/HelpFunc.h | 410 +++ .../avcap/windows/SampleGrabberCallback.h | 86 + gui/extensions/widget_manager/init.js | 58 +- gui/gui.js | 105 +- gui/gwlib.js | 22 +- gui/iphone_wm_gui.js | 2767 +++++++++-------- include/gpac/{carousel.h => ait.h} | 418 +-- include/gpac/avparse.h | 4 +- include/gpac/config_file.h | 10 + include/gpac/constants.h | 18 +- include/gpac/download.h | 13 +- include/gpac/dsmcc.h | 643 ++++ include/gpac/dvb_mpe.h | 4 + include/gpac/events.h | 444 +-- include/gpac/events_constants.h | 500 +++ include/gpac/internal/bifs_dev.h | 4 +- include/gpac/internal/bifs_tables.h | 2 +- include/gpac/internal/compositor_dev.h | 7 +- include/gpac/internal/dvb_mpe_dev.h | 6 + include/gpac/internal/ietf_dev.h | 2 +- include/gpac/internal/isomedia_dev.h | 101 +- include/gpac/internal/m3u8.h | 5 +- include/gpac/internal/mpd.h | 349 ++- include/gpac/internal/scenegraph_dev.h | 2 +- include/gpac/internal/terminal_dev.h | 34 +- include/gpac/isomedia.h | 65 +- include/gpac/list.h | 9 + include/gpac/math.h | 7 + include/gpac/media_tools.h | 15 +- include/gpac/mediaobject.h | 5 +- include/gpac/modules/service.h | 15 + include/gpac/mpeg4_odf.h | 2 +- include/gpac/mpegts.h | 68 +- include/gpac/nodes_mpeg4.h | 4 +- include/gpac/path2d.h | 2 +- include/gpac/scenegraph.h | 4 + include/gpac/scenegraph_svg.h | 15 +- include/gpac/setup.h | 2 +- include/gpac/terminal.h | 6 +- include/gpac/thread.h | 8 + include/gpac/tools.h | 175 +- include/gpac/xml.h | 1 + modules/Makefile | 17 +- modules/aac_in/aac_in.c | 6 + modules/aac_in/faad_dec.c | 3 + modules/ac3_in/ac3_in.c | 3 + modules/amr_dec/amr_in.c | 9 +- modules/amr_float_dec/amr_float_dec.c | 4 +- modules/avcap/Makefile | 57 + modules/avcap/avcap.cpp | 423 +++ modules/directfb_out/Makefile | 4 +- modules/directfb_out/directfb_out.c | 980 ++---- modules/directfb_out/directfb_out.h | 333 +- modules/directfb_out/directfb_wrapper.c | 830 +++++ modules/droid_cam/droid_cam.c | 715 +++++ modules/droid_mpegv/droid_mpegv.c | 387 +++ modules/dummy_in/dummy_in.c | 7 +- modules/dx_hw/dx_2d.c | 189 +- modules/dx_hw/dx_audio.c | 61 +- modules/dx_hw/dx_hw.h | 57 +- modules/dx_hw/dx_video.c | 52 +- modules/dx_hw/dx_window.c | 21 +- modules/ffmpeg_in/ffmpeg_decode.c | 95 +- modules/ffmpeg_in/ffmpeg_demux.c | 35 +- modules/ffmpeg_in/ffmpeg_in.h | 13 +- modules/freenect/Makefile | 65 + modules/freenect/freenect.c | 566 ++++ modules/ft_font/ft_font.c | 113 +- modules/gapi/gapi.cpp | 1 + modules/gpac_js/gpac_js.c | 10 +- modules/hyb_in/Makefile | 57 + modules/hyb_in/fm_fake_pull.c | 36 + modules/hyb_in/fm_fake_push.c | 106 +- modules/hyb_in/fm_mmbtools.c | 75 - modules/hyb_in/hyb_in.c | 97 +- modules/img_in/img_in.c | 17 +- modules/img_in/jp2_dec.c | 322 +- modules/ismacryp/ismacryp.c | 2 + modules/isom_in/isom_in.h | 4 + modules/isom_in/load.c | 2 +- modules/isom_in/read.c | 25 +- modules/isom_in/read_ch.c | 187 +- modules/libplayer/libplayer.c | 209 +- modules/mp3_in/mp3_in.c | 3 + modules/mpd_in/mpd_in.c | 1522 ++++++--- modules/mpegts_in/mpegts_in.c | 117 +- modules/opencv_is/Makefile | 4 +- modules/opensvc_dec/Makefile | 10 +- modules/opensvc_dec/opensvc_dec.c | 40 +- modules/osd/osd.c | 7 +- modules/platinum/GPACFileMediaServer.cpp | 2 +- modules/platinum/GPACPlatinum.cpp | 28 +- modules/platinum/GenericDevice.cpp | 21 +- modules/redirect_av/redirect_av.c | 34 +- modules/rtp_in/rtp_session.c | 4 +- modules/rtp_in/sdp_fetch.c | 10 +- modules/rtp_in/sdp_load.c | 4 +- modules/rvc_dec/Makefile | 2 +- modules/rvc_dec/rvc_dec.c | 2 +- modules/saf_in/saf_in.c | 5 +- modules/sdl_out/video.c | 40 +- modules/soft_raster/ftgrays.c | 8 +- modules/soft_raster/rast_soft.h | 2 - modules/soft_raster/raster_565.c | 6 - modules/soft_raster/raster_argb.c | 24 - modules/soft_raster/raster_rgb.c | 31 +- modules/soft_raster/surface.c | 3 - modules/svg_in/svg_in.c | 7 +- modules/timedtext/timedtext_in.c | 3 + modules/ui_rec/ui_rec.c | 4 +- modules/validator/README.TXT | 41 + modules/validator/validator.c | 1013 ++++++ modules/x11_out/Makefile | 14 +- modules/x11_out/x11_out.c | 5 +- modules/xvid_dec/Makefile | 2 +- .../bifs/bifs-linking-anchor-mp4-next.bt | 2 +- .../bifs/bifs-linking-inline-direct.bt | 2 +- src/Makefile | 3 +- src/bifs/bifs_node_tables.c | 2 +- src/bifs/com_dec.c | 29 +- src/bifs/com_enc.c | 11 +- src/bifs/conditional.c | 9 +- src/bifs/field_decode.c | 41 +- src/bifs/field_encode.c | 10 +- src/bifs/memory_decoder.c | 17 +- src/bifs/script_dec.c | 5 +- src/compositor/audio_input.c | 4 +- src/compositor/audio_render.c | 2 +- src/compositor/camera.c | 1 + src/compositor/compositor.c | 74 +- src/compositor/compositor_2d.c | 17 +- src/compositor/compositor_node_init.c | 15 +- src/compositor/drawable.c | 16 +- src/compositor/events.c | 54 +- src/compositor/font_engine.c | 6 +- src/compositor/gl_inc.h | 34 +- src/compositor/hc_flash_shape.c | 2 +- src/compositor/mesh.c | 18 +- src/compositor/mpeg4_audio.c | 8 +- src/compositor/mpeg4_bitmap.c | 9 +- src/compositor/mpeg4_form.c | 4 +- src/compositor/mpeg4_geometry_2d.c | 26 + src/compositor/mpeg4_geometry_ifs2d.c | 3 +- src/compositor/mpeg4_grouping_2d.c | 4 +- src/compositor/mpeg4_layer_2d.c | 4 +- src/compositor/mpeg4_layout.c | 4 +- src/compositor/mpeg4_text.c | 3 +- src/compositor/mpeg4_textures.c | 74 +- src/compositor/mpeg4_viewport.c | 3 +- src/compositor/navigate.c | 22 +- src/compositor/nodes_stacks.h | 4 + src/compositor/svg_geometry.c | 1 + src/compositor/svg_grouping.c | 14 +- src/compositor/svg_media.c | 64 +- src/compositor/svg_paint_servers.c | 15 + src/compositor/texturing.c | 1 + src/compositor/texturing_gl.c | 7 +- src/compositor/visual_manager_2d.c | 21 +- src/compositor/visual_manager_2d_draw.c | 28 +- src/compositor/visual_manager_3d.c | 28 +- src/compositor/visual_manager_3d_gl.c | 63 +- src/export.cpp | 74 +- src/ietf/rtcp.c | 10 +- src/ietf/rtp.c | 16 +- src/ietf/rtp_depacketizer.c | 14 +- src/ietf/rtp_packetizer.c | 3 +- src/ietf/rtp_pck_3gpp.c | 8 +- src/ietf/rtp_pck_mpeg4.c | 3 +- src/ietf/rtp_streamer.c | 2 + src/ietf/rtsp_common.c | 2 +- src/isomedia/box_code_3gpp.c | 1 + src/isomedia/box_code_base.c | 349 ++- src/isomedia/box_code_isma.c | 1 + src/isomedia/box_dump.c | 71 +- src/isomedia/box_funcs.c | 19 +- src/isomedia/isom_intern.c | 40 +- src/isomedia/isom_read.c | 194 +- src/isomedia/isom_write.c | 231 +- src/isomedia/media.c | 9 + src/isomedia/meta.c | 4 +- src/isomedia/movie_fragments.c | 639 +++- src/isomedia/stbl_read.c | 96 +- src/isomedia/stbl_write.c | 3 +- src/isomedia/track.c | 94 +- src/laser/lsr_dec.c | 21 +- src/laser/lsr_enc.c | 7 +- src/media_tools/ait.c | 774 +++++ src/media_tools/av_parsers.c | 320 +- src/media_tools/avilib.c | 4 +- src/media_tools/carousel.c | 409 --- src/media_tools/dsmcc.c | 1938 ++++++++++++ src/media_tools/dvb.c | 2 +- src/media_tools/dvb_mpe.c | 49 +- src/media_tools/filestreamer.c | 2 - src/media_tools/img.c | 76 +- src/media_tools/ismacryp.c | 3 +- src/media_tools/isom_hinter.c | 7 +- src/media_tools/isom_tools.c | 633 +++- src/media_tools/m2ts_mux.c | 228 +- src/media_tools/m3u8.c | 75 +- src/media_tools/media_export.c | 98 +- src/media_tools/media_import.c | 403 ++- src/media_tools/mpd.c | 1436 ++++++--- src/media_tools/mpegts.c | 449 ++- src/media_tools/text_import.c | 9 +- src/media_tools/vobsub.c | 2 +- src/odf/desc_private.c | 8 + src/odf/descriptors.c | 9 +- src/odf/odf_code.c | 46 +- src/odf/odf_command.c | 10 +- src/odf/odf_parse.c | 7 +- src/odf/slc.c | 6 +- src/scene_manager/encode_isom.c | 31 +- src/scene_manager/loader_bt.c | 142 +- src/scene_manager/loader_isom.c | 6 +- src/scene_manager/loader_qt.c | 7 +- src/scene_manager/loader_svg.c | 113 +- src/scene_manager/loader_xmt.c | 39 +- src/scene_manager/scene_dump.c | 71 +- src/scene_manager/scene_engine.c | 61 +- src/scene_manager/scene_manager.c | 10 - src/scene_manager/swf_bifs.c | 6 - src/scene_manager/swf_parse.c | 35 +- src/scene_manager/text_to_bifs.c | 4 - src/scenegraph/dom_events.c | 8 +- src/scenegraph/dom_smjs.c | 356 ++- src/scenegraph/mpeg4_nodes.c | 20 +- src/scenegraph/smil_anim.c | 57 +- src/scenegraph/smil_timing.c | 11 +- src/scenegraph/svg_attributes.c | 34 +- src/scenegraph/svg_smjs.c | 65 +- src/scenegraph/svg_types.c | 11 +- src/scenegraph/vrml_proto.c | 11 - src/scenegraph/vrml_route.c | 3 +- src/scenegraph/vrml_smjs.c | 59 +- src/scenegraph/vrml_tools.c | 6 +- src/scenegraph/xbl_process.c | 2 +- src/scenegraph/xml_ns.c | 2 +- src/terminal/channel.c | 63 +- src/terminal/clock.c | 6 +- src/terminal/decoder.c | 48 +- src/terminal/input_sensor.c | 13 +- src/terminal/media_control.c | 14 +- src/terminal/media_manager.c | 52 +- src/terminal/media_memory.c | 17 +- src/terminal/media_object.c | 65 +- src/terminal/media_sensor.c | 22 +- src/terminal/mpeg4_inline.c | 30 +- src/terminal/network_service.c | 180 +- src/terminal/object_browser.c | 17 +- src/terminal/object_manager.c | 191 +- src/terminal/scene.c | 229 +- src/terminal/terminal.c | 383 ++- src/utils/alloc.c | 39 +- src/utils/bitstream.c | 4 +- src/utils/cache.c | 6 +- src/utils/color.c | 136 +- src/utils/configfile.c | 20 + src/utils/downloader.c | 284 +- src/utils/error.c | 261 +- src/utils/list.c | 26 + src/utils/math.c | 22 +- src/utils/module.c | 14 +- src/utils/os_config_init.c | 10 +- src/utils/os_divers.c | 633 ++-- src/utils/os_module.c | 13 +- src/utils/os_net.c | 20 +- src/utils/os_thread.c | 165 +- src/utils/path2d.c | 6 +- src/utils/xml_parser.c | 18 + 504 files changed, 51759 insertions(+), 14492 deletions(-) delete mode 100644 applications/GPAX/GPAX.cpp delete mode 100644 applications/GPAX/GPAX.def delete mode 100644 applications/GPAX/GPAX.dsp delete mode 100644 applications/GPAX/GPAX.h delete mode 100644 applications/GPAX/GPAX.idl delete mode 100644 applications/GPAX/GPAX.rc delete mode 100644 applications/GPAX/GPAX.rgs delete mode 100644 applications/GPAX/GPAXPlugin.cpp delete mode 100644 applications/GPAX/GPAXPlugin.h delete mode 100644 applications/GPAX/GPAX_i.c delete mode 100644 applications/GPAX/GPAX_p.c delete mode 100644 applications/GPAX/GPAXps.def delete mode 100644 applications/GPAX/GPAXps.mk delete mode 100644 applications/GPAX/StdAfx.cpp delete mode 100644 applications/GPAX/StdAfx.h delete mode 100644 applications/GPAX/dlldata.c delete mode 100644 applications/GPAX/gpax.bmp delete mode 100644 applications/GPAX/resource.h create mode 100644 applications/hbbtvplayer/README.txt create mode 100644 applications/hbbtvplayer/REQUIREMENT create mode 100644 applications/hbbtvplayer/doc/avancement.txt create mode 100644 applications/hbbtvplayer/doc/introduction.txt create mode 100644 applications/hbbtvplayer/doc/projectmanagement.txt create mode 100644 applications/hbbtvplayer/getsources.sh create mode 100644 applications/hbbtvplayer/hbbtvbrowserplugin/README create mode 100644 applications/hbbtvplayer/hbbtvbrowserplugin/autogen.sh create mode 100644 applications/hbbtvplayer/hbbtvbrowserplugin/configure.ac create mode 100644 applications/hbbtvplayer/hbbtvbrowserplugin/doc/hbbtvBrowserPlugin-NPAPI-20111108.pptx create mode 100644 applications/hbbtvplayer/hbbtvbrowserplugin/doc/listeOIPFProfile.xlsx create mode 100644 applications/hbbtvplayer/hbbtvbrowserplugin/hbbtvbrowserplugin.pc.in create mode 100644 applications/hbbtvplayer/hbbtvbrowserplugin/hbbtvbrowserplugin.vcproj create mode 100644 applications/hbbtvplayer/hbbtvbrowserplugin/makefile.am create mode 100644 applications/hbbtvplayer/hbbtvbrowserplugin/makefile.old create mode 100644 applications/hbbtvplayer/hbbtvbrowserplugin/projectmanager/codeblocks/hbbtvbrowserplugin/hbbtvbrowserplugin.cbp create mode 100644 applications/hbbtvplayer/hbbtvbrowserplugin/projectmanager/codeblocks/hbbtvbrowserplugin/hbbtvbrowserplugin.layout create mode 100644 applications/hbbtvplayer/hbbtvbrowserplugin/releases/hbbtvbrowserplugin-0.01.tar.gz create mode 100644 applications/hbbtvplayer/hbbtvbrowserplugin/src/applicationclass.c create mode 100644 applications/hbbtvplayer/hbbtvbrowserplugin/src/applicationclass.h create mode 100644 applications/hbbtvplayer/hbbtvbrowserplugin/src/applicationprivatedataclass.c create mode 100644 applications/hbbtvplayer/hbbtvbrowserplugin/src/applicationprivatedataclass.h create mode 100644 applications/hbbtvplayer/hbbtvbrowserplugin/src/configurationclass.c create mode 100644 applications/hbbtvplayer/hbbtvbrowserplugin/src/configurationclass.h create mode 100644 applications/hbbtvplayer/hbbtvbrowserplugin/src/downloadclass.c create mode 100644 applications/hbbtvplayer/hbbtvbrowserplugin/src/downloadclass.h create mode 100644 applications/hbbtvplayer/hbbtvbrowserplugin/src/downloadcollectionclass.c create mode 100644 applications/hbbtvplayer/hbbtvbrowserplugin/src/downloadcollectionclass.h create mode 100644 applications/hbbtvplayer/hbbtvbrowserplugin/src/drmcontrolinfocollectionclass.c create mode 100644 applications/hbbtvplayer/hbbtvbrowserplugin/src/drmcontrolinfocollectionclass.h create mode 100644 applications/hbbtvplayer/hbbtvbrowserplugin/src/drmcontrolinformationclass.c create mode 100644 applications/hbbtvplayer/hbbtvbrowserplugin/src/drmcontrolinformationclass.h create mode 100644 applications/hbbtvplayer/hbbtvbrowserplugin/src/hbbtvbrowserplugin.c create mode 100644 applications/hbbtvplayer/hbbtvbrowserplugin/src/hbbtvbrowserplugin.h create mode 100644 applications/hbbtvplayer/hbbtvbrowserplugin/src/hbbtvbrowserpluginapi.c create mode 100644 applications/hbbtvplayer/hbbtvbrowserplugin/src/hbbtvbrowserpluginapi.h create mode 100644 applications/hbbtvplayer/hbbtvbrowserplugin/src/keysetclass.c create mode 100644 applications/hbbtvplayer/hbbtvbrowserplugin/src/keysetclass.h create mode 100644 applications/hbbtvplayer/hbbtvbrowserplugin/src/makefile.am create mode 100644 applications/hbbtvplayer/hbbtvbrowserplugin/src/oipfapplicationmanager.c create mode 100644 applications/hbbtvplayer/hbbtvbrowserplugin/src/oipfapplicationmanager.h create mode 100644 applications/hbbtvplayer/hbbtvbrowserplugin/src/oipfconfiguration.c create mode 100644 applications/hbbtvplayer/hbbtvbrowserplugin/src/oipfconfiguration.h create mode 100644 applications/hbbtvplayer/hbbtvbrowserplugin/src/oipfdownloadmanager.c create mode 100644 applications/hbbtvplayer/hbbtvbrowserplugin/src/oipfdownloadmanager.h create mode 100644 applications/hbbtvplayer/hbbtvbrowserplugin/src/oipfdownloadtrigger.c create mode 100644 applications/hbbtvplayer/hbbtvbrowserplugin/src/oipfdownloadtrigger.h create mode 100644 applications/hbbtvplayer/hbbtvbrowserplugin/src/videobroadcast.c create mode 100644 applications/hbbtvplayer/hbbtvbrowserplugin/src/videobroadcast.h create mode 100644 applications/hbbtvplayer/hbbtvbrowserplugin/src/webkit-plugin-header/npapi.h create mode 100644 applications/hbbtvplayer/hbbtvbrowserplugin/src/webkit-plugin-header/npfunctions.h create mode 100644 applications/hbbtvplayer/hbbtvbrowserplugin/src/webkit-plugin-header/npruntime.h create mode 100644 applications/hbbtvplayer/hbbtvbrowserplugin/src/webkit-plugin-header/nptypes.h create mode 100644 applications/hbbtvplayer/hbbtvterminal/autogen.sh create mode 100644 applications/hbbtvplayer/hbbtvterminal/configure.ac create mode 100644 applications/hbbtvplayer/hbbtvterminal/hbbtv_terminal.vcproj create mode 100644 applications/hbbtvplayer/hbbtvterminal/makefile.am create mode 100644 applications/hbbtvplayer/hbbtvterminal/makefile.old create mode 100644 applications/hbbtvplayer/hbbtvterminal/projectmanager/codeblocks/hbbtvterminal/HbbtvTerminal.cbp create mode 100644 applications/hbbtvplayer/hbbtvterminal/projectmanager/codeblocks/hbbtvterminal/HbbtvTerminal.layout create mode 100644 applications/hbbtvplayer/hbbtvterminal/src/hbbtv_channel.cpp create mode 100644 applications/hbbtvplayer/hbbtvterminal/src/hbbtv_demux.cpp create mode 100644 applications/hbbtvplayer/hbbtvterminal/src/hbbtv_keycontrol.cpp create mode 100644 applications/hbbtvplayer/hbbtvterminal/src/hbbtv_tools.cpp create mode 100644 applications/hbbtvplayer/hbbtvterminal/src/hbbtvterminal.cpp create mode 100644 applications/hbbtvplayer/hbbtvterminal/src/hbbtvterminal.h create mode 100644 applications/hbbtvplayer/hbbtvterminal/src/makefile.am create mode 100644 applications/hbbtvplayer/install.sh create mode 100644 applications/hbbtvplayer/listdependencies create mode 100644 applications/hbbtvplayer/projectmanager/codeblocks/hbbtvplayer.workspace create mode 100644 applications/m3u82mpd/m3u82mpd.vcproj delete mode 100644 applications/mp42ts/mp42ts.dsp delete mode 100644 applications/mp42ts/mp42ts.vcproj create mode 100644 applications/osmo4_ios/Resources/icon.png create mode 100644 applications/osmo4_ios/Resources/osmo.icns create mode 100644 applications/osmo4_ios/extract.c create mode 100644 applications/osmo4_ios/main.c create mode 100644 applications/osmo4_ios/osmo4ios-Info.plist create mode 100644 applications/osmo4_wx/Darwin.Info.plist create mode 100644 applications/osmo4_wx/Darwin.InfoPlist.strings create mode 100644 applications/osmo4_wx/Darwin.Osmo.icns create mode 100644 applications/osmo4_wx/Makefile create mode 100644 applications/osmo4_wx/Playlist.cpp create mode 100644 applications/osmo4_wx/Playlist.h create mode 100644 applications/osmo4_wx/fileprops.cpp create mode 100644 applications/osmo4_wx/fileprops.h create mode 100644 applications/osmo4_wx/menubtn.cpp create mode 100644 applications/osmo4_wx/menubtn.h rename bin/w32_rel/Osmo4.ico => applications/osmo4_wx/osmo4.ico (100%) create mode 100644 applications/osmo4_wx/osmo4.xpm create mode 100644 applications/osmo4_wx/playlist.xpm create mode 100644 applications/osmo4_wx/resource.h create mode 100644 applications/osmo4_wx/toolbar.xpm create mode 100644 applications/osmo4_wx/wxGPACControl.cpp create mode 100644 applications/osmo4_wx/wxGPACControl.h create mode 100644 applications/osmo4_wx/wxOsmo4.cpp create mode 100644 applications/osmo4_wx/wxOsmo4.h create mode 100644 applications/osmo4_wx/wxOsmo4.rc delete mode 100644 applications/osmozilla/Makefile delete mode 100644 applications/osmozilla/nsIOsmozilla.h delete mode 100644 applications/osmozilla/nsIOsmozilla.idl delete mode 100644 applications/osmozilla/nsIOsmozilla.xpt_linux delete mode 100644 applications/osmozilla/nsIOsmozilla.xpt_w32 delete mode 100644 applications/osmozilla/osmo_npapi.cpp delete mode 100644 applications/osmozilla/osmo_npapi.h delete mode 100644 applications/osmozilla/osmozilla.cpp delete mode 100644 applications/osmozilla/osmozilla.def delete mode 100644 applications/osmozilla/osmozilla.h delete mode 100644 applications/osmozilla/osmozilla.png delete mode 100644 applications/osmozilla/osmozilla.rc delete mode 100644 applications/osmozilla/readme.txt delete mode 100644 applications/osmozilla/resource.h delete mode 100644 bin/w32_rel/nsis_install/gpac_installer.nsi create mode 100644 extra_lib/include/OpenSVCDecoder/ControlLayer.h create mode 100644 extra_lib/include/OpenSVCDecoder/ParseAU.h create mode 100644 extra_lib/include/OpenSVCDecoder/SVCDecoder_ietr_api.h create mode 100644 extra_lib/include/OpenSVCDecoder/SvcInterface.h create mode 100644 extra_lib/include/avcap/CaptureDevice.h create mode 100644 extra_lib/include/avcap/CaptureHandler.h create mode 100644 extra_lib/include/avcap/CaptureManager.h create mode 100644 extra_lib/include/avcap/Connector.h create mode 100644 extra_lib/include/avcap/ConnectorManager.h create mode 100644 extra_lib/include/avcap/ControlManager.h create mode 100644 extra_lib/include/avcap/Control_avcap.h create mode 100644 extra_lib/include/avcap/DeviceCollector.h create mode 100644 extra_lib/include/avcap/DeviceDescriptor.h create mode 100644 extra_lib/include/avcap/FormatManager.h create mode 100644 extra_lib/include/avcap/IOBuffer.h create mode 100644 extra_lib/include/avcap/Interval.h create mode 100644 extra_lib/include/avcap/Manager.h create mode 100644 extra_lib/include/avcap/ProbeValues.h create mode 100644 extra_lib/include/avcap/Tuner_avcap.h create mode 100644 extra_lib/include/avcap/avcap-export.h create mode 100644 extra_lib/include/avcap/avcap.h create mode 100644 extra_lib/include/avcap/linux/AVC_ConnectorManager.h create mode 100644 extra_lib/include/avcap/linux/AVC_ControlManager.h create mode 100644 extra_lib/include/avcap/linux/AVC_Device.h create mode 100644 extra_lib/include/avcap/linux/AVC_DeviceDescriptor.h create mode 100644 extra_lib/include/avcap/linux/AVC_FormatManager.h create mode 100644 extra_lib/include/avcap/linux/AVC_Reader.h create mode 100644 extra_lib/include/avcap/linux/AVC_VidCapManager.h create mode 100644 extra_lib/include/avcap/linux/V4L1_Connector.h create mode 100644 extra_lib/include/avcap/linux/V4L1_ConnectorManager.h create mode 100644 extra_lib/include/avcap/linux/V4L1_Control.h create mode 100644 extra_lib/include/avcap/linux/V4L1_ControlManager.h create mode 100644 extra_lib/include/avcap/linux/V4L1_Device.h create mode 100644 extra_lib/include/avcap/linux/V4L1_DeviceDescriptor.h create mode 100644 extra_lib/include/avcap/linux/V4L1_FormatManager.h create mode 100644 extra_lib/include/avcap/linux/V4L1_VidCapManager.h create mode 100644 extra_lib/include/avcap/linux/V4L2_BoolControl.h create mode 100644 extra_lib/include/avcap/linux/V4L2_ButtonControl.h create mode 100644 extra_lib/include/avcap/linux/V4L2_Connector.h create mode 100644 extra_lib/include/avcap/linux/V4L2_ConnectorManager.h create mode 100644 extra_lib/include/avcap/linux/V4L2_ControlBase.h create mode 100644 extra_lib/include/avcap/linux/V4L2_ControlManager.h create mode 100644 extra_lib/include/avcap/linux/V4L2_CtrlClassControl.h create mode 100644 extra_lib/include/avcap/linux/V4L2_Device.h create mode 100644 extra_lib/include/avcap/linux/V4L2_DeviceDescriptor.h create mode 100644 extra_lib/include/avcap/linux/V4L2_FormatManager.h create mode 100644 extra_lib/include/avcap/linux/V4L2_IntControl.h create mode 100644 extra_lib/include/avcap/linux/V4L2_MenuControl.h create mode 100644 extra_lib/include/avcap/linux/V4L2_Tuner.h create mode 100644 extra_lib/include/avcap/linux/V4L2_VidCapManager.h create mode 100644 extra_lib/include/avcap/linux/error.h create mode 100644 extra_lib/include/avcap/linux/frame.h create mode 100644 extra_lib/include/avcap/linux/ieee1394io.h create mode 100644 extra_lib/include/avcap/linux/ivtv.h create mode 100644 extra_lib/include/avcap/linux/pwc-ioctl.h create mode 100644 extra_lib/include/avcap/linux/raw1394util.h create mode 100644 extra_lib/include/avcap/linux/uvc_compat.h create mode 100644 extra_lib/include/avcap/log.h create mode 100644 extra_lib/include/avcap/osx/QT_ConnectorManager.h create mode 100644 extra_lib/include/avcap/osx/QT_Control.h create mode 100644 extra_lib/include/avcap/osx/QT_ControlManager.h create mode 100644 extra_lib/include/avcap/osx/QT_Device.h create mode 100644 extra_lib/include/avcap/osx/QT_DeviceDescriptor.h create mode 100644 extra_lib/include/avcap/osx/QT_DeviceEnumerator.h create mode 100644 extra_lib/include/avcap/osx/QT_FormatManager.h create mode 100644 extra_lib/include/avcap/osx/QT_VidCapManager.h create mode 100644 extra_lib/include/avcap/osx/avcap-config.h create mode 100644 extra_lib/include/avcap/singleton.h create mode 100644 extra_lib/include/avcap/windows/Crossbar.h create mode 100644 extra_lib/include/avcap/windows/DS_Connector.h create mode 100644 extra_lib/include/avcap/windows/DS_ConnectorManager.h create mode 100644 extra_lib/include/avcap/windows/DS_Control.h create mode 100644 extra_lib/include/avcap/windows/DS_ControlManager.h create mode 100644 extra_lib/include/avcap/windows/DS_Device.h create mode 100644 extra_lib/include/avcap/windows/DS_DeviceDescriptor.h create mode 100644 extra_lib/include/avcap/windows/DS_FormatManager.h create mode 100644 extra_lib/include/avcap/windows/DS_Tuner.h create mode 100644 extra_lib/include/avcap/windows/DS_VidCapManager.h create mode 100644 extra_lib/include/avcap/windows/FormatNames.h create mode 100644 extra_lib/include/avcap/windows/HelpFunc.h create mode 100644 extra_lib/include/avcap/windows/SampleGrabberCallback.h rename include/gpac/{carousel.h => ait.h} (56%) create mode 100644 include/gpac/dsmcc.h create mode 100644 include/gpac/events_constants.h create mode 100644 modules/avcap/Makefile create mode 100644 modules/avcap/avcap.cpp create mode 100644 modules/directfb_out/directfb_wrapper.c create mode 100644 modules/droid_cam/droid_cam.c create mode 100644 modules/droid_mpegv/droid_mpegv.c create mode 100644 modules/freenect/Makefile create mode 100644 modules/freenect/freenect.c create mode 100644 modules/hyb_in/Makefile create mode 100644 modules/validator/README.TXT create mode 100644 modules/validator/validator.c create mode 100644 src/media_tools/ait.c delete mode 100644 src/media_tools/carousel.c create mode 100644 src/media_tools/dsmcc.c diff --git a/Makefile b/Makefile index a04681a..1d56214 100644 --- a/Makefile +++ b/Makefile @@ -64,7 +64,7 @@ install: rm -f $(DESTDIR)$(moddir)/nposmozilla.$(DYN_LIB_SUFFIX) $(MAKE) installdylib $(INSTALL) -d "$(DESTDIR)$(mandir)" - $(INSTALL) -d "$(DESTDIR)$(mandir)/man1" + $(INSTALL) -d "$(DESTDIR)$(mandir)/man1"; if [ -d doc ] ; then \ $(INSTALL) $(INSTFLAGS) -m 644 doc/man/mp4box.1 $(DESTDIR)$(mandir)/man1/ ; \ $(INSTALL) $(INSTFLAGS) -m 644 doc/man/mp4client.1 $(DESTDIR)$(mandir)/man1/ ; \ @@ -94,6 +94,7 @@ uninstall: rm -rf $(DESTDIR)$(mandir)/man1/mp4client.1 rm -rf $(DESTDIR)$(mandir)/man1/gpac.1 rm -rf $(DESTDIR)$(prefix)/share/gpac + rm -rf $(DESTDIR)$(prefix)/include/gpac installdylib: ifeq ($(CONFIG_WIN32),yes) diff --git a/applications/GPAX/GPAX.cpp b/applications/GPAX/GPAX.cpp deleted file mode 100644 index 97c8f11..0000000 --- a/applications/GPAX/GPAX.cpp +++ /dev/null @@ -1,76 +0,0 @@ -// GPAX.cpp : Implementation of DLL Exports. - - -// Note: Proxy/Stub Information -// To build a separate proxy/stub DLL, -// run nmake -f GPAXps.mk in the project directory. - -#include "stdafx.h" -#include "resource.h" -#include -#include "GPAX.h" - -#include "GPAX_i.c" -#include "GPAXPlugin.h" - - -CComModule _Module; - -BEGIN_OBJECT_MAP(ObjectMap) -OBJECT_ENTRY(CLSID_GPAX, CGPAXPlugin) -END_OBJECT_MAP() - -///////////////////////////////////////////////////////////////////////////// -// DLL Entry Point - -extern "C" -#ifdef _WIN32_WCE -BOOL WINAPI DllMain(HANDLE hInstance, DWORD dwReason, LPVOID /*lpReserved*/) -#else -BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID /*lpReserved*/) -#endif -{ - if (dwReason == DLL_PROCESS_ATTACH) - { - _Module.Init(ObjectMap, (HINSTANCE) hInstance, &LIBID_GPAXLib); - DisableThreadLibraryCalls((HINSTANCE) hInstance); - } - else if (dwReason == DLL_PROCESS_DETACH) - _Module.Term(); - return TRUE; // ok -} - -///////////////////////////////////////////////////////////////////////////// -// Used to determine whether the DLL can be unloaded by OLE - -STDAPI DllCanUnloadNow(void) -{ - return (_Module.GetLockCount()==0) ? S_OK : S_FALSE; -} - -///////////////////////////////////////////////////////////////////////////// -// Returns a class factory to create an object of the requested type - -STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv) -{ - return _Module.GetClassObject(rclsid, riid, ppv); -} - -///////////////////////////////////////////////////////////////////////////// -// DllRegisterServer - Adds entries to the system registry - -STDAPI DllRegisterServer(void) -{ - // registers object, typelib and all interfaces in typelib - return _Module.RegisterServer(TRUE); -} - -///////////////////////////////////////////////////////////////////////////// -// DllUnregisterServer - Removes entries from the system registry - -STDAPI DllUnregisterServer(void) -{ - return _Module.UnregisterServer(TRUE); -} - - diff --git a/applications/GPAX/GPAX.def b/applications/GPAX/GPAX.def deleted file mode 100644 index f61476d..0000000 --- a/applications/GPAX/GPAX.def +++ /dev/null @@ -1,9 +0,0 @@ -; GPAX.def : Declares the module parameters. - -LIBRARY "GPAX.dll" - -EXPORTS - DllCanUnloadNow PRIVATE - DllGetClassObject PRIVATE - DllRegisterServer PRIVATE - DllUnregisterServer PRIVATE diff --git a/applications/GPAX/GPAX.dsp b/applications/GPAX/GPAX.dsp deleted file mode 100644 index ac3d1a3..0000000 --- a/applications/GPAX/GPAX.dsp +++ /dev/null @@ -1,173 +0,0 @@ -# Microsoft Developer Studio Project File - Name="GPAX" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 - -CFG=GPAX - Win32 Release -!MESSAGE This is not a valid makefile. To build this project using NMAKE, -!MESSAGE use the Export Makefile command and run -!MESSAGE -!MESSAGE NMAKE /f "GPAX.mak". -!MESSAGE -!MESSAGE You can specify a configuration when running NMAKE -!MESSAGE by defining the macro CFG on the command line. For example: -!MESSAGE -!MESSAGE NMAKE /f "GPAX.mak" CFG="GPAX - Win32 Release" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "GPAX - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") -!MESSAGE "GPAX - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") -!MESSAGE - -# Begin Project -# PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "" -# PROP Scc_LocalPath "" -CPP=cl.exe -MTL=midl.exe -RSC=rc.exe - -!IF "$(CFG)" == "GPAX - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "Debug" -# PROP BASE Intermediate_Dir "Debug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "obj/w32_deb" -# PROP Intermediate_Dir "obj/w32_deb" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /MTd /W3 /Gm /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /Yu"stdafx.h" /FD /GZ /c -# ADD CPP /nologo /MDd /W3 /Gm /ZI /Od /I "../../include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /FR /Yu"stdafx.h" /FD /GZ /c -# ADD BASE RSC /l 0x804 /d "_DEBUG" -# ADD RSC /l 0x804 /d "_DEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386 /pdbtype:sept -# ADD LINK32 js32.lib zlib.lib winmm.lib ws2_32.lib /nologo /subsystem:windows /dll /debug /machine:I386 /out:"../../bin/w32_deb/GPAX.dll" /pdbtype:sept /libpath:"../../extra_lib/lib/w32_deb" -# Begin Custom Build - Performing registration -OutDir=.\obj/w32_deb -TargetPath=\CVS\gpac\bin\w32_deb\GPAX.dll -InputPath=\CVS\gpac\bin\w32_deb\GPAX.dll -SOURCE="$(InputPath)" - -"$(OutDir)\regsvr32.trg" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" - regsvr32 /s /c "$(TargetPath)" - echo regsvr32 exec. time > "$(OutDir)\regsvr32.trg" - -# End Custom Build - -!ELSEIF "$(CFG)" == "GPAX - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "obj/w32_rel" -# PROP BASE Intermediate_Dir "obj/w32_rel" -# PROP BASE Ignore_Export_Lib 0 -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "obj/w32_rel" -# PROP Intermediate_Dir "obj/w32_rel" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /MDd /W3 /Gm /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /FR /Yu"stdafx.h" /FD /GZ /c -# ADD CPP /nologo /MD /W3 /Gm /ZI /I "../../include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /FR /Yu"stdafx.h" /FD /GZ /c -# SUBTRACT CPP /O -# ADD BASE RSC /l 0x804 /d "_DEBUG" -# ADD RSC /l 0x804 /d "_DEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 js32.lib zlib.lib winmm.lib ws2_32.lib /nologo /subsystem:windows /dll /debug /machine:I386 /pdbtype:sept /libpath:"../gpac/extra_lib/lib/w32_deb" -# ADD LINK32 js32.lib zlib.lib winmm.lib ws2_32.lib /nologo /subsystem:windows /dll /debug /machine:I386 /out:"../../bin/w32_rel/GPAX.dll" /pdbtype:sept /libpath:"../../extra_lib/lib/w32_rel" -# Begin Special Build Tool -SOURCE="$(InputPath)" -PostBuild_Cmds=copy ..\..\bin\w32_rel\GPAX.dll "C:\Program Files\GPAC" -# End Special Build Tool - -!ENDIF - -# Begin Target - -# Name "GPAX - Win32 Debug" -# Name "GPAX - Win32 Release" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE=.\GPAX.cpp -# End Source File -# Begin Source File - -SOURCE=.\GPAX.def -# End Source File -# Begin Source File - -SOURCE=.\GPAX.idl -# ADD MTL /tlb ".\GPAX.tlb" /h "GPAX.h" /iid "GPAX_i.c" /Oicf -# End Source File -# Begin Source File - -SOURCE=.\GPAX.rc -# End Source File -# Begin Source File - -SOURCE=.\GPAXPlugin.cpp -# End Source File -# Begin Source File - -SOURCE=.\StdAfx.cpp -# ADD CPP /Yc"stdafx.h" -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# Begin Source File - -SOURCE=.\GPAXPlugin.h -# End Source File -# Begin Source File - -SOURCE=.\Resource.h -# End Source File -# Begin Source File - -SOURCE=.\StdAfx.h -# End Source File -# End Group -# Begin Group "Resource Files" - -# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" -# Begin Source File - -SOURCE=.\gpax.bmp -# End Source File -# Begin Source File - -SOURCE=.\GPAX.rgs -# End Source File -# Begin Source File - -SOURCE=.\GPAXProp.rgs -# End Source File -# End Group -# End Target -# End Project -# Section GPAX : {00000000-0000-0000-0000-800000800000} -# 1:21:IDS_DOCSTRINGGPAXProp:105 -# 1:12:IDD_GPAXPROP:107 -# 1:12:IDR_GPAXPROP:106 -# 1:20:IDS_HELPFILEGPAXProp:104 -# 1:17:IDS_TITLEGPAXProp:103 -# End Section diff --git a/applications/GPAX/GPAX.h b/applications/GPAX/GPAX.h deleted file mode 100644 index c38c76d..0000000 --- a/applications/GPAX/GPAX.h +++ /dev/null @@ -1,495 +0,0 @@ - -#pragma warning( disable: 4049 ) /* more than 64k source lines */ - -/* this ALWAYS GENERATED file contains the definitions for the interfaces */ - - - /* File created by MIDL compiler version 5.03.0286 */ -/* at Thu Jul 20 19:14:15 2006 - */ -/* Compiler settings for \CVS\gpac\applications\GPAX\GPAX.idl: - Oicf (OptLev=i2), W1, Zp8, env=Win32 (32b run), ms_ext, c_ext - error checks: allocation ref bounds_check enum stub_data - VC __declspec() decoration level: - __declspec(uuid()), __declspec(selectany), __declspec(novtable) - DECLSPEC_UUID(), MIDL_INTERFACE() -*/ -//@@MIDL_FILE_HEADING( ) - - -/* verify that the version is high enough to compile this file*/ -#ifndef __REQUIRED_RPCNDR_H_VERSION__ -#define __REQUIRED_RPCNDR_H_VERSION__ 440 -#endif - -#include "rpc.h" -#include "rpcndr.h" - -#ifndef __GPAX_h__ -#define __GPAX_h__ - -/* Forward Declarations */ - -#ifndef __IGPAX_FWD_DEFINED__ -#define __IGPAX_FWD_DEFINED__ -typedef interface IGPAX IGPAX; -#endif /* __IGPAX_FWD_DEFINED__ */ - - -#ifndef __IGPAXEvents_FWD_DEFINED__ -#define __IGPAXEvents_FWD_DEFINED__ -typedef interface IGPAXEvents IGPAXEvents; -#endif /* __IGPAXEvents_FWD_DEFINED__ */ - - -#ifndef __GPAX_FWD_DEFINED__ -#define __GPAX_FWD_DEFINED__ - -#ifdef __cplusplus -typedef class GPAX GPAX; -#else -typedef struct GPAX GPAX; -#endif /* __cplusplus */ - -#endif /* __GPAX_FWD_DEFINED__ */ - - -/* header files for imported files */ -#include "oaidl.h" -#include "ocidl.h" - -#ifdef __cplusplus -extern "C"{ -#endif - -void __RPC_FAR * __RPC_USER MIDL_user_allocate(size_t); -void __RPC_USER MIDL_user_free( void __RPC_FAR * ); - - -#ifndef __GPAXLib_LIBRARY_DEFINED__ -#define __GPAXLib_LIBRARY_DEFINED__ - -/* library GPAXLib */ -/* [helpstring][version][uuid] */ - - - -#define DISPID_SRC ( 100 ) - -#define DISPID_AutoStart ( 101 ) - -#define DISPID_PlayEvent ( 100 ) - -#define DISPID_PauseEvent ( 101 ) - -#define DISPID_StopEvent ( 102 ) - - -EXTERN_C const IID LIBID_GPAXLib; - -#ifndef __IGPAX_INTERFACE_DEFINED__ -#define __IGPAX_INTERFACE_DEFINED__ - -/* interface IGPAX */ -/* [object][oleautomation][hidden][dual][helpstring][uuid] */ - - -EXTERN_C const IID IID_IGPAX; - -#if defined(__cplusplus) && !defined(CINTERFACE) - - MIDL_INTERFACE("E2A9A937-BB35-47E0-8942-964806299AB4") - IGPAX : public IDispatch - { - public: - virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE Play( void) = 0; - - virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE Pause( void) = 0; - - virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE Stop( void) = 0; - - virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE Update( - /* [in] */ BSTR mtype, - /* [in] */ BSTR updates) = 0; - - virtual /* [helpstring][propget][id] */ HRESULT STDMETHODCALLTYPE get_src( - /* [retval][out] */ BSTR __RPC_FAR *url) = 0; - - virtual /* [helpstring][propput][id] */ HRESULT STDMETHODCALLTYPE put_src( - /* [in] */ BSTR url) = 0; - - virtual /* [helpstring][propget][id] */ HRESULT STDMETHODCALLTYPE get_AutoStart( - /* [retval][out] */ VARIANT_BOOL __RPC_FAR *autoplay) = 0; - - virtual /* [helpstring][propput][id] */ HRESULT STDMETHODCALLTYPE put_AutoStart( - /* [in] */ VARIANT_BOOL autoplay) = 0; - - }; - -#else /* C style interface */ - - typedef struct IGPAXVtbl - { - BEGIN_INTERFACE - - HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( - IGPAX __RPC_FAR * This, - /* [in] */ REFIID riid, - /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); - - ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( - IGPAX __RPC_FAR * This); - - ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( - IGPAX __RPC_FAR * This); - - HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetTypeInfoCount )( - IGPAX __RPC_FAR * This, - /* [out] */ UINT __RPC_FAR *pctinfo); - - HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetTypeInfo )( - IGPAX __RPC_FAR * This, - /* [in] */ UINT iTInfo, - /* [in] */ LCID lcid, - /* [out] */ ITypeInfo __RPC_FAR *__RPC_FAR *ppTInfo); - - HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetIDsOfNames )( - IGPAX __RPC_FAR * This, - /* [in] */ REFIID riid, - /* [size_is][in] */ LPOLESTR __RPC_FAR *rgszNames, - /* [in] */ UINT cNames, - /* [in] */ LCID lcid, - /* [size_is][out] */ DISPID __RPC_FAR *rgDispId); - - /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Invoke )( - IGPAX __RPC_FAR * This, - /* [in] */ DISPID dispIdMember, - /* [in] */ REFIID riid, - /* [in] */ LCID lcid, - /* [in] */ WORD wFlags, - /* [out][in] */ DISPPARAMS __RPC_FAR *pDispParams, - /* [out] */ VARIANT __RPC_FAR *pVarResult, - /* [out] */ EXCEPINFO __RPC_FAR *pExcepInfo, - /* [out] */ UINT __RPC_FAR *puArgErr); - - /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Play )( - IGPAX __RPC_FAR * This); - - /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Pause )( - IGPAX __RPC_FAR * This); - - /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Stop )( - IGPAX __RPC_FAR * This); - - /* [helpstring] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Update )( - IGPAX __RPC_FAR * This, - /* [in] */ BSTR mtype, - /* [in] */ BSTR updates); - - /* [helpstring][propget][id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_src )( - IGPAX __RPC_FAR * This, - /* [retval][out] */ BSTR __RPC_FAR *url); - - /* [helpstring][propput][id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *put_src )( - IGPAX __RPC_FAR * This, - /* [in] */ BSTR url); - - /* [helpstring][propget][id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_AutoStart )( - IGPAX __RPC_FAR * This, - /* [retval][out] */ VARIANT_BOOL __RPC_FAR *autoplay); - - /* [helpstring][propput][id] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *put_AutoStart )( - IGPAX __RPC_FAR * This, - /* [in] */ VARIANT_BOOL autoplay); - - END_INTERFACE - } IGPAXVtbl; - - interface IGPAX - { - CONST_VTBL struct IGPAXVtbl __RPC_FAR *lpVtbl; - }; - - - -#ifdef COBJMACROS - - -#define IGPAX_QueryInterface(This,riid,ppvObject) \ - (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) - -#define IGPAX_AddRef(This) \ - (This)->lpVtbl -> AddRef(This) - -#define IGPAX_Release(This) \ - (This)->lpVtbl -> Release(This) - - -#define IGPAX_GetTypeInfoCount(This,pctinfo) \ - (This)->lpVtbl -> GetTypeInfoCount(This,pctinfo) - -#define IGPAX_GetTypeInfo(This,iTInfo,lcid,ppTInfo) \ - (This)->lpVtbl -> GetTypeInfo(This,iTInfo,lcid,ppTInfo) - -#define IGPAX_GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId) \ - (This)->lpVtbl -> GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId) - -#define IGPAX_Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr) \ - (This)->lpVtbl -> Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr) - - -#define IGPAX_Play(This) \ - (This)->lpVtbl -> Play(This) - -#define IGPAX_Pause(This) \ - (This)->lpVtbl -> Pause(This) - -#define IGPAX_Stop(This) \ - (This)->lpVtbl -> Stop(This) - -#define IGPAX_Update(This,mtype,updates) \ - (This)->lpVtbl -> Update(This,mtype,updates) - -#define IGPAX_get_src(This,url) \ - (This)->lpVtbl -> get_src(This,url) - -#define IGPAX_put_src(This,url) \ - (This)->lpVtbl -> put_src(This,url) - -#define IGPAX_get_AutoStart(This,autoplay) \ - (This)->lpVtbl -> get_AutoStart(This,autoplay) - -#define IGPAX_put_AutoStart(This,autoplay) \ - (This)->lpVtbl -> put_AutoStart(This,autoplay) - -#endif /* COBJMACROS */ - - -#endif /* C style interface */ - - - -/* [helpstring] */ HRESULT STDMETHODCALLTYPE IGPAX_Play_Proxy( - IGPAX __RPC_FAR * This); - - -void __RPC_STUB IGPAX_Play_Stub( - IRpcStubBuffer *This, - IRpcChannelBuffer *_pRpcChannelBuffer, - PRPC_MESSAGE _pRpcMessage, - DWORD *_pdwStubPhase); - - -/* [helpstring] */ HRESULT STDMETHODCALLTYPE IGPAX_Pause_Proxy( - IGPAX __RPC_FAR * This); - - -void __RPC_STUB IGPAX_Pause_Stub( - IRpcStubBuffer *This, - IRpcChannelBuffer *_pRpcChannelBuffer, - PRPC_MESSAGE _pRpcMessage, - DWORD *_pdwStubPhase); - - -/* [helpstring] */ HRESULT STDMETHODCALLTYPE IGPAX_Stop_Proxy( - IGPAX __RPC_FAR * This); - - -void __RPC_STUB IGPAX_Stop_Stub( - IRpcStubBuffer *This, - IRpcChannelBuffer *_pRpcChannelBuffer, - PRPC_MESSAGE _pRpcMessage, - DWORD *_pdwStubPhase); - - -/* [helpstring] */ HRESULT STDMETHODCALLTYPE IGPAX_Update_Proxy( - IGPAX __RPC_FAR * This, - /* [in] */ BSTR mtype, - /* [in] */ BSTR updates); - - -void __RPC_STUB IGPAX_Update_Stub( - IRpcStubBuffer *This, - IRpcChannelBuffer *_pRpcChannelBuffer, - PRPC_MESSAGE _pRpcMessage, - DWORD *_pdwStubPhase); - - -/* [helpstring][propget][id] */ HRESULT STDMETHODCALLTYPE IGPAX_get_src_Proxy( - IGPAX __RPC_FAR * This, - /* [retval][out] */ BSTR __RPC_FAR *url); - - -void __RPC_STUB IGPAX_get_src_Stub( - IRpcStubBuffer *This, - IRpcChannelBuffer *_pRpcChannelBuffer, - PRPC_MESSAGE _pRpcMessage, - DWORD *_pdwStubPhase); - - -/* [helpstring][propput][id] */ HRESULT STDMETHODCALLTYPE IGPAX_put_src_Proxy( - IGPAX __RPC_FAR * This, - /* [in] */ BSTR url); - - -void __RPC_STUB IGPAX_put_src_Stub( - IRpcStubBuffer *This, - IRpcChannelBuffer *_pRpcChannelBuffer, - PRPC_MESSAGE _pRpcMessage, - DWORD *_pdwStubPhase); - - -/* [helpstring][propget][id] */ HRESULT STDMETHODCALLTYPE IGPAX_get_AutoStart_Proxy( - IGPAX __RPC_FAR * This, - /* [retval][out] */ VARIANT_BOOL __RPC_FAR *autoplay); - - -void __RPC_STUB IGPAX_get_AutoStart_Stub( - IRpcStubBuffer *This, - IRpcChannelBuffer *_pRpcChannelBuffer, - PRPC_MESSAGE _pRpcMessage, - DWORD *_pdwStubPhase); - - -/* [helpstring][propput][id] */ HRESULT STDMETHODCALLTYPE IGPAX_put_AutoStart_Proxy( - IGPAX __RPC_FAR * This, - /* [in] */ VARIANT_BOOL autoplay); - - -void __RPC_STUB IGPAX_put_AutoStart_Stub( - IRpcStubBuffer *This, - IRpcChannelBuffer *_pRpcChannelBuffer, - PRPC_MESSAGE _pRpcMessage, - DWORD *_pdwStubPhase); - - - -#endif /* __IGPAX_INTERFACE_DEFINED__ */ - - -#ifndef __IGPAXEvents_DISPINTERFACE_DEFINED__ -#define __IGPAXEvents_DISPINTERFACE_DEFINED__ - -/* dispinterface IGPAXEvents */ -/* [helpstring][uuid] */ - - -EXTERN_C const IID DIID_IGPAXEvents; - -#if defined(__cplusplus) && !defined(CINTERFACE) - - MIDL_INTERFACE("1FDA32FC-4C9A-461F-B33B-0715B0343006") - IGPAXEvents : public IDispatch - { - }; - -#else /* C style interface */ - - typedef struct IGPAXEventsVtbl - { - BEGIN_INTERFACE - - HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( - IGPAXEvents __RPC_FAR * This, - /* [in] */ REFIID riid, - /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); - - ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( - IGPAXEvents __RPC_FAR * This); - - ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( - IGPAXEvents __RPC_FAR * This); - - HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetTypeInfoCount )( - IGPAXEvents __RPC_FAR * This, - /* [out] */ UINT __RPC_FAR *pctinfo); - - HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetTypeInfo )( - IGPAXEvents __RPC_FAR * This, - /* [in] */ UINT iTInfo, - /* [in] */ LCID lcid, - /* [out] */ ITypeInfo __RPC_FAR *__RPC_FAR *ppTInfo); - - HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetIDsOfNames )( - IGPAXEvents __RPC_FAR * This, - /* [in] */ REFIID riid, - /* [size_is][in] */ LPOLESTR __RPC_FAR *rgszNames, - /* [in] */ UINT cNames, - /* [in] */ LCID lcid, - /* [size_is][out] */ DISPID __RPC_FAR *rgDispId); - - /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Invoke )( - IGPAXEvents __RPC_FAR * This, - /* [in] */ DISPID dispIdMember, - /* [in] */ REFIID riid, - /* [in] */ LCID lcid, - /* [in] */ WORD wFlags, - /* [out][in] */ DISPPARAMS __RPC_FAR *pDispParams, - /* [out] */ VARIANT __RPC_FAR *pVarResult, - /* [out] */ EXCEPINFO __RPC_FAR *pExcepInfo, - /* [out] */ UINT __RPC_FAR *puArgErr); - - END_INTERFACE - } IGPAXEventsVtbl; - - interface IGPAXEvents - { - CONST_VTBL struct IGPAXEventsVtbl __RPC_FAR *lpVtbl; - }; - - - -#ifdef COBJMACROS - - -#define IGPAXEvents_QueryInterface(This,riid,ppvObject) \ - (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) - -#define IGPAXEvents_AddRef(This) \ - (This)->lpVtbl -> AddRef(This) - -#define IGPAXEvents_Release(This) \ - (This)->lpVtbl -> Release(This) - - -#define IGPAXEvents_GetTypeInfoCount(This,pctinfo) \ - (This)->lpVtbl -> GetTypeInfoCount(This,pctinfo) - -#define IGPAXEvents_GetTypeInfo(This,iTInfo,lcid,ppTInfo) \ - (This)->lpVtbl -> GetTypeInfo(This,iTInfo,lcid,ppTInfo) - -#define IGPAXEvents_GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId) \ - (This)->lpVtbl -> GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId) - -#define IGPAXEvents_Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr) \ - (This)->lpVtbl -> Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr) - -#endif /* COBJMACROS */ - - -#endif /* C style interface */ - - -#endif /* __IGPAXEvents_DISPINTERFACE_DEFINED__ */ - - -EXTERN_C const CLSID CLSID_GPAX; - -#ifdef __cplusplus - -class DECLSPEC_UUID("181D18E6-4DC1-4B55-B72E-BE2A10064995") -GPAX; -#endif -#endif /* __GPAXLib_LIBRARY_DEFINED__ */ - -/* Additional Prototypes for ALL interfaces */ - -/* end of Additional Prototypes */ - -#ifdef __cplusplus -} -#endif - -#endif - - diff --git a/applications/GPAX/GPAX.idl b/applications/GPAX/GPAX.idl deleted file mode 100644 index 922910f..0000000 --- a/applications/GPAX/GPAX.idl +++ /dev/null @@ -1,84 +0,0 @@ -// GPAX.idl : IDL source for GPAX.dll -// - -// This file will be processed by the MIDL tool to -// produce the type library (GPAX.tlb) and marshalling code. - -import "oaidl.idl"; -import "ocidl.idl"; -#include "olectl.h" - - -[ - uuid(E64FAC7F-0134-4A75-A7DA-80D53EBC56A6), - version(1.0), - helpstring("GPAX ActiveX Control") -] - -library GPAXLib -{ - importlib("stdole2.tlb"); - - // Forward declare all types defined in this typelib - interface IGPAX; - dispinterface IGPAXEvents; - - const int DISPID_SRC = 100; - const int DISPID_AutoStart = 101; - - - //IDispatch interface - [ - odl, - uuid(E2A9A937-BB35-47E0-8942-964806299AB4), - helpstring("GPAC ActiveX Control"), - dual, - hidden, - oleautomation - ] - interface IGPAX : IDispatch - { - /*functions*/ - [helpstring("Play Movie")] HRESULT Play(); - [helpstring("Pause/Resume Movie")] HRESULT Pause(); - [helpstring("Stop Movie")] HRESULT Stop(); - [helpstring("Update Scene")] HRESULT Update([in] BSTR mtype, [in] BSTR updates); - - - /*properties*/ - [id(DISPID_SRC), propget, helpstring("Get/Set the media source")] - HRESULT src([out, retval] BSTR* url); - [id(DISPID_SRC), propput, helpstring("Get/Set the media source")] - HRESULT src([in] BSTR url); - - [id(DISPID_AutoStart), propget, helpstring("Get/Set automatic playback upon load")] - HRESULT AutoStart([out, retval] VARIANT_BOOL* autoplay); - [id(DISPID_AutoStart), propput, helpstring("Get/Set automatic playback upon load")] - HRESULT AutoStart([in] VARIANT_BOOL autoplay); - }; - - - //event interface - [ - uuid(1FDA32FC-4C9A-461F-B33B-0715B0343006), - helpstring("GPAX Control Events") - ] - dispinterface IGPAXEvents - { - properties: - methods: - }; - - - //AX control - [ - uuid(181D18E6-4DC1-4B55-B72E-BE2A10064995), - helpstring("GPAC Control"), - control - ] - coclass GPAX - { - [default] interface IGPAX; - [default, source] dispinterface IGPAXEvents; - }; -}; diff --git a/applications/GPAX/GPAX.rc b/applications/GPAX/GPAX.rc deleted file mode 100644 index 9ba86a4..0000000 --- a/applications/GPAX/GPAX.rc +++ /dev/null @@ -1,149 +0,0 @@ -//Microsoft Developer Studio generated resource script. -// -#include "resource.h" - -#define APSTUDIO_READONLY_SYMBOLS -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 2 resource. -// -#include "winres.h" - -///////////////////////////////////////////////////////////////////////////// -#undef APSTUDIO_READONLY_SYMBOLS - -///////////////////////////////////////////////////////////////////////////// -// Chinese (P.R.C.) resources - -#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS) -#ifdef _WIN32 -LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED -#pragma code_page(936) -#endif //_WIN32 - -#ifdef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// TEXTINCLUDE -// - -1 TEXTINCLUDE DISCARDABLE -BEGIN - "resource.h\0" -END - -2 TEXTINCLUDE DISCARDABLE -BEGIN - "#include ""winres.h""\r\n" - "\0" -END - -3 TEXTINCLUDE DISCARDABLE -BEGIN - "1 TYPELIB ""GPAX.tlb""\r\n" - "\0" -END - -#endif // APSTUDIO_INVOKED - -#endif // Chinese (P.R.C.) resources -///////////////////////////////////////////////////////////////////////////// - - -///////////////////////////////////////////////////////////////////////////// -// English (U.S.) resources - -#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) -#ifdef _WIN32 -LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US -#pragma code_page(1252) -#endif //_WIN32 - -#ifndef _MAC -///////////////////////////////////////////////////////////////////////////// -// -// Version -// - -VS_VERSION_INFO VERSIONINFO - FILEVERSION 1,0,0,1 - PRODUCTVERSION 1,0,0,1 - FILEFLAGSMASK 0x3fL -#ifdef _DEBUG - FILEFLAGS 0x1L -#else - FILEFLAGS 0x0L -#endif - FILEOS 0x4L - FILETYPE 0x2L - FILESUBTYPE 0x0L -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "040904b0" - BEGIN - VALUE "Comments", "\0" - VALUE "CompanyName", "ENST\0" - VALUE "FileDescription", "GPAX\0" - VALUE "FileVersion", "0.4.5\0" - VALUE "InternalName", "GPAX\0" - VALUE "LegalCopyright", "Copyright © 2007\0" - VALUE "LegalTrademarks", "\0" - VALUE "OriginalFilename", "GPAX.dll\0" - VALUE "PrivateBuild", "\0" - VALUE "ProductName", "ENST GPAX\0" - VALUE "ProductVersion", "0.4.5\0" - VALUE "SpecialBuild", "\0" - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x409, 1200 - END -END - -#endif // !_MAC - - -///////////////////////////////////////////////////////////////////////////// -// -// Bitmap -// - -IDB_GPAXPLUGIN BITMAP DISCARDABLE "gpax.bmp" - -///////////////////////////////////////////////////////////////////////////// -// -// REGISTRY -// - -IDR_GPAXPLUGIN REGISTRY DISCARDABLE "GPAX.rgs" - -///////////////////////////////////////////////////////////////////////////// -// -// String Table -// - -STRINGTABLE DISCARDABLE -BEGIN - IDS_PROJNAME "GPAX" - IDS_TITLEGPAXProp "&GPAX" - IDS_HELPFILEGPAXProp "Help File Name" - IDS_DOCSTRINGGPAXProp "URLs setting" -END - -#endif // English (U.S.) resources -///////////////////////////////////////////////////////////////////////////// - - - -#ifndef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 3 resource. -// -1 TYPELIB "GPAX.tlb" - -///////////////////////////////////////////////////////////////////////////// -#endif // not APSTUDIO_INVOKED - diff --git a/applications/GPAX/GPAX.rgs b/applications/GPAX/GPAX.rgs deleted file mode 100644 index 2949a85..0000000 --- a/applications/GPAX/GPAX.rgs +++ /dev/null @@ -1,175 +0,0 @@ -HKCR -{ - ForceRemove '.aac' - { - val 'Content Type' = s 'audio/aac' - } - ForceRemove '.amr' - { - val 'Content Type' = s 'audio/amr' - } - ForceRemove '.mp4' - { - val 'Content Type' = s 'application/mp4' - } - ForceRemove '.3gp' - { - val 'Content Type' = s 'video/3gpp' - } - ForceRemove '.3g2' - { - val 'Content Type' = s 'video/3gpp2' - } - ForceRemove '.wrl' - { - val 'Content Type' = s 'model/vrml' - } - ForceRemove '.x3dv' - { - val 'Content Type' = s 'model/x3d+vrml' - } - ForceRemove '.x3d' - { - val 'Content Type' = s 'model/x3d+xml' - } - ForceRemove '.svg' - { - val 'Content Type' = s 'image/svg+xml' - } - ForceRemove '.sdp' - { - val 'Content Type' = s 'application/sdp' - } - - GPAX.GPAXPlugin.1 = s 'GPAC ActiveX' - { - CLSID = s '{181D18E6-4DC1-4B55-B72E-BE2A10064995}' - } - GPAX.GPAXPlugin = s 'GPAC ActiveX' - { - CLSID = s '{181D18E6-4DC1-4B55-B72E-BE2A10064995}' - CurVer = s 'GPAX.GPAXPlugin.1' - } - NoRemove CLSID - { - ForceRemove {181D18E6-4DC1-4B55-B72E-BE2A10064995} = s 'GPAC ActiveX' - { - ProgID = s 'GPAX.GPAXPlugin.1' - VersionIndependentProgID = s 'GPAX.GPAXPlugin' - ForceRemove 'Programmable' - InprocServer32 = s '%MODULE%' - { - val ThreadingModel = s 'Both' - } - ForceRemove 'Control' - ForceRemove 'Insertable' - ForceRemove 'ToolboxBitmap32' = s '%MODULE%, 101' - 'MiscStatus' = s '0' - { - '1' = s '131473' - } - 'TypeLib' = s '{E64FAC7F-0134-4A75-A7DA-80D53EBC56A6}' - 'Version' = s '1.0' - ForceRemove 'EnableFullPage' - { - ForceRemove .mp4 - ForceRemove .3gp - ForceRemove .3g2 - ForceRemove .wrl - ForceRemove .x3d - ForceRemove .x3dv - ForceRemove .svg - } - } - } - - NoRemove MIME - { - NoRemove Database - { - NoRemove 'Content Type' - { - 'application/x-gpac' - { - val CLSID = s '{181D18E6-4DC1-4B55-B72E-BE2A10064995}' - val Extension = s '.gpac' - } - 'application/mp4' - { - val CLSID = s '{181D18E6-4DC1-4B55-B72E-BE2A10064995}' - val Extension = s '.mp4' - } - 'application/sdp' - { - val CLSID = s '{181D18E6-4DC1-4B55-B72E-BE2A10064995}' - val Extension = s '.sdp' - } - 'audio/aac' - { - val CLSID = s '{181D18E6-4DC1-4B55-B72E-BE2A10064995}' - val Extension = s '.aac' - } - 'audio/amr' - { - val CLSID = s '{181D18E6-4DC1-4B55-B72E-BE2A10064995}' - val Extension = s '.amr' - } - 'audio/mp4' - { - val CLSID = s '{181D18E6-4DC1-4B55-B72E-BE2A10064995}' - val Extension = s '.mp4' - } - 'audio/mpeg' - { - val CLSID = s '{181D18E6-4DC1-4B55-B72E-BE2A10064995}' - val Extension = s '.mp3' - } - 'image/svg+xml' - { - val CLSID = s '{181D18E6-4DC1-4B55-B72E-BE2A10064995}' - val Extension = s '.svg' - } - 'model/vrml' - { - val CLSID = s '{181D18E6-4DC1-4B55-B72E-BE2A10064995}' - val Extension = s '.wrl' - } - 'model/x3d+vrml' - { - val CLSID = s '{181D18E6-4DC1-4B55-B72E-BE2A10064995}' - val Extension = s '.x3dv' - } - 'model/x3d+xml' - { - val CLSID = s '{181D18E6-4DC1-4B55-B72E-BE2A10064995}' - val Extension = s '.x3d' - } - 'video/mp4' - { - val CLSID = s '{181D18E6-4DC1-4B55-B72E-BE2A10064995}' - val Extension = s '.mp4' - } - 'video/3gpp' - { - val CLSID = s '{181D18E6-4DC1-4B55-B72E-BE2A10064995}' - val Extension = s '.3gp' - } - 'video/3gpp2' - { - val CLSID = s '{181D18E6-4DC1-4B55-B72E-BE2A10064995}' - val Extension = s '.3g2' - } - 'video/avi' - { - val CLSID = s '{181D18E6-4DC1-4B55-B72E-BE2A10064995}' - val Extension = s '.avi' - } - 'video/mpeg' - { - val CLSID = s '{181D18E6-4DC1-4B55-B72E-BE2A10064995}' - val Extension = s '.mpg' - } - } - } - } -} diff --git a/applications/GPAX/GPAXPlugin.cpp b/applications/GPAX/GPAXPlugin.cpp deleted file mode 100644 index d37e823..0000000 --- a/applications/GPAX/GPAXPlugin.cpp +++ /dev/null @@ -1,635 +0,0 @@ -/* - * GPAC - Multimedia Framework C SDK - * - * Authors: Y.XI, X. ZHAO, J. Le Feuvre - * Copyright (c) ENST 2006-200x - * All rights reserved - * - * This file is part of GPAC / ActiveX control - * - * GPAC is gf_free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * GPAC is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; see the file COPYING. If not, write to - * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - * - */ - -#include "stdafx.h" -#include "GPAX.h" -#include "GPAXPlugin.h" -#include -#include -#include - -#ifndef _WIN32_WCE -#include -#include -#endif - -///////////////////////////////////////////////////////////////////////////// -// CGPAXPlugin - -#if 0 -static print_err(char *msg, char *title) -{ - u16 w_msg[1024], w_title[1024]; - CE_CharToWide(msg, w_msg); - CE_CharToWide(title, w_title); - ::MessageBox(NULL, w_msg, w_title, MB_OK); -} -#endif - - -void CGPAXPlugin::SetStatusText(char *msg) -{ -#ifndef _WIN32_WCE - if (m_pBrowser) { - if (msg) { - u16 w_msg[1024]; - gf_utf8_mbstowcs(w_msg, 1024, (const char **)&msg); - m_pBrowser->put_StatusText((BSTR) w_msg); - } else { - m_pBrowser->put_StatusText(L""); - } - } -#endif -} -//GPAC player Event Handler. not yet implemented, just dummies here -Bool CGPAXPlugin::EventProc(GF_Event *evt) -{ - char msg[1024]; - if (!m_term) return 0; - - switch (evt->type) { - case GF_EVENT_MESSAGE: - if (evt->message.error) { - sprintf(msg, "(GPAC) %s (%s)", evt->message.message, gf_error_to_string(evt->message.error)); - } else { - sprintf(msg, "(GPAC) %s", evt->message.message); - } - SetStatusText(msg); - break; - case GF_EVENT_PROGRESS: - if (evt->progress.done == evt->progress.total) { - SetStatusText(NULL); - } else { - char *szTitle = ""; - if (evt->progress.progress_type==0) szTitle = "Buffer "; - else if (evt->progress.progress_type==1) szTitle = "Download "; - else if (evt->progress.progress_type==2) szTitle = "Import "; - sprintf(msg, "(GPAC) %s: %02.2f", szTitle, (100.0*evt->progress.done) / evt->progress.total); - SetStatusText(msg); - } - break; - case GF_EVENT_CONNECT: - m_bIsConnected = evt->connect.is_connected; - break; - /*IGNORE any scene size, just work with the size allocated in the parent doc*/ - case GF_EVENT_SCENE_SIZE: - gf_term_set_size(m_term, m_width, m_height); - break; - /*window has been resized (full-screen plugin), resize*/ - case GF_EVENT_SIZE: - m_width = evt->size.width; - m_height = evt->size.height; - gf_term_set_size(m_term, m_width, m_height); - break; - case GF_EVENT_DBLCLICK: - gf_term_set_option(m_term, GF_OPT_FULLSCREEN, !gf_term_get_option(m_term, GF_OPT_FULLSCREEN)); - break; - case GF_EVENT_KEYDOWN: - if ((evt->key.flags & GF_KEY_MOD_ALT)) { - } else { - switch (evt->key.key_code) { - case GF_KEY_HOME: - gf_term_set_option(m_term, GF_OPT_NAVIGATION_TYPE, 1); - break; - case GF_KEY_ESCAPE: - gf_term_set_option(m_term, GF_OPT_FULLSCREEN, !gf_term_get_option(m_term, GF_OPT_FULLSCREEN)); - break; - } - } - break; - case GF_EVENT_NAVIGATE_INFO: - strcpy(msg, evt->navigate.to_url); - SetStatusText(msg); - break; - case GF_EVENT_NAVIGATE: - if (gf_term_is_supported_url(m_term, evt->navigate.to_url, 1, 1)) { - gf_term_navigate_to(m_term, evt->navigate.to_url); - return 1; - } -#ifndef _WIN32_WCE - else if (m_pBrowser) { - u32 i; - const char **sz_ptr; - u16 w_szTar[1024], w_szURL[1024]; - VARIANT target, flags; - flags.intVal = 0; - target.bstrVal = L"_SELF"; - - for (i=0; inavigate.param_count; i++) { - if (!strcmp(evt->navigate.parameters[i], "_parent")) target.bstrVal = L"_PARENT"; - else if (!strcmp(evt->navigate.parameters[i], "_blank")) target.bstrVal = L"_BLANK"; - else if (!strcmp(evt->navigate.parameters[i], "_top")) target.bstrVal = L"_TOP"; - else if (!strcmp(evt->navigate.parameters[i], "_new")) flags.intVal |= navOpenInNewWindow; - else if (!strnicmp(evt->navigate.parameters[i], "_target=", 8)) { - sz_ptr = & evt->navigate.parameters[i]+8; - gf_utf8_mbstowcs(w_szTar, 1024, (const char **)sz_ptr); - target.bstrVal = (BSTR) w_szTar; - } - } - sz_ptr = & evt->navigate.to_url; - gf_utf8_mbstowcs(w_szURL, 1024, (const char **)sz_ptr); - m_pBrowser->Navigate((BSTR) w_szURL, &flags, &target, NULL, NULL);; - return 1; - } -#endif - break; - } - return 0; -} - -Bool GPAX_EventProc(void *ptr, GF_Event *evt) -{ - CGPAXPlugin *_this = (CGPAXPlugin *)ptr; - return _this->EventProc(evt); -} - -//Read Parameters from pPropBag given by MSIE -Bool CGPAXPlugin::ReadParamString(LPPROPERTYBAG pPropBag, LPERRORLOG pErrorLog, - WCHAR *name, char *buf, int bufsize) -{ - VARIANT v; - HRESULT hr; - Bool retval=0; - - v.vt = VT_EMPTY; - v.bstrVal = NULL; - hr = pPropBag->Read(name, &v, pErrorLog); - if(SUCCEEDED(hr)) - { - if(v.vt==VT_BSTR && v.bstrVal) - { -// USES_CONVERSION; -// lstrcpyn(buf,OLE2T(v.bstrVal),bufsize); - const u16 *srcp = (const u16 *) v.bstrVal; - u32 len = gf_utf8_wcstombs(buf, bufsize, &srcp); - if (len>=0) { - buf[len] = 0; - retval=1; - } - } - VariantClear(&v); - } - return retval; -} - -void CGPAXPlugin::LoadDATAUrl() -{ -#ifndef _WIN32_WCE - HRESULT hr; - - if (m_url[0]) return; - /*get parent doc*/ - CComPtr spContainer; - if (m_spClientSite->GetContainer(&spContainer) != S_OK) - return; - CComPtr spDoc = CComQIPtr(spContainer); - CComPtr spColl; - if (spDoc->get_all(&spColl) != S_OK) - return; - /*get HTML in the doc*/ - CComPtr spDisp; - CComPtr sphtmlObjects; - - CComPtr spDispObjects; - if (spColl->tags(CComVariant("OBJECT"), &spDispObjects) != S_OK) - return; - CComPtr spObjs = CComQIPtr(spDispObjects); - - /*browse all objects and find us*/ - long lCount = 0; - spObjs->get_length(&lCount); - for (long lCnt = 0; lCnt < lCount; lCnt++) { - IDispatch *an_obj= NULL; - CComVariant varEmpty; - CComVariant varName; - varName.vt = VT_I4; - varName.lVal = lCnt; - hr = spObjs->item(varName, varEmpty, &an_obj); - varName.Clear(); - varEmpty.Clear(); - if (hr != S_OK) continue; - - /*get the IHTMLObject*/ - IHTMLObjectElement* pObjectElem=NULL; - an_obj->QueryInterface(IID_IHTMLObjectElement, (void**)&pObjectElem); - if (!pObjectElem) continue; - - /*get its parent owner - it MUST be us*/ - IDispatch *disp= NULL; - pObjectElem->get_object(&disp); - if (disp != this) continue; - - BSTR data = NULL; - if ((pObjectElem->get_data(&data) == S_OK) && data) { - const u16 *srcp = (const u16 *) data; - u32 len = gf_utf8_wcstombs(m_url, MAXLEN_URL, &srcp); - if (len>=0) m_url[len] = 0; - } - SysFreeString(data); - break; - } - - if (m_url) { - UpdateURL(); - } -#endif - -} - -// trap keys and forward on to the control -BOOL CGPAXPlugin::PreTranslateMessage(MSG* pMsg) -{ - switch (pMsg->message) - { - case WM_KEYDOWN: - case WM_KEYUP: - switch (pMsg->wParam) - { - case VK_UP: - case VK_DOWN: - case VK_LEFT: - case VK_RIGHT: - case VK_HOME: - case VK_END: - SendMessage (pMsg->message, pMsg->wParam, pMsg->lParam); - // Windowless controls won't be able to call SendMessage. - // Instead, just respond to the message here. - return TRUE; - } - break; - } - return FALSE; -// return COleControl::PreTranslateMessage(pMsg); -} - - - - -#define GPAC_REG_KEY HKEY_CURRENT_USER - -//Create window message fuction. when the window is created, also initialize a instance of -//GPAC player instance. -LRESULT CGPAXPlugin::OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) -{ - if (m_term) return 0; - const char *str; - - if (m_hWnd==NULL) return 0; - - - //Create a structure m_user for initialize the terminal. the parameters to set: - //1)config file path - //2)Modules file path - //3)window handler - //4)EventProc - memset(&m_user, 0, sizeof(m_user)); - - m_user.config = gf_cfg_init(NULL, NULL); - if(!m_user.config) { -#ifdef _WIN32_WCE - ::MessageBox(NULL, _T("GPAC Configuration file not found"), _T("Fatal Error"), MB_OK); -#else - ::MessageBox(NULL, "GPAC Configuration file not found", "Fatal Error", MB_OK); -#endif - goto err_exit; - } - - str = gf_cfg_get_key(m_user.config, "General", "ModulesDirectory"); - m_user.modules = gf_modules_new(str, m_user.config); - if(!gf_modules_get_count(m_user.modules)) goto err_exit; - - m_user.os_window_handler = m_hWnd; - m_user.opaque = this; - m_user.EventProc = GPAX_EventProc; - - //create a terminal - m_term = gf_term_new(&m_user); - - if (!m_term) goto err_exit; - - gf_term_set_option(m_term, GF_OPT_AUDIO_VOLUME, 100); - - LoadDATAUrl(); - - RECT rc; - ::GetWindowRect(m_hWnd, &rc); - m_width = rc.right-rc.left; - m_height = rc.bottom-rc.top; - if (m_bAutoStart && strlen(m_url)) Play(); - return 0; - - //Error Processing -err_exit: - if(m_user.modules) - gf_modules_del(m_user.modules); - m_user.modules = NULL; - if(m_user.config) - gf_cfg_del(m_user.config); - m_user.config = NULL; - return 1; -} - -void CGPAXPlugin::UnloadTerm() -{ - if (m_term) { - GF_Terminal *a_term = m_term; - m_term = NULL; - gf_term_del(a_term); - } - if (m_user.modules) gf_modules_del(m_user.modules); - if (m_user.config) gf_cfg_del(m_user.config); - memset(&m_user, 0, sizeof(m_user)); -} -CGPAXPlugin::~CGPAXPlugin() -{ - UnloadTerm(); -#ifndef _WIN32_WCE - if (m_pBrowser) m_pBrowser->Release(); - m_pBrowser = NULL; -#endif -} -LRESULT CGPAXPlugin::OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) -{ - UnloadTerm(); - return 0; -} - - -HRESULT CGPAXPlugin::OnDraw(ATL_DRAWINFO& di) -{ - if (m_term && m_bInitialDraw) { - m_bInitialDraw = FALSE; - if (m_bAutoStart) Play(); - } - return S_OK; -} - -// Load is called before OnCreate, but it may not be called at -// all if there are no parameters. -STDMETHODIMP CGPAXPlugin::Load(LPPROPERTYBAG pPropBag, LPERRORLOG pErrorLog) -{ - char szOpt[1024]; - // examine the / tag arguments - - m_url[0] = 0; - ReadParamString(pPropBag,pErrorLog,L"src", m_url, MAXLEN_URL); - if (!m_url[0]) - ReadParamString(pPropBag,pErrorLog,L"data", m_url, MAXLEN_URL); - - if (ReadParamString(pPropBag,pErrorLog,L"autostart", szOpt, 1024)) - m_bAutoStart = (!stricmp(szOpt, "false") || !stricmp(szOpt, "no")) ? 0 : 1; - - if (ReadParamString(pPropBag,pErrorLog,L"use3d", szOpt, 1024)) - m_bUse3D = (!stricmp(szOpt, "true") || !stricmp(szOpt, "yes")) ? 1 : 0; - - if (ReadParamString(pPropBag,pErrorLog,L"aspectratio", szOpt, 1024)) { - if (!stricmp(szOpt, "keep")) m_AR = GF_ASPECT_RATIO_KEEP; - else if (!stricmp(szOpt, "16:9")) m_AR = GF_ASPECT_RATIO_16_9; - else if (!stricmp(szOpt, "4:3")) m_AR = GF_ASPECT_RATIO_4_3; - else if (!stricmp(szOpt, "fill")) m_AR = GF_ASPECT_RATIO_FILL_SCREEN; - } - - if (ReadParamString(pPropBag,pErrorLog,L"loop", szOpt, 1024)) - m_bLoop = !stricmp(szOpt, "true") ? 0 : 1; - - UpdateURL(); - -#ifndef _WIN32_WCE - /*get the top-level container*/ - if (!m_pBrowser) { - IServiceProvider *isp, *isp2 = NULL; - if ( SUCCEEDED(m_spClientSite->QueryInterface(IID_IServiceProvider, reinterpret_cast(&isp)) ) ) { - - if (SUCCEEDED(isp->QueryService(SID_STopLevelBrowser, IID_IServiceProvider, reinterpret_cast(&isp2)) ) ) { - isp2->QueryService(SID_SWebBrowserApp, IID_IWebBrowser2, reinterpret_cast(&m_pBrowser)); - isp2->Release(); - } - isp->Release(); - } - } - if (m_pBrowser) m_pBrowser->put_StatusText(L"GPAC Ready"); -#endif - - return IPersistPropertyBagImpl::Load(pPropBag, pErrorLog); -} - -void CGPAXPlugin::UpdateURL() -{ - /*get absolute URL*/ - if (!strlen(m_url)) return; - IMoniker* pMoniker = NULL; - LPOLESTR sDisplayName; - - if (SUCCEEDED(m_spClientSite->GetMoniker(OLEGETMONIKER_TEMPFORUSER, - OLEWHICHMK_CONTAINER, - &pMoniker) ) ) { - char parent_url[1024]; - pMoniker->GetDisplayName(NULL, NULL, &sDisplayName); - wcstombs(parent_url, sDisplayName, 300); - pMoniker->Release(); - - char *abs_url = gf_url_concatenate(parent_url, m_url); - if (abs_url) { - strcpy(m_url, abs_url); - gf_free(abs_url); - } - } -} - -STDMETHODIMP CGPAXPlugin::Save(LPPROPERTYBAG pPropBag, BOOL fClearDirty, BOOL fSaveAllProperties) -{ - u16 wurl[MAXLEN_URL]; - const char *sptr; - u16 len; - - VARIANT value; - if( pPropBag == NULL) return E_INVALIDARG; - - VariantInit(&value); - - V_VT(&value) = VT_BOOL; - V_BOOL(&value) = m_bAutoStart ? VARIANT_TRUE : VARIANT_FALSE; - pPropBag->Write(OLESTR("AutoStart"), &value); - VariantClear(&value); - - V_VT(&value) = VT_BSTR; - - sptr = (const char *)m_url; - len = gf_utf8_mbstowcs(wurl, MAXLEN_URL, &sptr); - V_BSTR(&value) = SysAllocStringLen(NULL, len+1); - memcpy(V_BSTR(&value) , wurl, len*sizeof(u16)); - V_BSTR(&value) [len] = 0; - - pPropBag->Write(OLESTR("src"), &value); - VariantClear(&value); - return S_OK; -} - -STDMETHODIMP CGPAXPlugin::Play() -{ - if (m_term) { - if (!m_bIsConnected) { - if (strlen(m_url)) { - /*connect from 0 and pause if not autoplay*/ - const char *gui = gf_cfg_get_key(m_user.config, "General", "StartupFile"); - if (gui) { - gf_cfg_set_key(m_user.config, "Temp", "BrowserMode", "yes"); - gf_cfg_set_key(m_user.config, "Temp", "GUIStartupFile", m_url); - gf_term_connect(m_term, gui); - } else { - gf_term_connect(m_term, m_url); - } - gf_term_set_option(m_term, GF_OPT_ASPECT_RATIO, m_AR); - } - } else - gf_term_set_option(m_term, GF_OPT_PLAY_STATE, GF_STATE_PLAYING); //if target is connected, set it playing - } - return S_OK; -} - -STDMETHODIMP CGPAXPlugin::Pause() -{ - if(m_term) { - if (gf_term_get_option(m_term, GF_OPT_PLAY_STATE) == GF_STATE_PAUSED) { - gf_term_set_option(m_term, GF_OPT_PLAY_STATE, GF_STATE_PLAYING); - } else { - gf_term_set_option(m_term, GF_OPT_PLAY_STATE, GF_STATE_PAUSED); - } - } - return S_OK; -} - -STDMETHODIMP CGPAXPlugin::Stop() -{ - if(m_term) gf_term_disconnect(m_term); //set it stop - return S_OK; -} - -STDMETHODIMP CGPAXPlugin::Update(BSTR _mtype, BSTR _updates) -{ - if (m_term) { - u16 *srcp; - u32 len; - char mtype[1024], *updates; - - srcp = (u16 *) _mtype; - len = gf_utf8_wcstombs(mtype, 1024, (const u16 **)&srcp); - mtype[len] = 0; - - srcp = (u16 *)_updates; - len = gf_utf8_wcstombs(NULL, 0, (const u16 **)&srcp); - if (len) { - updates = (char *) gf_malloc(sizeof(char) * (len+1)); - srcp = (u16 *)_updates; - len = gf_utf8_wcstombs(updates, len, (const u16 **)&srcp); - updates[len] = 0; - gf_term_scene_update(m_term, mtype, updates); - gf_free(updates); - } - } - return S_OK; -} - -STDMETHODIMP CGPAXPlugin::get_src(BSTR *url) -{ - u16 wurl[MAXLEN_URL]; - const char *sptr; - u16 len; - if (url==NULL) return E_POINTER; - - sptr = (const char *)m_url; - len = gf_utf8_mbstowcs(wurl, MAXLEN_URL, &sptr); - *url = SysAllocStringLen(NULL, len+1); - memcpy(*url, wurl, len*sizeof(u16)); - *url[len] = 0; - return S_OK; -} -STDMETHODIMP CGPAXPlugin::put_src(BSTR url) -{ - const u16 *srcp = (const u16 *)url; - u32 len = gf_utf8_wcstombs(m_url, MAXLEN_URL, &srcp); - m_url[len] = 0; - UpdateURL(); - return S_OK; -} - -STDMETHODIMP CGPAXPlugin::get_AutoStart(VARIANT_BOOL *as) -{ - if (as==NULL) return E_POINTER; - *as = m_bAutoStart ? VARIANT_TRUE: VARIANT_FALSE; - return S_OK; -} -STDMETHODIMP CGPAXPlugin::put_AutoStart(VARIANT_BOOL as) -{ - m_bAutoStart = (as !=VARIANT_FALSE) ? TRUE: FALSE; - return S_OK; -} - -STDMETHODIMP CGPAXPlugin::GetInterfaceSafetyOptions( - REFIID riid, - DWORD *pdwSupportedOptions, - DWORD *pdwEnabledOptions -) -{ - if( (NULL == pdwSupportedOptions) || (NULL == pdwEnabledOptions) ) - return E_POINTER; - - *pdwSupportedOptions = INTERFACESAFE_FOR_UNTRUSTED_DATA|INTERFACESAFE_FOR_UNTRUSTED_CALLER; - - if ((IID_IDispatch == riid) || (IID_IGPAX == riid)) { - *pdwEnabledOptions = INTERFACESAFE_FOR_UNTRUSTED_CALLER; - return NOERROR; - } - else if (IID_IPersistPropertyBag == riid) { - *pdwEnabledOptions = INTERFACESAFE_FOR_UNTRUSTED_DATA; - return NOERROR; - } - *pdwEnabledOptions = 0; - return E_NOINTERFACE; -}; - -STDMETHODIMP CGPAXPlugin::SetInterfaceSafetyOptions( - REFIID riid, - DWORD dwOptionSetMask, - DWORD dwEnabledOptions -) -{ - if ((IID_IDispatch == riid) || (IID_IGPAX == riid) ) { - if( (INTERFACESAFE_FOR_UNTRUSTED_CALLER == dwOptionSetMask) - && (INTERFACESAFE_FOR_UNTRUSTED_CALLER == dwEnabledOptions) ) { - return NOERROR; - } - return E_FAIL; - } - else if (IID_IPersistPropertyBag == riid) { - if( (INTERFACESAFE_FOR_UNTRUSTED_DATA == dwOptionSetMask) - && (INTERFACESAFE_FOR_UNTRUSTED_DATA == dwEnabledOptions) ) { - return NOERROR; - } - return E_FAIL; - } - return E_FAIL; -}; - diff --git a/applications/GPAX/GPAXPlugin.h b/applications/GPAX/GPAXPlugin.h deleted file mode 100644 index 8bda8e4..0000000 --- a/applications/GPAX/GPAXPlugin.h +++ /dev/null @@ -1,258 +0,0 @@ -/* - * GPAC - Multimedia Framework C SDK - * - * Authors: Y.XI, X. ZHAO, J. Le Feuvre - * Copyright (c) ENST 2006-200x - * All rights reserved - * - * This file is part of GPAC / ActiveX control - * - * GPAC is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * GPAC is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; see the file COPYING. If not, write to - * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - * - */ - -#ifndef __GPAXPLUGIN_H_ -#define __GPAXPLUGIN_H_ - -#define MAXLEN_URL 300 - -#include "resource.h" // main symbols -#include -#include - - - -#include -#include -#include -#include -#include - -#if (_MSC_VER >= 1300) -using namespace ATL; -#endif - - -Bool GPAX_EventProc(void *ptr, GF_Event *evt); - -///////////////////////////////////////////////////////////////////////////// -// CGPAXPlugin -class ATL_NO_VTABLE CGPAXPlugin : - public CComObjectRootEx, - public IDispatchImpl, - public CComControl, - public CComCoClass, - public IOleControlImpl, - public IOleObjectImpl, - public IOleInPlaceActiveObjectImpl, - public IViewObjectExImpl, - public IOleInPlaceObjectWindowlessImpl, - public IProvideClassInfo2Impl<&CLSID_GPAX, &DIID_IGPAXEvents, &LIBID_GPAXLib>, - - public IPersistStreamInitImpl, - public ISupportErrorInfo, - public IConnectionPointContainerImpl, - public IPersistStorageImpl, - public ISpecifyPropertyPagesImpl, - public IQuickActivateImpl, - public IDataObjectImpl, - public IPropertyNotifySinkCP, - - public IPersistPropertyBagImpl, - public IObjectSafetyImpl - -{ -public: - CGPAXPlugin() { - m_term = NULL; - m_bAutoStart = TRUE; - m_bInitialDraw = TRUE; - m_bWindowOnly = TRUE; //to declare that the control is a window control in order - //to inherit the member variable m_hWnd which contains the window handler - m_bIsConnected = 0; - m_bUse3D = 0; - m_AR = GF_ASPECT_RATIO_KEEP; - m_url[0] = 0; -#ifndef _WIN32_WCE - m_pBrowser = NULL; -#endif - memset(&m_user, 0, sizeof(m_user)); - - m_dwCurrentSafety = INTERFACESAFE_FOR_UNTRUSTED_CALLER | INTERFACESAFE_FOR_UNTRUSTED_DATA; - } - - ~CGPAXPlugin(); - - Bool EventProc(GF_Event *evt); - - BOOL PreTranslateMessage(MSG* pMsg); - - DECLARE_REGISTRY_RESOURCEID(IDR_GPAXPLUGIN) - DECLARE_PROTECT_FINAL_CONSTRUCT() -#if (_MSC_VER >= 1300) - DECLARE_OLEMISC_STATUS(OLEMISC_ACTSLIKEBUTTON | OLEMISC_ACTIVATEWHENVISIBLE) -#endif - - static LPCTSTR GetWindowClassName() { return TEXT("GPAC ActiveX"); } - - BEGIN_COM_MAP(CGPAXPlugin) - COM_INTERFACE_ENTRY(IGPAX) - COM_INTERFACE_ENTRY(IDispatch) - COM_INTERFACE_ENTRY(IViewObjectEx) - COM_INTERFACE_ENTRY(IProvideClassInfo) - COM_INTERFACE_ENTRY(IOleControl) - COM_INTERFACE_ENTRY(IOleObject) - - COM_INTERFACE_ENTRY_IMPL(IViewObjectEx) - COM_INTERFACE_ENTRY_IMPL_IID(IID_IViewObject2, IViewObjectEx) - COM_INTERFACE_ENTRY_IMPL_IID(IID_IViewObject, IViewObjectEx) - COM_INTERFACE_ENTRY_IMPL_IID(IID_IOleWindow, IOleInPlaceObjectWindowless) - COM_INTERFACE_ENTRY_IMPL_IID(IID_IOleInPlaceObject, IOleInPlaceObjectWindowless) - COM_INTERFACE_ENTRY_IMPL_IID(IID_IOleWindow, IOleInPlaceActiveObject) - - COM_INTERFACE_ENTRY_IMPL(IOleInPlaceActiveObject) - COM_INTERFACE_ENTRY_IMPL(IOleInPlaceObjectWindowless) - -// COM_INTERFACE_ENTRY(IObjectSafety) - COM_INTERFACE_ENTRY_IID(IID_IObjectSafety, IObjectSafety) - COM_INTERFACE_ENTRY(IPersistPropertyBag) - COM_INTERFACE_ENTRY_IMPL_IID(IID_IPersist, IPersistPropertyBag) - -/* COM_INTERFACE_ENTRY(IViewObject) - COM_INTERFACE_ENTRY(IViewObject2) - COM_INTERFACE_ENTRY2(IOleWindow, IOleInPlaceObjectWindowless) - COM_INTERFACE_ENTRY(IOleInPlaceObject) - */ - - COM_INTERFACE_ENTRY(IProvideClassInfo2) - COM_INTERFACE_ENTRY(IPersistStreamInit) - COM_INTERFACE_ENTRY2(IPersist, IPersistStreamInit) - COM_INTERFACE_ENTRY(ISupportErrorInfo) - COM_INTERFACE_ENTRY(IConnectionPointContainer) - COM_INTERFACE_ENTRY(ISpecifyPropertyPages) - COM_INTERFACE_ENTRY(IQuickActivate) - COM_INTERFACE_ENTRY(IPersistStorage) - COM_INTERFACE_ENTRY(IDataObject) - - END_COM_MAP() - - BEGIN_PROP_MAP(CGPAXPlugin) - END_PROP_MAP() - - BEGIN_CONNECTION_POINT_MAP(CGPAXPlugin) - CONNECTION_POINT_ENTRY(IID_IPropertyNotifySink) - END_CONNECTION_POINT_MAP() - - BEGIN_MSG_MAP(CGPAXPlugin) - CHAIN_MSG_MAP(CComControl) - DEFAULT_REFLECTION_HANDLER() - MESSAGE_HANDLER(WM_CREATE, OnCreate) - MESSAGE_HANDLER(WM_DESTROY, OnDestroy) - - END_MSG_MAP() - // Handler prototypes: - // LRESULT MessageHandler(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); - // LRESULT CommandHandler(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled); - // LRESULT NotifyHandler(int idCtrl, LPNMHDR pnmh, BOOL& bHandled); - - - - // ISupportsErrorInfo - STDMETHOD(InterfaceSupportsErrorInfo)(REFIID riid) - { - static const IID* arr[] = - { - &IID_IGPAX, - }; - for (int i=0; i - // - // - //the interface IPersistPropertyBag enable MSIE and ActiveX Control to communicate these - //properties included in tags - STDMETHODIMP Load(LPPROPERTYBAG pPropBag, LPERRORLOG pErrorLog); - STDMETHODIMP Save(LPPROPERTYBAG, BOOL, BOOL); - - -private: - Bool ReadParamString(LPPROPERTYBAG pPropBag, LPERRORLOG pErrorLog, WCHAR *name, char *buf, int bufsize); - void SetStatusText(char *msg); - void UpdateURL(); - void UnloadTerm(); - void LoadDATAUrl(); - - GF_Terminal *m_term; - GF_User m_user; - char m_url[MAXLEN_URL]; -#ifndef _WIN32_WCE - /*pointer to the parent browser if any*/ - IWebBrowser2 *m_pBrowser; -#endif - - u32 m_width, m_height, m_AR; - Bool m_bIsConnected, m_bInitialDraw, m_bAutoStart, m_bUse3D, m_bLoop; - -}; - - - -#endif //__GPAXPLUGIN_H_ diff --git a/applications/GPAX/GPAX_i.c b/applications/GPAX/GPAX_i.c deleted file mode 100644 index 3be5d97..0000000 --- a/applications/GPAX/GPAX_i.c +++ /dev/null @@ -1,178 +0,0 @@ - -#pragma warning( disable: 4049 ) /* more than 64k source lines */ - -/* this ALWAYS GENERATED file contains the IIDs and CLSIDs */ - -/* link this file in with the server and any clients */ - - - /* File created by MIDL compiler version 5.03.0286 */ -/* at Thu Jul 20 19:14:15 2006 - */ -/* Compiler settings for \CVS\gpac\applications\GPAX\GPAX.idl: - Oicf (OptLev=i2), W1, Zp8, env=Win32 (32b run), ms_ext, c_ext - error checks: allocation ref bounds_check enum stub_data - VC __declspec() decoration level: - __declspec(uuid()), __declspec(selectany), __declspec(novtable) - DECLSPEC_UUID(), MIDL_INTERFACE() -*/ -//@@MIDL_FILE_HEADING( ) - -#if !defined(_M_IA64) && !defined(_M_AXP64) - -#ifdef __cplusplus -extern "C"{ -#endif - - -#include -#include - -#ifdef _MIDL_USE_GUIDDEF_ - -#ifndef INITGUID -#define INITGUID -#include -#undef INITGUID -#else -#include -#endif - -#define MIDL_DEFINE_GUID(type,name,l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8) \ - DEFINE_GUID(name,l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8) - -#else // !_MIDL_USE_GUIDDEF_ - -#ifndef __IID_DEFINED__ -#define __IID_DEFINED__ - -typedef struct _IID -{ - unsigned long x; - unsigned short s1; - unsigned short s2; - unsigned char c[8]; -} IID; - -#endif // __IID_DEFINED__ - -#ifndef CLSID_DEFINED -#define CLSID_DEFINED -typedef IID CLSID; -#endif // CLSID_DEFINED - -#define MIDL_DEFINE_GUID(type,name,l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8) \ - const type name = {l,w1,w2,{b1,b2,b3,b4,b5,b6,b7,b8}} - -#endif !_MIDL_USE_GUIDDEF_ - -MIDL_DEFINE_GUID(IID, LIBID_GPAXLib,0xE64FAC7F,0x0134,0x4A75,0xA7,0xDA,0x80,0xD5,0x3E,0xBC,0x56,0xA6); - - -MIDL_DEFINE_GUID(IID, IID_IGPAX,0xE2A9A937,0xBB35,0x47E0,0x89,0x42,0x96,0x48,0x06,0x29,0x9A,0xB4); - - -MIDL_DEFINE_GUID(IID, DIID_IGPAXEvents,0x1FDA32FC,0x4C9A,0x461F,0xB3,0x3B,0x07,0x15,0xB0,0x34,0x30,0x06); - - -MIDL_DEFINE_GUID(CLSID, CLSID_GPAX,0x181D18E6,0x4DC1,0x4B55,0xB7,0x2E,0xBE,0x2A,0x10,0x06,0x49,0x95); - -#undef MIDL_DEFINE_GUID - -#ifdef __cplusplus -} -#endif - - - -#endif /* !defined(_M_IA64) && !defined(_M_AXP64)*/ - - -#pragma warning( disable: 4049 ) /* more than 64k source lines */ - -/* this ALWAYS GENERATED file contains the IIDs and CLSIDs */ - -/* link this file in with the server and any clients */ - - - /* File created by MIDL compiler version 5.03.0286 */ -/* at Thu Jul 20 19:14:15 2006 - */ -/* Compiler settings for \CVS\gpac\applications\GPAX\GPAX.idl: - Oicf (OptLev=i2), W1, Zp8, env=Win64 (32b run,appending), ms_ext, c_ext, robust - error checks: allocation ref bounds_check enum stub_data - VC __declspec() decoration level: - __declspec(uuid()), __declspec(selectany), __declspec(novtable) - DECLSPEC_UUID(), MIDL_INTERFACE() -*/ -//@@MIDL_FILE_HEADING( ) - -#if defined(_M_IA64) || defined(_M_AXP64) - -#ifdef __cplusplus -extern "C"{ -#endif - - -#include -#include - -#ifdef _MIDL_USE_GUIDDEF_ - -#ifndef INITGUID -#define INITGUID -#include -#undef INITGUID -#else -#include -#endif - -#define MIDL_DEFINE_GUID(type,name,l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8) \ - DEFINE_GUID(name,l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8) - -#else // !_MIDL_USE_GUIDDEF_ - -#ifndef __IID_DEFINED__ -#define __IID_DEFINED__ - -typedef struct _IID -{ - unsigned long x; - unsigned short s1; - unsigned short s2; - unsigned char c[8]; -} IID; - -#endif // __IID_DEFINED__ - -#ifndef CLSID_DEFINED -#define CLSID_DEFINED -typedef IID CLSID; -#endif // CLSID_DEFINED - -#define MIDL_DEFINE_GUID(type,name,l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8) \ - const type name = {l,w1,w2,{b1,b2,b3,b4,b5,b6,b7,b8}} - -#endif !_MIDL_USE_GUIDDEF_ - -MIDL_DEFINE_GUID(IID, LIBID_GPAXLib,0xE64FAC7F,0x0134,0x4A75,0xA7,0xDA,0x80,0xD5,0x3E,0xBC,0x56,0xA6); - - -MIDL_DEFINE_GUID(IID, IID_IGPAX,0xE2A9A937,0xBB35,0x47E0,0x89,0x42,0x96,0x48,0x06,0x29,0x9A,0xB4); - - -MIDL_DEFINE_GUID(IID, DIID_IGPAXEvents,0x1FDA32FC,0x4C9A,0x461F,0xB3,0x3B,0x07,0x15,0xB0,0x34,0x30,0x06); - - -MIDL_DEFINE_GUID(CLSID, CLSID_GPAX,0x181D18E6,0x4DC1,0x4B55,0xB7,0x2E,0xBE,0x2A,0x10,0x06,0x49,0x95); - -#undef MIDL_DEFINE_GUID - -#ifdef __cplusplus -} -#endif - - - -#endif /* defined(_M_IA64) || defined(_M_AXP64)*/ - diff --git a/applications/GPAX/GPAX_p.c b/applications/GPAX/GPAX_p.c deleted file mode 100644 index 64594b3..0000000 --- a/applications/GPAX/GPAX_p.c +++ /dev/null @@ -1,602 +0,0 @@ -/* this ALWAYS GENERATED file contains the proxy stub code */ - - -/* File created by MIDL compiler version 5.01.0164 */ -/* at Mon Jul 17 15:58:48 2006 - */ -/* Compiler settings for D:\CVS\gpac\applications\GPAX\GPAX.idl: - Oicf (OptLev=i2), W1, Zp8, env=Win32, ms_ext, c_ext - error checks: allocation ref bounds_check enum stub_data -*/ -//@@MIDL_FILE_HEADING( ) - -#define USE_STUBLESS_PROXY - - -/* verify that the version is high enough to compile this file*/ -#ifndef __REDQ_RPCPROXY_H_VERSION__ -#define __REQUIRED_RPCPROXY_H_VERSION__ 440 -#endif - - -#include "rpcproxy.h" -#ifndef __RPCPROXY_H_VERSION__ -#error this stub requires an updated version of -#endif // __RPCPROXY_H_VERSION__ - - -#include "GPAX.h" - -#define TYPE_FORMAT_STRING_SIZE 59 -#define PROC_FORMAT_STRING_SIZE 213 - -typedef struct _MIDL_TYPE_FORMAT_STRING - { - short Pad; - unsigned char Format[ TYPE_FORMAT_STRING_SIZE ]; - } MIDL_TYPE_FORMAT_STRING; - -typedef struct _MIDL_PROC_FORMAT_STRING - { - short Pad; - unsigned char Format[ PROC_FORMAT_STRING_SIZE ]; - } MIDL_PROC_FORMAT_STRING; - - -extern const MIDL_TYPE_FORMAT_STRING __MIDL_TypeFormatString; -extern const MIDL_PROC_FORMAT_STRING __MIDL_ProcFormatString; - - -/* Standard interface: __MIDL_itf_GPAX_0000, ver. 0.0, - GUID={0x00000000,0x0000,0x0000,{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}} */ - - -/* Object interface: IUnknown, ver. 0.0, - GUID={0x00000000,0x0000,0x0000,{0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46}} */ - - -/* Object interface: IDispatch, ver. 0.0, - GUID={0x00020400,0x0000,0x0000,{0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46}} */ - - -/* Object interface: IGPAX, ver. 0.0, - GUID={0xE2A9A937,0xBB35,0x47E0,{0x89,0x42,0x96,0x48,0x06,0x29,0x9A,0xB4}} */ - - -extern const MIDL_STUB_DESC Object_StubDesc; - - -extern const MIDL_SERVER_INFO IGPAX_ServerInfo; - -#pragma code_seg(".orpc") -extern const USER_MARSHAL_ROUTINE_QUADRUPLE UserMarshalRoutines[1]; - -static const MIDL_STUB_DESC Object_StubDesc = - { - 0, - NdrOleAllocate, - NdrOleFree, - 0, - 0, - 0, - 0, - 0, - __MIDL_TypeFormatString.Format, - 1, /* -error bounds_check flag */ - 0x20000, /* Ndr library version */ - 0, - 0x50100a4, /* MIDL Version 5.1.164 */ - 0, - UserMarshalRoutines, - 0, /* notify & notify_flag routine table */ - 1, /* Flags */ - 0, /* Reserved3 */ - 0, /* Reserved4 */ - 0 /* Reserved5 */ - }; - -static const unsigned short IGPAX_FormatStringOffsetTable[] = - { - (unsigned short) -1, - (unsigned short) -1, - (unsigned short) -1, - (unsigned short) -1, - 0, - 22, - 44, - 66, - 100, - 128, - 156, - 184 - }; - -static const MIDL_SERVER_INFO IGPAX_ServerInfo = - { - &Object_StubDesc, - 0, - __MIDL_ProcFormatString.Format, - &IGPAX_FormatStringOffsetTable[-3], - 0, - 0, - 0, - 0 - }; - -static const MIDL_STUBLESS_PROXY_INFO IGPAX_ProxyInfo = - { - &Object_StubDesc, - __MIDL_ProcFormatString.Format, - &IGPAX_FormatStringOffsetTable[-3], - 0, - 0, - 0 - }; - -CINTERFACE_PROXY_VTABLE(15) _IGPAXProxyVtbl = -{ - &IGPAX_ProxyInfo, - &IID_IGPAX, - IUnknown_QueryInterface_Proxy, - IUnknown_AddRef_Proxy, - IUnknown_Release_Proxy , - 0 /* (void *)-1 /* IDispatch::GetTypeInfoCount */ , - 0 /* (void *)-1 /* IDispatch::GetTypeInfo */ , - 0 /* (void *)-1 /* IDispatch::GetIDsOfNames */ , - 0 /* IDispatch_Invoke_Proxy */ , - (void *)-1 /* IGPAX::Play */ , - (void *)-1 /* IGPAX::Pause */ , - (void *)-1 /* IGPAX::Stop */ , - (void *)-1 /* IGPAX::Update */ , - (void *)-1 /* IGPAX::get_URL */ , - (void *)-1 /* IGPAX::put_URL */ , - (void *)-1 /* IGPAX::get_AutoStart */ , - (void *)-1 /* IGPAX::put_AutoStart */ -}; - - -static const PRPC_STUB_FUNCTION IGPAX_table[] = -{ - STUB_FORWARDING_FUNCTION, - STUB_FORWARDING_FUNCTION, - STUB_FORWARDING_FUNCTION, - STUB_FORWARDING_FUNCTION, - NdrStubCall2, - NdrStubCall2, - NdrStubCall2, - NdrStubCall2, - NdrStubCall2, - NdrStubCall2, - NdrStubCall2, - NdrStubCall2 -}; - -CInterfaceStubVtbl _IGPAXStubVtbl = -{ - &IID_IGPAX, - &IGPAX_ServerInfo, - 15, - &IGPAX_table[-3], - CStdStubBuffer_DELEGATING_METHODS -}; - -#pragma data_seg(".rdata") - -static const USER_MARSHAL_ROUTINE_QUADRUPLE UserMarshalRoutines[1] = - { - - { - BSTR_UserSize - ,BSTR_UserMarshal - ,BSTR_UserUnmarshal - ,BSTR_UserFree - } - - }; - - -#if !defined(__RPC_WIN32__) -#error Invalid build platform for this stub. -#endif - -#if !(TARGET_IS_NT40_OR_LATER) -#error You need a Windows NT 4.0 or later to run this stub because it uses these features: -#error -Oif or -Oicf, [wire_marshal] or [user_marshal] attribute, more than 32 methods in the interface. -#error However, your C/C++ compilation flags indicate you intend to run this app on earlier systems. -#error This app will die there with the RPC_X_WRONG_STUB_VERSION error. -#endif - - -static const MIDL_PROC_FORMAT_STRING __MIDL_ProcFormatString = - { - 0, - { - - /* Procedure Play */ - - 0x33, /* FC_AUTO_HANDLE */ - 0x6c, /* Old Flags: object, Oi2 */ -/* 2 */ NdrFcLong( 0x0 ), /* 0 */ -/* 6 */ NdrFcShort( 0x7 ), /* 7 */ -#ifndef _ALPHA_ -/* 8 */ NdrFcShort( 0x8 ), /* x86, MIPS, PPC Stack size/offset = 8 */ -#else - NdrFcShort( 0x10 ), /* Alpha Stack size/offset = 16 */ -#endif -/* 10 */ NdrFcShort( 0x0 ), /* 0 */ -/* 12 */ NdrFcShort( 0x8 ), /* 8 */ -/* 14 */ 0x4, /* Oi2 Flags: has return, */ - 0x1, /* 1 */ - - /* Return value */ - -/* 16 */ NdrFcShort( 0x70 ), /* Flags: out, return, base type, */ -#ifndef _ALPHA_ -/* 18 */ NdrFcShort( 0x4 ), /* x86, MIPS, PPC Stack size/offset = 4 */ -#else - NdrFcShort( 0x8 ), /* Alpha Stack size/offset = 8 */ -#endif -/* 20 */ 0x8, /* FC_LONG */ - 0x0, /* 0 */ - - /* Procedure Pause */ - -/* 22 */ 0x33, /* FC_AUTO_HANDLE */ - 0x6c, /* Old Flags: object, Oi2 */ -/* 24 */ NdrFcLong( 0x0 ), /* 0 */ -/* 28 */ NdrFcShort( 0x8 ), /* 8 */ -#ifndef _ALPHA_ -/* 30 */ NdrFcShort( 0x8 ), /* x86, MIPS, PPC Stack size/offset = 8 */ -#else - NdrFcShort( 0x10 ), /* Alpha Stack size/offset = 16 */ -#endif -/* 32 */ NdrFcShort( 0x0 ), /* 0 */ -/* 34 */ NdrFcShort( 0x8 ), /* 8 */ -/* 36 */ 0x4, /* Oi2 Flags: has return, */ - 0x1, /* 1 */ - - /* Return value */ - -/* 38 */ NdrFcShort( 0x70 ), /* Flags: out, return, base type, */ -#ifndef _ALPHA_ -/* 40 */ NdrFcShort( 0x4 ), /* x86, MIPS, PPC Stack size/offset = 4 */ -#else - NdrFcShort( 0x8 ), /* Alpha Stack size/offset = 8 */ -#endif -/* 42 */ 0x8, /* FC_LONG */ - 0x0, /* 0 */ - - /* Procedure Stop */ - -/* 44 */ 0x33, /* FC_AUTO_HANDLE */ - 0x6c, /* Old Flags: object, Oi2 */ -/* 46 */ NdrFcLong( 0x0 ), /* 0 */ -/* 50 */ NdrFcShort( 0x9 ), /* 9 */ -#ifndef _ALPHA_ -/* 52 */ NdrFcShort( 0x8 ), /* x86, MIPS, PPC Stack size/offset = 8 */ -#else - NdrFcShort( 0x10 ), /* Alpha Stack size/offset = 16 */ -#endif -/* 54 */ NdrFcShort( 0x0 ), /* 0 */ -/* 56 */ NdrFcShort( 0x8 ), /* 8 */ -/* 58 */ 0x4, /* Oi2 Flags: has return, */ - 0x1, /* 1 */ - - /* Return value */ - -/* 60 */ NdrFcShort( 0x70 ), /* Flags: out, return, base type, */ -#ifndef _ALPHA_ -/* 62 */ NdrFcShort( 0x4 ), /* x86, MIPS, PPC Stack size/offset = 4 */ -#else - NdrFcShort( 0x8 ), /* Alpha Stack size/offset = 8 */ -#endif -/* 64 */ 0x8, /* FC_LONG */ - 0x0, /* 0 */ - - /* Procedure Update */ - -/* 66 */ 0x33, /* FC_AUTO_HANDLE */ - 0x6c, /* Old Flags: object, Oi2 */ -/* 68 */ NdrFcLong( 0x0 ), /* 0 */ -/* 72 */ NdrFcShort( 0xa ), /* 10 */ -#ifndef _ALPHA_ -/* 74 */ NdrFcShort( 0x10 ), /* x86, MIPS, PPC Stack size/offset = 16 */ -#else - NdrFcShort( 0x20 ), /* Alpha Stack size/offset = 32 */ -#endif -/* 76 */ NdrFcShort( 0x0 ), /* 0 */ -/* 78 */ NdrFcShort( 0x8 ), /* 8 */ -/* 80 */ 0x6, /* Oi2 Flags: clt must size, has return, */ - 0x3, /* 3 */ - - /* Parameter mtype */ - -/* 82 */ NdrFcShort( 0x8b ), /* Flags: must size, must free, in, by val, */ -#ifndef _ALPHA_ -/* 84 */ NdrFcShort( 0x4 ), /* x86, MIPS, PPC Stack size/offset = 4 */ -#else - NdrFcShort( 0x8 ), /* Alpha Stack size/offset = 8 */ -#endif -/* 86 */ NdrFcShort( 0x1a ), /* Type Offset=26 */ - - /* Parameter updates */ - -/* 88 */ NdrFcShort( 0x8b ), /* Flags: must size, must free, in, by val, */ -#ifndef _ALPHA_ -/* 90 */ NdrFcShort( 0x8 ), /* x86, MIPS, PPC Stack size/offset = 8 */ -#else - NdrFcShort( 0x10 ), /* Alpha Stack size/offset = 16 */ -#endif -/* 92 */ NdrFcShort( 0x1a ), /* Type Offset=26 */ - - /* Return value */ - -/* 94 */ NdrFcShort( 0x70 ), /* Flags: out, return, base type, */ -#ifndef _ALPHA_ -/* 96 */ NdrFcShort( 0xc ), /* x86, MIPS, PPC Stack size/offset = 12 */ -#else - NdrFcShort( 0x18 ), /* Alpha Stack size/offset = 24 */ -#endif -/* 98 */ 0x8, /* FC_LONG */ - 0x0, /* 0 */ - - /* Procedure get_URL */ - -/* 100 */ 0x33, /* FC_AUTO_HANDLE */ - 0x6c, /* Old Flags: object, Oi2 */ -/* 102 */ NdrFcLong( 0x0 ), /* 0 */ -/* 106 */ NdrFcShort( 0xb ), /* 11 */ -#ifndef _ALPHA_ -/* 108 */ NdrFcShort( 0xc ), /* x86, MIPS, PPC Stack size/offset = 12 */ -#else - NdrFcShort( 0x18 ), /* Alpha Stack size/offset = 24 */ -#endif -/* 110 */ NdrFcShort( 0x0 ), /* 0 */ -/* 112 */ NdrFcShort( 0x8 ), /* 8 */ -/* 114 */ 0x5, /* Oi2 Flags: srv must size, has return, */ - 0x2, /* 2 */ - - /* Parameter mrl */ - -/* 116 */ NdrFcShort( 0x2113 ), /* Flags: must size, must free, out, simple ref, srv alloc size=8 */ -#ifndef _ALPHA_ -/* 118 */ NdrFcShort( 0x4 ), /* x86, MIPS, PPC Stack size/offset = 4 */ -#else - NdrFcShort( 0x8 ), /* Alpha Stack size/offset = 8 */ -#endif -/* 120 */ NdrFcShort( 0x2c ), /* Type Offset=44 */ - - /* Return value */ - -/* 122 */ NdrFcShort( 0x70 ), /* Flags: out, return, base type, */ -#ifndef _ALPHA_ -/* 124 */ NdrFcShort( 0x8 ), /* x86, MIPS, PPC Stack size/offset = 8 */ -#else - NdrFcShort( 0x10 ), /* Alpha Stack size/offset = 16 */ -#endif -/* 126 */ 0x8, /* FC_LONG */ - 0x0, /* 0 */ - - /* Procedure put_URL */ - -/* 128 */ 0x33, /* FC_AUTO_HANDLE */ - 0x6c, /* Old Flags: object, Oi2 */ -/* 130 */ NdrFcLong( 0x0 ), /* 0 */ -/* 134 */ NdrFcShort( 0xc ), /* 12 */ -#ifndef _ALPHA_ -/* 136 */ NdrFcShort( 0xc ), /* x86, MIPS, PPC Stack size/offset = 12 */ -#else - NdrFcShort( 0x18 ), /* Alpha Stack size/offset = 24 */ -#endif -/* 138 */ NdrFcShort( 0x0 ), /* 0 */ -/* 140 */ NdrFcShort( 0x8 ), /* 8 */ -/* 142 */ 0x6, /* Oi2 Flags: clt must size, has return, */ - 0x2, /* 2 */ - - /* Parameter mrl */ - -/* 144 */ NdrFcShort( 0x8b ), /* Flags: must size, must free, in, by val, */ -#ifndef _ALPHA_ -/* 146 */ NdrFcShort( 0x4 ), /* x86, MIPS, PPC Stack size/offset = 4 */ -#else - NdrFcShort( 0x8 ), /* Alpha Stack size/offset = 8 */ -#endif -/* 148 */ NdrFcShort( 0x1a ), /* Type Offset=26 */ - - /* Return value */ - -/* 150 */ NdrFcShort( 0x70 ), /* Flags: out, return, base type, */ -#ifndef _ALPHA_ -/* 152 */ NdrFcShort( 0x8 ), /* x86, MIPS, PPC Stack size/offset = 8 */ -#else - NdrFcShort( 0x10 ), /* Alpha Stack size/offset = 16 */ -#endif -/* 154 */ 0x8, /* FC_LONG */ - 0x0, /* 0 */ - - /* Procedure get_AutoStart */ - -/* 156 */ 0x33, /* FC_AUTO_HANDLE */ - 0x6c, /* Old Flags: object, Oi2 */ -/* 158 */ NdrFcLong( 0x0 ), /* 0 */ -/* 162 */ NdrFcShort( 0xd ), /* 13 */ -#ifndef _ALPHA_ -/* 164 */ NdrFcShort( 0xc ), /* x86, MIPS, PPC Stack size/offset = 12 */ -#else - NdrFcShort( 0x18 ), /* Alpha Stack size/offset = 24 */ -#endif -/* 166 */ NdrFcShort( 0x0 ), /* 0 */ -/* 168 */ NdrFcShort( 0xe ), /* 14 */ -/* 170 */ 0x4, /* Oi2 Flags: has return, */ - 0x2, /* 2 */ - - /* Parameter autoplay */ - -/* 172 */ NdrFcShort( 0x2150 ), /* Flags: out, base type, simple ref, srv alloc size=8 */ -#ifndef _ALPHA_ -/* 174 */ NdrFcShort( 0x4 ), /* x86, MIPS, PPC Stack size/offset = 4 */ -#else - NdrFcShort( 0x8 ), /* Alpha Stack size/offset = 8 */ -#endif -/* 176 */ 0x6, /* FC_SHORT */ - 0x0, /* 0 */ - - /* Return value */ - -/* 178 */ NdrFcShort( 0x70 ), /* Flags: out, return, base type, */ -#ifndef _ALPHA_ -/* 180 */ NdrFcShort( 0x8 ), /* x86, MIPS, PPC Stack size/offset = 8 */ -#else - NdrFcShort( 0x10 ), /* Alpha Stack size/offset = 16 */ -#endif -/* 182 */ 0x8, /* FC_LONG */ - 0x0, /* 0 */ - - /* Procedure put_AutoStart */ - -/* 184 */ 0x33, /* FC_AUTO_HANDLE */ - 0x6c, /* Old Flags: object, Oi2 */ -/* 186 */ NdrFcLong( 0x0 ), /* 0 */ -/* 190 */ NdrFcShort( 0xe ), /* 14 */ -#ifndef _ALPHA_ -/* 192 */ NdrFcShort( 0xc ), /* x86, MIPS, PPC Stack size/offset = 12 */ -#else - NdrFcShort( 0x18 ), /* Alpha Stack size/offset = 24 */ -#endif -/* 194 */ NdrFcShort( 0x6 ), /* 6 */ -/* 196 */ NdrFcShort( 0x8 ), /* 8 */ -/* 198 */ 0x4, /* Oi2 Flags: has return, */ - 0x2, /* 2 */ - - /* Parameter autoplay */ - -/* 200 */ NdrFcShort( 0x48 ), /* Flags: in, base type, */ -#ifndef _ALPHA_ -/* 202 */ NdrFcShort( 0x4 ), /* x86, MIPS, PPC Stack size/offset = 4 */ -#else - NdrFcShort( 0x8 ), /* Alpha Stack size/offset = 8 */ -#endif -/* 204 */ 0x6, /* FC_SHORT */ - 0x0, /* 0 */ - - /* Return value */ - -/* 206 */ NdrFcShort( 0x70 ), /* Flags: out, return, base type, */ -#ifndef _ALPHA_ -/* 208 */ NdrFcShort( 0x8 ), /* x86, MIPS, PPC Stack size/offset = 8 */ -#else - NdrFcShort( 0x10 ), /* Alpha Stack size/offset = 16 */ -#endif -/* 210 */ 0x8, /* FC_LONG */ - 0x0, /* 0 */ - - 0x0 - } - }; - -static const MIDL_TYPE_FORMAT_STRING __MIDL_TypeFormatString = - { - 0, - { - NdrFcShort( 0x0 ), /* 0 */ -/* 2 */ - 0x12, 0x0, /* FC_UP */ -/* 4 */ NdrFcShort( 0xc ), /* Offset= 12 (16) */ -/* 6 */ - 0x1b, /* FC_CARRAY */ - 0x1, /* 1 */ -/* 8 */ NdrFcShort( 0x2 ), /* 2 */ -/* 10 */ 0x9, /* Corr desc: FC_ULONG */ - 0x0, /* */ -/* 12 */ NdrFcShort( 0xfffc ), /* -4 */ -/* 14 */ 0x6, /* FC_SHORT */ - 0x5b, /* FC_END */ -/* 16 */ - 0x17, /* FC_CSTRUCT */ - 0x3, /* 3 */ -/* 18 */ NdrFcShort( 0x8 ), /* 8 */ -/* 20 */ NdrFcShort( 0xfffffff2 ), /* Offset= -14 (6) */ -/* 22 */ 0x8, /* FC_LONG */ - 0x8, /* FC_LONG */ -/* 24 */ 0x5c, /* FC_PAD */ - 0x5b, /* FC_END */ -/* 26 */ 0xb4, /* FC_USER_MARSHAL */ - 0x83, /* 131 */ -/* 28 */ NdrFcShort( 0x0 ), /* 0 */ -/* 30 */ NdrFcShort( 0x4 ), /* 4 */ -/* 32 */ NdrFcShort( 0x0 ), /* 0 */ -/* 34 */ NdrFcShort( 0xffffffe0 ), /* Offset= -32 (2) */ -/* 36 */ - 0x11, 0x4, /* FC_RP [alloced_on_stack] */ -/* 38 */ NdrFcShort( 0x6 ), /* Offset= 6 (44) */ -/* 40 */ - 0x13, 0x0, /* FC_OP */ -/* 42 */ NdrFcShort( 0xffffffe6 ), /* Offset= -26 (16) */ -/* 44 */ 0xb4, /* FC_USER_MARSHAL */ - 0x83, /* 131 */ -/* 46 */ NdrFcShort( 0x0 ), /* 0 */ -/* 48 */ NdrFcShort( 0x4 ), /* 4 */ -/* 50 */ NdrFcShort( 0x0 ), /* 0 */ -/* 52 */ NdrFcShort( 0xfffffff4 ), /* Offset= -12 (40) */ -/* 54 */ - 0x11, 0xc, /* FC_RP [alloced_on_stack] [simple_pointer] */ -/* 56 */ 0x6, /* FC_SHORT */ - 0x5c, /* FC_PAD */ - - 0x0 - } - }; - -const CInterfaceProxyVtbl * _GPAX_ProxyVtblList[] = -{ - ( CInterfaceProxyVtbl *) &_IGPAXProxyVtbl, - 0 -}; - -const CInterfaceStubVtbl * _GPAX_StubVtblList[] = -{ - ( CInterfaceStubVtbl *) &_IGPAXStubVtbl, - 0 -}; - -PCInterfaceName const _GPAX_InterfaceNamesList[] = -{ - "IGPAX", - 0 -}; - -const IID * _GPAX_BaseIIDList[] = -{ - &IID_IDispatch, - 0 -}; - - -#define _GPAX_CHECK_IID(n) IID_GENERIC_CHECK_IID( _GPAX, pIID, n) - -int __stdcall _GPAX_IID_Lookup( const IID * pIID, int * pIndex ) -{ - - if(!_GPAX_CHECK_IID(0)) - { - *pIndex = 0; - return 1; - } - - return 0; -} - -const ExtendedProxyFileInfo GPAX_ProxyFileInfo = -{ - (PCInterfaceProxyVtblList *) & _GPAX_ProxyVtblList, - (PCInterfaceStubVtblList *) & _GPAX_StubVtblList, - (const PCInterfaceName * ) & _GPAX_InterfaceNamesList, - (const IID ** ) & _GPAX_BaseIIDList, - & _GPAX_IID_Lookup, - 1, - 2, - 0, /* table of [async_uuid] interfaces */ - 0, /* Filler1 */ - 0, /* Filler2 */ - 0 /* Filler3 */ -}; diff --git a/applications/GPAX/GPAXps.def b/applications/GPAX/GPAXps.def deleted file mode 100644 index c4ab3de..0000000 --- a/applications/GPAX/GPAXps.def +++ /dev/null @@ -1,11 +0,0 @@ - -LIBRARY "GPAXPS" - -DESCRIPTION 'Proxy/Stub DLL' - -EXPORTS - DllGetClassObject @1 PRIVATE - DllCanUnloadNow @2 PRIVATE - GetProxyDllInfo @3 PRIVATE - DllRegisterServer @4 PRIVATE - DllUnregisterServer @5 PRIVATE diff --git a/applications/GPAX/GPAXps.mk b/applications/GPAX/GPAXps.mk deleted file mode 100644 index 70cf327..0000000 --- a/applications/GPAX/GPAXps.mk +++ /dev/null @@ -1,16 +0,0 @@ - -GPAXps.dll: dlldata.obj GPAX_p.obj GPAX_i.obj - link /dll /out:GPAXps.dll /def:GPAXps.def /entry:DllMain dlldata.obj GPAX_p.obj GPAX_i.obj \ - kernel32.lib rpcndr.lib rpcns4.lib rpcrt4.lib oleaut32.lib uuid.lib \ - -.c.obj: - cl /c /Ox /DWIN32 /D_WIN32_WINNT=0x0400 /DREGISTER_PROXY_DLL \ - $< - -clean: - @del GPAXps.dll - @del GPAXps.lib - @del GPAXps.exp - @del dlldata.obj - @del GPAX_p.obj - @del GPAX_i.obj diff --git a/applications/GPAX/StdAfx.cpp b/applications/GPAX/StdAfx.cpp deleted file mode 100644 index a5eea17..0000000 --- a/applications/GPAX/StdAfx.cpp +++ /dev/null @@ -1,12 +0,0 @@ -// stdafx.cpp : source file that includes just the standard includes -// stdafx.pch will be the pre-compiled header -// stdafx.obj will contain the pre-compiled type information - -#include "stdafx.h" - -#ifdef _ATL_STATIC_REGISTRY -#include -#include -#endif - -#include diff --git a/applications/GPAX/StdAfx.h b/applications/GPAX/StdAfx.h deleted file mode 100644 index 597b3ca..0000000 --- a/applications/GPAX/StdAfx.h +++ /dev/null @@ -1,34 +0,0 @@ -// stdafx.h : include file for standard system include files, -// or project specific include files that are used frequently, -// but are changed infrequently - -#if !defined(AFX_STDAFX_H__2CD656F1_059C_4EC4_9EAA_8FECF66BB748__INCLUDED_) -#define AFX_STDAFX_H__2CD656F1_059C_4EC4_9EAA_8FECF66BB748__INCLUDED_ - -#if _MSC_VER > 1000 -#pragma once -#endif // _MSC_VER > 1000 - -#define STRICT - -#ifndef _WIN32_WCE - -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0501 -#endif -#define _ATL_APARTMENT_THREADED - -#endif //_WIN32_WCE - - -#include -//You may derive a class from CComModule and use it if you want to override -//something, but do not change the name of _Module -extern CComModule _Module; -#include -#include - -//{{AFX_INSERT_LOCATION}} -// Microsoft Visual C++ will insert additional declarations immediately before the previous line. - -#endif // !defined(AFX_STDAFX_H__2CD656F1_059C_4EC4_9EAA_8FECF66BB748__INCLUDED) diff --git a/applications/GPAX/dlldata.c b/applications/GPAX/dlldata.c deleted file mode 100644 index 86db18b..0000000 --- a/applications/GPAX/dlldata.c +++ /dev/null @@ -1,38 +0,0 @@ -/********************************************************* - DllData file -- generated by MIDL compiler - - DO NOT ALTER THIS FILE - - This file is regenerated by MIDL on every IDL file compile. - - To completely reconstruct this file, delete it and rerun MIDL - on all the IDL files in this DLL, specifying this file for the - /dlldata command line option - -*********************************************************/ - -#define PROXY_DELEGATION - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -EXTERN_PROXY_FILE( GPAX ) - - -PROXYFILE_LIST_START -/* Start of list */ - REFERENCE_PROXY_FILE( GPAX ), -/* End of list */ -PROXYFILE_LIST_END - - -DLLDATA_ROUTINES( aProxyFileList, GET_DLL_CLSID ) - -#ifdef __cplusplus -} /*extern "C" */ -#endif - -/* end of generated dlldata file */ diff --git a/applications/GPAX/gpax.bmp b/applications/GPAX/gpax.bmp deleted file mode 100644 index 3ff99ff3a736c68624df2285444fa06e7fb7e705..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 246 zcmZuqF%H5o47^H6WrIw#Q&!%=&R@a;>XdAx6J&}y@s&KOSx7n=Cq+!yzB^w|kx%D0 z*1EDB8T&69Y-(UC|07OHXv`|iyds3aS^}yAqOvi#%Tw{cx(nB_$-NMhCK{eVname, "BitWrapper")) { + fprintf(f, "\t/*GPAC private*/\n"); + fprintf(f, "\tu32 buffer_len;\n"); + } fprintf(f, "} M_%s;\n\n\n", n->name); } @@ -789,12 +793,12 @@ void WriteNodeQuant(FILE *f, BNode *n) if (bf->hasBounds) { if (!strcmp(bf->b_min, "+I") || !strcmp(bf->b_min, " +I") || !strcmp(bf->b_min, "I")) { fprintf(f, "\t\t*b_min = FIX_MAX;\n"); - } else if (!strcmp(bf->b_min, "-I")) { + } else if (!strcmp(bf->b_min, "-I") || !strcmp(bf->b_min, "-65536")) { fprintf(f, "\t\t*b_min = FIX_MIN;\n"); } else { fprintf(f, "\t\t*b_min = %s;\n", GetFixedPrintout(bf->b_min)); } - if (!strcmp(bf->b_max, "+I") || !strcmp(bf->b_max, " +I") || !strcmp(bf->b_max, "I")) { + if (!strcmp(bf->b_max, "+I") || !strcmp(bf->b_max, " +I") || !strcmp(bf->b_max, "I") || !strcmp(bf->b_max, "65535")) { fprintf(f, "\t\t*b_max = FIX_MAX;\n"); } else { fprintf(f, "\t\t*b_max = %s;\n", GetFixedPrintout(bf->b_max)); diff --git a/applications/hbbtvplayer/README.txt b/applications/hbbtvplayer/README.txt new file mode 100644 index 0000000..c050ddb --- /dev/null +++ b/applications/hbbtvplayer/README.txt @@ -0,0 +1,15 @@ +/*****************************************************************************/ + +Documentation summary + + 'doc/introduction.txt' to know the purpose of the project + + 'doc/projectmanangement.txt' to know the organisation of files and directories. + + 'doc/avancement.txt' to know the progress in the development of the project + +/*****************************************************************************/ + +1. Run getsources.sh (without sudo) to get the sources of gpac and/or webkit if necessary + +2. Run 'sudo ./install.sh' with the appropriate parameter (full, player, gpac, webkit or dependencies) diff --git a/applications/hbbtvplayer/REQUIREMENT b/applications/hbbtvplayer/REQUIREMENT new file mode 100644 index 0000000..328c4d0 --- /dev/null +++ b/applications/hbbtvplayer/REQUIREMENT @@ -0,0 +1,10 @@ + +install.sh script will work on the follwoing conditions of execution : + +linux ubuntu 11.04 environnement +graphic drivers supporting transparency +installed xulrunner with correct library path (that could be not the default case in old version of xulrunner) + +the execution of hbbtvplayer require a enought large screen to display several windows, including a main window able in 720p resolution (1280x720) + + diff --git a/applications/hbbtvplayer/doc/avancement.txt b/applications/hbbtvplayer/doc/avancement.txt new file mode 100644 index 0000000..0b196b9 --- /dev/null +++ b/applications/hbbtvplayer/doc/avancement.txt @@ -0,0 +1,17 @@ + +mardi 12 avril 2011, 09:09:57 (UTC+0200) + +- video on TVview : bug of displaying, video truncated + +jeudi 2 décembre 2010, 14:14:23 (UTC+0100) + +- adding Remote Control keys as buttons, simulating the RCU + +lundi 22 novembre 2010, 09:50:31 (UTC+0100) + +- construction du GTK global, liaison avec Webkit par le GTKWidget dedie... liaosion avec GPAC , en fenetre +- would be nice to find a way to do all in one window. + +jeudi 14 octobre 2010, 10:15:36 (UTC+0200) + +- création diff --git a/applications/hbbtvplayer/doc/introduction.txt b/applications/hbbtvplayer/doc/introduction.txt new file mode 100644 index 0000000..a9953ac --- /dev/null +++ b/applications/hbbtvplayer/doc/introduction.txt @@ -0,0 +1,5 @@ + +HbbTV Player + +the goal is to get a HbbTVTerminal emulation on a PC and be able to play HbbTV applications, that could be include in a Stream. +The player will receveive input of HbbtvApplication entry files, or TS-stream featuring HBBTV. diff --git a/applications/hbbtvplayer/doc/projectmanagement.txt b/applications/hbbtvplayer/doc/projectmanagement.txt new file mode 100644 index 0000000..a94288d --- /dev/null +++ b/applications/hbbtvplayer/doc/projectmanagement.txt @@ -0,0 +1,16 @@ + +the project include 2 sub-project : + +- hbbtvterminal : a terminal able to play Video/Audio, an RCU emulation, and display a webbrowser linking to the hbbtvbrowserplugin (see below) + +- hbbtvbrowserplugin : a NPAPI plugin able to play HbbtvApps in a webbrowser . + + +CODEBLOCKS : + +for the use of codeblocks , you can find a workspace in /projectmanager/codeblocks/hbbtvplayer/ + +each project contains a projectmanager directory, including a codeblocks specification projects : + +/hbbtvterminal/projectmanager/codeblocks/hbbtvterminal/hbbtvterminal.cbp +/hbbtvbrowserplugin/projectmanager/codeblocks/hbbtvbrowserplugin/hbbtvbrowserplugin.cbp diff --git a/applications/hbbtvplayer/getsources.sh b/applications/hbbtvplayer/getsources.sh new file mode 100644 index 0000000..b8a7447 --- /dev/null +++ b/applications/hbbtvplayer/getsources.sh @@ -0,0 +1,44 @@ +#!/bin/sh -e + +WEBKIT=0 +GPAC=0 + +if [ -z $1 ] ; then + echo "\nDO NOT USE sudo FOR THIS SCRIPT" + echo "\nUsage: You must choose options :" + echo "\n\033[31m full - build the whole package (gpac+webkit+hbbtvplayer) Recommended for computer without gpac \033[0m" + echo "\n\033[33m webkit - download Webkit sources and install it\033[0m" + echo "\n\033[33m gpac - download gpac sources and install it\033[0m" + exit 1 +fi + +for i in $* ; do + if [ "$i" = "full" ] ; then + echo -e "\033[31m Usage: $0 Full Building : Activated \033[0m" + WEBKIT=1 + GPAC=1 + fi + + if [ "$i" = "webkit" ] ; then + echo -e "\033[33m Usage: $0 Webkit Building : Activated \033[0m" + WEBKIT=1 + fi + + if [ "$i" = "gpac" ] ; then + echo -e "\033[33m Usage: $0 gpac Building : Activated \033[0m" + WEBKIT=1 + fi +done + +if [ $GPAC -eq 1 ] ; then + svn checkout https://gpac.svn.sourceforge.net/svnroot/gpac/trunk/gpac gpac +fi + +if [ $WEBKIT -eq 1 ] ; then + svn checkout http://svn.webkit.org/repository/webkit/trunk WebKit --depth files -r 97300 # 98458 ? + svn checkout http://svn.webkit.org/repository/webkit/trunk/Source WebKit/Source -r 97300 + svn checkout http://svn.webkit.org/repository/webkit/trunk/Tools WebKit/Tools -r 97300 + svn checkout http://svn.webkit.org/repository/webkit/trunk/WebKitLibraries WebKit/WebKitLibraries -r 97300 +fi + + diff --git a/applications/hbbtvplayer/hbbtvbrowserplugin/README b/applications/hbbtvplayer/hbbtvbrowserplugin/README new file mode 100644 index 0000000..d3b8d3f --- /dev/null +++ b/applications/hbbtvplayer/hbbtvbrowserplugin/README @@ -0,0 +1,14 @@ +****************************************** +HBBTVBROWSERPLUGIN v0.01 + +To build : + + make + +To install : + + sudo make install + +******************************************* + + diff --git a/applications/hbbtvplayer/hbbtvbrowserplugin/autogen.sh b/applications/hbbtvplayer/hbbtvbrowserplugin/autogen.sh new file mode 100644 index 0000000..124f59c --- /dev/null +++ b/applications/hbbtvplayer/hbbtvbrowserplugin/autogen.sh @@ -0,0 +1,5 @@ +#! /bin/sh + +autoreconf --install -v + +./configure diff --git a/applications/hbbtvplayer/hbbtvbrowserplugin/configure.ac b/applications/hbbtvplayer/hbbtvbrowserplugin/configure.ac new file mode 100644 index 0000000..ff93781 --- /dev/null +++ b/applications/hbbtvplayer/hbbtvbrowserplugin/configure.ac @@ -0,0 +1,19 @@ + + +AC_INIT([hbbtvbrowserplugin], [0.1.0], [stanislas.selle@telecom-paristech.fr]) + +AC_CONFIG_AUX_DIR([build-aux]) +AC_CONFIG_MACRO_DIR([m4]) + +m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) + +AM_INIT_AUTOMAKE([foreign -Wall -Werror]) +AC_PROG_CC +AC_PROG_LIBTOOL + +PKG_CHECK_MODULES([GLIB], [glib-2.0 >= 2.28.0]) +PKG_CHECK_MODULES([GTK], [gtk+-2.0 >= 2.20.1]) +AC_SUBST([PLUGINDIR], [/usr/lib/mozilla/plugins]) +LT_INIT +AC_CONFIG_FILES([makefile src/makefile hbbtvbrowserplugin.pc]) +AC_OUTPUT diff --git a/applications/hbbtvplayer/hbbtvbrowserplugin/doc/hbbtvBrowserPlugin-NPAPI-20111108.pptx b/applications/hbbtvplayer/hbbtvbrowserplugin/doc/hbbtvBrowserPlugin-NPAPI-20111108.pptx new file mode 100644 index 0000000000000000000000000000000000000000..90203fa87e86d1b2d1839c7b00d08dc4c3fc4639 GIT binary patch literal 651985 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 z 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¨Pr^f* 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
wY1I)IJ7R|AxnOOE;U? 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 zν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 literal 0 HcmV?d00001 diff --git a/applications/osmo4_ios/extract.c b/applications/osmo4_ios/extract.c new file mode 100644 index 0000000..8d100c8 --- /dev/null +++ b/applications/osmo4_ios/extract.c @@ -0,0 +1,702 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * Copyright (c) 2005-200X ENST + * All rights reserved + * + * This file is part of GPAC / command-line client + * + * GPAC is gf_free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "libgpac_symbols.h" + +#ifdef WIN32 +#include +#else +typedef struct tagBITMAPFILEHEADER +{ + u16 bfType; + u32 bfSize; + u16 bfReserved1; + u16 bfReserved2; + u32 bfOffBits; +} BITMAPFILEHEADER; + +typedef struct tagBITMAPINFOHEADER{ + u32 biSize; + s32 biWidth; + s32 biHeight; + u16 biPlanes; + u16 biBitCount; + u32 biCompression; + u32 biSizeImage; + s32 biXPelsPerMeter; + s32 biYPelsPerMeter; + u32 biClrUsed; + u32 biClrImportant; +} BITMAPINFOHEADER; + +#define BI_RGB 0L + +#endif + + +#include +#include +#include + +extern Bool is_connected; +extern GF_Terminal *term; +extern u32 Duration; +extern GF_Err last_error; + +static GFINLINE u8 colmask(s32 a, s32 n) +{ + s32 mask = (1 << n) - 1; + return (u8) (a & (0xff & ~mask)) | ((-((a >> n) & 1)) & mask); +} + +static u32 put_pixel(FILE *fout, u32 type, u32 pf, char *ptr) +{ + u16 col; + switch (pf) { + case GF_PIXEL_RGB_32: + case GF_PIXEL_ARGB: + fputc(ptr[0], fout); + fputc(ptr[1], fout); + fputc(ptr[2], fout); + return 4; + + case GF_PIXEL_BGR_32: + case GF_PIXEL_RGBA: + //probably due to tinygl bug - verify +#ifndef GPAC_USE_TINYGL + fputc(ptr[3], fout); + fputc(ptr[2], fout); + fputc(ptr[1], fout); +#else + fputc(ptr[2], fout); + fputc(ptr[1], fout); + fputc(ptr[0], fout); +#endif + return 4; + + case GF_PIXEL_RGB_24: + fputc(ptr[2], fout); + fputc(ptr[1], fout); + fputc(ptr[0], fout); + return 3; + + case GF_PIXEL_BGR_24: + fputc(ptr[2], fout); + fputc(ptr[1], fout); + fputc(ptr[0], fout); + return 3; + case GF_PIXEL_RGB_565: + col = * (u16 *)ptr; + fputc(colmask(col << 3, 3), fout); + fputc(colmask(col >> (5 - 2), 2), fout); + fputc(colmask(col >> (11 - 3), 3), fout); + return 2; + + case GF_PIXEL_RGB_555: + col = * (u16 *)ptr; + fputc(colmask(col << 3, 3), fout); + fputc(colmask(col >> (5 - 3), 3), fout); + fputc(colmask(col >> (10 - 3), 3), fout); + return 2; + /* this is used to write the byte depthbuffer in greyscale when dumping depth*/ + case GF_PIXEL_GREYSCALE: + /* bmp always needs 3 pixels */ + fputc(ptr[0], fout); + fputc(ptr[0], fout); + fputc(ptr[0], fout); + /* if printing the characters corresponding to the float depth buffer: */ + /* + { + u32 i=0; + while (ptr[i]!='\0') { + fputc(ptr[i], fout); + i++; + } + fputc('\b', fout); + } + */ + return 1; + + + case 0: + fputc(ptr[0], fout); + return 1; + } + return 0; +} + +void write_bmp(GF_VideoSurface *fb, char *rad_name, u32 img_num) +{ + char str[GF_MAX_PATH]; + BITMAPFILEHEADER fh; + BITMAPINFOHEADER fi; + FILE *fout; + u32 j, i; + char *ptr, *prev; + + prev = strrchr(rad_name, '.'); + //if (prev) prev[0] = '\0'; + + if (fb->pixel_format==GF_PIXEL_GREYSCALE) sprintf(str, "%s_%d_depth.bmp", rad_name, img_num); + else sprintf(str, "%s_%d.bmp", rad_name, img_num); + + fout = gf_f64_open(str, "wb"); + if (!fout) return; + + memset(&fh, 0, sizeof(fh)); + fh.bfType = 19778; + fh.bfOffBits = 14 + 40; + + memset(&fi, 0, sizeof(char)*40); + fi.biSize = sizeof(char)*40; + fi.biWidth = fb->width; + fi.biHeight = fb->height; + fi.biPlanes = 1; + if (fb->pixel_format==GF_PIXEL_GREYSCALE) fi.biBitCount = 24; + else fi.biBitCount = 24; + fi.biCompression = BI_RGB; + fi.biSizeImage = fb->pitch_y * fb->height; + + /*NOT ALIGNED!!*/ + gf_fwrite(&fh.bfType, 2, 1, fout); + gf_fwrite(&fh.bfSize, 4, 1, fout); + gf_fwrite(&fh.bfReserved1, 2, 1, fout); + gf_fwrite(&fh.bfReserved2, 2, 1, fout); + gf_fwrite(&fh.bfOffBits, 4, 1, fout); + + gf_fwrite(&fi, 1, 40, fout); +//#ifndef GPAC_USE_TINYGL + for (j=fb->height; j>0; j--) { + ptr = fb->video_buffer + (j-1)*fb->pitch_y; + for (i=0;iwidth; i++) { + u32 res = put_pixel(fout, 0, fb->pixel_format, ptr); + assert(res); + ptr += res; + } + } +//#else +#if 0 + for (j=0; jheight; j++) { + ptr = fb->video_buffer + j*fb->pitch; + for (i=0;iwidth; i++) { + u32 res = put_pixel(fout, 0, fb->pixel_format, ptr); + assert(res); + ptr += res; + } + } +#endif + + fclose(fout); +} + +/*writes onto a file the content of the framebuffer in *fb interpreted as the byte depthbuffer */ +/*it's also possible to write a float depthbuffer by passing the floats to strings and writing chars in putpixel - see comments*/ +void write_depthfile(GF_VideoSurface *fb, char *rad_name, u32 img_num) +{ + FILE *fout; + u32 i, j; + char val; + unsigned char *depth; + + depth = (unsigned char *) fb->video_buffer; + + fout = gf_f64_open("dump_depth", "wb"); + if (!fout) return; + for (j=0; jheight; j++) { + for (i=0;iwidth; i++) { + +#ifdef GPAC_USE_TINYGL + val = fputc(depth[2*i+j*fb->width*sizeof(unsigned short)], fout); + val = fputc(depth[2*i+j*fb->width*sizeof(unsigned short) + 1], fout); +#else + val = fputc(depth[i+j*fb->width], fout); +#endif + } + } + fclose(fout); +} + +void write_texture_file(GF_VideoSurface *fb, char *rad_name, u32 img_num, u32 dump_mode) +{ + + FILE *fout; + u32 i, j; + char val; + unsigned char *buf; + + buf = (unsigned char *) fb->video_buffer; + + if (dump_mode==6) fout = gf_f64_open("dump_rgbds", "wb"); + else if (dump_mode==9) fout = gf_f64_open("dump_rgbd", "wb"); + else return; + + if (!fout) return; + for (j=0; jheight; j++) { + for (i=0;iwidth*4; i++) { + val = fputc(buf[i+j*fb->pitch_y], fout); + } + } + fclose(fout); +} + + +void write_raw(GF_VideoSurface *fb, char *rad_name, u32 img_num) +{ + u32 j, i; + char *ptr, *prev; + char str[GF_MAX_PATH]; + FILE *fout; + prev = strrchr(rad_name, '.'); + if (prev) prev[0] = '\0'; + if (img_num<10) { + sprintf(str, "%s_00%d.raw", rad_name, img_num); + } else if (img_num<100) { + sprintf(str, "%s_0%d.raw", rad_name, img_num); + } else { + sprintf(str, "%s_%d.raw", rad_name, img_num); + } + + fout = gf_f64_open(str, "wb"); + if (!fout) return; + + + for (j=0;jheight; j++) { + ptr = fb->video_buffer + j*fb->pitch_y; + for (i=0;iwidth; i++) { + u32 res = put_pixel(fout, 0, fb->pixel_format, ptr); + assert(res); + ptr += res; + } + } + fclose(fout); +} + + +/* creates a .bmp format greyscale image of the byte depthbuffer and a binary with only the content of the depthbuffer */ +void dump_depth (GF_Terminal *term, char *rad_name, u32 dump_type, u32 frameNum, char *conv_buf, void *avi_out) +{ + GF_Err e; + u32 i, k; + GF_VideoSurface fb; + + /*lock it*/ + e = gf_sc_get_screen_buffer(term->compositor, &fb, 1); + if (e) fprintf(stdout, "Error grabbing depth buffer: %s\n", gf_error_to_string(e)); + else fprintf(stdout, "OK\n"); + /*export frame*/ + switch (dump_type) { + case 1: + case 8: + /*reverse frame*/ + for (k=0; k> 8/*(11 - 3)*/, 3); + dst[1] = colmask(src_16 >> 3/*(5 - 2)*/, 2); + dst[0] = colmask(src_16 << 3, 3); + src+=2; + break; + case GF_PIXEL_RGB_555: + src_16 = * (u16 *)src; + dst[2] = colmask(src_16 >> 7/*(10 - 3)*/, 3); + dst[1] = colmask(src_16 >> 2/*(5 - 3)*/, 3); + dst[0] = colmask(src_16 << 3, 3); + src+=2; + break; + /*for depth .avi*/ + case GF_PIXEL_GREYSCALE: + dst[0] = src[0]; + dst[1] = src[0]; + dst[2] = src[0]; + src+=1; + break; + } + dst += 3; + } + } +#ifndef GPAC_DISABLE_AVILIB + if (AVI_write_frame(avi_out, conv_buf, fb.height*fb.width*3, 1) <0) + printf("Error writing frame\n"); +#endif + break; + case 2: + write_bmp(&fb, rad_name, frameNum); + break; + case 3: + write_raw(&fb, rad_name, frameNum); + break; + case 4: + write_depthfile(&fb, rad_name, frameNum); + break; + case 7: + write_bmp(&fb, rad_name, frameNum); + break; + + } + /*unlock it*/ + /*in -depth -avi mode, do not release it yet*/ + if (dump_type!=8) gf_sc_release_screen_buffer(term->compositor, &fb); +} + +void dump_frame(GF_Terminal *term, char *rad_name, u32 dump_type, u32 frameNum, char *conv_buf, void *avi_out) +{ + GF_Err e = GF_OK; + u32 i, k, out_size; + GF_VideoSurface fb; + + /*lock it*/ + if (dump_type==5 || dump_type==6) e = gf_sc_get_screen_buffer(term->compositor, &fb, 2); + else if (dump_type== 9 || dump_type==10) e = gf_sc_get_screen_buffer(term->compositor, &fb, 3); + else e = gf_sc_get_screen_buffer(term->compositor, &fb, 0); + if (e) fprintf(stdout, "Error grabbing frame buffer: %s\n", gf_error_to_string(e)); + + if (dump_type!=5 && dump_type!= 10) { + out_size = fb.height*fb.width*3; + } else { + out_size = fb.height*fb.width*4; + } + /*export frame*/ + switch (dump_type) { + case 1: + case 5: + case 10: + case 8: + /*reverse frame*/ + for (k=0; k> 8/*(11 - 3)*/, 3); + dst[1] = colmask(src_16 >> 3/*(5 - 2)*/, 2); + dst[0] = colmask(src_16 << 3, 3); + src+=2; + dst+=3; + } + break; + case GF_PIXEL_RGB_555: + for (i=0;i> 7/*(10 - 3)*/, 3); + dst[1] = colmask(src_16 >> 2/*(5 - 3)*/, 3); + dst[0] = colmask(src_16 << 3, 3); + src+=2; + dst +=3; + } + break; + } + } +#ifndef GPAC_DISABLE_AVILIB + if (dump_type!=5 && dump_type!= 10) { + if (AVI_write_frame(avi_out, conv_buf, out_size, 1) <0) + printf("Error writing frame\n"); + } else { + if (AVI_write_frame(avi_out, conv_buf, out_size, 1) <0) + printf("Error writing frame\n"); + } +#endif + break; + case 2: + write_bmp(&fb, rad_name, frameNum); + break; + case 6: + case 9: + write_texture_file(&fb, rad_name, frameNum, dump_type); + break; + + case 3: + write_raw(&fb, rad_name, frameNum); + break; + } + /*unlock it*/ + gf_sc_release_screen_buffer(term->compositor, &fb); +} + +Bool dump_file(char *url, u32 dump_mode, Double fps, u32 width, u32 height, Float scale, u32 *times, u32 nb_times) +{ + GF_Err e; + u32 i = 0; + GF_VideoSurface fb; + char szPath[GF_MAX_PATH]; + char *prev=NULL; + + prev = strstr(url, "://"); + if (prev) { + prev = strrchr(url, '/'); + if (prev) prev++; + } + + if (!prev) prev = url; + strcpy(szPath, prev); + prev = strrchr(szPath, '.'); + if (prev) prev[0] = 0; + + fprintf(stdout, "Opening URL %s\n", url); + /*connect in pause mode*/ + gf_term_connect_from_time(term, url, 0, 1); + + while (!term->compositor->scene + || term->compositor->msg_type + || (gf_term_get_option(term, GF_OPT_PLAY_STATE) == GF_STATE_STEP_PAUSE) + ) { + if (last_error) return 1; + gf_term_process_flush(term); + gf_sleep(10); + } + + if (width && height) { + gf_term_set_size(term, width, height); + gf_term_process_flush(term); + } +#ifndef GPAC_USE_TINYGL + printf("not tinygl\n"); + e = gf_sc_get_screen_buffer(term->compositor, &fb, 0); +#else + printf("tinygl\n"); + e = gf_sc_get_screen_buffer(term->compositor, &fb, 1); +#endif + if (e != GF_OK) { + fprintf(stdout, "Error grabbing screen buffer: %s\n", gf_error_to_string(e)); + return 0; + } + width = fb.width; + height = fb.height; + gf_sc_release_screen_buffer(term->compositor, &fb); + + if (scale != 1) { + width = (u32)(width * scale); + height = (u32)(height * scale); + gf_term_set_size(term, width, height); + gf_term_process_flush(term); + } + + /*we work in RGB24, and we must make sure the pitch is %4*/ + if ((width*3)%4) { + fprintf(stdout, "Adjusting width (%d) to have a stride multiple of 4\n", width); + while ((width*3)%4) width--; + + gf_term_set_size(term, width, height); + gf_term_process_flush(term); + + gf_sc_get_screen_buffer(term->compositor, &fb, 0); + width = fb.width; + height = fb.height; + gf_sc_release_screen_buffer(term->compositor, &fb); + } + + if (dump_mode==1 || dump_mode==5 || dump_mode==8 || dump_mode==10) { +#ifdef GPAC_DISABLE_AVILIB + fprintf(stdout, "AVILib is disabled in this build of GPAC\n"); + return 0; +#else + u32 time, prev_time, nb_frames, dump_dur; + char *conv_buf; + avi_t *avi_out = NULL; + avi_t *depth_avi_out = NULL; + char szPath_depth[GF_MAX_PATH]; + char comp[5]; + strcpy(szPath_depth, szPath); + strcat(szPath, ".avi"); + avi_out = AVI_open_output_file(szPath); + if (!avi_out) { + fprintf(stdout, "Error creating AVI file %s\n", szPath); + return 1; + } + if (dump_mode==8) { + strcat(szPath_depth, "_depth.avi"); + depth_avi_out = AVI_open_output_file(szPath_depth); + if (!depth_avi_out) { + fprintf(stdout, "Error creating AVI file %s\n", szPath); + return 1; + } + } + + if (!fps) fps = 25.0; + time = prev_time = 0; + nb_frames = 0; + + if (nb_times==2) { + prev_time = times[0]; + dump_dur = times[1] - times[0]; + } else { + dump_dur = times[0] ? times[0] : Duration; + } + if (!dump_dur) { + fprintf(stdout, "Warning: file has no duration, defaulting to 1 sec\n"); + dump_dur = 1000; + } + + comp[0] = comp[1] = comp[2] = comp[3] = comp[4] = 0; + AVI_set_video(avi_out, width, height, fps, comp); + if (dump_mode==8) AVI_set_video(depth_avi_out, width, height, fps, comp); + if (dump_mode != 5 && dump_mode!=10) conv_buf = gf_malloc(sizeof(char) * width * height * 3); + else conv_buf = gf_malloc(sizeof(char) * width * height * 4); + /*step to first frame*/ + if (prev_time) gf_term_step_clocks(term, prev_time); + + while (time < dump_dur) { + while ((gf_term_get_option(term, GF_OPT_PLAY_STATE) == GF_STATE_STEP_PAUSE)) { + gf_term_process_flush(term); + } + fprintf(stdout, "Dumping %02d/100\r", (u32) ((100.0*prev_time)/dump_dur) ); + + if (dump_mode==8) { + /*we'll dump both buffers at once*/ + gf_mx_p(term->compositor->mx); + dump_depth(term, szPath_depth, dump_mode, i+1, conv_buf, depth_avi_out); + dump_frame(term, szPath, dump_mode, i+1, conv_buf, avi_out); + gf_mx_v(term->compositor->mx); + + } + else dump_frame(term, szPath, dump_mode, i+1, conv_buf, avi_out); + + nb_frames++; + time = (u32) (nb_frames*1000/fps); + gf_term_step_clocks(term, time - prev_time); + prev_time = time; + } + AVI_close(avi_out); + if (dump_mode==8) AVI_close(depth_avi_out); + gf_free(conv_buf); + fprintf(stdout, "AVI Extraction 100/100\n"); +#endif /*GPAC_DISABLE_AVILIB*/ + } else { + if (times[0]) gf_term_step_clocks(term, times[0]); + + for (i=0; i +#include + +#else +#include /*for GetModuleFileName*/ +#include /*for _mkdir*/ +#include /*for getting user-dir*/ +#ifndef SHGFP_TYPE_CURRENT +#define SHGFP_TYPE_CURRENT 0 /*needed for MinGW*/ +#endif + +#ifdef _MSC_VER +/*get rid of console*/ +#if 0 +#pragma comment(linker,"/SUBSYSTEM:WINDOWS") +#pragma comment(linker,"/ENTRY:main") +#else +#pragma comment(linker,"/SUBSYSTEM:CONSOLE") +#endif + +#endif // _MSC_VER + +#endif //WIN32 + + +/*local prototypes*/ +void PrintWorldInfo(GF_Terminal *term); +void ViewOD(GF_Terminal *term, u32 OD_ID, u32 number); +void PrintODList(GF_Terminal *term, GF_ObjectManager *root_odm, u32 num, u32 indent, char *root_name); + +void ViewODs(GF_Terminal *term, Bool show_timing); +void PrintGPACConfig(); + +static Bool restart = 0; +#if defined(__DARWIN__) || defined(__APPLE__) +static Bool not_threaded = 1; +#else +static Bool not_threaded = 0; +#endif +static Bool no_audio = 0; +static Bool no_regulation = 0; +static Bool bench_mode = 0; +Bool is_connected = 0; +Bool startup_file = 0; +GF_User user; +GF_Terminal *term; +u64 Duration; +GF_Err last_error = GF_OK; + +static Fixed bench_speed = FLT2FIX(20); + +static Bool request_next_playlist_item = 0; + +static GF_Config *cfg_file; +static Bool display_rti = 0; +static Bool Run; +static Bool CanSeek = 0; +static u32 Volume=100; +static char the_url[GF_MAX_PATH]; +static char pl_path[GF_MAX_PATH]; +static Bool no_mime_check = 1; +static Bool be_quiet = 0; +static u32 log_time_start = 0; + +static u32 forced_width=0; +static u32 forced_height=0; + +/*windowless options*/ +u32 align_mode = 0; +u32 init_w = 0; +u32 init_h = 0; +u32 last_x, last_y; +Bool right_down = 0; + +void dump_frame(GF_Terminal *term, char *rad_path, u32 dump_type, u32 frameNum); +Bool dump_file(char *the_url, u32 dump_mode, Double fps, u32 width, u32 height, Float scale, u32 *times, u32 nb_times); + +void PrintUsage() +{ + fprintf(stdout, "Usage Osmo4iOS [options] [filename]\n" + "\t-c fileName: user-defined configuration file\n" + "\t-rti fileName: logs run-time info (FPS, CPU, Mem usage) to file\n" + "\t-rtix fileName: same as -rti but driven by GPAC logs\n" + "\t-quiet: removes script message, buffering and downloading status\n" + "\t-opt option: Overrides an option in the configuration file. String format is section:key=value\n" + "\t-log-file file: sets output log file.\n" + "\t-log-level lev: sets log level. Possible values are:\n" + "\t \"error\" : logs only error messages\n" + "\t \"warning\" : logs error+warning messages\n" + "\t \"info\" : logs error+warning+info messages\n" + "\t \"debug\" : logs all messages\n" + "\n" + "\t-log-tools lt: sets tool(s) to log. List of \':\'-separated values:\n" + "\t \"core\" : libgpac core\n" + "\t \"coding\" : bitstream formats (audio, video, scene)\n" + "\t \"container\" : container formats (ISO File, MPEG-2 TS, AVI, ...)\n" + "\t \"network\" : network data exept RTP trafic\n" + "\t \"rtp\" : rtp trafic\n" + "\t \"author\" : authoring tools (hint, import, export)\n" + "\t \"sync\" : terminal sync layer\n" + "\t \"codec\" : terminal codec messages\n" + "\t \"parser\" : scene parsers (svg, xmt, bt) and other\n" + "\t \"media\" : terminal media object management\n" + "\t \"scene\" : scene graph and scene manager\n" + "\t \"script\" : scripting engine messages\n" + "\t \"interact\" : interaction engine (events, scripts, etc)\n" + "\t \"compose\" : composition engine (2D, 3D, etc)\n" + "\t \"service\" : network service management\n" + "\t \"mmio\" : Audio/Video HW I/O management\n" + "\t \"none\" : no tool logged\n" + "\t \"all\" : all tools logged\n" + "\n" + "\t-size WxH: specifies visual size (default: scene size)\n" + "\t-scale s: scales the visual size (default: 1)\n" +#if defined(__DARWIN__) || defined(__APPLE__) + "\t-thread: enables thread usage for terminal and compositor \n" +#else + "\t-no-thread: disables thread usage (except for audio)\n" +#endif + "\t-no-audio: disables audio \n" + "\t-no-wnd: uses windowless mode (Win32 only)\n" + "\t-align vh: specifies v and h alignment for windowless mode\n" + " possible v values: t(op), m(iddle), b(ottom)\n" + " possible h values: l(eft), m(iddle), r(ight)\n" + " default alignment is top-left\n" + " default alignment is top-left\n" + "\t-pause: pauses at first frame\n" + "\n" + "Dumper Options:\n" + "\t-bmp [times]: dumps given frames to bmp\n" + "\t-raw [times]: dumps given frames to bmp\n" + "\t-avi [times]: dumps given file to raw avi\n" + "\t-rgbds: dumps the RGBDS pixel format texture\n" + " with -avi [times]: dumps an rgbds-format .avi\n" + "\t-rgbd: dumps the RGBD pixel format texture\n" + " with -avi [times]: dumps an rgbd-format .avi\n" + "\t-depth: dumps depthmap (z-buffer) frames\n" + " with -avi [times]: dumps depthmap in grayscale .avi\n" + " with -bmp: dumps depthmap in grayscale .bmp\n" + "\t-fps FPS: specifies frame rate for AVI dumping (default: 25.0)\n" + "\t-2d: uses 2D compositor\n" + "\t-3d: uses 3D compositor\n" + "\t-fill: uses fill aspect ratio for dumping (default: none)\n" + "\t-show: show window while dumping (default: no)\n" + "MP4Client - GPAC command line player and dumper - version %s\n" + "GPAC Written by Jean Le Feuvre (c) 2001-2005 - ENST (c) 2005-200X\n", + + GPAC_FULL_VERSION + ); +} + +void PrintHelp() +{ + fprintf(stdout, "MP4Client command keys:\n" + "\to: connect to the specified URL\n" + "\tO: connect to the specified URL in playlist mode\n" + "\tN: switch to the next URL in the playlist (works with return key as well)\n" + "\tr: restart current presentation\n" + "\tp: play/pause the presentation\n" + "\ts: step one frame ahead\n" + "\tz: seek into presentation\n" + "\tt: print current timing\n" + "\n" + "\tw: view world info\n" + "\tv: view Object Descriptor list\n" + "\ti: view Object Descriptor info (by ID)\n" + "\tj: view Object Descriptor info (by number)\n" + "\tb: view media objects timing and buffering info\n" + "\tm: view media objects buffering and memory info\n" + "\td: dumps scene graph\n" + "\n" + "\tC: Enable Streaming Cache\n" + "\tS: Stops Streaming Cache and save to file\n" + "\tA: Aborts Streaming Cache\n" + "\n" + "\tk: turns stress mode on/off\n" + "\tn: changes navigation mode\n" + "\tx: reset to last active viewpoint\n" + "\n" + "\t2: restart using 2D compositor\n" + "\t3: restart using 3D compositor\n" + "\n" + "\t4: forces 4/3 Aspect Ratio\n" + "\t5: forces 16/9 Aspect Ratio\n" + "\t6: forces no Aspect Ratio (always fill screen)\n" + "\t7: forces original Aspect Ratio (default)\n" + "\n" + "\tL: changes to new log level. CF MP4Client usage for possible values\n" + "\tT: select new tools to log. CF MP4Client usage for possible values\n" + "\n" + "\tl: list available modules\n" + "\tc: prints some GPAC configuration info\n" + "\tR: toggles run-time info display on/off\n" + "\tq: exit the application\n" + "\th: print this message\n" + "\n" + "MP4Client - GPAC command line player - version %s\n" + "GPAC Written by Jean Le Feuvre (c) 2001-2005 - ENST (c) 2005-200X\n", + + GPAC_FULL_VERSION + ); +} + + +static void PrintTime(u64 time) +{ + u32 ms, h, m, s; + h = (u32) (time / 1000 / 3600); + m = (u32) (time / 1000 / 60 - h*60); + s = (u32) (time / 1000 - h*3600 - m*60); + ms = (u32) (time - (h*3600 + m*60 + s) * 1000); + fprintf(stdout, "%02d:%02d:%02d.%02d", h, m, s, ms); +} + + +static u32 rti_update_time_ms = 200; +static FILE *rti_logs = NULL; +static u64 memory_at_gpac_startup = 0; + +static void UpdateRTInfo(const char *legend) +{ + GF_SystemRTInfo rti; + + /*refresh every second*/ + if (!display_rti && !rti_logs) return; + if (!gf_sys_get_rti(rti_update_time_ms, &rti, 0) && !legend) + return; + + if (display_rti) { + if (!rti.process_memory) rti.process_memory = (u32) (memory_at_gpac_startup-rti.physical_memory_avail); + if (!rti.gpac_memory) rti.gpac_memory = (u32) (memory_at_gpac_startup-rti.physical_memory_avail); + + if (display_rti==2) { + fprintf(stdout, "FPS %02.2f - CPU %02d (%02d) - Mem %d kB\r", + gf_term_get_framerate(term, 0), rti.total_cpu_usage, rti.process_cpu_usage, (u32) (rti.gpac_memory / 1024) ); + } else { + char szMsg[1024]; + GF_Event evt; + + sprintf(szMsg, "FPS %02.2f - CPU %02d (%02d) - Mem %d kB", + gf_term_get_framerate(term, 0), rti.total_cpu_usage, rti.process_cpu_usage, (u32) (rti.gpac_memory / 1024) ); + evt.type = GF_EVENT_SET_CAPTION; + evt.caption.caption = szMsg; + gf_term_user_event(term, &evt); + } + } + if (rti_logs) { + fprintf(rti_logs, "% 8d\t% 8d\t% 8d\t% 4d\t% 8d\t%s", + gf_sys_clock(), + gf_term_get_time_in_ms(term), + rti.total_cpu_usage, + (u32) gf_term_get_framerate(term, 0), + (u32) (rti.gpac_memory / 1024), + legend ? legend : "" + ); + if (!legend) fprintf(rti_logs, "\n"); + } +} + +static void ResetCaption() +{ + GF_Event event; + if (display_rti) return; + event.type = GF_EVENT_SET_CAPTION; + if (is_connected) { + char szName[1024]; + NetInfoCommand com; + + event.caption.caption = NULL; + /*get any service info*/ + if (!startup_file && gf_term_get_service_info(term, gf_term_get_root_object(term), &com) == GF_OK) { + strcpy(szName, ""); + if (com.track_info) { + char szBuf[10]; + sprintf(szBuf, "%02d ", (u32) (com.track_info>>16) ); + strcat(szName, szBuf); + } + if (com.artist) { strcat(szName, com.artist); strcat(szName, " "); } + if (com.name) { strcat(szName, com.name); strcat(szName, " "); } + if (com.album) { strcat(szName, "("); strcat(szName, com.album); strcat(szName, ")"); } + + if (strlen(szName)) event.caption.caption = szName; + } + if (!event.caption.caption) { + char *str = strrchr(the_url, '\\'); + if (!str) str = strrchr(the_url, '/'); + event.caption.caption = str ? str+1 : the_url; + } + } else { + event.caption.caption = "GPAC MP4Client " GPAC_FULL_VERSION; + } + gf_term_user_event(term, &event); +} + +#ifdef WIN32 +u32 get_sys_col(int idx) +{ + u32 res; + DWORD val = GetSysColor(idx); + res = (val)&0xFF; res<<=8; + res |= (val>>8)&0xFF; res<<=8; + res |= (val>>16)&0xFF; + return res; +} +#endif + +void switch_bench() +{ + if (is_connected) { + bench_mode = !bench_mode; + display_rti = !display_rti; + ResetCaption(); + gf_term_set_speed(term, bench_mode ? bench_speed : FIX_ONE); + } +} + +Bool GPAC_EventProc(void *ptr, GF_Event *evt) +{ + if (!term) return 0; + + switch (evt->type) { + case GF_EVENT_DURATION: + Duration = 1000; + Duration = (u64) (((s64) Duration) * evt->duration.duration); + CanSeek = evt->duration.can_seek; + break; + case GF_EVENT_MESSAGE: + { + const char *servName; + if (!evt->message.service || !strcmp(evt->message.service, the_url)) { + servName = "main service"; + } else if (!strnicmp(evt->message.service, "data:", 5)) { + servName = ""; + } else { + servName = evt->message.service; + } + if (!evt->message.message) return 0; + if (evt->message.error) { + if (!is_connected) last_error = evt->message.error; + fprintf(stderr, "%s (%s): %s\n", evt->message.message, servName, gf_error_to_string(evt->message.error)); + } else if (!be_quiet) + fprintf(stderr, "(%s) %s\r", servName, evt->message.message); + } + break; + case GF_EVENT_PROGRESS: + { + char *szTitle = ""; + if (evt->progress.progress_type==0) szTitle = "Buffer "; + else if (evt->progress.progress_type==1) szTitle = "Download "; + else if (evt->progress.progress_type==2) szTitle = "Import "; + gf_set_progress(szTitle, evt->progress.done, evt->progress.total); + } + break; + + + case GF_EVENT_DBLCLICK: + gf_term_set_option(term, GF_OPT_FULLSCREEN, !gf_term_get_option(term, GF_OPT_FULLSCREEN)); + return 0; + + case GF_EVENT_MOUSEDOWN: + if (evt->mouse.button==GF_MOUSE_RIGHT) { + right_down = 1; + last_x = evt->mouse.x; + last_y = evt->mouse.y; + } + return 0; + case GF_EVENT_MOUSEUP: + if (evt->mouse.button==GF_MOUSE_RIGHT) { + right_down = 0; + last_x = evt->mouse.x; + last_y = evt->mouse.y; + } + return 0; + case GF_EVENT_MOUSEMOVE: + if (right_down && (user.init_flags & GF_TERM_WINDOWLESS) ) { + GF_Event move; + move.move.x = evt->mouse.x - last_x; + move.move.y = last_y-evt->mouse.y; + move.type = GF_EVENT_MOVE; + move.move.relative = 1; + gf_term_user_event(term, &move); + } + return 0; + + case GF_EVENT_KEYUP: + switch (evt->key.key_code) { + case GF_KEY_SPACE: + if (evt->key.flags & GF_KEY_MOD_CTRL) switch_bench(); + break; + } + break; + case GF_EVENT_KEYDOWN: + gf_term_process_shortcut(term, evt); + switch (evt->key.key_code) { + case GF_KEY_SPACE: + if (evt->key.flags & GF_KEY_MOD_CTRL) { + /*ignore key repeat*/ + if (!bench_mode) switch_bench(); + } + break; + case GF_KEY_PAGEDOWN: + case GF_KEY_MEDIANEXTTRACK: + request_next_playlist_item = 1; + break; + case GF_KEY_MEDIAPREVIOUSTRACK: + break; + case GF_KEY_ESCAPE: + gf_term_set_option(term, GF_OPT_FULLSCREEN, !gf_term_get_option(term, GF_OPT_FULLSCREEN)); + break; + case GF_KEY_F: + if (evt->key.flags & GF_KEY_MOD_CTRL) fprintf(stderr, "Rendering rate: %f FPS\n", gf_term_get_framerate(term, 0)); + break; + case GF_KEY_T: + if (evt->key.flags & GF_KEY_MOD_CTRL) fprintf(stderr, "Scene Time: %f \n", gf_term_get_time_in_ms(term)/1000.0); + break; + case GF_KEY_D: + if (evt->key.flags & GF_KEY_MOD_CTRL) gf_term_set_option(term, GF_OPT_DRAW_MODE, (gf_term_get_option(term, GF_OPT_DRAW_MODE)==GF_DRAW_MODE_DEFER) ? GF_DRAW_MODE_IMMEDIATE : GF_DRAW_MODE_DEFER ); + break; + case GF_KEY_4: + if (evt->key.flags & GF_KEY_MOD_CTRL) + gf_term_set_option(term, GF_OPT_ASPECT_RATIO, GF_ASPECT_RATIO_4_3); + break; + case GF_KEY_5: + if (evt->key.flags & GF_KEY_MOD_CTRL) + gf_term_set_option(term, GF_OPT_ASPECT_RATIO, GF_ASPECT_RATIO_16_9); + break; + case GF_KEY_6: + if (evt->key.flags & GF_KEY_MOD_CTRL) + gf_term_set_option(term, GF_OPT_ASPECT_RATIO, GF_ASPECT_RATIO_FILL_SCREEN); + break; + case GF_KEY_7: + if (evt->key.flags & GF_KEY_MOD_CTRL) + gf_term_set_option(term, GF_OPT_ASPECT_RATIO, GF_ASPECT_RATIO_KEEP); + break; + case GF_KEY_P: + if (evt->key.flags & GF_KEY_MOD_CTRL && is_connected) { + Bool is_pause = gf_term_get_option(term, GF_OPT_PLAY_STATE); + fprintf(stderr, "[Status: %s]\n", is_pause ? "Playing" : "Paused"); + gf_term_set_option(term, GF_OPT_PLAY_STATE, (gf_term_get_option(term, GF_OPT_PLAY_STATE)==GF_STATE_PAUSED) ? GF_STATE_PLAYING : GF_STATE_PAUSED); + } + break; + case GF_KEY_S: + if ((evt->key.flags & GF_KEY_MOD_CTRL) && is_connected) { + gf_term_set_option(term, GF_OPT_PLAY_STATE, GF_STATE_STEP_PAUSE); + fprintf(stderr, "Step time: "); + PrintTime(gf_term_get_time_in_ms(term)); + fprintf(stderr, "\n"); + } + break; + case GF_KEY_B: + if ((evt->key.flags & GF_KEY_MOD_CTRL) && is_connected) + ViewODs(term, 1); + break; + case GF_KEY_M: + if ((evt->key.flags & GF_KEY_MOD_CTRL) && is_connected) + ViewODs(term, 0); + break; + case GF_KEY_H: + if ((evt->key.flags & GF_KEY_MOD_CTRL) && is_connected) + gf_term_switch_quality(term, 1); + break; + case GF_KEY_L: + if ((evt->key.flags & GF_KEY_MOD_CTRL) && is_connected) + gf_term_switch_quality(term, 0); + break; + } + break; + + case GF_EVENT_CONNECT: + if (evt->connect.is_connected) { + is_connected = 1; + fprintf(stderr, "Service Connected\n"); + } else if (is_connected) { + fprintf(stderr, "Service %s\n", is_connected ? "Disconnected" : "Connection Failed"); + is_connected = 0; + Duration = 0; + } + if (init_w && init_h) { + gf_term_set_size(term, init_w, init_h); + } + ResetCaption(); + break; + case GF_EVENT_EOS: + restart = 1; + break; + case GF_EVENT_SIZE: + if (user.init_flags & GF_TERM_WINDOWLESS) { + GF_Event move; + move.type = GF_EVENT_MOVE; + move.move.align_x = align_mode & 0xFF; + move.move.align_y = (align_mode>>8) & 0xFF; + move.move.relative = 2; + gf_term_user_event(term, &move); + } + break; + case GF_EVENT_SCENE_SIZE: + if (forced_width && forced_height) { + GF_Event size; + size.type = GF_EVENT_SIZE; + size.size.width = forced_width; + size.size.height = forced_height; + gf_term_user_event(term, &size); + } + break; + + case GF_EVENT_METADATA: + ResetCaption(); + break; + + case GF_EVENT_QUIT: + Run = 0; + break; + case GF_EVENT_DISCONNECT: + gf_term_disconnect(term); + break; + case GF_EVENT_MIGRATE: + { + } + break; + case GF_EVENT_NAVIGATE_INFO: + if (evt->navigate.to_url) fprintf(stderr, "Go to URL: \"%s\"\r", evt->navigate.to_url); + break; + case GF_EVENT_NAVIGATE: + if (gf_term_is_supported_url(term, evt->navigate.to_url, 1, no_mime_check)) { + strcpy(the_url, evt->navigate.to_url); + fprintf(stderr, "Navigating to URL %s\n", the_url); + gf_term_navigate_to(term, evt->navigate.to_url); + return 1; + } else { + fprintf(stderr, "Navigation destination not supported\nGo to URL: %s\n", evt->navigate.to_url); + } + break; + case GF_EVENT_SET_CAPTION: + gf_term_user_event(term, evt); + break; + case GF_EVENT_AUTHORIZATION: + if (!strlen(evt->auth.user)) { + fprintf(stderr, "Authorization required for site %s\n", evt->auth.site_url); + fprintf(stderr, "login: "); + scanf("%s", evt->auth.user); + } else { + fprintf(stderr, "Authorization required for %s@%s\n", evt->auth.user, evt->auth.site_url); + } + fprintf(stderr, "password: "); + gf_prompt_set_echo_off(1); + scanf("%s", evt->auth.password); + gf_prompt_set_echo_off(0); + return 1; + case GF_EVENT_SYS_COLORS: +#ifdef WIN32 + evt->sys_cols.sys_colors[0] = get_sys_col(COLOR_ACTIVEBORDER); + evt->sys_cols.sys_colors[1] = get_sys_col(COLOR_ACTIVECAPTION); + evt->sys_cols.sys_colors[2] = get_sys_col(COLOR_APPWORKSPACE); + evt->sys_cols.sys_colors[3] = get_sys_col(COLOR_BACKGROUND); + evt->sys_cols.sys_colors[4] = get_sys_col(COLOR_BTNFACE); + evt->sys_cols.sys_colors[5] = get_sys_col(COLOR_BTNHIGHLIGHT); + evt->sys_cols.sys_colors[6] = get_sys_col(COLOR_BTNSHADOW); + evt->sys_cols.sys_colors[7] = get_sys_col(COLOR_BTNTEXT); + evt->sys_cols.sys_colors[8] = get_sys_col(COLOR_CAPTIONTEXT); + evt->sys_cols.sys_colors[9] = get_sys_col(COLOR_GRAYTEXT); + evt->sys_cols.sys_colors[10] = get_sys_col(COLOR_HIGHLIGHT); + evt->sys_cols.sys_colors[11] = get_sys_col(COLOR_HIGHLIGHTTEXT); + evt->sys_cols.sys_colors[12] = get_sys_col(COLOR_INACTIVEBORDER); + evt->sys_cols.sys_colors[13] = get_sys_col(COLOR_INACTIVECAPTION); + evt->sys_cols.sys_colors[14] = get_sys_col(COLOR_INACTIVECAPTIONTEXT); + evt->sys_cols.sys_colors[15] = get_sys_col(COLOR_INFOBK); + evt->sys_cols.sys_colors[16] = get_sys_col(COLOR_INFOTEXT); + evt->sys_cols.sys_colors[17] = get_sys_col(COLOR_MENU); + evt->sys_cols.sys_colors[18] = get_sys_col(COLOR_MENUTEXT); + evt->sys_cols.sys_colors[19] = get_sys_col(COLOR_SCROLLBAR); + evt->sys_cols.sys_colors[20] = get_sys_col(COLOR_3DDKSHADOW); + evt->sys_cols.sys_colors[21] = get_sys_col(COLOR_3DFACE); + evt->sys_cols.sys_colors[22] = get_sys_col(COLOR_3DHIGHLIGHT); + evt->sys_cols.sys_colors[23] = get_sys_col(COLOR_3DLIGHT); + evt->sys_cols.sys_colors[24] = get_sys_col(COLOR_3DSHADOW); + evt->sys_cols.sys_colors[25] = get_sys_col(COLOR_WINDOW); + evt->sys_cols.sys_colors[26] = get_sys_col(COLOR_WINDOWFRAME); + evt->sys_cols.sys_colors[27] = get_sys_col(COLOR_WINDOWTEXT); + return 1; +#else + memset(evt->sys_cols.sys_colors, 0, sizeof(u32)*28); + return 1; +#endif + break; + } + return 0; +} + + +void list_modules(GF_ModuleManager *modules) +{ + u32 i; + fprintf(stderr, "\rAvailable modules:\n"); + for (i=0; isimulation_time)) { + Run = 0; + } + continue; + } + c = gf_prompt_get_char(); + +force_input: + switch (c) { + case 'q': + Run = 0; + break; + case 'X': + exit(0); + break; + case 'Q': + break; + case 'o': + startup_file = 0; + gf_term_disconnect(term); + fprintf(stdout, "Enter the absolute URL\n"); + scanf("%s", the_url); + if (rti_file) init_rti_logs(rti_file, the_url, use_rtix); + gf_term_connect(term, the_url); + break; + case 'O': + gf_term_disconnect(term); + fprintf(stdout, "Enter the absolute URL to the playlist\n"); + scanf("%s", the_url); + playlist = gf_f64_open(the_url, "rt"); + if (playlist) { + fscanf(playlist, "%s", the_url); + fprintf(stdout, "Opening URL %s\n", the_url); + gf_term_connect(term, the_url); + } + break; + case '\n': + case 'N': + if (playlist) { + gf_term_disconnect(term); + + if (fscanf(playlist, "%s", the_url) == EOF) { + fprintf(stdout, "No more items - exiting\n"); + Run = 0; + } else { + fprintf(stdout, "Opening URL %s\n", the_url); + gf_term_connect_with_path(term, the_url, pl_path); + } + } + break; + case 'P': + if (playlist) { + u32 count; + gf_term_disconnect(term); + scanf("%d", &count); + while (count) { + fscanf(playlist, "%s", the_url); + count--; + } + fprintf(stdout, "Opening URL %s\n", the_url); + gf_term_connect(term, the_url); + } + break; + case 'r': + if (is_connected) { + gf_term_disconnect(term); + gf_term_connect(term, startup_file ? gf_cfg_get_key(cfg_file, "General", "StartupFile") : the_url); + } + break; + + case 'D': + if (is_connected) gf_term_disconnect(term); + break; + + case 'p': + if (is_connected) { + Bool is_pause = gf_term_get_option(term, GF_OPT_PLAY_STATE); + fprintf(stdout, "[Status: %s]\n", is_pause ? "Playing" : "Paused"); + gf_term_set_option(term, GF_OPT_PLAY_STATE, is_pause ? GF_STATE_PLAYING : GF_STATE_PAUSED); + } + break; + case 's': + if (is_connected) { + gf_term_set_option(term, GF_OPT_PLAY_STATE, GF_STATE_STEP_PAUSE); + fprintf(stdout, "Step time: "); + PrintTime(gf_term_get_time_in_ms(term)); + fprintf(stdout, "\n"); + } + break; + + case 'z': + if (!CanSeek || (Duration<=2000)) { + fprintf(stdout, "scene not seekable\n"); + } else { + Double res; + s32 seekTo; + fprintf(stdout, "Duration: "); + PrintTime(Duration); + res = gf_term_get_time_in_ms(term); + res *= 100; res /= (s64)Duration; + fprintf(stdout, " (current %.2f %%)\nEnter Seek percentage:\n", res); + if (scanf("%d", &seekTo) == 1) { + if (seekTo > 100) seekTo = 100; + res = (Double)(s64)Duration; res /= 100; res *= seekTo; + gf_term_play_from_time(term, (u64) (s64) res, 0); + } + } + break; + + case 't': + { + if (is_connected) { + fprintf(stdout, "Current Time: "); + PrintTime(gf_term_get_time_in_ms(term)); + fprintf(stdout, " - Duration: "); + PrintTime(Duration); + fprintf(stdout, "\n"); + } + } + break; + case 'w': + if (is_connected) PrintWorldInfo(term); + break; + case 'v': + if (is_connected) PrintODList(term, NULL, 0, 0, "Root"); + break; + case 'i': + if (is_connected) { + u32 ID; + fprintf(stdout, "Enter OD ID (0 for main OD): "); + fflush(stdout); + scanf("%d", &ID); + ViewOD(term, ID, (u32)-1); + } + break; + case 'j': + if (is_connected) { + u32 num; + fprintf(stdout, "Enter OD number (0 for main OD): "); + fflush(stdout); + scanf("%d", &num); + ViewOD(term, (u32)-1, num); + } + break; + case 'b': + if (is_connected) ViewODs(term, 1); + break; + + case 'm': + if (is_connected) ViewODs(term, 0); + break; + + case 'l': + list_modules(user.modules); + break; + + case 'n': + if (is_connected) set_navigation(); + break; + case 'x': + if (is_connected) gf_term_set_option(term, GF_OPT_NAVIGATION_TYPE, 0); + break; + + case 'd': + if (is_connected) { + GF_ObjectManager *odm = NULL; + char radname[GF_MAX_PATH], *sExt; + GF_Err e; + u32 i, count, odid; + Bool xml_dump, std_out; + fprintf(stdout, "Enter Inline OD ID if any or 0"); + fflush(stdout); + radname[0] = 0; + scanf("%d", &odid); + if (odid) { + GF_ObjectManager *root_odm = gf_term_get_root_object(term); + if (!root_odm) break; + count = gf_term_get_object_count(term, root_odm); + for (i=0; iobjectDescriptorID==odid) break; + } + odm = NULL; + } + } + fprintf(stdout, "Enter file radical name (+\'.x\' for XML dumping) - \"std\" for stdout: "); + fflush(stdout); + scanf("%s", radname); + sExt = strrchr(radname, '.'); + xml_dump = 0; + if (sExt) { + if (!stricmp(sExt, ".x")) xml_dump = 1; + sExt[0] = 0; + } + std_out = strnicmp(radname, "std", 3) ? 0 : 1; + e = gf_term_dump_scene(term, std_out ? NULL : radname, NULL, xml_dump, 0, odm); + fprintf(stdout, "Dump done (%s)\n", gf_error_to_string(e)); + } + break; + + case 'c': + PrintGPACConfig(); + break; + case '3': + { + Bool use_3d = !gf_term_get_option(term, GF_OPT_USE_OPENGL); + if (gf_term_set_option(term, GF_OPT_USE_OPENGL, use_3d)==GF_OK) { + fprintf(stdout, "Using %s for 2D drawing\n", use_3d ? "OpenGL" : "2D rasterizer"); + } + } + break; + case 'k': + { + Bool opt = gf_term_get_option(term, GF_OPT_STRESS_MODE); + opt = !opt; + fprintf(stdout, "Turning stress mode %s\n", opt ? "on" : "off"); + gf_term_set_option(term, GF_OPT_STRESS_MODE, opt); + } + break; + case '4': gf_term_set_option(term, GF_OPT_ASPECT_RATIO, GF_ASPECT_RATIO_4_3); break; + case '5': gf_term_set_option(term, GF_OPT_ASPECT_RATIO, GF_ASPECT_RATIO_16_9); break; + case '6': gf_term_set_option(term, GF_OPT_ASPECT_RATIO, GF_ASPECT_RATIO_FILL_SCREEN); break; + case '7': gf_term_set_option(term, GF_OPT_ASPECT_RATIO, GF_ASPECT_RATIO_KEEP); break; + + case 'C': + switch (gf_term_get_option(term, GF_OPT_MEDIA_CACHE)) { + case GF_MEDIA_CACHE_DISABLED: gf_term_set_option(term, GF_OPT_MEDIA_CACHE, GF_MEDIA_CACHE_ENABLED); break; + case GF_MEDIA_CACHE_ENABLED: gf_term_set_option(term, GF_OPT_MEDIA_CACHE, GF_MEDIA_CACHE_DISABLED); break; + case GF_MEDIA_CACHE_RUNNING: fprintf(stdout, "Streaming Cache is running - please stop it first\n"); continue; + } + switch (gf_term_get_option(term, GF_OPT_MEDIA_CACHE)) { + case GF_MEDIA_CACHE_ENABLED: fprintf(stdout, "Streaming Cache Enabled\n"); break; + case GF_MEDIA_CACHE_DISABLED: fprintf(stdout, "Streaming Cache Disabled\n"); break; + case GF_MEDIA_CACHE_RUNNING: fprintf(stdout, "Streaming Cache Running\n"); break; + } + break; + case 'S': + case 'A': + if (gf_term_get_option(term, GF_OPT_MEDIA_CACHE)==GF_MEDIA_CACHE_RUNNING) { + gf_term_set_option(term, GF_OPT_MEDIA_CACHE, (c=='S') ? GF_MEDIA_CACHE_DISABLED : GF_MEDIA_CACHE_DISCARD); + fprintf(stdout, "Streaming Cache stopped\n"); + } else { + fprintf(stdout, "Streaming Cache not running\n"); + } + break; + case 'R': + display_rti = !display_rti; + ResetCaption(); + break; + case 'F': + if (display_rti) display_rti = 0; + else display_rti = 2; + ResetCaption(); + break; + + case 'u': + { + GF_Err e; + char szCom[8192]; + fprintf(stdout, "Enter command to send:\n"); + fflush(stdin); + szCom[0] = 0; + scanf("%[^\t\n]", szCom); + e = gf_term_scene_update(term, NULL, szCom); + if (e) fprintf(stdout, "Processing command failed: %s\n", gf_error_to_string(e)); + } + break; + + case 'L': + { + char szLog[1024]; + fprintf(stdout, "Enter new log level:\n"); + scanf("%s", szLog); + gf_log_modify_tools_levels( szLog ); + } + break; + case 'g': + { + GF_SystemRTInfo rti; + gf_sys_get_rti(rti_update_time_ms, &rti, 0); + fprintf(stdout, "GPAC allocated memory "LLD"\n", rti.gpac_memory); + } + break; + case 'M': + { + u32 size; + fprintf(stdout, "Enter new video cache memory in kBytes (current %d):\n", gf_term_get_option(term, GF_OPT_VIDEO_CACHE_SIZE)); + scanf("%d", &size); + gf_term_set_option(term, GF_OPT_VIDEO_CACHE_SIZE, size); + } + break; + + case 'E': + gf_term_set_option(term, GF_OPT_RELOAD_CONFIG, 1); + break; + + case 'B': + switch_bench(); + break; + + /*extract to PNG*/ + case 'Z': + { + GF_VideoSurface fb; + GF_Err e; + e = gf_term_get_screen_buffer(term, &fb); + if (e) { + fprintf(stdout, "Error dumping screen buffer %s\n", gf_error_to_string(e) ); + } else { + u32 dst_size = fb.width*fb.height*3; + char *dst=malloc(sizeof(char)*dst_size); + + e = gf_img_png_enc(fb.video_buffer, fb.width, fb.height, fb.pitch_y, fb.pixel_format, dst, &dst_size); + if (e) { + fprintf(stdout, "Error encoding PNG %s\n", gf_error_to_string(e) ); + } else { + FILE *png = gf_f64_open("dump.png", "wb"); + if (!png) { + fprintf(stdout, "Error writing file dump.png\n"); + } else { + gf_fwrite(dst, dst_size, 1, png); + fclose(png); + fprintf(stdout, "Writing file dump.png\n"); + } + } + if (dst) free(dst); + gf_term_release_screen_buffer(term, &fb); + } + } + break; + + case 'h': + PrintHelp(); + break; + default: + break; + } + } + + gf_term_disconnect(term); + if (rti_file) UpdateRTInfo("Disconnected\n"); + + fprintf(stdout, "Deleting terminal... "); + if (playlist) fclose(playlist); + gf_term_del(term); + fprintf(stdout, "OK\n"); + + fprintf(stdout, "GPAC cleanup ...\n"); + gf_modules_del(user.modules); + gf_cfg_del(cfg_file); + +#ifdef GPAC_MEMORY_TRACKING + if (enable_mem_tracker) { + gf_memory_print(); + fprintf(stdout, "print any key\n"); + while (!gf_prompt_has_input()) { + gf_sleep(100); + } + } +#endif + + gf_sys_close(); + if (rti_logs) fclose(rti_logs); + if (logfile) fclose(logfile); + fprintf(stdout, "Bye\n"); + return 0; +} + +void PrintWorldInfo(GF_Terminal *term) +{ + u32 i; + const char *title; + GF_List *descs; + descs = gf_list_new(); + title = gf_term_get_world_info(term, NULL, descs); + if (!title && !gf_list_count(descs)) { + fprintf(stdout, "No World Info available\n"); + } else { + fprintf(stdout, "\t%s\n", title ? title : "No title available"); + for (i=0; iobjectDescriptorID); + } + + szIndent[indent]=' '; + szIndent[indent+1]=0; + indent++; + + count = gf_term_get_object_count(term, root_odm); + for (i=0; iobjectDescriptorID); + } + fprintf(stdout, " - %s\n", (odi.od_type==GF_STREAM_VISUAL) ? "Video" : (odi.od_type==GF_STREAM_AUDIO) ? "Audio" : "Systems"); + break; + } + } + } +} + +void ViewOD(GF_Terminal *term, u32 OD_ID, u32 number) +{ + GF_MediaInfo odi; + u32 i, j, count, d_enum,id; + GF_Err e; + char code[5]; + NetStatCommand com; + GF_ObjectManager *odm, *root_odm = gf_term_get_root_object(term); + if (!root_odm) return; + + odm = NULL; + if ((!OD_ID && (number == (u32)(-1))) || + ((OD_ID == (u32)(-1)) && !number)) { + odm = root_odm; + if ((gf_term_get_object_info(term, odm, &odi) != GF_OK)) odm=NULL; + } else { + count = gf_term_get_object_count(term, root_odm); + for (i=0; iobjectDescriptorID == OD_ID)) break; + else if (i == (u32)(number-1)) break; + } + odm = NULL; + } + } + if (!odm) { + if (number == (u32)-1) fprintf(stdout, "cannot find OD with ID %d\n", OD_ID); + else fprintf(stdout, "cannot find OD with number %d\n", number); + return; + } + if (!odi.od) { + if (number == (u32)-1) fprintf(stdout, "Object %d not attached yet\n", OD_ID); + else fprintf(stdout, "Object #%d not attached yet\n", number); + return; + } + + if (!odi.od) { + fprintf(stdout, "Service not attached\n"); + return; + } + + if (odi.od->tag==GF_ODF_IOD_TAG) { + fprintf(stdout, "InitialObjectDescriptor %d\n", odi.od->objectDescriptorID); + fprintf(stdout, "Profiles and Levels: Scene %x - Graphics %x - Visual %x - Audio %x - OD %x\n", + odi.scene_pl, odi.graphics_pl, odi.visual_pl, odi.audio_pl, odi.OD_pl); + fprintf(stdout, "Inline Profile Flag %d\n", odi.inline_pl); + } else { + fprintf(stdout, "ObjectDescriptor %d\n", odi.od->objectDescriptorID); + } + + fprintf(stdout, "Object Duration: "); + if (odi.duration) { + PrintTime((u32) (odi.duration*1000)); + } else { + fprintf(stdout, "unknown"); + } + fprintf(stdout, "\n"); + + if (odi.owns_service) { + fprintf(stdout, "Service Handler: %s\n", odi.service_handler); + fprintf(stdout, "Service URL: %s\n", odi.service_url); + } + if (odi.codec_name) { + Float avg_dec_time; + switch (odi.od_type) { + case GF_STREAM_VISUAL: + fprintf(stdout, "Video Object: Width %d - Height %d\r\n", odi.width, odi.height); + fprintf(stdout, "Media Codec: %s\n", odi.codec_name); + if (odi.par) fprintf(stdout, "Pixel Aspect Ratio: %d:%d\n", (odi.par>>16)&0xFF, (odi.par)&0xFF); + break; + case GF_STREAM_AUDIO: + fprintf(stdout, "Audio Object: Sample Rate %d - %d channels\r\n", odi.sample_rate, odi.num_channels); + fprintf(stdout, "Media Codec: %s\n", odi.codec_name); + break; + case GF_STREAM_SCENE: + case GF_STREAM_PRIVATE_SCENE: + if (odi.width && odi.height) { + fprintf(stdout, "Scene Description - Width %d - Height %d\n", odi.width, odi.height); + } else { + fprintf(stdout, "Scene Description - no size specified\n"); + } + fprintf(stdout, "Scene Codec: %s\n", odi.codec_name); + break; + case GF_STREAM_TEXT: + if (odi.width && odi.height) { + fprintf(stdout, "Text Object: Width %d - Height %d\n", odi.width, odi.height); + } else { + fprintf(stdout, "Text Object: No size specified\n"); + } + fprintf(stdout, "Text Codec %s\n", odi.codec_name); + break; + } + + avg_dec_time = 0; + if (odi.nb_dec_frames) { + avg_dec_time = (Float) odi.total_dec_time; + avg_dec_time /= odi.nb_dec_frames; + } + fprintf(stdout, "\tBitrate over last second: %d kbps\n\tMax bitrate over one second: %d kbps\n\tAverage Decoding Time %.2f ms (%d max)\n\tTotal decoded frames %d\n", + (u32) odi.avg_bitrate/1024, odi.max_bitrate/1024, avg_dec_time, odi.max_dec_time, odi.nb_dec_frames); + } + if (odi.protection) fprintf(stdout, "Encrypted Media%s\n", (odi.protection==2) ? " NOT UNLOCKED" : ""); + + count = gf_list_count(odi.od->ESDescriptors); + fprintf(stdout, "%d streams in OD\n", count); + for (i=0; iESDescriptors, i); + + fprintf(stdout, "\nStream ID %d - Clock ID %d\n", esd->ESID, esd->OCRESID); + if (esd->dependsOnESID) fprintf(stdout, "\tDepends on Stream ID %d for decoding\n", esd->dependsOnESID); + + switch (esd->decoderConfig->streamType) { + case GF_STREAM_OD: fprintf(stdout, "\tOD Stream - version %d\n", esd->decoderConfig->objectTypeIndication); break; + case GF_STREAM_OCR: fprintf(stdout, "\tOCR Stream\n"); break; + case GF_STREAM_SCENE: fprintf(stdout, "\tScene Description Stream - version %d\n", esd->decoderConfig->objectTypeIndication); break; + case GF_STREAM_VISUAL: + fprintf(stdout, "\tVisual Stream - media type: "); + switch (esd->decoderConfig->objectTypeIndication) { + case GPAC_OTI_VIDEO_MPEG4_PART2: fprintf(stdout, "MPEG-4\n"); break; + case GPAC_OTI_VIDEO_MPEG2_SIMPLE: fprintf(stdout, "MPEG-2 Simple Profile\n"); break; + case GPAC_OTI_VIDEO_MPEG2_MAIN: fprintf(stdout, "MPEG-2 Main Profile\n"); break; + case GPAC_OTI_VIDEO_MPEG2_SNR: fprintf(stdout, "MPEG-2 SNR Profile\n"); break; + case GPAC_OTI_VIDEO_MPEG2_SPATIAL: fprintf(stdout, "MPEG-2 Spatial Profile\n"); break; + case GPAC_OTI_VIDEO_MPEG2_HIGH: fprintf(stdout, "MPEG-2 High Profile\n"); break; + case GPAC_OTI_VIDEO_MPEG2_422: fprintf(stdout, "MPEG-2 422 Profile\n"); break; + case GPAC_OTI_VIDEO_MPEG1: fprintf(stdout, "MPEG-1\n"); break; + case GPAC_OTI_IMAGE_JPEG: fprintf(stdout, "JPEG\n"); break; + case GPAC_OTI_IMAGE_PNG: fprintf(stdout, "PNG\n"); break; + case GPAC_OTI_IMAGE_JPEG_2000: fprintf(stdout, "JPEG2000\n"); break; + + case GPAC_OTI_MEDIA_GENERIC: + memcpy(code, esd->decoderConfig->decoderSpecificInfo->data, 4); + code[4] = 0; + fprintf(stdout, "GPAC Intern (%s)\n", code); + break; + default: + fprintf(stdout, "Private Type (0x%x)\n", esd->decoderConfig->objectTypeIndication); + break; + } + break; + + case GF_STREAM_AUDIO: + fprintf(stdout, "\tAudio Stream - media type: "); + switch (esd->decoderConfig->objectTypeIndication) { + case GPAC_OTI_AUDIO_AAC_MPEG4: fprintf(stdout, "MPEG-4\n"); break; + case GPAC_OTI_AUDIO_AAC_MPEG2_MP: fprintf(stdout, "MPEG-2 AAC Main Profile\n"); break; + case GPAC_OTI_AUDIO_AAC_MPEG2_LCP: fprintf(stdout, "MPEG-2 AAC LowComplexity Profile\n"); break; + case GPAC_OTI_AUDIO_AAC_MPEG2_SSRP: fprintf(stdout, "MPEG-2 AAC Scalable Sampling Rate Profile\n"); break; + case GPAC_OTI_AUDIO_MPEG2_PART3: fprintf(stdout, "MPEG-2 Audio\n"); break; + case GPAC_OTI_AUDIO_MPEG1: fprintf(stdout, "MPEG-1 Audio\n"); break; + case GPAC_OTI_AUDIO_EVRC_VOICE: fprintf(stdout, "EVRC Audio\n"); break; + case GPAC_OTI_AUDIO_SMV_VOICE: fprintf(stdout, "SMV Audio\n"); break; + case GPAC_OTI_AUDIO_13K_VOICE: fprintf(stdout, "QCELP Audio\n"); break; + case GPAC_OTI_MEDIA_GENERIC: + memcpy(code, esd->decoderConfig->decoderSpecificInfo->data, 4); + code[4] = 0; + fprintf(stdout, "GPAC Intern (%s)\n", code); + break; + default: + fprintf(stdout, "Private Type (0x%x)\n", esd->decoderConfig->objectTypeIndication); + break; + } + break; + case GF_STREAM_MPEG7: fprintf(stdout, "\tMPEG-7 Stream - version %d\n", esd->decoderConfig->objectTypeIndication); break; + case GF_STREAM_IPMP: fprintf(stdout, "\tIPMP Stream - version %d\n", esd->decoderConfig->objectTypeIndication); break; + case GF_STREAM_OCI: fprintf(stdout, "\tOCI Stream - version %d\n", esd->decoderConfig->objectTypeIndication); break; + case GF_STREAM_MPEGJ: fprintf(stdout, "\tMPEGJ Stream - version %d\n", esd->decoderConfig->objectTypeIndication); break; + case GF_STREAM_INTERACT: fprintf(stdout, "\tUser Interaction Stream - version %d\n", esd->decoderConfig->objectTypeIndication); break; + case GF_STREAM_TEXT: fprintf(stdout, "\tStreaming Text Stream - version %d\n", esd->decoderConfig->objectTypeIndication); break; + default: fprintf(stdout, "Unknown Stream\r\n"); break; + } + + fprintf(stdout, "\tBuffer Size %d\n\tAverage Bitrate %d bps\n\tMaximum Bitrate %d bps\n", esd->decoderConfig->bufferSizeDB, esd->decoderConfig->avgBitrate, esd->decoderConfig->maxBitrate); + if (esd->slConfig->predefined==SLPredef_SkipSL) { + fprintf(stdout, "\tNot using MPEG-4 Synchronization Layer\n"); + } else { + fprintf(stdout, "\tStream Clock Resolution %d\n", esd->slConfig->timestampResolution); + } + if (esd->URLString) fprintf(stdout, "\tStream Location: %s\n", esd->URLString); + + /*check language*/ + if (esd->langDesc) { + u32 i=0; + char lan[4], *szLang; + lan[0] = esd->langDesc->langCode>>16; + lan[1] = (esd->langDesc->langCode>>8)&0xFF; + lan[2] = (esd->langDesc->langCode)&0xFF; + lan[3] = 0; + + if ((lan[0]=='u') && (lan[1]=='n') && (lan[2]=='d')) szLang = "Undetermined"; + else { + szLang = lan; + while (GF_ISO639_Lang[i]) { + if (GF_ISO639_Lang[i+2][0] && strstr(GF_ISO639_Lang[i+1], lan)) { + szLang = (char*) GF_ISO639_Lang[i]; + break; + } + i+=3; + } + } + fprintf(stdout, "\tStream Language: %s\n", szLang); + } + } + fprintf(stdout, "\n"); + /*check OCI (not everything interests us) - FIXME: support for unicode*/ + count = gf_list_count(odi.od->OCIDescriptors); + if (count) { + fprintf(stdout, "%d Object Content Information descriptors in OD\n", count); + for (i=0; iOCIDescriptors, i); + switch (desc->tag) { + case GF_ODF_SEGMENT_TAG: + { + GF_Segment *sd = (GF_Segment *) desc; + fprintf(stdout, "Segment Descriptor: Name: %s - start time %g sec - duration %g sec\n", sd->SegmentName, sd->startTime, sd->Duration); + } + break; + case GF_ODF_CC_NAME_TAG: + { + GF_CC_Name *ccn = (GF_CC_Name *)desc; + fprintf(stdout, "Content Creators:\n"); + for (j=0; jContentCreators); j++) { + GF_ContentCreatorInfo *ci = (GF_ContentCreatorInfo *) gf_list_get(ccn->ContentCreators, j); + if (!ci->isUTF8) continue; + fprintf(stdout, "\t%s\n", ci->contentCreatorName); + } + } + break; + + case GF_ODF_SHORT_TEXT_TAG: + { + GF_ShortTextual *std = (GF_ShortTextual *)desc; + fprintf(stdout, "Description:\n\tEvent: %s\n\t%s\n", std->eventName, std->eventText); + } + break; + default: + break; + } + } + fprintf(stdout, "\n"); + } + + switch (odi.status) { + case 0: fprintf(stdout, "Stopped - "); break; + case 1: fprintf(stdout, "Playing - "); break; + case 2: fprintf(stdout, "Paused - "); break; + case 3: fprintf(stdout, "Not setup yet\n"); return; + default: fprintf(stdout, "Setup Failed\n"); return; + } + if (odi.buffer>=0) fprintf(stdout, "Buffer: %d ms - ", odi.buffer); + else fprintf(stdout, "Not buffering - "); + fprintf(stdout, "Clock drift: %d ms\n", odi.clock_drift); + if (odi.db_unit_count) fprintf(stdout, "%d AU in DB\n", odi.db_unit_count); + if (odi.cb_max_count) fprintf(stdout, "Composition Buffer: %d CU (%d max)\n", odi.cb_unit_count, odi.cb_max_count); + fprintf(stdout, "\n"); + + if (odi.owns_service) { + const char *url; + u32 done, total, bps; + d_enum = 0; + while (gf_term_get_download_info(term, odm, &d_enum, &url, NULL, &done, &total, &bps)) { + if (d_enum==1) fprintf(stdout, "Current Downloads in service:\n"); + if (done && total) { + fprintf(stdout, "%s: %d / %d bytes (%.2f %%) - %.2f kBps\n", url, done, total, (100.0f*done)/total, ((Float)bps)/1024.0f); + } else { + fprintf(stdout, "%s: %.2f kbps\n", url, ((Float)8*bps)/1024.0f); + } + } + if (!d_enum) fprintf(stdout, "No Downloads in service\n"); + fprintf(stdout, "\n"); + } + d_enum = 0; + while (gf_term_get_channel_net_info(term, odm, &d_enum, &id, &com, &e)) { + if (e) continue; + if (!com.bw_down && !com.bw_up) continue; + + fprintf(stdout, "Stream ID %d statistics:\n", id); + if (com.multiplex_port) { + fprintf(stdout, "\tMultiplex Port %d - multiplex ID %d\n", com.multiplex_port, com.port); + } else { + fprintf(stdout, "\tPort %d\n", com.port); + } + fprintf(stdout, "\tPacket Loss Percentage: %.4f\n", com.pck_loss_percentage); + fprintf(stdout, "\tDown Bandwidth: %d bps\n", com.bw_down); + if (com.bw_up) fprintf(stdout, "\tUp Bandwidth: %d bps\n", com.bw_up); + if (com.ctrl_port) { + if (com.multiplex_port) { + fprintf(stdout, "\tControl Multiplex Port: %d - Control Multiplex ID %d\n", com.multiplex_port, com.ctrl_port); + } else { + fprintf(stdout, "\tControl Port: %d\n", com.ctrl_port); + } + fprintf(stdout, "\tDown Bandwidth: %d bps\n", com.ctrl_bw_down); + fprintf(stdout, "\tUp Bandwidth: %d bps\n", com.ctrl_bw_up); + } + fprintf(stdout, "\n"); + } +} + +void PrintODTiming(GF_Terminal *term, GF_ObjectManager *odm) +{ + GF_MediaInfo odi; + if (!odm) return; + + if (gf_term_get_object_info(term, odm, &odi) != GF_OK) return; + if (!odi.od) { + fprintf(stdout, "Service not attached\n"); + return; + } + + fprintf(stdout, "OD %d: ", odi.od->objectDescriptorID); + switch (odi.status) { + case 1: fprintf(stdout, "Playing - "); break; + case 2: fprintf(stdout, "Paused - "); break; + default: fprintf(stdout, "Stopped - "); break; + } + if (odi.buffer>=0) fprintf(stdout, "Buffer: %d ms - ", odi.buffer); + else fprintf(stdout, "Not buffering - "); + fprintf(stdout, "Clock drift: %d ms", odi.clock_drift); + fprintf(stdout, " - time: "); + PrintTime((u32) (odi.current_time*1000)); + fprintf(stdout, "\n"); +} + +void PrintODBuffer(GF_Terminal *term, GF_ObjectManager *odm) +{ + Float avg_dec_time; + GF_MediaInfo odi; + if (!odm) return; + + if (gf_term_get_object_info(term, odm, &odi) != GF_OK) return; + if (!odi.od) { + fprintf(stdout, "Service not attached\n"); + return; + } + + fprintf(stdout, "OD %d: ", odi.od->objectDescriptorID); + switch (odi.status) { + case 1: fprintf(stdout, "Playing"); break; + case 2: fprintf(stdout, "Paused"); break; + default: fprintf(stdout, "Stopped"); break; + } + if (odi.buffer>=0) fprintf(stdout, " - Buffer: %d ms", odi.buffer); + if (odi.db_unit_count) fprintf(stdout, " - DB: %d AU", odi.db_unit_count); + if (odi.cb_max_count) fprintf(stdout, " - CB: %d/%d CUs", odi.cb_unit_count, odi.cb_max_count); + + fprintf(stdout, "\n * %d decoded frames - %d dropped frames\n", odi.nb_dec_frames, odi.nb_droped); + avg_dec_time = 0; + if (odi.nb_dec_frames) { avg_dec_time = (Float) odi.total_dec_time; avg_dec_time /= odi.nb_dec_frames; } + fprintf(stdout, " * Avg Bitrate %d kbps (%d max) - Avg Decoding Time %.2f ms (%d max)\n", + (u32) odi.avg_bitrate/1024, odi.max_bitrate/1024, avg_dec_time, odi.max_dec_time); +} + +void ViewODs(GF_Terminal *term, Bool show_timing) +{ + u32 i, count; + GF_ObjectManager *odm, *root_odm = gf_term_get_root_object(term); + if (!root_odm) return; + + if (show_timing) { + PrintODTiming(term, root_odm); + } else { + PrintODBuffer(term, root_odm); + } + count = gf_term_get_object_count(term, root_odm); + for (i=0; i + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + com.gpac.${PRODUCT_NAME:identifier} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + Osmo4 + CFBundlePackageType + APPL + CFBundleShortVersionString + 9.9.9 + CFBundleSignature + gpac + CFBundleVersion + 9999 + + diff --git a/applications/osmo4_wx/Darwin.Info.plist b/applications/osmo4_wx/Darwin.Info.plist new file mode 100644 index 0000000..710c02d --- /dev/null +++ b/applications/osmo4_wx/Darwin.Info.plist @@ -0,0 +1,32 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + Osmo4 + CFBundleGetInfoString + Osmo4 0.4.2 + CFBundleIconFile + Osmo + CFBundleIdentifier + org.gpac.Osmo4 + CFBundleInfoDictionaryVersion + 0.4.2 + CFBundlePackageType + APPL + CFBundleShortVersionString + 0.4.2 + CFBundleSignature + Omo4 + CFBundleVersion + 0.4.2 + NSHumanReadableCopyright + Copyright (c) 2003-2005 gpac + NSMainNibFile + NSMainNibFile + NSPrincipalClass + NSApplication + + diff --git a/applications/osmo4_wx/Darwin.InfoPlist.strings b/applications/osmo4_wx/Darwin.InfoPlist.strings new file mode 100644 index 0000000000000000000000000000000000000000..2efc670b7db95be4b0ebd929b71de20bda6a0430 GIT binary patch literal 456 zcmZ{g$qK?i5Jcls zij2nEX(?A*t#y?zkJ_1-=s;zsm}rOr{}H!<+|oYPA9SR;L48S86__Qyp8D$QgxW*r zYXUza8g)2jO;Ja1n&j;qy`vS~1p55Pi9;p9Rje>$*|=$N>;l#R!ZCB6NBFw=pWHzG z(5t-I!_}TypB_RyKBupvvDG3A>ACFEdOMlO(U2`fRAbHX;j#}`i*zS>tk-t(I)$%1 E0HUT#W&i*H literal 0 HcmV?d00001 diff --git a/applications/osmo4_wx/Darwin.Osmo.icns b/applications/osmo4_wx/Darwin.Osmo.icns new file mode 100644 index 0000000000000000000000000000000000000000..a598061aa6d15577ad67469469bec642644b3eca GIT binary patch literal 38570 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 literal 0 HcmV?d00001 diff --git a/applications/osmo4_wx/Makefile b/applications/osmo4_wx/Makefile new file mode 100644 index 0000000..e15bdac --- /dev/null +++ b/applications/osmo4_wx/Makefile @@ -0,0 +1,94 @@ +include ../../config.mak + +vpath %.cpp $(SRC_PATH)/applications/osmo4_wx + +CFLAGS= $(CPPFLAGS) -I"$(SRC_PATH)/include" + +ifeq ($(DEBUGBUILD), yes) +CFLAGS+=-g +LDFLAGS+=-g +endif + +ifeq ($(GPROFBUILD), yes) +CFLAGS+=-pg +LDFLAGS+=-pg +endif + +ifeq ($(GPACREADONLY), yes) +CFLAGS+=-DGPAC_READ_ONLY +endif + +#common obj +OBJS= wxOsmo4.o wxGPACControl.o fileprops.o Playlist.o menubtn.o + +ifeq ($(CONFIG_WIN32),yes) +EXE=.exe +PROG=Osmo4$(EXE) +else +EXT= +PROG=Osmo4 +endif + +#3 - spidermonkey support +ifeq ($(CONFIG_JS),no) +else +SCENEGRAPH_CFLAGS+=$(JS_FLAGS) +ifeq ($(CONFIG_JS),local) +NEED_LOCAL_LIB="yes" +endif +LINKFLAGS+=$(JS_LIBS) +endif + + +SRCS := $(OBJS:.o=.cpp) + +all: $(PROG) + +Osmo4$(EXE): $(OBJS) + $(CC) $(LDFLAGS) -o ../../bin/gcc/$@ $(OBJS) -L../../bin/gcc -lgpac $(WX_LFLAGS) $(LINKFLAGS) + +%.o: %.cpp + $(CXX) $(CFLAGS) $(WX_CFLAGS) -c -o $@ $< + +clean: + rm -f $(OBJS) ../../bin/gcc/$(PROG) + +install: +ifeq ($(CONFIG_DARWIN),yes) + mkdir -p $(DESTDIR)$(mac_apps)/Osmo4.app/Contents/MacOS + mkdir -p $(DESTDIR)$(mac_apps)/Osmo4.app/Contents/Resources/English.lproj + cp ./Darwin.Info.plist \ + $(DESTDIR)$(mac_apps)/Osmo4.app/Contents/Info.plist + cp ./Darwin.InfoPlist.strings \ + $(DESTDIR)$(mac_apps)/Osmo4.app/Contents/Resources/English.lproj/InfoPlist.strings + cp ./Darwin.Osmo.icns \ + $(DESTDIR)$(mac_apps)/Osmo4.app/Contents/Resources/Osmo.icns + install -m 755 $(INSTFLAGS) ../../bin/gcc/Osmo4 \ + $(DESTDIR)$(mac_apps)/Osmo4.app/Contents/MacOS + echo -n 'APPLOsm4' > $(DESTDIR)$(mac_apps)/Osmo4.app/Contents/PkgInfo +else + rm -f wxOsmo4.o + mkdir -p $(DESTDIR)$(prefix)/bin + install -m 755 $(INSTFLAGS) ../../bin/gcc/Osmo4 "$(DESTDIR)$(prefix)/bin" +endif + +uninstall: + rm -rf $(DESTDIR)$(prefix)/bin/Osmo4 + +dep: + +depend: + rm -f .depend + $(CC) -MM $(CFLAGS) $(SRCS) 1>.depend + +distclean: clean + rm -f Makefile.bak .depend + + + +# include dependency files if they exist +# +ifneq ($(wildcard .depend),) +include .depend +endif + diff --git a/applications/osmo4_wx/Playlist.cpp b/applications/osmo4_wx/Playlist.cpp new file mode 100644 index 0000000..7ed47a6 --- /dev/null +++ b/applications/osmo4_wx/Playlist.cpp @@ -0,0 +1,826 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Osmo4 wxWidgets GUI + * + * GPAC is gf_free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + */ + +#include "wxOsmo4.h" +#include "Playlist.h" + + +#include "playlist.xpm" + +PLEntry::PLEntry(wxString url) +{ + m_url = gf_strdup(url.mb_str(wxConvUTF8)); + Bool is_remote = 0;; + wxCharBuffer the_url = (const char *) url.mb_str(wxConvUTF8); + const char *_url = strstr(the_url, "://"); + if (_url) {_url += 3; is_remote = 1; } + else _url = (const char *) the_url; + + char *str = (char*)strrchr(_url, '\\'); + if (!str) str = (char*)strrchr(_url, '/'); + if (str && strlen(str+1)) { + m_disp_name = gf_strdup(str+1); + str = strrchr(m_disp_name, '.'); + if (str) str[0] = 0; + } else { + m_disp_name = gf_strdup(_url); + if (!is_remote) { + str = strrchr(m_disp_name, '.'); + if (str) str[0] = 0; + } + } + m_duration = 0; + m_bIsDead = 0; + m_bIsPlaying = 0; + m_bIsSelected = 0; +} + +PLEntry::~PLEntry() +{ + if (m_url) gf_free(m_url); + if (m_disp_name) gf_free(m_disp_name); + +} + +wxPlaylist::wxPlaylist(wxWindow *parent) + : wxFrame(parent, -1, wxString(_T("Osmo4 Playlist")), wxDefaultPosition, wxDefaultSize, + wxCLOSE_BOX | wxSYSTEM_MENU | wxCAPTION | wxRESIZE_BORDER) +{ + + m_pApp = (wxOsmo4Frame *)parent; + + m_pOpen = new wxBitmap(pl_open); + m_pSave = new wxBitmap(pl_save); + m_pAdd = new wxBitmap(pl_add); + m_pRem = new wxBitmap(pl_rem); + m_pUp = new wxBitmap(pl_up); + m_pDown = new wxBitmap(pl_down); + m_pSort = new wxBitmap(pl_sort); + + m_pToolBar = CreateToolBar(wxTB_HORIZONTAL); + m_pAddBut = new wxMenuButton(m_pToolBar, ID_PL_ADD_FILE, *m_pAdd); + wxMenu *menu = new wxMenu(); + menu->Append(ID_PL_ADD_URL, wxT("&Url")); + menu->Append(ID_PL_ADD_DIR, wxT("&Directory")); + menu->Append(ID_PL_ADD_DIR_REC, wxT("&Directory and subfolders")); + m_pAddBut->AssignMenu(menu); + m_pAddBut->SetToolTip(wxString(wxT("Add Files"))); + + m_pRemBut = new wxMenuButton(m_pToolBar, ID_PL_REM_FILE, *m_pRem); + menu = new wxMenu(); + menu->Append(ID_PL_REM_ALL, wxT("&Clear")); + menu->Append(ID_PL_REM_DEAD, wxT("&Remove dead entries")); + m_pRemBut->AssignMenu(menu); + m_pRemBut->SetToolTip(wxString(wxT("Remove Selected Files"))); + + m_pSortBut = new wxMenuButton(m_pToolBar, ID_PL_SORT_FILE, *m_pSort); + menu = new wxMenu(); + menu->Append(ID_PL_SORT_TITLE, wxT("&Sort by Title")); + menu->Append(ID_PL_SORT_FILE, wxT("&Sort by file name")); + menu->Append(ID_PL_SORT_DUR, wxT("&Sort by Duration")); + menu->AppendSeparator(); + menu->Append(ID_PL_REVERSE, wxT("&Reverse")); + menu->Append(ID_PL_RANDOMIZE, wxT("&Randomize")); + m_pSortBut->AssignMenu(menu); + m_pSortBut->SetToolTip(wxString(wxT("Sort Playlist by filename"))); + + m_pToolBar->AddTool(ID_PL_OPEN, wxT(""), *m_pOpen, wxT("Open Playlist")); + m_pToolBar->AddTool(ID_PL_SAVE, wxT(""), *m_pSave, wxT("Save Playlist")); + m_pToolBar->AddSeparator(); + m_pToolBar->AddControl(m_pAddBut); + m_pToolBar->AddControl(m_pRemBut); + m_pToolBar->AddSeparator(); + m_pToolBar->AddTool(ID_PL_UP, wxT(""), *m_pUp, wxT("Moves Selected Files Up")); + m_pToolBar->AddTool(ID_PL_DOWN, wxT(""), *m_pDown, wxT("Moves Selected Files Down")); + m_pToolBar->AddSeparator(); + m_pToolBar->AddControl(m_pSortBut); + m_pToolBar->Realize(); + + m_FileList = new wxListCtrl(this, ID_FILE_LIST, wxDefaultPosition, wxDefaultSize, wxLC_REPORT); + + m_FileList->InsertColumn(0, wxT(""), wxLIST_FORMAT_LEFT, 1); + m_FileList->InsertColumn(1, wxT("Title"), wxLIST_FORMAT_LEFT, 1); + m_FileList->InsertColumn(2, wxT("Duration"), wxLIST_FORMAT_LEFT, 1); + + m_entries = gf_list_new(); + m_cur_entry = -1; + m_all_dead_entries = -1; + + SetSize(220, 380); + Centre(); +} + +wxPlaylist::~wxPlaylist() +{ + Clear(); + gf_list_del(m_entries); + delete m_pAddBut; + delete m_pRemBut; + delete m_pSortBut; + delete m_pOpen; + delete m_pSave; + delete m_pAdd; + delete m_pRem; + delete m_pUp; + delete m_pDown; + delete m_pSort; +} + + +BEGIN_EVENT_TABLE(wxPlaylist, wxWindow) + EVT_CLOSE(wxPlaylist::OnClose) + EVT_SIZE(wxPlaylist::OnSize) + EVT_TOOL(ID_PL_ADD_FILE, wxPlaylist::OnAddFile) + EVT_TOOL(ID_PL_ADD_URL, wxPlaylist::OnAddURL) + EVT_TOOL(ID_PL_ADD_DIR, wxPlaylist::OnAddDir) + EVT_TOOL(ID_PL_ADD_DIR_REC, wxPlaylist::OnAddDirRec) + EVT_TOOL(ID_PL_REM_FILE, wxPlaylist::OnRemFile) + EVT_TOOL(ID_PL_REM_ALL, wxPlaylist::OnRemAll) + EVT_TOOL(ID_PL_REM_DEAD, wxPlaylist::OnRemDead) + EVT_TOOL(ID_PL_UP, wxPlaylist::OnSelUp) + EVT_TOOL(ID_PL_DOWN, wxPlaylist::OnSelDown) + EVT_TOOL(ID_PL_SAVE, wxPlaylist::OnSave) + EVT_TOOL(ID_PL_OPEN, wxPlaylist::OnOpen) + EVT_MENU(ID_PL_PLAY, wxPlaylist::OnPlay) + EVT_MENU(ID_PL_RANDOMIZE, wxPlaylist::OnRandomize) + EVT_MENU(ID_PL_REVERSE, wxPlaylist::OnReverseList) + EVT_MENU(ID_PL_SEL_REV, wxPlaylist::OnReverseSelection) + EVT_MENU(ID_PL_SORT_TITLE, wxPlaylist::OnSortTitle) + EVT_MENU(ID_PL_SORT_FILE, wxPlaylist::OnSortFile) + EVT_MENU(ID_PL_SORT_DUR, wxPlaylist::OnSortDuration) + EVT_LIST_ITEM_ACTIVATED(ID_FILE_LIST, wxPlaylist::OnItemActivate) + EVT_LIST_ITEM_RIGHT_CLICK(ID_FILE_LIST, wxPlaylist::OnRightClick) +END_EVENT_TABLE() + +void wxPlaylist::OnClose(wxCloseEvent &event) +{ + if (event.CanVeto()) { + event.Veto(); + Hide(); + } +} + +void wxPlaylist::OnSize(wxSizeEvent &event) +{ + wxSize s = event.GetSize(); + m_FileList->SetSize(0, 0, s.GetWidth()-2, s.GetHeight()); + m_FileList->SetColumnWidth(0, 30); + m_FileList->SetColumnWidth(2, 60); + m_FileList->SetColumnWidth(1, s.GetWidth()-96); +} + +void wxPlaylist::Clear() +{ + m_FileList->DeleteAllItems(); + while (gf_list_count(m_entries)) { + PLEntry *ple = (PLEntry *) gf_list_get(m_entries, 0); + gf_list_rem(m_entries, 0); + delete ple; + } + m_cur_entry = -1; +} + +void wxPlaylist::ClearButPlaying() +{ + PLEntry *p = NULL; + if (m_cur_entry >= 0) { + p = (PLEntry *) gf_list_get(m_entries, m_cur_entry); + gf_list_rem(m_entries, m_cur_entry); + } + Clear(); + if (p) { + gf_list_add(m_entries, p); + m_cur_entry = 0; + } + RefreshList(); +} + +void wxPlaylist::UpdateEntry(u32 idx) +{ + char szText[20]; + + PLEntry *ple = (PLEntry *) gf_list_get(m_entries, idx); + if (idx+1<10) sprintf(szText, "00%d", idx+1); + else if (idx+1<100) sprintf(szText, "0%d", idx+1); + else sprintf(szText, "%d", idx+1); + m_FileList->SetItem(idx, 0, wxString(szText, wxConvUTF8)); + + wxString str; + if (ple->m_bIsDead) str = wxT("!! ") + wxString(ple->m_disp_name, wxConvUTF8) + wxT(" (DEAD)!!)"); + else if (ple->m_bIsPlaying) str = wxT(">> ") + wxString(ple->m_disp_name, wxConvUTF8) + wxT(" >>"); + else str = wxString(ple->m_disp_name, wxConvUTF8); + m_FileList->SetItem(idx, 1, str); + + if (ple->m_duration) { + u32 h = (u32) (ple->m_duration / 3600); + u32 m = (u32) (ple->m_duration / 60) - h*60; + u32 s = (u32) (ple->m_duration) - h*3600 - m*60; + m_FileList->SetItem(idx, 2, wxString::Format(wxT("%02d:%02d:%02d"), h, m, s) ); + } else { + m_FileList->SetItem(idx, 2, wxT("Unknown")); + } +} + +void wxPlaylist::RefreshList() +{ + u32 i, top_idx; + char szPath[GF_MAX_PATH]; + Bool first_sel; + + top_idx = m_FileList->GetTopItem(); + m_FileList->DeleteAllItems(); + + first_sel = 0; + for (i=0; iInsertItem(i, wxT("")); + /*cast for 64-bit compilation*/ + m_FileList->SetItemData(i, (unsigned long) ple); + UpdateEntry(i); + + if (ple->m_bIsPlaying) m_cur_entry = i; + + if (ple->m_bIsSelected) { + m_FileList->SetItemState(i, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED | wxLIST_MASK_STATE); + ple->m_bIsSelected = 0; + /*ensure first item of selection is visible*/ + if (!first_sel) { + first_sel = 1; + top_idx = i; + } + } + } + + if (m_cur_entry >= (s32) gf_list_count(m_entries)-1) m_cur_entry = gf_list_count(m_entries)-1; + else { + s32 last_idx = top_idx + m_FileList->GetCountPerPage(); + m_FileList->EnsureVisible(top_idx); + if (gf_list_count(m_entries)<1+ (u32) last_idx) last_idx = gf_list_count(m_entries)-1; + m_FileList->EnsureVisible(last_idx); + } + + strcpy((char *) szPath, m_pApp->szAppPath); +#ifdef WIN32 + strcat(szPath, "gpac_pl.m3u"); +#else + strcat(szPath, ".gpac_pl.m3u"); +#endif + Save(szPath, 1); +} + +void wxPlaylist::OnAddFile(wxCommandEvent &WXUNUSED(event)) +{ + wxFileDialog dlg(this, wxT("Select file(s)"), wxT(""), wxT(""), m_pApp->GetFileFilter(), wxOPEN | wxCHANGE_DIR | /*wxHIDE_READONLY |*/ wxMULTIPLE); + + if (dlg.ShowModal() == wxID_OK) { + wxArrayString stra; + dlg.GetPaths(stra); + for (u32 i=0; im_pApp->m_term, item_name, 0, 1)) { + PLEntry *ple = new PLEntry(wxString(item_path, wxConvUTF8) ); + gf_list_add(_this->m_entries, ple); + } + return 0; +} + +static Bool pl_enum_dir_dirs(void *cbck, char *item_name, char *item_path) +{ + gf_enum_directory(item_path, 0, pl_enum_dir_item, cbck, NULL); + gf_enum_directory(item_path, 1, pl_enum_dir_dirs, cbck, NULL); + return 0; +} + + +void wxPlaylist::AddDir(Bool do_recurse) +{ + wxDirDialog dlg(this); + dlg.SetPath(wxString(szCacheDir, wxConvUTF8) ); + if (dlg.ShowModal() != wxID_OK) return; + + strcpy(szCacheDir, dlg.GetPath().mb_str(wxConvUTF8)); + gf_enum_directory(szCacheDir, 0, pl_enum_dir_item, this, NULL); + if (do_recurse) gf_enum_directory(szCacheDir, 1, pl_enum_dir_dirs, this, NULL); + m_all_dead_entries = -1; + RefreshList(); +} +void wxPlaylist::OnAddDir(wxCommandEvent &WXUNUSED(event)) +{ + AddDir(0); +} +void wxPlaylist::OnAddDirRec(wxCommandEvent &WXUNUSED(event)) +{ + AddDir(1); +} + +void wxPlaylist::OnAddURL(wxCommandEvent &WXUNUSED(event)) +{ + OpenURLDlg dlg(this, m_pApp->m_user.config); + if (dlg.ShowModal() != wxID_OK) return; + PLEntry *ple = new PLEntry(dlg.m_urlVal); + gf_list_add(m_entries, ple); + m_all_dead_entries = -1; + RefreshList(); +} + +void wxPlaylist::OnRemFile(wxCommandEvent &WXUNUSED(event)) +{ + if (!m_FileList->GetSelectedItemCount()) return; + + long item = -1; + for (;;) { + item = m_FileList->GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); + if (item == -1) break; + PLEntry *ple = (PLEntry *) m_FileList->GetItemData(item); + gf_list_del_item(m_entries, ple); + delete ple; + } + RefreshList(); +} + +void wxPlaylist::OnRemAll(wxCommandEvent &WXUNUSED(event)) +{ + Clear(); + RefreshList(); + m_cur_entry = -1; + m_all_dead_entries = 1; +} + +void wxPlaylist::OnRemDead(wxCommandEvent &WXUNUSED(event)) +{ + for (u32 i=0; im_bIsDead) continue; + gf_list_rem(m_entries, i); + i--; + delete ple; + } + m_all_dead_entries = gf_list_count(m_entries) ? 0 : 1; + RefreshList(); +} + + +void wxPlaylist::OnSelUp(wxCommandEvent &WXUNUSED(event)) +{ + s32 i; + if (!m_FileList->GetSelectedItemCount()) return; + long item = -1; + item = m_FileList->GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); + if (item <= 0) return; + + item = -1; + for (;;) { + item = m_FileList->GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); + if (item == -1) break; + PLEntry *ple = (PLEntry *) m_FileList->GetItemData(item); + i = gf_list_del_item(m_entries, ple); + assert(i>=1); + gf_list_insert(m_entries, ple, i-1); + ple->m_bIsSelected = 1; + } + RefreshList(); +} + +void wxPlaylist::OnSelDown(wxCommandEvent &WXUNUSED(event)) +{ + s32 i; + + if (!m_FileList->GetSelectedItemCount()) return; + long item = -1; + for (;;) { + item = m_FileList->GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); + if (item == -1) break; + } + if ((u32) item + 1 == gf_list_count(m_entries)) return; + + item = -1; + for (;;) { + item = m_FileList->GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); + if (item == -1) break; + PLEntry *ple = (PLEntry *) m_FileList->GetItemData(item); + i = gf_list_del_item(m_entries, ple); + assert(i>=1); + gf_list_insert(m_entries, ple, i+1); + ple->m_bIsSelected = 1; + } + RefreshList(); +} + + + +void wxPlaylist::OnSave(wxCommandEvent & WXUNUSED(event)) +{ + Bool save_m3u; + char szPath[GF_MAX_PATH]; + if (!gf_list_count(m_entries)) return; + + wxFileDialog dlg(this, wxT("Select file(s)"), wxT(""), wxT(""), wxT("M3U Playlists|*.m3u|ShoutCast Playlists|*.pls|"), wxSAVE | wxCHANGE_DIR | wxOVERWRITE_PROMPT); + if (dlg.ShowModal() != wxID_OK) return; + + strcpy(szPath, dlg.GetPath().mb_str(wxConvUTF8)); + strlwr(szPath); + save_m3u = (dlg.GetFilterIndex()==0) ? 1 : 0; + if (save_m3u) { + if (!strstr(szPath, ".m3u")) { + strcpy(szPath, dlg.GetPath().mb_str(wxConvUTF8)); + strcat(szPath, ".m3u"); + } else { + strcpy(szPath, dlg.GetPath().mb_str(wxConvUTF8)); + } + } else { + if (!strstr(szPath, ".pls")) { + strcpy(szPath, dlg.GetPath().mb_str(wxConvUTF8)); + strcat(szPath, ".pls"); + } else { + strcpy(szPath, dlg.GetPath().mb_str(wxConvUTF8)); + } + } + Save(szPath, save_m3u); +} + +void wxPlaylist::Save(char *szPath, Bool save_m3u) +{ + FILE *out = fopen(szPath, "wt"); + if (!save_m3u) + fprintf(out, "[playlist]\nNumberOfEntries=%d\n", gf_list_count(m_entries)); + + for (u32 i=0; im_url); + } else { + fprintf(out, "File%d=%s\n", i+1, ple->m_url); + fprintf(out, "Title%d=%s\n", i+1, ple->m_disp_name); + if (ple->m_duration) fprintf(out, "Length%d=%d\n", i+1, ple->m_duration); + else fprintf(out, "Length%d=-1\n", i+1); + } + } + if (!save_m3u) fprintf(out, "Version=2\n"); + + fprintf(out, "\n"); + fclose(out); +} + +void wxPlaylist::OnOpen(wxCommandEvent & WXUNUSED(event)) +{ + wxFileDialog dlg(this, wxT("Select file(s)"), wxT(""), wxT(""), wxT("M3U & PLS Playlists|*.m3u;*.pls|M3U Playlists|*.m3u|ShoutCast Playlists|*.pls|"), wxOPEN | wxCHANGE_DIR/* | wxHIDE_READONLY*/); + if (dlg.ShowModal() != wxID_OK) return; + + Clear(); + OpenPlaylist(dlg.GetPath()); + m_cur_entry = 0; + Play(); +} + +void wxPlaylist::OpenPlaylist(wxString filename) +{ + FILE *pl; + PLEntry *ple; + Bool load_m3u, go; + char szLine[GF_MAX_PATH]; + pl = fopen(filename.mb_str(wxConvUTF8) , "rt"); + if (!pl) return; + + ple = NULL; + load_m3u = 1; + while (!feof(pl)) { + fgets(szLine, GF_MAX_PATH, pl); + go = 1; + while (go) { + switch (szLine[strlen(szLine)-1]) { + case '\n': + case '\r': + case ' ': + szLine[strlen(szLine)-1] = 0; + break; + default: + go = 0; + break; + } + } + if (!strlen(szLine)) continue; + if (!stricmp(szLine, "[playlist]")) { + load_m3u = 0; + } else if (load_m3u) { + ple = new PLEntry(wxString(szLine, wxConvUTF8) ); + gf_list_add(m_entries, ple); + } else if (!strnicmp(szLine, "file", 4)) { + char *st = strchr(szLine, '='); + if (!st) ple = NULL; + else { + ple = new PLEntry(wxString(st + 1, wxConvUTF8) ); + gf_list_add(m_entries, ple); + } + } else if (ple && !strnicmp(szLine, "Length", 6)) { + char *st = strchr(szLine, '='); + s32 d = atoi(st + 1); + if (d>0) ple->m_duration = d; + } else if (ple && !strnicmp(szLine, "Title", 5)) { + char *st = strchr(szLine, '='); + gf_free(ple->m_disp_name); + ple->m_disp_name = gf_strdup(st + 6); + } + } + fclose(pl); + m_all_dead_entries = -1; + m_cur_entry = -1; + RefreshList(); +} + +void wxPlaylist::OnRightClick(wxListEvent & event) +{ + if (!m_FileList->GetItemCount()) return; + + wxMenu *popup = new wxMenu(); + + if (m_FileList->GetSelectedItemCount()==1) { + popup->Append(ID_PL_PLAY, wxT("Play")); + popup->AppendSeparator(); + } + popup->Append(ID_PL_SEL_REV, wxT("Inverse Selection")); + if (m_FileList->GetSelectedItemCount()) popup->Append(ID_PL_REM_FILE, wxT("Remove File(s)")); + if (m_FileList->GetItemCount()>1) { + popup->AppendSeparator(); + popup->Append(ID_PL_SORT_TITLE, wxT("Sort By Title")); + popup->Append(ID_PL_SORT_FILE, wxT("Sort By File Name")); + popup->Append(ID_PL_SORT_DUR, wxT("Sort By Duration")); + popup->AppendSeparator(); + popup->Append(ID_PL_REVERSE, wxT("Reverse List")); + popup->Append(ID_PL_RANDOMIZE, wxT("Randomize")); + } + + PopupMenu(popup, event.GetPoint()); + delete popup; +} + +void wxPlaylist::OnReverseSelection(wxCommandEvent &WXUNUSED(event) ) +{ + u32 i; + long item = -1; + for (;;) { + item = m_FileList->GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); + if (item == -1) break; + PLEntry *ple = (PLEntry *) m_FileList->GetItemData(item); + ple->m_bIsSelected = 1; + } + for (i=0; im_bIsSelected = !ple->m_bIsSelected; + } + RefreshList(); +} + +void wxPlaylist::OnReverseList(wxCommandEvent &WXUNUSED(event) ) +{ + u32 count = gf_list_count(m_entries); + u32 hcount = count / 2; + count--; + for (u32 i=0; i1) { + u32 pos = gf_rand() % (gf_list_count(m_entries)-1); + PLEntry *ple = (PLEntry *)gf_list_get(m_entries, pos); + gf_list_rem(m_entries, pos); + gf_list_add(new_entries, ple); + } + PLEntry *ple = (PLEntry *)gf_list_get(m_entries, 0); + gf_list_rem(m_entries, 0); + gf_list_add(new_entries, ple); + + gf_list_del(m_entries); + m_entries = new_entries; + m_cur_entry = -1; + RefreshList(); +} + +void wxPlaylist::Sort(u32 type) +{ + u32 i, j, smallest; + + for (i=0; im_url, ple2->m_url); + break; + case 1: + test = stricmp(ple1->m_disp_name, ple2->m_disp_name); + break; + case 2: + test = ple1->m_duration - ple2->m_duration; + break; + } + if (test<0) smallest = j; + } + PLEntry *ple = (PLEntry *)gf_list_get(m_entries, smallest); + gf_list_rem(m_entries, smallest); + gf_list_insert(m_entries, ple, i); + } + m_cur_entry = -1; + RefreshList(); +} + +void wxPlaylist::OnSortFile(wxCommandEvent &WXUNUSED(event) ) { Sort(0); } +void wxPlaylist::OnSortTitle(wxCommandEvent &WXUNUSED(event) ) { Sort(1); } +void wxPlaylist::OnSortDuration(wxCommandEvent &WXUNUSED(event) ) { Sort(2); } + +void wxPlaylist::RefreshCurrent() +{ + PLEntry *ple; + if (m_cur_entry<0) return; + ple = (PLEntry *) gf_list_get(m_entries, m_cur_entry); + if (ple && ple->m_bIsPlaying) { + ple->m_bIsPlaying = 0; + UpdateEntry(m_cur_entry); + } +} + +Bool wxPlaylist::HasValidEntries() +{ + u32 nb_dead = 0; + if (m_all_dead_entries==-1) { + for (u32 i=0; im_bIsPlaying = 0; + if (ple->m_bIsDead) nb_dead ++; + } + m_all_dead_entries = (nb_dead==gf_list_count(m_entries)) ? 1 : 0; + } + return !m_all_dead_entries; +} + +void wxPlaylist::Play() +{ + PLEntry *ple; + + if (!HasValidEntries()) return; + + RefreshCurrent(); + + if (m_cur_entry >= (s32)gf_list_count(m_entries)) { + if (!m_pApp->m_loop) return; + m_cur_entry = 0; + } + + ple = (PLEntry *) gf_list_get(m_entries, m_cur_entry); + if (!ple || ple->m_bIsDead) { + m_cur_entry++; + Play(); + } else { + char szPLE[20]; + ple->m_bIsPlaying = 1; + UpdateEntry(m_cur_entry); + sprintf(szPLE, "%d", m_cur_entry); + gf_cfg_set_key(m_pApp->m_user.config, "General", "PLEntry", szPLE); + m_pApp->DoConnect(); + } +} + +void wxPlaylist::OnItemActivate(wxListEvent &WXUNUSED(event) ) +{ + long item = m_FileList->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); + if (item==-1) return; + RefreshCurrent(); + m_cur_entry = item; + Play(); +} + + +void wxPlaylist::OnPlay(wxCommandEvent &WXUNUSED(event)) +{ + long item = m_FileList->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); + if (item==-1) return; + + RefreshCurrent(); + m_cur_entry = item; + Play(); +} + +void wxPlaylist::Truncate() +{ + if (m_cur_entry<0) return; + while ((u32) m_cur_entry+1 < gf_list_count(m_entries)) { + PLEntry *ple = (PLEntry *) gf_list_get(m_entries, m_cur_entry+1); + gf_list_rem(m_entries, m_cur_entry+1); + delete ple; + } + RefreshList(); +} + +void wxPlaylist::QueueURL(wxString filename) +{ + char *ext = (char*)strrchr(filename.mb_str(wxConvUTF8), '.'); + if (ext && (!stricmp(ext, ".m3u") || !stricmp(ext, ".pls")) ) { + OpenPlaylist(filename); + } else { + PLEntry *ple = new PLEntry(filename); + gf_list_add(m_entries, ple); + } +} + +void wxPlaylist::PlayNext() +{ + RefreshCurrent(); + if (1+m_cur_entry < (s32)gf_list_count(m_entries)) { + m_cur_entry++; + Play(); + } +} + +void wxPlaylist::PlayPrev() +{ + RefreshCurrent(); + if (m_cur_entry>0) { + m_cur_entry--; + Play(); + } +} + +void wxPlaylist::SetDead() +{ + PLEntry *ple = (PLEntry *) gf_list_get(m_entries, m_cur_entry); + if (ple) { + ple->m_bIsDead = 1; + UpdateEntry(m_cur_entry); + if (ple->m_bIsPlaying) PlayNext(); + m_all_dead_entries = -1; + } +} +void wxPlaylist::SetDuration(u32 duration) +{ + PLEntry *ple = (PLEntry *) gf_list_get(m_entries, m_cur_entry); + if (ple) { + ple->m_duration = duration; + UpdateEntry(m_cur_entry); + } +} + +wxString wxPlaylist::GetDisplayName() +{ + if (m_cur_entry>=0) { + PLEntry *ple = (PLEntry *) gf_list_get(m_entries, m_cur_entry); + if (ple) return wxString(wxString(ple->m_disp_name, wxConvUTF8) ); + } + return wxT(""); +} + +wxString wxPlaylist::GetURL() +{ + PLEntry *ple = (PLEntry *) gf_list_get(m_entries, m_cur_entry); + if (ple) return wxString(ple->m_url, wxConvUTF8); + return wxT(""); +} + diff --git a/applications/osmo4_wx/Playlist.h b/applications/osmo4_wx/Playlist.h new file mode 100644 index 0000000..0ebb230 --- /dev/null +++ b/applications/osmo4_wx/Playlist.h @@ -0,0 +1,134 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Osmo4 wxWidgets GUI + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + */ + +#ifndef _PLAYLIST_H +#define _PLAYLIST_H + +#include "wx/wxprec.h" + +#ifndef WX_PRECOMP + #include "wx/wx.h" +#endif + +#include "menubtn.h" + +enum +{ + ID_FILE_LIST = 1000, +}; + +class wxOsmo4Frame; + +class PLEntry +{ +public: + PLEntry(wxString url); + ~PLEntry(); + + char *m_url; + char *m_disp_name; + u32 m_duration; + + Bool m_bIsSelected; + Bool m_bIsDead; + Bool m_bIsPlaying; +}; + + +class wxPlaylist : public wxFrame +{ +public: + wxPlaylist(wxWindow *parent); + virtual ~wxPlaylist(); + + void Clear(); + void ClearButPlaying(); + void RefreshList(); + + void Truncate(); + void QueueURL(wxString filename); + void Play(); + void PlayNext(); + void PlayPrev(); + void SetDead(); + void SetDuration(u32 duration); + Bool HasValidEntries(); + void OpenPlaylist(wxString fileName); + + /*for current entry played*/ + wxString GetDisplayName(); + wxString GetURL(); + + s32 m_cur_entry; + GF_List *m_entries; + + wxOsmo4Frame *m_pApp; + +private: + DECLARE_EVENT_TABLE() + + void OnClose(wxCloseEvent &event); + void OnSize(wxSizeEvent &event); + void OnAddFile(wxCommandEvent &event); + void OnAddURL(wxCommandEvent &event); + void OnAddDir(wxCommandEvent &event); + void OnAddDirRec(wxCommandEvent &event); + void OnRemFile(wxCommandEvent &event); + void OnRemAll(wxCommandEvent &event); + void OnRemDead(wxCommandEvent &event); + void OnSelUp(wxCommandEvent &event); + void OnSelDown(wxCommandEvent &event); + void OnSave(wxCommandEvent &event); + void OnOpen(wxCommandEvent &event); + void OnRightClick(wxListEvent & event); + void OnReverseSelection(wxCommandEvent &event); + void OnReverseList(wxCommandEvent &event); + void OnRandomize(wxCommandEvent &event); + void OnSortFile(wxCommandEvent &event); + void OnSortTitle(wxCommandEvent &event); + void OnSortDuration(wxCommandEvent &event); + void OnItemActivate(wxListEvent &event); + void OnPlay(wxCommandEvent &event); + + + void Sort(u32 type); + void UpdateEntry(u32 idx); + void RefreshCurrent(); + void Save(char *szPath, Bool save_m3u); + + wxBitmap *m_pOpen, *m_pSave, *m_pAdd, *m_pRem, *m_pUp, *m_pDown, *m_pSort; + wxMenuButton *m_pAddBut, *m_pRemBut, *m_pSortBut; + wxToolBar *m_pToolBar; + wxListCtrl *m_FileList; + char szCacheDir[GF_MAX_PATH]; + s32 m_all_dead_entries; + + void AddDir(Bool do_recurse); +}; + + + +#endif + diff --git a/applications/osmo4_wx/fileprops.cpp b/applications/osmo4_wx/fileprops.cpp new file mode 100644 index 0000000..4697d55 --- /dev/null +++ b/applications/osmo4_wx/fileprops.cpp @@ -0,0 +1,608 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Osmo4 wxWidgets GUI + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + */ + +#include "fileprops.h" +#include "wxOsmo4.h" +#include "Playlist.h" +#include +#include +#include +#include +/*ISO 639 languages*/ +#include + + +wxFileProps::wxFileProps(wxWindow *parent) + : wxDialog(parent, -1, wxString(_T("File Properties"))) +{ + + m_pApp = (wxOsmo4Frame *)parent; + SetSize(540, 260); + assert(m_pApp->m_pPlayList); + + m_pTreeView = new wxTreeCtrl(this, ID_TREE_VIEW, wxPoint(4, 2), wxSize(200, 180), wxTR_DEFAULT_STYLE | wxSUNKEN_BORDER); + + new wxStaticText(this, 0, _T("Information"), wxPoint(210, 2), wxSize(60, 20)); + m_pViewSel = new wxComboBox(this, ID_VIEW_SEL, _T(""), wxPoint(280, 2), wxSize(120, 24), 0, NULL, wxCB_READONLY); + m_pViewSel->Append(wxT("General")); + m_pViewSel->Append(wxT("Streams")); + m_pViewSel->Append(wxT("Playback")); + m_pViewSel->Append(wxT("Network")); + m_pViewSel->SetSelection(0); + + m_pViewInfo = new wxTextCtrl(this, -1, wxT(""), wxPoint(210, 30), wxSize(320, 200), wxTE_MULTILINE | wxTE_READONLY | wxHSCROLL | wxSUNKEN_BORDER); + +#ifdef WIN32 + m_pViewInfo->SetBackgroundColour(wxColour(wxT("LIGHT GREY"))); +#endif + + m_pViewWI = new wxButton(this, ID_VIEW_WI, wxT("View World Info"), wxPoint(4, 174), wxSize(200, 40)); + m_pViewSG = new wxButton(this, ID_VIEW_SG, wxT("View Scene Graph"), wxPoint(4, 220), wxSize(200, 40)); + + + wxString str = m_pApp->m_pPlayList->GetDisplayName(); + str += wxT(" Properties"); + SetTitle(str); + + m_pTimer = new wxTimer(); + m_pTimer->SetOwner(this, ID_OD_TIMER); + m_pTimer->Start(500, 0); + RewriteODTree(); + +} + +wxFileProps::~wxFileProps() +{ + m_pTimer->Stop(); + delete m_pTimer; +} + + +BEGIN_EVENT_TABLE(wxFileProps, wxDialog) + EVT_TREE_ITEM_ACTIVATED(ID_TREE_VIEW, wxFileProps::OnSetSelection) + EVT_TREE_SEL_CHANGED(ID_TREE_VIEW, wxFileProps::OnSetSelection) + EVT_TREE_ITEM_EXPANDED(ID_TREE_VIEW, wxFileProps::OnSetSelection) + EVT_TREE_ITEM_COLLAPSED(ID_TREE_VIEW, wxFileProps::OnSetSelection) + EVT_TIMER(ID_OD_TIMER, wxFileProps::OnTimer) + EVT_BUTTON(ID_VIEW_SG, wxFileProps::OnViewSG) + EVT_BUTTON(ID_VIEW_WI, wxFileProps::OnViewWorld) + EVT_COMBOBOX(ID_VIEW_SEL, wxFileProps::OnSelectInfo) +END_EVENT_TABLE() + +void wxFileProps::RewriteODTree() +{ + GF_ObjectManager *root_odm = gf_term_get_root_object(m_pApp->m_term); + if (!root_odm) return; + + m_pTreeView->DeleteAllItems(); + ODTreeData *root = new ODTreeData(root_odm); + m_pTreeView->AddRoot(wxT("Root OD"), -1, -1, root); + wxTreeItemId rootId = m_pTreeView->GetRootItem(); + + WriteInlineTree(root); + SetInfo(root_odm); +} + +void wxFileProps::WriteInlineTree(ODTreeData *root) +{ + /*browse all ODs*/ + u32 count = gf_term_get_object_count(m_pApp->m_term, root->m_pODMan); + + for (u32 i=0; im_term, root->m_pODMan, i); + if (!odm) return; + ODTreeData *odd = new ODTreeData(odm); + m_pTreeView->AppendItem(root->GetId(), wxT("Object Descriptor"), -1, -1, odd); + + /*if inline propagate*/ + switch (gf_term_object_subscene_type(m_pApp->m_term, odm)) { + case 1: + m_pTreeView->SetItemText(odd->GetId(), wxT("Root Scene")); + WriteInlineTree(odd); + break; + case 2: + m_pTreeView->SetItemText(odd->GetId(), wxT("Inline Scene")); + WriteInlineTree(odd); + break; + case 3: + m_pTreeView->SetItemText(odd->GetId(), wxT("Extern Proto Lib")); + break; + default: + break; + } + } +} + +void wxFileProps::OnSetSelection(wxTreeEvent& event) +{ + ODTreeData *odd = (ODTreeData *) m_pTreeView->GetItemData(event.GetItem()); + SetInfo(odd->m_pODMan); +} + +void wxFileProps::SetInfo(GF_ObjectManager *odm) +{ + m_current_odm = odm; + + switch (m_pViewSel->GetSelection()) { + case 3: SetNetworkInfo(); break; + case 2: SetDecoderInfo(); break; + case 1: SetStreamsInfo(); break; + default: SetGeneralInfo(); break; + } +} + +void wxFileProps::OnTimer(wxTimerEvent& WXUNUSED(event)) +{ + switch (m_pViewSel->GetSelection()) { + case 2: SetDecoderInfo(); break; + } +} +void wxFileProps::OnSelectInfo(wxCommandEvent & WXUNUSED(event) ) +{ + SetInfo(m_current_odm); +} + +void wxFileProps::SetGeneralInfo() +{ + wxString info; + GF_MediaInfo odi; + u32 h, m, s; + u32 i, j; + + info = wxT(""); + m_pViewInfo->Clear(); + m_pViewInfo->AppendText(info); + + if (!m_current_odm || gf_term_get_object_info(m_pApp->m_term, m_current_odm, &odi) != GF_OK) return; + + if (odi.has_profiles) info += wxT("Initial "); + info += wxString::Format(wxT("Object Descriptor ID %d\n"), odi.od->objectDescriptorID); + if (odi.duration) { + h = (u32) (odi.duration / 3600); + m = (u32) (odi.duration / 60) - h*60; + s = (u32) (odi.duration) - h*3600 - m*60; + info += wxString::Format(wxT("Duration %02d:%02d:%02d\n"), h, m, s); + } else { + info += wxT("Unknown duration\n"); + } + + if (odi.owns_service) { + info += wxT("Service Handler: ") + wxString(odi.service_handler, wxConvUTF8) + wxT("\n"); + info += wxT("Service URL: ") + wxString(odi.service_url, wxConvUTF8) + wxT("\n"); + } + + if (odi.od->URLString) { + info += wxT("Remote OD - URL: ") + wxString(odi.od->URLString, wxConvUTF8) + wxT("\n"); + } + + if (odi.codec_name) { + switch (odi.od_type) { + case GF_STREAM_VISUAL: + info += wxString::Format(wxT("Video Object: Width %d - Height %d\n"), odi.width, odi.height); + info += wxT("Media Codec ") + wxString(odi.codec_name, wxConvUTF8) + wxT("\n"); + break; + case GF_STREAM_AUDIO: + info += wxString::Format(wxT("Audio Object: Sample Rate %d - %d channels\n"), odi.sample_rate, odi.num_channels); + info += wxT("Media Codec ") + wxString(odi.codec_name, wxConvUTF8) + wxT("\n"); + break; + case GF_STREAM_PRIVATE_SCENE: + case GF_STREAM_SCENE: + if (odi.width && odi.height) { + info += wxString::Format(wxT("Scene Description: Width %d - Height %d\n"), odi.width, odi.height); + } else { + info += wxT("Scene Description: No size specified\n"); + } + info += wxT("Scene Codec ") + wxString(odi.codec_name, wxConvUTF8) + wxT("\n"); + break; + case GF_STREAM_TEXT: + if (odi.width && odi.height) { + info += wxString::Format(wxT("Text Object: Width %d - Height %d\n"), odi.width, odi.height); + } else { + info += wxString::Format(wxT("Text Object: No size specified\n")); + } + info += wxT("Text Codec ") + wxString(odi.codec_name, wxConvUTF8) + wxT("\n"); + break; + } + } + if (odi.protection==2) info += wxT("Encrypted Media NOT UNLOCKED"); + else if (odi.protection==1) info += wxT("Encrypted Media"); + + if (!gf_list_count(odi.od->OCIDescriptors)) { + m_pViewInfo->Clear(); + m_pViewInfo->AppendText(info); + return; + } + + info += wxT("\nObject Content Information:\n"); + + /*check OCI (not everything interests us) - FIXME: support for unicode*/ + for (i=0; iOCIDescriptors); i++) { + GF_Descriptor *desc = (GF_Descriptor *) gf_list_get(odi.od->OCIDescriptors, i); + switch (desc->tag) { + case GF_ODF_SEGMENT_TAG: + { + GF_Segment *sd = (GF_Segment *) desc; + info += wxT("\nSegment Descriptor:\nName: ") + wxString((char *) sd->SegmentName, wxConvUTF8); + info += wxString::Format(wxT(" - start time %g sec - duration %g sec\n"), sd->startTime, sd->Duration); + } + break; + case GF_ODF_CC_NAME_TAG: + { + GF_CC_Name *ccn = (GF_CC_Name *)desc; + info += wxT("\nContent Creators:\n"); + for (j=0; jContentCreators); j++) { + GF_ContentCreatorInfo *ci = (GF_ContentCreatorInfo *) gf_list_get(ccn->ContentCreators, j); + if (!ci->isUTF8) continue; + info += wxT("\t") + wxString(ci->contentCreatorName, wxConvUTF8) + wxT("\n"); + } + } + break; + + case GF_ODF_SHORT_TEXT_TAG: + { + GF_ShortTextual *std = (GF_ShortTextual *)desc; + info += wxT("\n") + wxString(std->eventName, wxConvUTF8) + wxT(": ") + wxString(std->eventText, wxConvUTF8) + wxT("\n"); + } + break; + /*todo*/ + case GF_ODF_CC_DATE_TAG: + break; + default: + break; + } + + } + + m_pViewInfo->Clear(); + m_pViewInfo->AppendText(info); +} + +void wxFileProps::SetStreamsInfo() +{ + u32 i, count; + wxString info; + GF_MediaInfo odi; + char code[5]; + + info = wxT(""); + m_pViewInfo->Clear(); + m_pViewInfo->AppendText(info); + + if (!m_current_odm || gf_term_get_object_info(m_pApp->m_term, m_current_odm, &odi) != GF_OK) return; + + if (odi.has_profiles) { + info += wxString::Format(wxT("\tOD Profile@Level %d\n"), odi.OD_pl); + info += wxString::Format(wxT("\tScene Profile@Level %d\n"), odi.scene_pl); + info += wxString::Format(wxT("\tGraphics Profile@Level %d\n"), odi.graphics_pl); + info += wxString::Format(wxT("\tAudio Profile@Level %d\n"), odi.audio_pl); + info += wxString::Format(wxT("\tVisual Profile@Level %d\n"), odi.scene_pl); + if (odi.inline_pl) info += wxT("\tInline Content use same profiles\n"); + info += wxT("\n"); + } + + count = gf_list_count(odi.od->ESDescriptors); + + for (i=0; iESDescriptors, i); + + info += wxString::Format(wxT("Stream ID %d - Clock ID %d\n"), esd->ESID, esd->OCRESID); + if (esd->dependsOnESID) { + info += wxString::Format(wxT("\tDepends on Stream ID %d for decoding\n"), esd->dependsOnESID); + } + switch (esd->decoderConfig->streamType) { + case GF_STREAM_OD: + info += wxString::Format(wxT("\tOD Stream - version %d\n"), esd->decoderConfig->objectTypeIndication); + break; + case GF_STREAM_OCR: + info += wxT("\tObject Clock Reference Stream\n"); + break; + case GF_STREAM_SCENE: + info += wxString::Format(wxT("\tScene Description Stream - version %d\n"), esd->decoderConfig->objectTypeIndication); + break; + case GF_STREAM_PRIVATE_SCENE: + info += wxString::Format(wxT("\tGPAC Private Scene Description Stream\n")); + break; + case GF_STREAM_VISUAL: + info += wxT("\tVisual Stream - media type: "); + switch (esd->decoderConfig->objectTypeIndication) { + case GPAC_OTI_VIDEO_MPEG4_PART2: info += wxT("MPEG-4\n"); break; + case GPAC_OTI_VIDEO_MPEG2_SIMPLE: info += wxT("MPEG-2 Simple Profile\n"); break; + case GPAC_OTI_VIDEO_MPEG2_MAIN: info += wxT("MPEG-2 Main Profile\n"); break; + case GPAC_OTI_VIDEO_MPEG2_SNR: info += wxT("MPEG-2 SNR Profile\n"); break; + case GPAC_OTI_VIDEO_MPEG2_SPATIAL: info += wxT("MPEG-2 Spatial Profile\n"); break; + case GPAC_OTI_VIDEO_MPEG2_HIGH: info += wxT("MPEG-2 High Profile\n"); break; + case GPAC_OTI_VIDEO_MPEG2_422: info += wxT("MPEG-2 422 Profile\n"); break; + case GPAC_OTI_VIDEO_MPEG1: info += wxT("MPEG-1\n"); break; + case GPAC_OTI_IMAGE_JPEG: info += wxT("JPEG\n"); break; + case GPAC_OTI_IMAGE_PNG: info += wxT("PNG\n"); break; + case GPAC_OTI_IMAGE_JPEG_2000: info += wxT("JPEG2000\n"); break; + case 0x80: + memcpy(code, esd->decoderConfig->decoderSpecificInfo->data, 4); + code[4] = 0; + info += wxT("GPAC Intern (") + wxString(code, wxConvUTF8) + wxT(")\n"); + break; + default: + info += wxString::Format(wxT("Private/Unknown Type (0x%x)\n"), esd->decoderConfig->objectTypeIndication); + break; + } + break; + + case GF_STREAM_AUDIO: + info += wxT("\tAudio Stream - media type: "); + switch (esd->decoderConfig->objectTypeIndication) { + case GPAC_OTI_AUDIO_AAC_MPEG4: info += wxT("MPEG-4\n"); break; + case GPAC_OTI_AUDIO_AAC_MPEG2_MP: info += wxT("MPEG-2 AAC Main Profile\n"); break; + case GPAC_OTI_AUDIO_AAC_MPEG2_LCP: info += wxT("MPEG-2 AAC LowComplexity Profile\n"); break; + case GPAC_OTI_AUDIO_AAC_MPEG2_SSRP: info += wxT("MPEG-2 AAC Scalable Sampling Rate Profile\n"); break; + case GPAC_OTI_AUDIO_MPEG2_PART3: info += wxT("MPEG-2 Audio\n"); break; + case GPAC_OTI_AUDIO_MPEG1: info += wxT("MPEG-1 Audio\n"); break; + case 0xA0: info += wxT("EVRC Audio\n"); break; + case 0xA1: info += wxT("SMV Audio\n"); break; + case 0xE1: info += wxT("QCELP Audio\n"); break; + case 0x80: + memcpy(code, esd->decoderConfig->decoderSpecificInfo->data, 4); + code[4] = 0; + info += wxT("GPAC Intern (") + wxString(code, wxConvUTF8) + wxT(")\n"); + break; + default: + info += wxString::Format(wxT("Private/Unknown Type (0x%x)\n"), esd->decoderConfig->objectTypeIndication); + break; + } + break; + case GF_STREAM_MPEG7: + info += wxString::Format(wxT("\tMPEG-7 Stream - version %d\n"), esd->decoderConfig->objectTypeIndication); + break; + case GF_STREAM_IPMP: + info += wxString::Format(wxT("\tIPMP Stream - version %d\n"), esd->decoderConfig->objectTypeIndication); + break; + case GF_STREAM_OCI: + info += wxString::Format(wxT("\tOCI Stream - version %d\n"), esd->decoderConfig->objectTypeIndication); + break; + case GF_STREAM_MPEGJ: + info += wxString::Format(wxT("\tMPEGJ Stream - version %d\n"), esd->decoderConfig->objectTypeIndication); + break; + case GF_STREAM_INTERACT: + info += wxString::Format(wxT("\tUser Interaction Stream - version %d\n"), esd->decoderConfig->objectTypeIndication); + break; + default: + info += wxT("Private/Unknown\n"); + break; + } + + info += wxString::Format(wxT("\tBuffer Size %d\n\tAverage Bitrate %d bps\n\tMaximum Bitrate %d bps\n"), esd->decoderConfig->bufferSizeDB, esd->decoderConfig->avgBitrate, esd->decoderConfig->maxBitrate); + if (esd->slConfig->predefined==SLPredef_SkipSL) { + info += wxString::Format(wxT("\tNot using MPEG-4 Synchronization Layer\n")); + } else { + info += wxString::Format(wxT("\tStream Clock Resolution %d\n"), esd->slConfig->timestampResolution); + } + if (esd->URLString) + info += wxT("\tStream Location: ") + wxString(esd->URLString, wxConvUTF8) + wxT("\n"); + + /*check language*/ + if (esd->langDesc) { + u32 i=0; + char lan[4], *szLang; + lan[0] = esd->langDesc->langCode>>16; + lan[1] = (esd->langDesc->langCode>>8)&0xFF; + lan[2] = (esd->langDesc->langCode)&0xFF; + lan[3] = 0; + + if ((lan[0]=='u') && (lan[1]=='n') && (lan[2]=='d')) szLang = (char*) "Undetermined"; + else { + szLang = lan; + while (GF_ISO639_Lang[i]) { + if (GF_ISO639_Lang[i+2][0] && strstr(GF_ISO639_Lang[i+1], lan)) { + szLang = (char*) GF_ISO639_Lang[i]; + break; + } + i+=3; + } + } + info += wxString::Format(wxT("\tStream Language: %s\n"), szLang); + } + + } + m_pViewInfo->Clear(); + m_pViewInfo->AppendText(info); +} + + +void wxFileProps::SetDecoderInfo() +{ + GF_MediaInfo odi; + wxString info; + u32 h, m, s; + + if (!m_current_odm || gf_term_get_object_info(m_pApp->m_term, m_current_odm, &odi)) { + m_pViewInfo->Clear(); + m_pViewInfo->AppendText(info); + return; + } + + info = wxT("Status: "); + switch (odi.status) { + case 0: + case 1: + case 2: + h = (u32) (odi.current_time / 3600); + m = (u32) (odi.current_time / 60) - h*60; + s = (u32) (odi.current_time) - h*3600 - m*60; + if (odi.status==0) info += wxT("Stopped"); + else if (odi.status==1) info += wxT("Playing"); + else info += wxT("Paused"); + info += wxString::Format(wxT("\nObject Time: %02d:%02d:%02d\n"), h, m, s); + break; + case 3: + info += wxT("Not Setup\n"); + m_pViewInfo->Clear(); + m_pViewInfo->AppendText(info); + return; + default: + info += wxT("Setup Failed\n"); + m_pViewInfo->Clear(); + m_pViewInfo->AppendText(info); + return; + } + /*get clock drift*/ + info += wxString::Format(wxT("Clock drift: %d ms\n"), odi.clock_drift); + /*get buffering*/ + if (odi.buffer>=0) info += wxString::Format(wxT("Buffering Time: %d ms\n"), odi.buffer); + else if (odi.buffer==-1) info += wxT("Not buffering\n"); + else info += wxT("Not Playing\n"); + + /*get DB occupation*/ + if (odi.buffer>=0) info += wxString::Format(wxT("Decoding Buffer: %d Access Units\n"), odi.db_unit_count); + /*get CB occupation*/ + if (odi.cb_max_count) + info += wxString::Format(wxT("Composition Memory: %d/%d Units\n"), odi.cb_unit_count, odi.cb_max_count); + + Float avg_dec_time = 0; + if (odi.nb_dec_frames) { + avg_dec_time = (Float) odi.total_dec_time; + avg_dec_time /= odi.nb_dec_frames; + } + info += wxString::Format(wxT("Average Bitrate %d kbps (%d max)\nAverage Decoding Time %.2f ms (%d max)\nTotal decoded frames %d - %d dropped\n"), + (u32) odi.avg_bitrate/1024, odi.max_bitrate/1024, avg_dec_time, odi.max_dec_time, odi.nb_dec_frames, odi.nb_droped); + + m_pViewInfo->Clear(); + m_pViewInfo->AppendText(info); +} + +void wxFileProps::SetNetworkInfo() +{ + wxString info; + u32 id; + NetStatCommand com; + GF_MediaInfo odi; + u32 d_enum; + GF_Err e; + + info = wxT(""); + m_pViewInfo->Clear(); + m_pViewInfo->AppendText(wxT("")); + + if (!m_current_odm || gf_term_get_object_info(m_pApp->m_term, m_current_odm, &odi) != GF_OK) return; + + if (odi.owns_service) { + const char *url, *path; + u32 done, total, bps; + info = wxT("Current Downloads in service:\n"); + d_enum = 0; + while (gf_term_get_download_info(m_pApp->m_term, m_current_odm, &d_enum, &url, &path, &done, &total, &bps)) { + info += wxString(url, wxConvUTF8); + if (total) { + info += wxString::Format(wxT(": %d / %d bytes (%.2f %%) - %.2f kBps\n"), done, total, (100.0*done)/total, ((Double)bps)/1024); + } else { + info += wxString::Format(wxT(": %.2f kBps\n"), ((Double)bps)/1024); + } + } + if (!d_enum) info = wxT("No Downloads in service\n"); + info += wxT("\n"); + } + + d_enum = 0; + while (gf_term_get_channel_net_info(m_pApp->m_term, m_current_odm, &d_enum, &id, &com, &e)) { + if (e) continue; + if (!com.bw_down && !com.bw_up) continue; + + info += wxString::Format(wxT("Stream ID %d statistics:\n"), id); + if (com.multiplex_port) { + info += wxString::Format(wxT("\tMultiplex Port %d - multiplex ID %d\n"), com.multiplex_port, com.port); + } else { + info += wxString::Format(wxT("\tPort %d\n"), com.port); + } + info += wxString::Format(wxT("\tPacket Loss Percentage: %.4f\n"), com.pck_loss_percentage); + info += wxString::Format(wxT("\tDown Bandwidth: %.3f bps\n"), ((Float)com.bw_down)/1024); + if (com.bw_up) info += wxString::Format(wxT("\tUp Bandwidth: %d bps\n"), com.bw_up); + if (com.ctrl_port) { + if (com.multiplex_port) { + info += wxString::Format(wxT("\tControl Multiplex Port: %d - Control Multiplex ID %d\n"), com.multiplex_port, com.ctrl_port); + } else { + info += wxString::Format(wxT("\tControl Port: %d\n"), com.ctrl_port); + } + info += wxString::Format(wxT("\tControl Down Bandwidth: %d bps\n"), com.ctrl_bw_down); + info += wxString::Format(wxT("\tControl Up Bandwidth: %d bps\n"), com.ctrl_bw_up); + } + info += wxT("\n"); + } + m_pViewInfo->Clear(); + m_pViewInfo->AppendText(info); +} + + +void wxFileProps::OnViewWorld(wxCommandEvent &WXUNUSED(event)) +{ + wxString wit; + const char *str; + GF_List *descs; + descs = gf_list_new(); + str = gf_term_get_world_info(m_pApp->m_term, m_current_odm, descs); + + if (!str) { + wxMessageDialog(this, wxT("No World Info available"), wxT("Sorry!"), wxOK).ShowModal(); + return; + } + + wit = wxT(""); + for (u32 i=0; gf_list_count(descs); i++) { + const char *d = (const char *) gf_list_get(descs, i); + wit += wxString(d, wxConvUTF8); + wit += wxT("\n"); + } + wxMessageDialog(this, wit, wxString(str, wxConvUTF8), wxOK).ShowModal(); + gf_list_del(descs); +} + +void wxFileProps::OnViewSG(wxCommandEvent &WXUNUSED(event)) +{ + const char *sOpt; + Bool dump_xmt; + wxFileName out_file; + char szOutFile[GF_MAX_PATH]; + wxString fname; + + sOpt = gf_cfg_get_key(m_pApp->m_user.config, "General", "CacheDirectory"); + out_file.AssignDir(wxString(sOpt, wxConvUTF8) ); + + sOpt = gf_cfg_get_key(m_pApp->m_user.config, "General", "ViewXMT"); + out_file.SetFullName(wxT("scene_dump")); + if (sOpt && !stricmp(sOpt, "yes")) { + dump_xmt = 1; + } else { + dump_xmt = 0; + } + strcpy(szOutFile, out_file.GetFullName().mb_str(wxConvUTF8)); + + GF_Err e = gf_term_dump_scene(m_pApp->m_term, szOutFile, NULL, dump_xmt, 0, m_current_odm); + if (e) { + wxMessageDialog dlg(this, wxString(gf_error_to_string(e), wxConvUTF8), wxT("Error while dumping"), wxOK); + dlg.ShowModal(); + } else { + wxString cmd = get_pref_browser(m_pApp->m_user.config); + cmd += wxT(" "); + cmd += wxString(szOutFile, wxConvUTF8); + wxExecute(cmd); + } +} diff --git a/applications/osmo4_wx/fileprops.h b/applications/osmo4_wx/fileprops.h new file mode 100644 index 0000000..a1ba51b --- /dev/null +++ b/applications/osmo4_wx/fileprops.h @@ -0,0 +1,83 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Osmo4 wxWidgets GUI + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + */ + +#ifndef _FILEPROPS_H +#define _FILEPROPS_H + +#include "wx/wxprec.h" + +#ifndef WX_PRECOMP + #include "wx/wx.h" +#endif + +#include + +#include + +/*abstract class for all items in the tree*/ +class ODTreeData : public wxTreeItemData +{ +public: + ODTreeData(GF_ObjectManager *odm) : wxTreeItemData(), m_pODMan(odm) {} + GF_ObjectManager *m_pODMan; +}; + + +class wxOsmo4Frame; +class wxFileProps : public wxDialog +{ +public: + wxFileProps(wxWindow *parent); + virtual ~wxFileProps(); + +private: + DECLARE_EVENT_TABLE() + + wxOsmo4Frame *m_pApp; + + wxTreeCtrl *m_pTreeView; + wxTextCtrl *m_pViewInfo; + wxComboBox *m_pViewSel; + wxButton *m_pViewWI, *m_pViewSG; + wxTimer *m_pTimer; + + GF_ObjectManager *m_current_odm; + + void RewriteODTree(); + void SetGeneralInfo(); + void SetStreamsInfo(); + void SetDecoderInfo(); + void SetNetworkInfo(); + void WriteInlineTree(ODTreeData *pRoot); + void OnSetSelection(wxTreeEvent &event); + void OnSelectInfo(wxCommandEvent &event); + void OnTimer(wxTimerEvent &event); + void OnViewWorld(wxCommandEvent &event); + void OnViewSG(wxCommandEvent &event); + void SetInfo(GF_ObjectManager *odm); +}; + +#endif + diff --git a/applications/osmo4_wx/menubtn.cpp b/applications/osmo4_wx/menubtn.cpp new file mode 100644 index 0000000..a13e58c --- /dev/null +++ b/applications/osmo4_wx/menubtn.cpp @@ -0,0 +1,863 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: wxMenuButton +// Purpose: A button with a dropdown wxMenu +// Author: John Labenski +// Modified by: +// Created: 11/05/2002 +// RCS-ID: +// Copyright: (c) John Labenki +// Licence: wxWidgets licence +///////////////////////////////////////////////////////////////////////////// + +#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA) + #pragma implementation "menubtn.h" +#endif + +// For compilers that support precompilation, includes "wx/wx.h". +#include "wx/wxprec.h" + +#ifdef __BORLANDC__ + #pragma hdrstop +#endif + +#ifndef WX_PRECOMP + #include "wx/control.h" + #include "wx/menu.h" + #include "wx/settings.h" + #include "wx/bitmap.h" + #include "wx/pen.h" + #include "wx/dc.h" +#endif // WX_PRECOMP + +#include +#include +#include +#include + +#include "menubtn.h" + + + +// ========================================================================== +// wxCustomButton +// ========================================================================== +IMPLEMENT_DYNAMIC_CLASS( wxCustomButton, wxControl ) + +BEGIN_EVENT_TABLE(wxCustomButton,wxControl) + EVT_MOUSE_EVENTS ( wxCustomButton::OnMouseEvents ) + EVT_PAINT ( wxCustomButton::OnPaint ) + EVT_SIZE ( wxCustomButton::OnSize ) +END_EVENT_TABLE() + +wxCustomButton::~wxCustomButton() +{ + if (HasCapture()) ReleaseMouse(); + if (m_timer) delete m_timer; +} + +void wxCustomButton::Init() +{ + m_focused = FALSE; + m_labelMargin = wxSize(4,4); + m_bitmapMargin = wxSize(2,2); + m_down = 0; + m_timer = NULL; + m_eventType = 0; + m_button_style = wxCUSTBUT_TOGGLE|wxCUSTBUT_BOTTOM; +} + +bool wxCustomButton::Create(wxWindow* parent, wxWindowID id, + const wxString& label, const wxBitmap &bitmap, + const wxPoint& pos, const wxSize& size, + long style, const wxValidator& val, + const wxString& name) +{ + if (!wxControl::Create(parent,id,pos,size,wxNO_BORDER|wxCLIP_CHILDREN,val,name)) + return FALSE; + + wxControl::SetLabel(label); + wxControl::SetBackgroundColour(parent->GetBackgroundColour()); + wxControl::SetForegroundColour(parent->GetForegroundColour()); + wxControl::SetFont(parent->GetFont()); + + if (bitmap.Ok()) m_bmpLabel = bitmap; + + if (!SetButtonStyle(style)) return FALSE; + + wxSize bestSize = DoGetBestSize(); + SetSize(wxSize(size.x<0 ? bestSize.x:size.x, size.y<0 ? bestSize.y:size.y)); +#if (wxMINOR_VERSION<8) + SetBestSize(GetSize()); +#else + SetInitialSize(GetSize()); +#endif + + CalcLayout(TRUE); + return TRUE; +} + +void wxCustomButton::SetValue(bool depressed) +{ + wxCHECK_RET(!(m_button_style & wxCUSTBUT_NOTOGGLE), wxT("can't set button state")); + m_down = depressed ? 1 : 0; + Refresh(FALSE); +} + +bool wxCustomButton::SetButtonStyle(long style) +{ + int n_styles = 0; + if ((style & wxCUSTBUT_LEFT) != 0) n_styles++; + if ((style & wxCUSTBUT_RIGHT) != 0) n_styles++; + if ((style & wxCUSTBUT_TOP) != 0) n_styles++; + if ((style & wxCUSTBUT_BOTTOM) != 0) n_styles++; + wxCHECK_MSG(n_styles < 2, FALSE, wxT("Only one wxCustomButton label position allowed")); + + n_styles = 0; + if ((style & wxCUSTBUT_NOTOGGLE) != 0) n_styles++; + if ((style & wxCUSTBUT_BUTTON) != 0) n_styles++; + if ((style & wxCUSTBUT_TOGGLE) != 0) n_styles++; + if ((style & wxCUSTBUT_BUT_DCLICK_TOG) != 0) n_styles++; + if ((style & wxCUSTBUT_TOG_DCLICK_BUT) != 0) n_styles++; + wxCHECK_MSG(n_styles < 2, FALSE, wxT("Only one wxCustomButton style allowed")); + + m_button_style = style; + + if ((m_button_style & wxCUSTBUT_BUTTON) != 0) + m_down = 0; + + CalcLayout(TRUE); + return TRUE; +} + +void wxCustomButton::SetLabel( const wxString &label ) +{ + wxControl::SetLabel(label); + CalcLayout(TRUE); +} + +// sequence of events in GTK is up, dclick, up. + +void wxCustomButton::OnMouseEvents(wxMouseEvent& event) +{ + if (m_button_style & wxCUSTBUT_NOTOGGLE) return; + + if (event.LeftDown() || event.RightDown()) + { + if (!HasCapture()) + CaptureMouse(); // keep depressed until up + + m_down++; + Redraw(); + } + else if (event.LeftDClick() || event.RightDClick()) + { + m_down++; // GTK eats second down event + Redraw(); + } + else if (event.LeftUp()) + { + if (HasCapture()) + ReleaseMouse(); + + m_eventType = wxEVT_LEFT_UP; + +#if (wxMINOR_VERSION<8) + if (wxRect(wxPoint(0,0), GetSize()).Inside(event.GetPosition())) +#else + if (wxRect(wxPoint(0,0), GetSize()).Contains(event.GetPosition())) +#endif + { + if ((m_button_style & wxCUSTBUT_BUTTON) && (m_down > 0)) + { + m_down = 0; + Redraw(); + SendEvent(); + return; + } + else + { + if (!m_timer) + { + m_timer = new wxTimer(this, m_down+1); + m_timer->Start(200, TRUE); + } + else + { + m_eventType = wxEVT_LEFT_DCLICK; + } + + if ((m_button_style & wxCUSTBUT_TOGGLE) && + (m_button_style & wxCUSTBUT_TOG_DCLICK_BUT)) m_down++; + } + } + + Redraw(); + } + else if (event.RightUp()) + { + if (HasCapture()) + ReleaseMouse(); + + m_eventType = wxEVT_RIGHT_UP; + +#if (wxMINOR_VERSION<8) + if (wxRect(wxPoint(0,0), GetSize()).Inside(event.GetPosition())) +#else + if (wxRect(wxPoint(0,0), GetSize()).Contains(event.GetPosition())) +#endif + { + if ((m_button_style & wxCUSTBUT_BUTTON) && (m_down > 0)) + { + m_down = 0; + Redraw(); + SendEvent(); + return; + } + else + { + m_down++; + + if (!m_timer) + { + m_timer = new wxTimer(this, m_down); + m_timer->Start(250, TRUE); + } + else + { + m_eventType = wxEVT_RIGHT_DCLICK; + } + } + } + + Redraw(); + } + else if (event.Entering()) + { + m_focused = TRUE; + if ((event.LeftIsDown() || event.RightIsDown()) && HasCapture()) + m_down++; + + Redraw(); + } + else if (event.Leaving()) + { + m_focused = FALSE; + if ((event.LeftIsDown() || event.RightIsDown()) && HasCapture()) + m_down--; + + Redraw(); + } +} + + + +void wxCustomButton::SendEvent() +{ + if (((m_button_style & wxCUSTBUT_TOGGLE) && (m_eventType == wxEVT_LEFT_UP)) || + ((m_button_style & wxCUSTBUT_BUT_DCLICK_TOG) && (m_eventType == wxEVT_LEFT_DCLICK)) || + ((m_button_style & wxCUSTBUT_TOG_DCLICK_BUT) && (m_eventType == wxEVT_LEFT_UP))) + { + wxCommandEvent eventOut(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, GetId()); + eventOut.SetInt(m_down%2 ? 1 : 0); + eventOut.SetExtraLong(m_eventType); + eventOut.SetEventObject(this); + GetEventHandler()->ProcessEvent(eventOut); + } + else + { + wxCommandEvent eventOut(wxEVT_COMMAND_BUTTON_CLICKED, GetId()); + eventOut.SetInt(0); + eventOut.SetExtraLong(m_eventType); + eventOut.SetEventObject(this); + GetEventHandler()->ProcessEvent(eventOut); + } +} + +wxBitmap wxCustomButton::CreateBitmapDisabled(const wxBitmap &bitmap) const +{ + wxCHECK_MSG(bitmap.Ok(), wxNullBitmap, wxT("invalid bitmap")); + + unsigned char br = GetBackgroundColour().Red(); + unsigned char bg = GetBackgroundColour().Green(); + unsigned char bb = GetBackgroundColour().Blue(); + + wxImage image = bitmap.ConvertToImage(); + int pos, width = image.GetWidth(), height = image.GetHeight(); + unsigned char *img_data = image.GetData(); + + for (int j=0; j lh ? bh : lh; + if (has_bitmap && has_label) lw -= wxMin(m_labelMargin.x, m_bitmapMargin.x); + return wxSize(lw+bw, h); + } + + int w = bw > lw ? bw : lw; + if (has_bitmap && has_label) lh -= wxMin(m_labelMargin.y, m_bitmapMargin.y); + return wxSize(w, lh+bh); +} + +void wxCustomButton::CalcLayout(bool refresh) +{ + int w, h; + GetSize(&w,&h); + + int bw = 0, bh = 0; + int lw = 0, lh = 0; + + if (m_bmpLabel.Ok()) // assume they're all the same size + { + bw = m_bmpLabel.GetWidth(); + bh = m_bmpLabel.GetHeight(); + } + wxString label = GetLabel(); + if (!label.IsEmpty()) + { + GetTextExtent(label, &lw, &lh); + } + + // Center the label or bitmap if only one or the other + if (!m_bmpLabel.Ok()) + { + m_bitmapPos = wxPoint(0,0); + m_labelPos = wxPoint((w-lw)/2, (h-lh)/2); + } + else if (label.IsEmpty()) + { + m_bitmapPos = wxPoint((w-bw)/2, (h-bh)/2); + m_labelPos = wxPoint(0,0); + } + else if (m_button_style & wxCUSTBUT_LEFT) + { + int mid_margin = wxMax(m_labelMargin.x, m_bitmapMargin.x); + m_labelPos = wxPoint((w - (bw+lw+m_labelMargin.x+m_bitmapMargin.x+mid_margin))/2 + m_labelMargin.x, (h - lh)/2); + m_bitmapPos = wxPoint(m_labelPos.x + lw + mid_margin, (h - bh)/2); + } + else if (m_button_style & wxCUSTBUT_RIGHT) + { + int mid_margin = wxMax(m_labelMargin.x, m_bitmapMargin.x); + m_bitmapPos = wxPoint((w - (bw+lw+m_labelMargin.x+m_bitmapMargin.x+mid_margin))/2 + m_bitmapMargin.x, (h - bh)/2); + m_labelPos = wxPoint(m_bitmapPos.x + bw + mid_margin, (h - lh)/2); + } + else if (m_button_style & wxCUSTBUT_TOP) + { + int mid_margin = wxMax(m_labelMargin.y, m_bitmapMargin.y); + m_labelPos = wxPoint((w - lw)/2, (h - (bh+lh+m_labelMargin.y+m_bitmapMargin.y+mid_margin))/2 + m_labelMargin.y); + m_bitmapPos = wxPoint((w - bw)/2, m_labelPos.y + lh + mid_margin); + } + else // if (m_button_style & wxCUSTBUT_BOTTOM) DEFAULT + { + int mid_margin = wxMax(m_labelMargin.y, m_bitmapMargin.y); + m_bitmapPos = wxPoint((w - bw)/2, (h - (bh+lh+m_labelMargin.y+m_bitmapMargin.y+mid_margin))/2 + m_bitmapMargin.y); + m_labelPos = wxPoint((w - lw)/2, m_bitmapPos.y + bh + mid_margin); + } + + if (refresh) Refresh(FALSE); +} + + +/* XPM */ +static const char *down_arrow_xpm_data[] = { +/* columns rows colors chars-per-pixel */ +"5 3 2 1", +" c None", +"a c Black", +/* pixels */ +"aaaaa", +" aaa ", +" a "}; + +static wxBitmap s_dropdownBitmap; // all buttons share the same bitmap + +enum +{ + IDD_DROPDOWN_BUTTON = 100 +}; + +//----------------------------------------------------------------------------- +// wxMenuButtonEvents +//----------------------------------------------------------------------------- + +DEFINE_LOCAL_EVENT_TYPE(wxEVT_MENUBUTTON_OPEN) + +// ========================================================================== +// MenuDropButton +// ========================================================================== + +class MenuDropButton : public wxCustomButton +{ +public: + MenuDropButton( wxWindow *parent, wxWindowID id, long style) : wxCustomButton() + { + if (!s_dropdownBitmap.Ok()) + s_dropdownBitmap = wxBitmap(down_arrow_xpm_data); + + Create( parent, id, wxEmptyString, s_dropdownBitmap, wxDefaultPosition, + wxSize(wxMENUBUTTON_DROP_WIDTH, wxMENUBUTTON_DROP_HEIGHT), style); + } + + virtual void Paint( wxDC &dc ) + { + wxCustomButton *labelBut = ((wxMenuButton*)GetParent())->GetLabelButton(); + + // pretend that both buttons have focus (for flat style) + if (labelBut) + { + wxPoint p = GetParent()->ScreenToClient(wxGetMousePosition()); + +#if (wxMINOR_VERSION<8) + if (GetRect().Inside(p) || labelBut->GetRect().Inside(p)) +#else + if (GetRect().Contains(p) || labelBut->GetRect().Contains(p)) +#endif + { + m_focused = TRUE; + + if (!labelBut->GetFocused()) + labelBut->SetFocused(TRUE); + } + else + { + m_focused = FALSE; + + if (labelBut->GetFocused()) + labelBut->SetFocused(FALSE); + } + } + + wxCustomButton::Paint(dc); + } +}; + +// ========================================================================== +// MenuLabelButton +// ========================================================================== + +class MenuLabelButton : public wxCustomButton +{ +public: + MenuLabelButton( wxWindow* parent, wxWindowID id, + const wxString &label, + const wxBitmap &bitmap, + long style ) : wxCustomButton() + { + Create(parent, id, label, bitmap, wxDefaultPosition, wxDefaultSize, style); + } + + virtual void Paint( wxDC &dc ) + { + wxCustomButton *dropBut = ((wxMenuButton*)GetParent())->GetDropDownButton(); + + // pretend that both buttons have focus (for flat style) + if (dropBut) + { + wxPoint p = GetParent()->ScreenToClient(wxGetMousePosition()); + +#if (wxMINOR_VERSION<8) + if (GetRect().Inside(p) || dropBut->GetRect().Inside(p)) +#else + if (GetRect().Contains(p) || dropBut->GetRect().Contains(p)) +#endif + { + m_focused = TRUE; + + if (!dropBut->GetFocused()) + dropBut->SetFocused(TRUE); + } + else + { + m_focused = FALSE; + + if (dropBut->GetFocused()) + dropBut->SetFocused(FALSE); + } + } + + wxCustomButton::Paint(dc); + } +}; + +// ========================================================================== +// wxMenuButton +// ========================================================================== + +IMPLEMENT_DYNAMIC_CLASS( wxMenuButton, wxControl ) + +BEGIN_EVENT_TABLE(wxMenuButton,wxControl) + EVT_BUTTON(wxID_ANY, wxMenuButton::OnButton) + +#ifdef __WXMSW__ + EVT_MENU(wxID_ANY, wxMenuButton::OnMenu) +#endif +END_EVENT_TABLE() + +wxMenuButton::~wxMenuButton() +{ + AssignMenu(NULL, TRUE); +} + +void wxMenuButton::Init() +{ + m_labelButton = NULL; + m_dropdownButton = NULL; + m_menu = NULL; + m_menu_static = FALSE; + m_style = 0; +} + +bool wxMenuButton::Create( wxWindow* parent, wxWindowID id, + const wxString &label, + const wxBitmap &bitmap, + const wxPoint& pos, + const wxSize& size, + long style, + const wxValidator& val, + const wxString& name) +{ + m_style = style; + + long flat = style & wxMENUBUT_FLAT; + + wxControl::Create(parent,id,pos,size,wxNO_BORDER|wxCLIP_CHILDREN,val,name); + wxControl::SetLabel(label); + SetBackgroundColour(parent->GetBackgroundColour()); + SetForegroundColour(parent->GetForegroundColour()); + SetFont(parent->GetFont()); + + m_labelButton = new MenuLabelButton(this, id, label, bitmap, wxCUSTBUT_BUTTON|flat); + m_dropdownButton = new MenuDropButton(this, IDD_DROPDOWN_BUTTON, wxCUSTBUT_BUTTON|flat); + + wxSize bestSize = DoGetBestSize(); + SetSize( wxSize(size.x < 0 ? bestSize.x : size.x, + size.y < 0 ? bestSize.y : size.y) ); + +#if (wxMINOR_VERSION<8) + SetBestSize(GetSize()); +#else + SetInitialSize(GetSize()); +#endif + + return TRUE; +} + +#ifdef __WXMSW__ +// FIXME - I think there was a patch to fix this +void wxMenuButton::OnMenu( wxCommandEvent &event ) +{ + event.Skip(); + wxMenuItem *mi = m_menu->FindItem(event.GetId()); + if (mi && (mi->GetKind() == wxITEM_RADIO)) + m_menu->Check(event.GetId(), TRUE); +} +#endif // __WXMSW__ + +void wxMenuButton::OnButton( wxCommandEvent &event) +{ + int win_id = event.GetId(); + + if (win_id == IDD_DROPDOWN_BUTTON) + { + wxNotifyEvent mevent(wxEVT_MENUBUTTON_OPEN, GetId()); + mevent.SetEventObject(this); + if (GetEventHandler()->ProcessEvent(mevent) && !mevent.IsAllowed()) + return; + + if (!m_menu) + return; + + PopupMenu(m_menu, wxPoint(0, GetSize().y)); + + m_labelButton->Refresh(FALSE); + m_dropdownButton->Refresh(FALSE); + } + else if (win_id == m_labelButton->GetId()) + { + + wxCommandEvent cevent(wxEVT_COMMAND_MENU_SELECTED, win_id); + cevent.SetEventObject(this); + cevent.SetId(win_id); + GetParent()->GetEventHandler()->ProcessEvent(cevent); + + if (!m_menu) return; + + const wxMenuItemList &items = m_menu->GetMenuItems(); + int first_radio_id = -1; + int checked_id = -1; + bool check_next = FALSE; + + // find the next available radio item to check + for (wxMenuItemList::Node *node = items.GetFirst(); node; node = node->GetNext()) + { + wxMenuItem *mi = (wxMenuItem*)node->GetData(); + if (mi && (mi->GetKind() == wxITEM_RADIO)) + { + if (first_radio_id == -1) + first_radio_id = mi->GetId(); + + if (check_next) + { + check_next = FALSE; + checked_id = mi->GetId(); + break; + } + else if (mi->IsChecked()) + check_next = TRUE; + } + } + // the last item was checked, go back to the first + if (check_next && (first_radio_id != -1)) + checked_id = first_radio_id; + + if (checked_id != -1) + { + m_menu->Check(checked_id, TRUE); + + wxCommandEvent mevent( wxEVT_COMMAND_MENU_SELECTED, checked_id); + mevent.SetEventObject( m_menu ); + mevent.SetInt(1); + GetEventHandler()->ProcessEvent(mevent); + } + } +} + +int wxMenuButton::GetSelection() const +{ + wxCHECK_MSG(m_menu != NULL, wxNOT_FOUND, wxT("No attached menu in wxMenuButton::GetSelection")); + + const wxMenuItemList &items = m_menu->GetMenuItems(); + + for (wxMenuItemList::Node *node = items.GetFirst(); node; node = node->GetNext()) + { + wxMenuItem *mi = (wxMenuItem*)node->GetData(); + if (mi && (mi->GetKind() == wxITEM_RADIO)) + { + if (mi->IsChecked()) + return mi->GetId(); + } + } + + return wxNOT_FOUND; +} + +void wxMenuButton::AssignMenu(wxMenu *menu, bool static_menu) +{ + if (!m_menu_static && m_menu) + delete m_menu; + + m_menu = menu; + m_menu_static = static_menu; +} + +void wxMenuButton::SetToolTip(const wxString &tip) +{ + wxWindow::SetToolTip(tip); + ((wxWindow*)m_labelButton)->SetToolTip(tip); + ((wxWindow*)m_dropdownButton)->SetToolTip(tip); +} +void wxMenuButton::SetToolTip(wxToolTip *tip) +{ + wxWindow::SetToolTip(tip); + ((wxWindow*)m_labelButton)->SetToolTip(tip); + ((wxWindow*)m_dropdownButton)->SetToolTip(tip); +} + +void wxMenuButton::DoSetSize(int x, int y, int width, int height, int sizeFlags) +{ + wxSize curSize( GetSize() ); + wxSize bestSize( DoGetBestSize() ); + + if (width == -1) + width = curSize.GetWidth(); + if (width < 10) + width = bestSize.GetWidth(); + + if (height == -1) + height = curSize.GetHeight(); + if (height < 5) + height = bestSize.GetHeight(); + + wxWindow::DoSetSize(x, y, width, height, sizeFlags); + + if (m_labelButton) + m_labelButton->SetSize(0, 0, width - wxMENUBUTTON_DROP_WIDTH, height); + if (m_dropdownButton) + m_dropdownButton->SetSize(width-wxMENUBUTTON_DROP_WIDTH, 0, wxMENUBUTTON_DROP_WIDTH, height); +} + +wxSize wxMenuButton::DoGetBestSize() +{ + if (!m_labelButton || !m_dropdownButton) + return wxSize(wxMENUBUTTON_DROP_WIDTH+wxMENUBUTTON_DROP_HEIGHT, wxMENUBUTTON_DROP_HEIGHT); + + wxSize size = m_labelButton->GetBestSize(); + size.x += wxMENUBUTTON_DROP_WIDTH; + return size; +} diff --git a/applications/osmo4_wx/menubtn.h b/applications/osmo4_wx/menubtn.h new file mode 100644 index 0000000..441c7b7 --- /dev/null +++ b/applications/osmo4_wx/menubtn.h @@ -0,0 +1,314 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: wxMenuButton +// Purpose: A button with a dropdown wxMenu +// Author: John Labenski +// Modified by: +// Created: 11/05/2002 +// Copyright: (c) John Labenski +// Licence: wxWidgets licence +///////////////////////////////////////////////////////////////////////////// + +/* + +wxMenuButton is a button that drops down an assigned wxMenu + +Create the button with either a text or bitmap label. + Create a new wxMenu and call AssignMenu and thats it. When you press the + dropdown button the menu appears. When you press the label button the next + wxITEM_RADIO (ie wxMenuItem::GetKind) in the menu is selected round robin. + If there are no radio items then it really just acts like a menubar, though + this is probably not too useful. The events sent in this case are EVT_MENUs + either generated by the menu when you click on it or created when you click + on the label to select the next radio item. +*/ + +#ifndef _WX_MENUBTN_H_ +#define _WX_MENUBTN_H_ + +#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA) + #pragma interface "menubtn.h" +#endif + +class wxMenu; +class wxBitmap; +class wxCustomButton; + +//----------------------------------------------------------------------------- +// wxCustomButton styles +//----------------------------------------------------------------------------- + +enum wxCustomButton_Style +{ + // Position of the label, use only one + wxCUSTBUT_LEFT = 0x0001, + wxCUSTBUT_RIGHT = 0x0002, + wxCUSTBUT_TOP = 0x0004, + wxCUSTBUT_BOTTOM = 0x0008, + // Button style, use only one + wxCUSTBUT_NOTOGGLE = 0x0100, + wxCUSTBUT_BUTTON = 0x0200, + wxCUSTBUT_TOGGLE = 0x0400, + wxCUSTBUT_BUT_DCLICK_TOG = 0x0800, + wxCUSTBUT_TOG_DCLICK_BUT = 0x1000, + // drawing styles + wxCUSTBUT_FLAT = 0x2000 // flat, mouseover raises if not depressed +}; + +//----------------------------------------------------------------------------- +// wxCustomButton +//----------------------------------------------------------------------------- + +class WXDLLEXPORT wxCustomButton : public wxControl +{ +public: + + wxCustomButton() : wxControl() { Init(); } + + // wxToggleButton or wxButton compatible constructor (also wxTextCtrl) + wxCustomButton(wxWindow* parent, wxWindowID id, + const wxString& label, + const wxPoint& pos = wxDefaultPosition, + const wxSize& size = wxDefaultSize, + long style = wxCUSTBUT_TOGGLE, + const wxValidator& val = wxDefaultValidator, + const wxString& name = wxT("wxCustomButton")) + : wxControl() + { + Init(); + Create(parent,id,label,wxNullBitmap,pos,size,style,val,name); + } + + // wxBitmapButton compatible constructor + wxCustomButton(wxWindow *parent, wxWindowID id, + const wxBitmap& bitmap, + const wxPoint& pos = wxDefaultPosition, + const wxSize& size = wxDefaultSize, + long style = wxCUSTBUT_TOGGLE, + const wxValidator& val = wxDefaultValidator, + const wxString& name = wxT("wxCustomButton")) + : wxControl() + { + Init(); + Create(parent,id,wxEmptyString,bitmap,pos,size,style,val,name); + } + + // Native constructor + wxCustomButton(wxWindow *parent, wxWindowID id, + const wxString& label, const wxBitmap& bitmap, + const wxPoint& pos = wxDefaultPosition, + const wxSize& size = wxDefaultSize, + long style = wxCUSTBUT_TOGGLE|wxCUSTBUT_BOTTOM, + const wxValidator& val = wxDefaultValidator, + const wxString& name = wxT("wxCustomButton")) + : wxControl() + { + Init(); + Create(parent,id,label,bitmap,pos,size,style,val,name); + } + + virtual ~wxCustomButton(); + + bool Create(wxWindow* parent, + wxWindowID id, + const wxString& label, + const wxBitmap &bitmap, + const wxPoint& pos = wxDefaultPosition, + const wxSize& size = wxDefaultSize, + long style = 0, + const wxValidator& val = wxDefaultValidator, + const wxString& name = wxT("wxCustomButton")); + + bool GetValue() const { return m_down%2 != 0; } + void SetValue( bool depressed ); + + // Use combinations of wxCustomButton_Style(s) + long GetButtonStyle() const { return m_button_style; } + bool SetButtonStyle( long style ); + + // Set the text label, wxEmptyString for none + void SetLabel( const wxString &label ); + + // set the bitmaps, ONLY this Label bitmap is used for calculating control size + // all bitmaps will be centered accordingly in any case + // call SetSet(GetBestSize()) if you change their size and want the control to resize appropriately + void SetBitmapLabel(const wxBitmap& bitmap); + void SetBitmapSelected(const wxBitmap& sel) { m_bmpSelected = sel; CalcLayout(TRUE); }; + void SetBitmapFocus(const wxBitmap& focus) { m_bmpFocus = focus; CalcLayout(TRUE); }; + void SetBitmapDisabled(const wxBitmap& disabled) { m_bmpDisabled = disabled; CalcLayout(TRUE); }; + // wxBitmapButton compatibility + void SetLabel(const wxBitmap& bitmap) { SetBitmapLabel(bitmap); } + + // retrieve the bitmaps + const wxBitmap& GetBitmapLabel() const { return m_bmpLabel; } + const wxBitmap& GetBitmapSelected() const { return m_bmpSelected; } + const wxBitmap& GetBitmapFocus() const { return m_bmpFocus; } + const wxBitmap& GetBitmapDisabled() const { return m_bmpDisabled; } + + // Creates a "disabled" bitmap by dithering it with the background colour + wxBitmap CreateBitmapDisabled(const wxBitmap &bitmap) const; + + // set/get the margins (in pixels) around the label and bitmap + // if fit = TRUE then resize the button to fit + void SetMargins(const wxSize &margin, bool fit = FALSE); + + // set/get the margins around the text label + // the inter bitmap/label margin is the max of either margin, not the sum + void SetLabelMargin(const wxSize &margin, bool fit = FALSE); + wxSize GetLabelMargin() const { return m_labelMargin; } + // set/get the margins around the bitmap + // the inter bitmap/label margin is the max of either margin, not the sum + void SetBitmapMargin(const wxSize &margin, bool fit = FALSE); + wxSize GetBitmapMargin() const { return m_bitmapMargin; } + + // can be used to activate the focused behavior (see MenuButton) + void SetFocused(bool focused) { m_focused = focused; Refresh(FALSE); } + bool GetFocused() const { return m_focused; } + +protected: + void OnPaint(wxPaintEvent &event); + void Redraw(); + virtual void Paint( wxDC &dc ); + + virtual wxSize DoGetBestSize() const; + + virtual void SendEvent(); + + void OnMouseEvents(wxMouseEvent &event); + + void OnSize( wxSizeEvent &event ); + + virtual void CalcLayout(bool refresh); + + long m_down; // toggle state if m_down%2 then depressed + bool m_focused; // mouse in window + long m_button_style; + + // the bitmaps for various states + wxBitmap m_bmpLabel, + m_bmpSelected, + m_bmpFocus, + m_bmpDisabled; + + // the margins around the label/bitmap + wxSize m_labelMargin, + m_bitmapMargin; + + wxPoint m_bitmapPos, + m_labelPos; + + wxTimer *m_timer; + + wxEventType m_eventType; // store the mouse event type + +private: + void Init(); + DECLARE_DYNAMIC_CLASS(wxCustomButton) + DECLARE_EVENT_TABLE() +}; + +//----------------------------------------------------------------------------- +// wxMenuButton styles +//----------------------------------------------------------------------------- + +#define wxMENUBUTTON_DROP_WIDTH 10 +#define wxMENUBUTTON_DROP_HEIGHT 22 + +enum wxMenuButton_Styles +{ + wxMENUBUT_FLAT = wxCUSTBUT_FLAT +}; + +//----------------------------------------------------------------------------- +// wxMenuButton +//----------------------------------------------------------------------------- + +class wxMenuButton : public wxControl +{ +public: + + wxMenuButton() : wxControl() { Init(); } + + // Use this constructor if you need one compatible with a wxBitmapButton + // setup the button later with AssignMenu + wxMenuButton( wxWindow* parent, wxWindowID id, + const wxBitmap &bitmap, + const wxPoint& pos = wxDefaultPosition, + const wxSize& size = wxDefaultSize, + long style = 0, + const wxValidator& val = wxDefaultValidator, + const wxString& name = wxT("wxMenuButton")) + : wxControl() + { + Init(); + Create(parent,id,wxEmptyString,bitmap,pos,size,style,val,name); + } + + virtual ~wxMenuButton(); + + bool Create( wxWindow* parent, + wxWindowID id, + const wxString &label, + const wxBitmap &bitmap, + const wxPoint& pos = wxDefaultPosition, + const wxSize& size = wxDefaultSize, + long style = wxNO_BORDER, + const wxValidator& val = wxDefaultValidator, + const wxString& name = wxT("wxMenuButton")); + + // Gets the id of the first selected radio item or wxNOT_FOUND (-1) if none + int GetSelection() const; + + // This menu will be displayed when the dropdown button is pressed. + // if static_menu is FALSE it will be deleted when the buttton is destroyed. + void AssignMenu(wxMenu *menu, bool static_menu = FALSE); + + wxMenu *GetMenu() const { return m_menu; } + + // get a pointer to the label button, for turning it into a toggle perhaps + wxCustomButton *GetLabelButton() const { return m_labelButton; } + wxCustomButton *GetDropDownButton() const { return m_dropdownButton; } + + void SetToolTip(const wxString &tip); + void SetToolTip(wxToolTip *tip); + +protected: + void OnButton(wxCommandEvent &event); + + virtual void DoSetSize(int x, int y, int width, int height, + int sizeFlags = wxSIZE_AUTO); + + virtual wxSize DoGetBestSize(); + +// FIXME! - in MSW the radio items don't check themselves +#ifdef __WXMSW__ + void OnMenu( wxCommandEvent &event ); +#endif + + wxCustomButton *m_labelButton; + wxCustomButton *m_dropdownButton; + + wxMenu *m_menu; + bool m_menu_static; + long m_style; + +private: + void Init(); + DECLARE_DYNAMIC_CLASS(wxMenuButton) + DECLARE_EVENT_TABLE() +}; + +//----------------------------------------------------------------------------- +// wxMenuButtonEvents +// +// EVT_MENUBUTTON_OPEN(id, fn) - menu is about to be opened, (dis)(en)able items +// or call Veto() to stop menu from popping up +// this is a wxNotifyEvent +//----------------------------------------------------------------------------- + +BEGIN_DECLARE_EVENT_TYPES() + DECLARE_LOCAL_EVENT_TYPE( wxEVT_MENUBUTTON_OPEN, 0 ) +END_DECLARE_EVENT_TYPES() + +#define EVT_MENUBUTTON_OPEN(id, fn) DECLARE_EVENT_TABLE_ENTRY(wxEVT_MENUBUTTON_OPEN, id, wxID_ANY, (wxObjectEventFunction) (wxEventFunction) (wxCommandEventFunction) (wxNotifyEventFunction) & fn, (wxObject *) NULL ), + +#endif // _WX_MENUBTN_H_ diff --git a/bin/w32_rel/Osmo4.ico b/applications/osmo4_wx/osmo4.ico similarity index 100% rename from bin/w32_rel/Osmo4.ico rename to applications/osmo4_wx/osmo4.ico diff --git a/applications/osmo4_wx/osmo4.xpm b/applications/osmo4_wx/osmo4.xpm new file mode 100644 index 0000000..bec8e6a --- /dev/null +++ b/applications/osmo4_wx/osmo4.xpm @@ -0,0 +1,301 @@ +/* XPM */ +static const char * osmo4[] = { +"32 32 266 2", +" c None", +". c #990909", +"+ c #A10505", +"@ c #AB0404", +"# c #AF0202", +"$ c #B10303", +"% c #AE0202", +"& c #A90505", +"* c #A10606", +"= c #990E0E", +"- c #990D0D", +"; c #A80303", +"> c #BD0000", +", c #D40000", +"' c #DE0000", +") c #E20000", +"! c #E10000", +"~ c #DC0000", +"{ c #CD0000", +"] c #B80101", +"^ c #A40909", +"/ c #A20505", +"( c #BD0101", +"_ c #E00000", +": c #CB0000", +"< c #A80000", +"[ c #7E0101", +"} c #6B0202", +"| c #670202", +"1 c #720202", +"2 c #920000", +"3 c #B70000", +"4 c #D70000", +"5 c #B80303", +"6 c #9D0B0B", +"7 c #980909", +"8 c #B00202", +"9 c #DB0000", +"0 c #B50909", +"a c #8A1313", +"b c #1C0505", +"c c #070505", +"d c #080808", +"e c #0A0A0A", +"f c #090909", +"g c #070707", +"h c #0C0404", +"i c #5B1414", +"j c #A70F0F", +"k c #CC0303", +"l c #CC0000", +"m c #AB0505", +"n c #950D0D", +"o c #B60101", +"p c #DD0000", +"q c #CF0202", +"r c #9C1414", +"s c #141414", +"t c #0C0C0C", +"u c #0F0F0F", +"v c #111111", +"w c #101010", +"x c #0E0E0E", +"y c #B70707", +"z c #DF0000", +"A c #D50000", +"B c #A90606", +"C c #970E0E", +"D c #B30202", +"E c #C50505", +"F c #812121", +"G c #191919", +"H c #161616", +"I c #181818", +"J c #171717", +"K c #151515", +"L c #AF0A0A", +"M c #D70101", +"N c #D60000", +"O c #AD0606", +"P c #AA0303", +"Q c #DA0000", +"R c #C80505", +"S c #8E1E1E", +"T c #1D1D1D", +"U c #1F1F1F", +"V c #202020", +"W c #B00909", +"X c #CE0000", +"Y c #9F0A0A", +"Z c #9B0808", +"` c #D20000", +" . c #D80101", +".. c #8B1B1B", +"+. c #2D2D2D", +"@. c #272727", +"#. c #262626", +"$. c #252525", +"%. c #BB0606", +"&. c #BB0303", +"*. c #AF0303", +"=. c #AF0B0B", +"-. c #303030", +";. c #2B2B2B", +">. c #323232", +",. c #CD0202", +"'. c #A60A0A", +"). c #9E0909", +"!. c #CF0000", +"~. c #CE0404", +"{. c #292929", +"]. c #B10808", +"^. c #A70303", +"/. c #B60A0A", +"(. c #961010", +"_. c #B90000", +":. c #9C1212", +"<. c #1A1A1A", +"[. c #222222", +"}. c #1C1C1C", +"|. c #C20202", +"1. c #A30606", +"2. c #C60000", +"3. c #2A2A2A", +"4. c #3B3B3B", +"5. c #444444", +"6. c #434343", +"7. c #3A3A3A", +"8. c #1E1E1E", +"9. c #131313", +"0. c #BB0A0A", +"a. c #AD0707", +"b. c #9A1515", +"c. c #D30000", +"d. c #353535", +"e. c #484848", +"f. c #5F5F5F", +"g. c #6C6C6C", +"h. c #646464", +"i. c #565656", +"j. c #343434", +"k. c #212121", +"l. c #121212", +"m. c #B30505", +"n. c #B10404", +"o. c #9A1111", +"p. c #424242", +"q. c #555555", +"r. c #6B6B6B", +"s. c #757575", +"t. c #6E6E6E", +"u. c #606060", +"v. c #4E4E4E", +"w. c #3F3F3F", +"x. c #2C2C2C", +"y. c #B10909", +"z. c #B60707", +"A. c #9B1515", +"B. c #D00000", +"C. c #494949", +"D. c #595959", +"E. c #676767", +"F. c #696969", +"G. c #616161", +"H. c #525252", +"I. c #454545", +"J. c #B20707", +"K. c #363636", +"L. c #626262", +"M. c #5B5B5B", +"N. c #505050", +"O. c #282828", +"P. c #B80B0B", +"Q. c #AC0707", +"R. c #AA1111", +"S. c #0D0D0D", +"T. c #585858", +"U. c #545454", +"V. c #4B4B4B", +"W. c #BF0303", +"X. c #9F0B0B", +"Y. c #B90707", +"Z. c #242424", +"`. c #313131", +" + c #3E3E3E", +".+ c #474747", +"++ c #4D4D4D", +"@+ c #4F4F4F", +"#+ c #4C4C4C", +"$+ c #3D3D3D", +"%+ c #D90000", +"&+ c #8E1010", +"*+ c #A00808", +"=+ c #D00303", +"-+ c #3C3C3C", +";+ c #0B0B0B", +">+ c #AE0808", +",+ c #C10101", +"'+ c #BB0202", +")+ c #BA0A0A", +"!+ c #1B1B1B", +"~+ c #2F2F2F", +"{+ c #C10303", +"]+ c #A10A0A", +"^+ c #9F0707", +"/+ c #D80000", +"(+ c #BE0303", +"_+ c #821C1C", +":+ c #C20404", +"<+ c #232323", +"[+ c #A90C0C", +"}+ c #970C0C", +"|+ c #960E0E", +"1+ c #BE0101", +"2+ c #C60505", +"3+ c #131212", +"4+ c #B00B0B", +"5+ c #CE0101", +"6+ c #A80707", +"7+ c #990B0B", +"8+ c #C40404", +"9+ c #AD0909", +"0+ c #D00101", +"a+ c #B00505", +"b+ c #7B1C1C", +"c+ c #960C0C", +"d+ c #BC0202", +"e+ c #BC0808", +"f+ c #962525", +"g+ c #482E2E", +"h+ c #1E1616", +"i+ c #141010", +"j+ c #2F2020", +"k+ c #703636", +"l+ c #A81212", +"m+ c #A90707", +"n+ c #8E1313", +"o+ c #D30606", +"p+ c #B91E1E", +"q+ c #901919", +"r+ c #661010", +"s+ c #540F0F", +"t+ c #4F0D0D", +"u+ c #5A0F0F", +"v+ c #7A1515", +"w+ c #A21E1E", +"x+ c #C51212", +"y+ c #D60101", +"z+ c #980D0D", +"A+ c #930C0C", +"B+ c #B30404", +"C+ c #C60202", +"D+ c #7B1919", +"E+ c #8C1515", +"F+ c #9F0909", +"G+ c #B20303", +"H+ c #C30000", +"I+ c #CA0000", +"J+ c #C90000", +"K+ c #BC0101", +"L+ c #AA0505", +"M+ c #8F1111", +"N+ c #7F1E1E", +"O+ c #732626", +"P+ c #762A2A", +"Q+ c #6E2828", +" ", +" . + @ # $ % & * = ", +" - ; > , ' ) ) ! ) ! ~ { ] ^ ", +" / ( ' _ : < [ } | 1 2 3 4 ) , 5 6 ", +" 7 8 9 ' 0 a b c d e e f g h i j k ) l m ", +" n o p q r s t u v v w t x y z A B ", +" C D ) E F G H I G J K L M N O ", +" P Q R S T U U T V W ' X Y ", +" Z ` ... +.@.#.$. %.! &. ", +" *.) =. -.;.>. ,.4 '. ", +" ).!.~. {. ].) ( ", +" ^.z /. $. A ` (. ", +" _.! :. K K <.V [.}.K K |.z 1. ", +" 2.9 v <.3.4.5.6.7.;.8.9.J 0.) a. ", +" b.!.c. w }.d.e.f.g.h.i.5.j.k.l. m._ n. ", +" o.` !. u J 3.p.q.r.s.t.u.v.w.x.T w y._ z. ", +" A.B.` u k.j.C.D.E.g.F.G.H.I.j.$.9.l. m._ J. ", +" : 4 s v #.K.C.q.u.h.L.M.N.I.K.O.J x P.) Q. ", +" > z R. S.9.#.d.5.v.i.D.T.U.V.p.j.{.G t W._ X. ", +" % ) Y. l.9.Z.`. +.+++N.@+#+I.$+>.@.I t { %+&+ ", +" *+, =+ x w V ;.K.$+6.I.I.p.-+K.;.[.H ;+ >+z ,+ ", +" '+_ )+ x S.!+Z.~+d.7.-+-+7.d.~+#.8.9.;+ {+_ ]+ ", +" ^+/+, e s }.$.;.~+`.`.-.;.#.U J S.w z.' (+_+ ", +" ] ) :+ t S.K T [.@.O.O.@.<+U I v ;+ [+` ~ }+ ", +" |+1+) 2+ e t 9.I }.T 8.}.G K u e 3+ 4+5+' 6+ ", +" 7+l ) 8+ e ;+u 9.K K 9.v S.f w 9+0+) a+b+ ", +" c+d+) N e+f+g+h+;+f e f i+j+k+l+|.~ /+m+ ", +" n+D N ! o+p+q+r+s+t+u+v+w+x+y+! X z+ ", +" A+B+X _ ! ~ /+N %+' ) ~ C+Y D+ ", +" E+F+G+H+I+l J+K+L+M+N+ ", +" O+P+Q+ ", +" "}; diff --git a/applications/osmo4_wx/playlist.xpm b/applications/osmo4_wx/playlist.xpm new file mode 100644 index 0000000..cfad2dc --- /dev/null +++ b/applications/osmo4_wx/playlist.xpm @@ -0,0 +1,158 @@ +/* XPM */ +static const char* pl_open[] = { +"16 16 5 1", +" c #000000", +"! c #808000", +"# c #C0C0C0", +"$ c #FFFF00", +"% c #FFFFFF", +"################", +"################", +"######### ####", +"######## ### # #", +"############# #", +"# ######## #", +" %$% #####", +" $%$%$%$%$ #####", +" %$%$%$%$% #####", +" $%$% #", +" %$% !!!!!!!!! #", +" $% !!!!!!!!! ##", +" % !!!!!!!!! ###", +" !!!!!!!!! ####", +" #####", +"################"}; + +/* XPM */ +static const char* pl_save[] = { +"16 16 3 1", +" c #000000", +"! c #808000", +"# c #C0C0C0", +"################", +"# #", +"# ! ######## # #", +"# ! ######## #", +"# ! ######## ! #", +"# ! ######## ! #", +"# ! ######## ! #", +"# ! ######## ! #", +"# !! !! #", +"# !!!!!!!!!!!! #", +"# !! ! #", +"# !! ## ! #", +"# !! ## ! #", +"# !! ## ! #", +"## #", +"################"}; + +/* XPM */ +static const char* pl_add[] = { +"16 16 2 1", +" c #000000", +"! c #C0C0C0", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!", +"!!!!!! !!!!!!!", +"!!!!!! !!!!!!!", +"!!!!!! !!!!!!!", +"!!!!!! !!!!!!!", +"!! !!!", +"!! !!!", +"!! !!!", +"!!!!!! !!!!!!!", +"!!!!!! !!!!!!!", +"!!!!!! !!!!!!!", +"!!!!!! !!!!!!!", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!"}; + +/* XPM */ +static const char* pl_rem[] = { +"16 16 2 1", +" c #000000", +"! c #C0C0C0", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!", +"!! !!!", +"!! !!!", +"!! !!!", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!"}; + +/* XPM */ +static const char* pl_up[] = { +"16 16 2 1", +" c #000000", +"! c #C0C0C0", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!", +"!!!!!!! !!!!!!!!", +"!!!!!! !!!!!!!", +"!!!!! !!!!!!", +"!!!! !!!!!", +"!!! !!!!", +"!!!!!! !!!!!!!", +"!!!!!! !!!!!!!", +"!!!!!! !!!!!!!", +"!!!!!! !!!!!!!", +"!!!!!! !!!!!!!", +"!!!!!! !!!!!!!", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!"}; + +/* XPM */ +static const char* pl_down[] = { +"16 16 2 1", +" c #000000", +"! c #C0C0C0", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!", +"!!!!!! !!!!!!!", +"!!!!!! !!!!!!!", +"!!!!!! !!!!!!!", +"!!!!!! !!!!!!!", +"!!!!!! !!!!!!!", +"!!!!!! !!!!!!!", +"!!! !!!!", +"!!!! !!!!!", +"!!!!! !!!!!!", +"!!!!!! !!!!!!!", +"!!!!!!! !!!!!!!!", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!"}; + +/* XPM */ +static const char* pl_sort[] = { +"16 16 2 1", +" c #000000", +"! c #C0C0C0", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!", +"!!!! !!!!! !!!!!", +"!!!! !!!! !!!!", +"!!!! !!! !!!", +"!!!! !!!!! !!!!!", +"!!!! !!!!! !!!!!", +"!!!! !!!!! !!!!!", +"!!!! !!!!! !!!!!", +"!! !!! !!!!!", +"!!! !!!! !!!!!", +"!!!! !!!!! !!!!!", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!"}; + diff --git a/applications/osmo4_wx/resource.h b/applications/osmo4_wx/resource.h new file mode 100644 index 0000000..ace6dd4 --- /dev/null +++ b/applications/osmo4_wx/resource.h @@ -0,0 +1,16 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by wxOsmo4.rc +// +#define IDI_OSMO_ICON 101 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 104 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1000 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/applications/osmo4_wx/toolbar.xpm b/applications/osmo4_wx/toolbar.xpm new file mode 100644 index 0000000..887d59e --- /dev/null +++ b/applications/osmo4_wx/toolbar.xpm @@ -0,0 +1,254 @@ +/* XPM */ +static const char* tool_open_file[] = { +"16 16 2 1", +" c #000000", +"! c none", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!", +"!!!!!!! !!!!!!!", +"!!!!!! !!!!!!", +"!!!!! !!!!!", +"!!!! !!!!", +"!!! !!!", +"!! !!", +"!! !!", +"!!!!!!!!!!!!!!!!", +"!! !!", +"!! !!", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!"}; + +/* XPM */ +static const char* tool_prev[] = { +"16 16 3 1", +" c #000000", +"! c none", +"# c #FF0000", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!! !!", +"!!!!!!!!!! !!", +"!!!!!!!! ## !!", +"!!!!!! #### !!", +"!!!! ###### !!", +"!!! ######## !!", +"!!!! ##### !!", +"!!!!!! ### !!", +"!!!!!!!! # !!", +"!!!!!!!!!! !!", +"!!!!!!!!!!!! !!", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!"}; + +/* XPM */ +static const char* tool_next[] = { +"16 16 3 1", +" c #000000", +"! c none", +"# c #FF0000", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!", +"!! !!!!!!!!!!!!", +"!! !!!!!!!!!!", +"!! ## !!!!!!!!", +"!! #### !!!!!!", +"!! ###### !!!!", +"!! ######## !!!", +"!! ###### !!!!", +"!! #### !!!!!!", +"!! ## !!!!!!!!", +"!! !!!!!!!!!!", +"!! !!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!"}; + +/* XPM */ +static const char* tool_play[] = { +"16 16 2 1", +" c #000000", +"! c none", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!", +"!! !!!!!!!!!!!!", +"!! !!!!!!!!!!", +"!! !!!!!!!!", +"!! !!!!!!", +"!! !!!!", +"!! !!", +"!! !!!!", +"!! !!!!!!", +"!! !!!!!!!!", +"!! !!!!!!!!!!", +"!! !!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!"}; + +/* XPM */ +static const char* tool_pause[] = { +"16 16 3 1", +" c #000000", +"! c #808080", +"# c none", +"################", +"################", +"################", +"### !##! ###", +"### !##! ###", +"### !##! ###", +"### !##! ###", +"### !##! ###", +"### !##! ###", +"### !##! ###", +"### !##! ###", +"### !##! ###", +"### !##! ###", +"### !##! ###", +"################", +"################"}; + + +/* XPM */ +static const char* tool_step[] = { +"16 16 2 1", +" c #000000", +"! c none", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!", +"!! !!!!!!!!!!!", +"!! !!!!!!!!!", +"!! !!!!!!!", +"!!!! !!!!!", +"!!!!!! !!!", +"!!!!!!!! !!", +"!!!!!! !!!", +"!!!! !!!!!", +"!! !!!!!!!", +"!! !!!!!!!!!", +"!! !!!!!!!!!!!", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!"}; + + +/* XPM */ +static const char* tool_stop[] = { +"16 16 2 1", +" c #000000", +"! c none", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!", +"!! !!", +"!! !!", +"!! !!", +"!! !!", +"!! !!", +"!! !!", +"!! !!", +"!! !!", +"!! !!", +"!! !!", +"!! !!", +"!!!!!!!!!!!!!!!!", +"!!!!!!!!!!!!!!!!"}; + +/* XPM */ +static const char* tool_info[] = { +"16 16 3 1", +" c #000000", +"! c none", +"# c #0000FF", +"!!!!!!!!!!!!!!!!", +"!!!!! !!!!!", +"!!! !!!", +"!! ###### !!", +"! ####!!#### !", +"! ####!!#### !", +" ############ ", +" #####!!##### ", +" ######!!##### ", +" #####!!##### ", +" #####!!##### ", +"! ####!!#### !", +"! #####!!### !", +"!! ###### !!", +"!!! !!!", +"!!!!! !!!!!"}; + + +/* XPM */ +static const char* tool_config[] = { +"16 16 3 1", +" c #000000", +"! c #808080", +"# c none", +"################", +"################", +"## ## ######", +"## ## # ## # ##", +"## ## #", +"## #!# ####### #", +"## # #!# ## ## #", +"## #!# #!# ! # #", +"## # #!# ## ## #", +"## #!# #!##### #", +"## # #!# ## ## #", +"## #!# #!# ! # #", +"## # #!# ## ## #", +"## #", +"################", +"################"}; + +/* XPM */ +static const char* tool_sw_2d[] = { +"16 16 4 1", +" c #FF0000", +". c #C0C0C0", +"+ c #0000FF", +"@ c #000000", +" .............. ", +". ..++..++++.. .", +".. +..+..+..+ ..", +"... ..+..+.. ...", +".... +...+. +...", +"...+. +..+ .+...", +"...+++ .+ ++....", +"....... .......", +"....@@ @@ @.....", +"...@. .@.@ .....", +"...@ ....@. ....", +"... ..@@.@.. ...", +".. @...@.@.@. ..", +". ..@@@@@@@@.. .", +" .............. ", +"................"}; + +/* XPM */ +static const char* tool_sw_3d[] = { +"16 16 4 1", +" c #FFFFFF", +". c #C0C0C0", +"+ c #0000FF", +"@ c #000000", +" ", +"................", +"....++..++++....", +"...+..+..+..+...", +"......+..+..+...", +"....++...+..+...", +"...+..+..+..+...", +"...++++.++++....", +"................", +"....@@@@@@@.....", +"...@...@.@......", +"...@.....@......", +"...@..@@.@......", +"...@...@.@.@....", +"....@@@@@@@@....", +"................"}; + diff --git a/applications/osmo4_wx/wxGPACControl.cpp b/applications/osmo4_wx/wxGPACControl.cpp new file mode 100644 index 0000000..7d16802 --- /dev/null +++ b/applications/osmo4_wx/wxGPACControl.cpp @@ -0,0 +1,1031 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Osmo4 wxWidgets GUI + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + */ + +#include "wxOsmo4.h" +#include +#include +#include +#include +#include +#include + +#include + +#include "wxGPACControl.h" + + +#define NUM_RATES 11 +static const char *BIFSRates[11] = +{ + "5.0", + "7.5", + "10.0", + "12.5", + "15.0", + "24.0", + "25.0", + "30.0", + "50.0", + "60.0", + "100.0" +}; + +void wxGPACControl::SetYUVLabel() +{ + u32 yuv_format = gf_term_get_option(m_pApp->m_term, GF_OPT_YUV_FORMAT); + if (!yuv_format) { + m_yuvtxt->SetLabel(wxT("(No YUV used)")); + } else { + char str[100]; + sprintf(str, "(%s used)", gf_4cc_to_str(yuv_format)); + m_yuvtxt->SetLabel(wxString(str, wxConvUTF8) ); + } +} + +wxGPACControl::wxGPACControl(wxWindow *parent) + : wxDialog(parent, -1, wxString(wxT("GPAC Control Panel"))) +{ + const char *sOpt; + SetSize(320, 240); + u32 i; + wxBoxSizer *bs; + Centre(); + + m_pApp = (wxOsmo4Frame *)parent; + + s_main = new wxBoxSizer(wxVERTICAL); + + s_header = new wxBoxSizer(wxHORIZONTAL); + //s_header->Add(new wxStaticText(this, 0, wxT("Category"), wxDefaultPosition, wxSize(60, 20)), wxALIGN_CENTER); + m_select = new wxComboBox(this, ID_SELECT, wxT(""), wxDefaultPosition, wxSize(120, 30), 0, NULL, wxCB_READONLY); + s_header->Add(m_select, 2, wxALIGN_CENTER | wxADJUST_MINSIZE); + s_header->Add( new wxButton(this, ID_APPLY, wxT("Apply"), wxDefaultPosition, +#ifdef WIN32 + wxSize(40, 20) +#else + wxSize(40, 30) +#endif + ), + 1, wxALIGN_TOP|wxALIGN_RIGHT|wxADJUST_MINSIZE); + s_main->Add(s_header, 0, wxEXPAND, 0); + + /*general section*/ + s_general = new wxBoxSizer(wxVERTICAL); + m_loop = new wxCheckBox(this, 0, wxT("Loop at End"), wxPoint(10, 40), wxSize(140, 20)); + s_general->Add(m_loop); + m_lookforsubs = new wxCheckBox(this, 0, wxT("Look for Subtitles"), wxPoint(180, 40), wxSize(140, 20)); + s_general->Add(m_lookforsubs); + m_noconsole = new wxCheckBox(this, 0, wxT("Disable console messages"), wxPoint(10, 80), wxSize(180, 20)); + s_general->Add(m_noconsole); + m_viewxmt = new wxCheckBox(this, 0, wxT("View graph in XMT-A format"), wxPoint(10, 120), wxSize(180, 20)); + s_general->Add(m_viewxmt); + s_main->Add(s_general, 0, wxEXPAND, 0); + + /*MPEG-4 systems*/ + s_mpeg4 = new wxBoxSizer(wxVERTICAL); + bs = new wxBoxSizer(wxHORIZONTAL); + bs->Add(new wxStaticText(this, 0, wxT("Prefered Stream Language")), wxALIGN_CENTER | wxADJUST_MINSIZE); + m_lang = new wxComboBox(this, 0, wxT(""), wxDefaultPosition, wxDefaultSize, 0, NULL, wxCB_READONLY); + bs->Add(m_lang, wxALIGN_CENTER | wxADJUST_MINSIZE); + s_mpeg4->Add(bs, 0, wxALL|wxEXPAND, 2); + + bs = new wxBoxSizer(wxHORIZONTAL); + bs->Add(new wxStaticText(this, 0, wxT("Decoder Threading Mode")), wxALIGN_CENTER | wxADJUST_MINSIZE); + m_thread = new wxComboBox(this, 0, wxT(""), wxDefaultPosition, wxDefaultSize, 0, NULL, wxCB_READONLY); + bs->Add(m_thread, wxALIGN_CENTER | wxADJUST_MINSIZE); + s_mpeg4->Add(bs, 0, wxALL|wxEXPAND, 2); + m_bifsalwaysdrawn = new wxCheckBox(this, 0, wxT("Always draw late BIFS frames")); + s_mpeg4->Add(m_bifsalwaysdrawn); + m_singletime = new wxCheckBox(this, 0, wxT("Force Single Timeline")); + s_mpeg4->Add(m_singletime); + s_main->Add(s_mpeg4, 0, wxEXPAND, 0); + + /*media decoders*/ + s_mdec = new wxBoxSizer(wxVERTICAL); + bs = new wxBoxSizer(wxHORIZONTAL); + bs->Add(new wxStaticText(this, 0, wxT("Prefered Audio Output")), wxALIGN_CENTER | wxADJUST_MINSIZE); + m_decaudio = new wxComboBox(this, 0, wxT(""), wxDefaultPosition, wxDefaultSize, 0, NULL, wxCB_READONLY); + bs->Add(m_decaudio, wxALIGN_CENTER | wxADJUST_MINSIZE); + s_mdec->Add(bs, 0, wxALL|wxEXPAND, 2); + bs = new wxBoxSizer(wxHORIZONTAL); + bs->Add(new wxStaticText(this, 0, wxT("Prefered Video Output")), wxALIGN_CENTER | wxADJUST_MINSIZE); + m_decvideo = new wxComboBox(this, 0, wxT(""), wxDefaultPosition, wxDefaultSize, 0, NULL, wxCB_READONLY); + bs->Add(m_decvideo, wxALIGN_CENTER | wxADJUST_MINSIZE); + s_mdec->Add(bs, 0, wxALL|wxEXPAND, 2); + s_main->Add(s_mdec, 0, wxEXPAND, 0); + + /*Rendering*/ + s_rend = new wxBoxSizer(wxVERTICAL); + bs = new wxBoxSizer(wxHORIZONTAL); + bs->Add(new wxStaticText(this, 0, wxT("Target Frame Rate")), wxALIGN_CENTER | wxADJUST_MINSIZE); + m_fps = new wxComboBox(this, 0, wxT(""), wxDefaultPosition, wxDefaultSize, 0, NULL, wxCB_READONLY); + bs->Add(m_fps, wxALIGN_CENTER | wxADJUST_MINSIZE); + s_rend->Add(bs, 0, wxALL|wxEXPAND, 2); + bs = new wxBoxSizer(wxHORIZONTAL); + bs->Add(new wxStaticText(this, 0, wxT("Anti-Aliasing")), wxALIGN_CENTER | wxADJUST_MINSIZE); + m_aa = new wxComboBox(this, 0, wxT(""), wxDefaultPosition, wxDefaultSize, 0, NULL, wxCB_READONLY); + bs->Add(m_aa, wxALIGN_CENTER | wxADJUST_MINSIZE); + s_rend->Add(bs, 0, wxALL|wxEXPAND, 2); + bs = new wxBoxSizer(wxHORIZONTAL); + bs->Add(new wxStaticText(this, 0, wxT("Graphics Driver")), wxALIGN_CENTER | wxADJUST_MINSIZE); + m_graph = new wxComboBox(this, 0, wxT(""), wxDefaultPosition, wxDefaultSize, 0, NULL, wxCB_READONLY); + bs->Add(m_graph, wxALIGN_CENTER | wxADJUST_MINSIZE); + s_rend->Add(bs, 0, wxALL|wxEXPAND, 2); + bs = new wxBoxSizer(wxHORIZONTAL); + m_draw_bounds = new wxComboBox(this, 0, wxT(""), wxDefaultPosition, wxDefaultSize, 0, NULL, wxCB_READONLY); + bs->Add(new wxStaticText(this, 0, wxT("Bounds")), wxALIGN_CENTER | wxADJUST_MINSIZE | wxALIGN_RIGHT); + bs->Add(m_draw_bounds, wxALIGN_CENTER | wxADJUST_MINSIZE); + s_rend->Add(bs, 0, wxALL|wxEXPAND, 2); + m_fast = new wxCheckBox(this, 0, wxT("Fast Rendering")); + m_force_size = new wxCheckBox(this, 0, wxT("Force Scene Size")); + bs = new wxBoxSizer(wxHORIZONTAL); + bs->Add(m_fast, wxALIGN_CENTER | wxADJUST_MINSIZE); + bs->Add(m_force_size, wxALIGN_CENTER | wxADJUST_MINSIZE); + s_rend->Add(bs, 0, wxALL|wxEXPAND, 2); + m_use3D = new wxCheckBox(this, 0, wxT("Use 3D Renderer")); + s_rend->Add(m_use3D, 0, wxALL|wxEXPAND, 2); + m_bWas3D = m_use3D->GetValue(); + s_main->Add(s_rend, 0, wxEXPAND, 0); + + /*Render 2D*/ + s_rend2d = new wxBoxSizer(wxVERTICAL); + m_direct = new wxCheckBox(this, 0, wxT("Direct Rendering")); + s_rend2d->Add(m_direct, 0, wxALL|wxEXPAND, 2); + m_scalable = new wxCheckBox(this, 0, wxT("Scalable Zoom")); + s_rend2d->Add(m_scalable, 0, wxALL|wxEXPAND, 2); + bs = new wxBoxSizer(wxHORIZONTAL); + m_noyuv = new wxCheckBox(this, 0, wxT("Disable YUV hardware")); + bs->Add(m_noyuv, wxALIGN_CENTER | wxADJUST_MINSIZE); + m_yuvtxt = new wxStaticText(this, 0, wxT("(No YUV used)"), wxDefaultPosition, wxSize(60, 20), wxALIGN_LEFT); + bs->Add(m_yuvtxt, wxALIGN_CENTER|wxADJUST_MINSIZE); + s_rend2d->Add(bs, 0, wxALL|wxEXPAND, 2); + s_main->Add(s_rend2d, 0, wxEXPAND, 0); + + /*Render 3D*/ + s_rend3d = new wxBoxSizer(wxVERTICAL); + m_raster_outlines = new wxCheckBox(this, 0, wxT("Use OpenGL Raster outlines")); + s_rend3d->Add(m_raster_outlines, 0, wxALL|wxEXPAND, 2); + m_polyaa = new wxCheckBox(this, 0, wxT("Enable polygon anti-aliasing")); + s_rend3d->Add(m_polyaa, 0, wxALL|wxEXPAND, 2); + m_nobackcull = new wxCheckBox(this, 0, wxT("Disable backface culling")); + s_rend3d->Add(m_nobackcull, 0, wxALL|wxEXPAND, 2); + + bs = new wxBoxSizer(wxHORIZONTAL); + bs->Add(new wxStaticText(this, 0, wxT("Wireframe mode")), wxALIGN_CENTER | wxADJUST_MINSIZE); + m_wire = new wxComboBox(this, 0, wxT(""), wxDefaultPosition, wxDefaultSize, 0, NULL, wxCB_READONLY); + bs->Add(m_wire, wxALIGN_CENTER | wxADJUST_MINSIZE); + s_rend3d->Add(bs, 0, wxALL|wxEXPAND, 2); + + bs = new wxBoxSizer(wxHORIZONTAL); + bs->Add(new wxStaticText(this, 0, wxT("Draw Normals")), wxALIGN_CENTER | wxADJUST_MINSIZE); + m_normals = new wxComboBox(this, 0, wxT(""), wxDefaultPosition, wxDefaultSize, 0, NULL, wxCB_READONLY); + bs->Add(m_normals, wxALIGN_CENTER | wxADJUST_MINSIZE); + s_rend3d->Add(bs, 0, wxALL|wxEXPAND, 2); + + m_emulpow2 = new wxCheckBox(this, 0, wxT("Emulate power-of-two textures for video")); + s_rend3d->Add(m_emulpow2, 0, wxALL|wxEXPAND, 2); + m_norectext = new wxCheckBox(this, 0, wxT("Disable rectangular texture extensions")); + s_rend3d->Add(m_norectext, 0, wxALL|wxEXPAND, 2); + m_copypixels = new wxCheckBox(this, 0, wxT("Bitmap node uses direct pixel copy")); + s_rend3d->Add(m_copypixels, 0, wxALL|wxEXPAND, 2); + s_main->Add(s_rend3d, 0, wxEXPAND, 0); + + /*video*/ + s_video = new wxBoxSizer(wxVERTICAL); + bs = new wxBoxSizer(wxHORIZONTAL); + bs->Add(new wxStaticText(this, 0, wxT("Video Driver")), wxALIGN_CENTER | wxADJUST_MINSIZE | wxALIGN_RIGHT); + m_video = new wxComboBox(this, 0, wxT(""), wxDefaultPosition, wxDefaultSize, 0, NULL, wxCB_READONLY); + bs->Add(m_video , wxALIGN_CENTER | wxADJUST_MINSIZE); + s_video->Add(bs, 0, wxALL|wxEXPAND, 2); + m_switchres = new wxCheckBox(this, 0, wxT("Change video resolution in fullscreen")); + s_video->Add(m_switchres, 0, wxALL|wxEXPAND, 2); + m_usehwmem = new wxCheckBox(this, 0, wxT("Use hardware memory in 2D mode")); + s_video->Add(m_usehwmem, 0, wxALL|wxEXPAND, 2); + s_main->Add(s_video, 0, wxEXPAND, 0); + + + /*audio*/ + s_audio = new wxBoxSizer(wxVERTICAL); + bs = new wxBoxSizer(wxHORIZONTAL); + bs->Add(new wxStaticText(this, 0, wxT("Audio Driver")), wxALIGN_CENTER | wxADJUST_MINSIZE); + m_audio = new wxComboBox(this, ID_AUDIO_DRIVER, wxT(""), wxDefaultPosition, wxDefaultSize, 0, NULL, wxCB_READONLY); + bs->Add(m_audio, wxALIGN_CENTER | wxADJUST_MINSIZE); + s_audio->Add(bs, 0, wxALL|wxEXPAND, 2); + m_forcecfg = new wxCheckBox(this, ID_FORCE_AUDIO, wxT("Force Audio Config")); + m_forcecfg->SetValue(1); + s_audio->Add(m_forcecfg, 0, wxALL|wxEXPAND, 2); + + + bs = new wxBoxSizer(wxHORIZONTAL); + bs->Add(new wxStaticText(this, 0, wxT("Number of buffers")), wxALIGN_CENTER|wxADJUST_MINSIZE); + m_nbbuf = new wxSpinCtrl(this, -1, wxT(""), wxDefaultPosition, wxSize(20, 20), wxSP_WRAP | wxSP_ARROW_KEYS, 1, 30, 15); + m_nbbuf->SetValue(8); + bs->Add(m_nbbuf, wxALIGN_CENTER | wxADJUST_MINSIZE); + s_audio->Add(bs, 0, wxALL|wxEXPAND, 2); + + bs = new wxBoxSizer(wxHORIZONTAL); + bs->Add(new wxStaticText(this, 0, wxT("Total length in ms")), wxALIGN_CENTER | wxADJUST_MINSIZE); + m_buflen = new wxSpinCtrl(this, -1, wxT(""), wxDefaultPosition, wxSize(20, 20), wxSP_WRAP | wxSP_ARROW_KEYS, 1, 1000); + m_buflen->SetValue(400); + bs->Add(m_buflen, wxALIGN_CENTER | wxADJUST_MINSIZE|wxLEFT,10); + s_audio->Add(bs, 0, wxALL|wxEXPAND, 2); + + m_noresync = new wxCheckBox(this, -1, wxT("Disable Resynchronization")); + s_audio->Add(m_noresync); + m_nomulitch = new wxCheckBox(this, -1, wxT("Disable Multichannel")); + s_audio->Add(m_nomulitch); +#ifdef WIN32 + m_notifs = new wxCheckBox(this, -1, wxT("Disable DirectSound Notifications")); + s_audio->Add(m_notifs); +#endif + s_main->Add(s_audio, 0, wxEXPAND, 0); + + /*font*/ + s_font = new wxBoxSizer(wxVERTICAL); + bs = new wxBoxSizer(wxHORIZONTAL); + bs->Add(new wxStaticText(this, 0, wxT("Font Engine")), wxALIGN_CENTER | wxADJUST_MINSIZE | wxALIGN_RIGHT); + m_font = new wxComboBox(this, 0, wxT(""), wxDefaultPosition, wxDefaultSize, 0, NULL, wxCB_READONLY); + bs->Add(m_font, wxALIGN_CENTER | wxADJUST_MINSIZE); + s_font->Add(bs, 0, wxALL|wxEXPAND, 2); + bs = new wxBoxSizer(wxHORIZONTAL); + bs->Add(new wxStaticText(this, 0, wxT("System Font Directory")), wxALIGN_CENTER | wxADJUST_MINSIZE | wxALIGN_RIGHT); + m_fontdir = new wxButton(this, ID_FONT_DIR, wxT("..."), wxDefaultPosition, wxDefaultSize); + bs->Add(m_fontdir, wxALIGN_CENTER | wxADJUST_MINSIZE); + s_font->Add(bs, 0, wxALL|wxEXPAND, 2); + bs = new wxBoxSizer(wxHORIZONTAL); + bs->Add(new wxStaticText(this, 0, wxT("Text Texturing Mode")), wxALIGN_CENTER | wxADJUST_MINSIZE | wxALIGN_RIGHT); + m_texturemode = new wxComboBox(this, 0, wxT(""), wxDefaultPosition, wxDefaultSize, 0, NULL, wxCB_READONLY); + bs->Add(m_texturemode, wxALIGN_CENTER | wxADJUST_MINSIZE); + s_font->Add(bs, 0, wxALL|wxEXPAND, 2); + s_main->Add(s_font, 0, wxEXPAND, 0); + + /*download*/ + s_dnld = new wxBoxSizer(wxVERTICAL); + bs = new wxBoxSizer(wxHORIZONTAL); + bs->Add(new wxStaticText(this, 0, wxT("Cache Directory")), wxALIGN_CENTER | wxADJUST_MINSIZE | wxALIGN_RIGHT); + m_cachedir = new wxButton(this, ID_CACHE_DIR, wxT("...")); + bs->Add(m_cachedir, wxALIGN_CENTER | wxADJUST_MINSIZE); + s_dnld->Add(bs, 0, wxALL|wxEXPAND, 2); + m_cleancache = new wxCheckBox(this, -1, wxT("Remove temp files on exit")); + s_dnld->Add(m_cleancache); + m_restartcache = new wxCheckBox(this, -1, wxT("Always redownload incomplete cached files")); + s_dnld->Add(m_restartcache); + bs = new wxBoxSizer(wxHORIZONTAL); + m_progressive = new wxCheckBox(this, ID_PROGRESSIVE, wxT("XML progressive load")); + bs->Add(m_progressive, wxALIGN_CENTER | wxADJUST_MINSIZE); + m_sax_duration = new wxTextCtrl(this, 0, wxT(""), wxPoint(10, 120), wxSize(60, 20)); + bs->Add(m_sax_duration, wxALIGN_CENTER | wxADJUST_MINSIZE); + bs->Add(new wxStaticText(this, 0, wxT("max load slice (ms)")), wxADJUST_MINSIZE | wxALIGN_CENTER); + s_dnld->Add(bs, 0, wxALL|wxEXPAND, 2); + bs = new wxBoxSizer(wxHORIZONTAL); + m_use_proxy = new wxCheckBox(this, ID_USE_PROXY, wxT("Use proxy")); + bs->Add(m_use_proxy, wxALIGN_CENTER | wxADJUST_MINSIZE); + m_proxy_name = new wxTextCtrl(this, 0, wxT(""), wxPoint(10, 120), wxSize(60, 20)); + bs->Add(m_proxy_name, wxALIGN_CENTER | wxADJUST_MINSIZE); + s_dnld->Add(bs, 0, wxALL|wxEXPAND, 2); + s_main->Add(s_dnld, 0, wxEXPAND, 0); + + /*streaming*/ + s_stream = new wxBoxSizer(wxVERTICAL); + bs = new wxBoxSizer(wxHORIZONTAL); + bs->Add(new wxStaticText(this, 0, wxT("Default RTSP port")), wxALIGN_CENTER | wxADJUST_MINSIZE | wxALIGN_RIGHT); + m_port = new wxComboBox(this, ID_RTSP_PORT, wxT(""), wxPoint(160, 40), wxSize(140, 30), 0, NULL, wxCB_READONLY); + bs->Add(m_port, wxALIGN_CENTER | wxADJUST_MINSIZE); + s_stream->Add(bs, 0, wxALL|wxEXPAND, 2); + m_rtsp = new wxCheckBox(this, ID_RTP_OVER_RTSP, wxT("RTP over RTSP"), wxPoint(10, 80), wxSize(140, 20)); + m_reorder = new wxCheckBox(this, -1, wxT("use RTP reordering"), wxPoint(160, 80), wxSize(130, 20)); + bs = new wxBoxSizer(wxHORIZONTAL); + bs->Add(m_rtsp, wxALIGN_CENTER | wxADJUST_MINSIZE); + bs->Add(m_reorder, wxALIGN_CENTER | wxADJUST_MINSIZE); + s_stream->Add(bs, 0, wxALL|wxEXPAND, 2); + bs = new wxBoxSizer(wxHORIZONTAL); + m_timeout = new wxTextCtrl(this, 0, wxT(""), wxPoint(10, 120), wxSize(60, 20)); + bs->Add(new wxStaticText(this, 0, wxT("Control Timeout (ms): ")), wxALIGN_CENTER | wxADJUST_MINSIZE | wxALIGN_RIGHT); + bs->Add(m_timeout, wxALIGN_CENTER | wxADJUST_MINSIZE); + s_stream->Add(bs, 0, wxALL|wxEXPAND, 2); + bs = new wxBoxSizer(wxHORIZONTAL); + m_buffer = new wxTextCtrl(this, 0, wxT(""), wxPoint(10, 150), wxSize(60, 20)); + bs->Add(new wxStaticText(this, 0, wxT("Media Buffering (ms): ")), wxALIGN_CENTER | wxADJUST_MINSIZE | wxALIGN_RIGHT); + bs->Add(m_buffer, wxALIGN_CENTER | wxADJUST_MINSIZE); + s_stream->Add(bs, 0, wxALL|wxEXPAND, 2); + bs = new wxBoxSizer(wxHORIZONTAL); + m_dorebuffer = new wxCheckBox(this, ID_RTSP_REBUFFER, wxT("Rebuffer if below")); + bs->Add(m_dorebuffer, wxALIGN_CENTER | wxADJUST_MINSIZE); + m_rebuffer = new wxTextCtrl(this, 0, wxT(""), wxPoint(200, 180), wxSize(60, 20)); + bs->Add(m_rebuffer, wxALIGN_CENTER | wxADJUST_MINSIZE); + m_rebuffer->Disable(); + s_stream->Add(bs, 0, wxALL|wxEXPAND, 2); + s_main->Add(s_stream, 0, wxEXPAND, 0); + + /*streaming cache*/ + s_rec = new wxBoxSizer(wxVERTICAL); + bs = new wxBoxSizer(wxHORIZONTAL); + bs->Add(new wxStaticText(this, 0, wxT("Record To: ")), wxALIGN_CENTER | wxADJUST_MINSIZE | wxALIGN_RIGHT); + m_recdir = new wxButton(this, ID_RECORD_DIR, wxT("...")); + bs->Add(m_recdir, wxALIGN_CENTER | wxADJUST_MINSIZE); + s_rec->Add(bs, 0, wxALL|wxEXPAND, 2); + m_overwrite = new wxCheckBox(this, -1, wxT("Overwrite existing files")); + s_rec->Add(m_overwrite); + bs = new wxBoxSizer(wxHORIZONTAL); + m_usename = new wxCheckBox(this, ID_USE_FILENAME, wxT("Use filename")); + m_recfile = new wxTextCtrl(this, 0, wxT("")); + bs->Add(m_usename, wxALIGN_CENTER | wxADJUST_MINSIZE); + bs->Add(m_recfile, wxALIGN_CENTER | wxADJUST_MINSIZE); + s_rec->Add(bs, 0, wxALL|wxEXPAND, 2); + s_main->Add(s_rec, 0, wxEXPAND, 0); + + /*load options*/ + GF_Config *cfg = m_pApp->m_user.config; + /*general*/ + sOpt = gf_cfg_get_key(cfg, "General", "Loop"); + m_loop->SetValue((sOpt && !stricmp(sOpt, "yes")) ? 1 : 0); + sOpt = gf_cfg_get_key(cfg, "General", "LookForSubtitles"); + m_lookforsubs->SetValue((sOpt && !stricmp(sOpt, "no")) ? 1 : 0); + sOpt = gf_cfg_get_key(cfg, "General", "ConsoleOff"); + m_noconsole->SetValue((sOpt && !stricmp(sOpt, "yes")) ? 1 : 0); + sOpt = gf_cfg_get_key(cfg, "General", "ViewXMT"); + m_viewxmt->SetValue((sOpt && !stricmp(sOpt, "yes")) ? 1 : 0); + + /*systems config*/ + sOpt = gf_cfg_get_key(cfg, "Systems", "Language3CC"); + if (!sOpt) sOpt = "eng"; + u32 select = 0; + i=0; + while (GF_ISO639_Lang[i]) { + /*only use common languages (having both 2- and 3-char code names)*/ + if (GF_ISO639_Lang[i+2][0]) { + m_lang->Append(wxString(GF_ISO639_Lang[i], wxConvUTF8) ); + if (sOpt && !stricmp(sOpt, GF_ISO639_Lang[i+1])) select = m_lang->GetCount() - 1; + } + i+=3; + } + m_lang->SetSelection(select); + sOpt = gf_cfg_get_key(cfg, "Systems", "ThreadingPolicy"); + select = 0; + m_thread->Append(wxT("Single Thread")); + m_thread->Append(wxT("Mutli Thread")); + if (sOpt && !stricmp(sOpt, "Multi")) select = 1; + m_thread->Append(wxT("Free")); + if (sOpt && !stricmp(sOpt, "Free")) select = 2; + m_thread->SetSelection(select); + sOpt = gf_cfg_get_key(cfg, "Systems", "ForceSingleClock"); + m_singletime->SetValue((sOpt && !stricmp(sOpt, "yes")) ? 1 : 0); + sOpt = gf_cfg_get_key(cfg, "Systems", "AlwaysDrawBIFS"); + m_bifsalwaysdrawn->SetValue((sOpt && !stricmp(sOpt, "yes")) ? 1 : 0); + + + /*audio dec enum*/ + sOpt = gf_cfg_get_key(cfg, "Systems", "DefAudioDec"); + u32 count = gf_modules_get_count(m_pApp->m_user.modules); + GF_BaseDecoder *ifc_d; + select = 0; + s32 to_sel = 0; + for (i=0; im_user.modules, i, GF_MEDIA_DECODER_INTERFACE); + if (!ifc_d) continue; + if (ifc_d->CanHandleStream(ifc_d, GF_STREAM_AUDIO, NULL, 0)) { + if (sOpt && !stricmp(ifc_d->module_name, sOpt)) select = to_sel; + m_decaudio->Append(wxString(ifc_d->module_name, wxConvUTF8) ); + to_sel++; + } + gf_modules_close_interface((GF_BaseInterface *) ifc_d); + } + m_decaudio->SetSelection(select); + + /*video dec enum*/ + sOpt = gf_cfg_get_key(cfg, "Systems", "DefVideoDec"); + select = to_sel = 0; + for (i=0; im_user.modules, i, GF_MEDIA_DECODER_INTERFACE); + if (!ifc_d) continue; + if (ifc_d->CanHandleStream(ifc_d, GF_STREAM_VISUAL, NULL, 0)) { + if (sOpt && !stricmp(ifc_d->module_name, sOpt)) select = to_sel; + m_decvideo->Append(wxString(ifc_d->module_name, wxConvUTF8) ); + to_sel++; + } + gf_modules_close_interface((GF_BaseInterface *) ifc_d); + } + m_decvideo->SetSelection(select); + + /*rendering FIXME*/ + m_bWas3D = 0; + m_use3D->SetValue(m_bWas3D ? 1 : 0); + + sOpt = gf_cfg_get_key(cfg, "Compositor", "ForceSceneSize"); + m_force_size->SetValue( (sOpt && !stricmp(sOpt, "yes")) ? 1 : 0); + + sOpt = gf_cfg_get_key(cfg, "Compositor", "FrameRate"); + if (!sOpt) sOpt = "30.0"; + select = 0; + for (i = 0; iAppend(wxString(BIFSRates[i], wxConvUTF8) ); + if (sOpt && !stricmp(sOpt, BIFSRates[i]) ) select = i; + } + m_fps->SetSelection(select); + + sOpt = gf_cfg_get_key(cfg, "Compositor", "HighSpeed"); + m_fast->SetValue( (sOpt && !stricmp(sOpt, "yes")) ? 1 : 0); + + sOpt = gf_cfg_get_key(cfg, "Compositor", "AntiAlias"); + m_aa->Append(wxT("None")); + m_aa->Append(wxT("Text only")); + m_aa->Append(wxT("Complete")); + select = 2; + if (sOpt && !stricmp(sOpt, "Text")) select = 1; + else if (sOpt && !stricmp(sOpt, "None")) select = 0; + m_aa->SetSelection(select); + + sOpt = gf_cfg_get_key(cfg, "Compositor", "BoundingVolume"); + m_draw_bounds->Append(wxT("None")); + m_draw_bounds->Append(wxT("Box/Rect")); + m_draw_bounds->Append(wxT("AABB Tree")); + select = 0; + if (sOpt && !stricmp(sOpt, "Box")) select = 1; + else if (sOpt && !stricmp(sOpt, "AABB")) select = 2; + m_draw_bounds->SetSelection(select); + + /*render2d*/ + sOpt = gf_cfg_get_key(cfg, "Compositor", "DirectDraw"); + m_direct->SetValue( (sOpt && !stricmp(sOpt, "yes")) ? 1 : 0); + sOpt = gf_cfg_get_key(cfg, "Compositor", "ScalableZoom"); + m_scalable->SetValue( (sOpt && !stricmp(sOpt, "yes")) ? 1 : 0); + sOpt = gf_cfg_get_key(cfg, "Compositor", "DisableYUV"); + m_noyuv->SetValue( (sOpt && !stricmp(sOpt, "yes")) ? 1 : 0); + SetYUVLabel(); + + /*graphics driver enum*/ + sOpt = gf_cfg_get_key(cfg, "Compositor", "Raster2D"); + GF_BaseInterface *ifce; + select = to_sel = 0; + for (i=0; im_user.modules, i, GF_RASTER_2D_INTERFACE); + if (!ifce) continue; + if (sOpt && !stricmp(((GF_BaseInterface *)ifce)->module_name, sOpt)) select = to_sel; + m_graph->Append(wxString(((GF_BaseInterface *)ifce)->module_name, wxConvUTF8) ); + gf_modules_close_interface(ifce); + to_sel++; + } + m_graph->SetSelection(select); + + /*render3d*/ + sOpt = gf_cfg_get_key(cfg, "Compositor", "RasterOutlines"); + m_raster_outlines->SetValue( (sOpt && !stricmp(sOpt, "yes")) ? 1 : 0); + sOpt = gf_cfg_get_key(cfg, "Compositor", "EmulatePOW2"); + m_emulpow2->SetValue((sOpt && !stricmp(sOpt, "yes")) ? 1 : 0); + sOpt = gf_cfg_get_key(cfg, "Compositor", "PolygonAA"); + m_polyaa->SetValue((sOpt && !stricmp(sOpt, "yes")) ? 1 : 0); + sOpt = gf_cfg_get_key(cfg, "Compositor", "BackFaceCulling"); + m_nobackcull->SetValue((sOpt && !stricmp(sOpt, "Off")) ? 1 : 0); + sOpt = gf_cfg_get_key(cfg, "Compositor", "Wireframe"); + sOpt = gf_cfg_get_key(cfg, "Compositor", "BitmapCopyPixels"); + m_copypixels->SetValue((sOpt && !stricmp(sOpt, "yes")) ? 1 : 0); + sOpt = gf_cfg_get_key(cfg, "Compositor", "DisableRectExt"); + m_norectext->SetValue((sOpt && !stricmp(sOpt, "yes")) ? 1 : 0); + m_wire->Append(wxT("No Wireframe")); + m_wire->Append(wxT("Wireframe Only")); + m_wire->Append(wxT("Solid and Wireframe")); + sOpt = gf_cfg_get_key(cfg, "Compositor", "Wireframe"); + if (sOpt && !stricmp(sOpt, "WireOnly")) m_wire->SetSelection(1); + else if (sOpt && !stricmp(sOpt, "WireOnSolid")) m_wire->SetSelection(2); + else m_wire->SetSelection(0); + m_normals->Append(wxT("Never")); + m_normals->Append(wxT("Per Face")); + m_normals->Append(wxT("Per Vertex")); + sOpt = gf_cfg_get_key(cfg, "Compositor", "DrawNormals"); + if (sOpt && !stricmp(sOpt, "PerFace")) m_normals->SetSelection(1); + else if (sOpt && !stricmp(sOpt, "PerVertex")) m_normals->SetSelection(2); + else m_normals->SetSelection(0); + + /*video*/ + sOpt = gf_cfg_get_key(cfg, "Video", "SwitchResolution"); + m_switchres->SetValue( (sOpt && !stricmp(sOpt, "yes")) ? 1 : 0); + sOpt = gf_cfg_get_key(cfg, "Video", "UseHardwareMemory"); + m_usehwmem->SetValue( (sOpt && !stricmp(sOpt, "yes")) ? 1 : 0); + sOpt = gf_cfg_get_key(cfg, "Video", "DriverName"); + select = to_sel = 0; + for (i=0; im_user.modules, i, GF_VIDEO_OUTPUT_INTERFACE); + if (!ifce) continue; + if (sOpt && !stricmp(((GF_BaseInterface *)ifce)->module_name, sOpt)) select = to_sel; + m_video->Append(wxString(((GF_BaseInterface *)ifce)->module_name, wxConvUTF8) ); + gf_modules_close_interface(ifce); + to_sel++; + } + m_video->SetSelection(select); + + /*audio*/ + sOpt = gf_cfg_get_key(cfg, "Audio", "ForceConfig"); + m_forcecfg->SetValue( (sOpt && !stricmp(sOpt, "yes")) ? 1 : 0); + sOpt = gf_cfg_get_key(cfg, "Audio", "NumBuffers"); + m_nbbuf->SetValue( sOpt ? wxString(sOpt, wxConvUTF8) : wxT("2")); + sOpt = gf_cfg_get_key(cfg, "Audio", "TotalDuration"); + m_buflen->SetValue( sOpt ? wxString(sOpt, wxConvUTF8) : wxT("120")); + wxCommandEvent event; + ForceAudio(event); + sOpt = gf_cfg_get_key(cfg, "Audio", "NoResync"); + m_noresync->SetValue( (sOpt && !stricmp(sOpt, "yes")) ? 1 : 0); + sOpt = gf_cfg_get_key(cfg, "Audio", "DisableMultiChannel"); + m_nomulitch->SetValue( (sOpt && !stricmp(sOpt, "yes")) ? 1 : 0); + + /*driver enum*/ + sOpt = gf_cfg_get_key(cfg, "Audio", "DriverName"); + select = to_sel = 0; + for (i=0; im_user.modules, i, GF_AUDIO_OUTPUT_INTERFACE); + if (!ifce) continue; + if (sOpt && !stricmp(((GF_BaseInterface *)ifce)->module_name, sOpt)) select = to_sel; + m_audio->Append(wxString(((GF_BaseInterface *)ifce)->module_name, wxConvUTF8) ); + gf_modules_close_interface(ifce); + to_sel++; + } + m_audio->SetSelection(select); +#ifdef WIN32 + sOpt = gf_cfg_get_key(cfg, "Audio", "DisableNotification"); + m_notifs->SetValue( (sOpt && !stricmp(sOpt, "yes")) ? 1 : 0); + wxCommandEvent audevt; + OnSetAudioDriver(audevt); +#endif + + /*font*/ + sOpt = gf_cfg_get_key(cfg, "FontEngine", "FontReader"); + to_sel = select = 0; + for (i=0; im_user.modules, i, GF_FONT_READER_INTERFACE); + if (!ifce) continue; + if (sOpt && !stricmp(((GF_BaseInterface *)ifce)->module_name, sOpt)) select = to_sel; + m_font->Append(wxString(((GF_BaseInterface *)ifce)->module_name, wxConvUTF8) ); + gf_modules_close_interface(ifce); + to_sel++; + } + m_font->SetSelection(select); + sOpt = gf_cfg_get_key(cfg, "FontEngine", "FontDirectory"); + if (sOpt) m_fontdir->SetLabel(wxString(sOpt, wxConvUTF8) ); + sOpt = gf_cfg_get_key(cfg, "Compositor", "TextureTextMode"); + m_texturemode->Append(wxT("Default")); + m_texturemode->Append(wxT("Never")); + m_texturemode->Append(wxT("Always")); + if (sOpt && !stricmp(sOpt, "Always")) m_texturemode->SetSelection(2); + else if (sOpt && !stricmp(sOpt, "3D")) m_texturemode->SetSelection(1); + else m_texturemode->SetSelection(0); + + /*downloader*/ + sOpt = gf_cfg_get_key(cfg, "General", "CacheDirectory"); + if (sOpt) m_cachedir->SetLabel(wxString(sOpt, wxConvUTF8) ); + sOpt = gf_cfg_get_key(cfg, "Downloader", "CleanCache"); + m_cleancache->SetValue( (sOpt && !stricmp(sOpt, "yes")) ? 1 : 0); + sOpt = gf_cfg_get_key(cfg, "Downloader", "RestartFiles"); + m_restartcache->SetValue( (sOpt && !stricmp(sOpt, "yes")) ? 1 : 0); + sOpt = gf_cfg_get_key(cfg, "SAXLoader", "Progressive"); + m_progressive->SetValue( (sOpt && !stricmp(sOpt, "yes")) ? 1 : 0); + sOpt = gf_cfg_get_key(cfg, "SAXLoader", "MaxDuration"); + m_sax_duration->SetValue(sOpt ? wxString(sOpt, wxConvUTF8) : wxT("30")); + if (! m_progressive->GetValue()) m_sax_duration->Enable(0); + + sOpt = gf_cfg_get_key(cfg, "HTTPProxy", "Enabled"); + m_use_proxy->SetValue( (sOpt && !stricmp(sOpt, "yes")) ? 1 : 0); + char szProxy[GF_MAX_PATH]; + strcpy(szProxy, ""); + sOpt = gf_cfg_get_key(cfg, "HTTPProxy", "Name"); + if (sOpt) { + strcat(szProxy, sOpt); + sOpt = gf_cfg_get_key(cfg, "HTTPProxy", "Port"); + if (sOpt) { + strcat(szProxy, ":"); + strcat(szProxy, sOpt); + } + } else { + m_use_proxy->SetValue(0); + } + m_proxy_name->SetValue( wxString((char *)szProxy, wxConvUTF8) ); + if (! m_use_proxy->GetValue()) m_proxy_name->Enable(0); + + /*streaming*/ + m_port->Append(wxT("554 (RTSP standard)")); + m_port->Append(wxT("7070 (RTSP ext)")); + m_port->Append(wxT("80 (RTSP / HTTP tunnel)")); + m_port->Append(wxT("8080 (RTSP / HTTP tunnel)")); + sOpt = gf_cfg_get_key(cfg, "Streaming", "DefaultPort"); + u32 port = 554; + Bool force_rtsp = 0; + if (sOpt) port = atoi(sOpt); + switch (port) { + case 8080: + m_port->SetSelection(3); + force_rtsp = 1; + break; + case 80: + m_port->SetSelection(2); + force_rtsp = 1; + break; + case 7070: + m_port->SetSelection(1); + break; + default: + m_port->SetSelection(0); + break; + } + + Bool use_rtsp = 0; + sOpt = gf_cfg_get_key(cfg, "Streaming", "RTPoverRTSP"); + if (sOpt && !stricmp(sOpt, "yes")) use_rtsp = 1; + + if (force_rtsp) { + m_rtsp->SetValue(1); + m_rtsp->Enable(0); + m_reorder->SetValue(0); + m_reorder->Enable(0); + } else { + m_rtsp->SetValue(use_rtsp ? 1 : 0); + m_rtsp->Enable(1); + m_reorder->Enable(1); + sOpt = gf_cfg_get_key(cfg, "Streaming", "ReorderSize"); + m_reorder->SetValue( (sOpt && !stricmp(sOpt, "0")) ? 1 : 0); + } + sOpt = gf_cfg_get_key(cfg, "Streaming", "RTSPTimeout"); + m_timeout->SetValue(sOpt ? wxString(sOpt, wxConvUTF8) : wxT("30000")); + sOpt = gf_cfg_get_key(cfg, "Network", "BufferLength"); + m_buffer->SetValue(sOpt ? wxString(sOpt, wxConvUTF8) : wxT("3000")); + sOpt = gf_cfg_get_key(cfg, "Network", "RebufferLength"); + u32 buf_len = 0; + if (sOpt) buf_len = atoi(sOpt); + if (buf_len) { + m_dorebuffer->SetValue(1); + m_rebuffer->SetValue(wxString(sOpt, wxConvUTF8)); + m_rebuffer->Enable(1); + } else { + m_dorebuffer->SetValue(0); + m_rebuffer->SetValue(wxT("0")); + m_rebuffer->Enable(0); + } + + RTPoverRTSP(event); + + sOpt = gf_cfg_get_key(cfg, "StreamingCache", "RecordDirectory"); + if (!sOpt) sOpt = gf_cfg_get_key(cfg, "General", "CacheDirectory"); + if (sOpt) m_recdir->SetLabel(wxString(sOpt, wxConvUTF8)); + sOpt = gf_cfg_get_key(cfg, "StreamingCache", "KeepExistingFiles"); + m_overwrite->SetValue((sOpt && !stricmp(sOpt, "yes")) ? 0 : 1); + + sOpt = gf_cfg_get_key(cfg, "StreamingCache", "BaseFileName"); + if (sOpt) { + m_usename->SetValue(1); + m_recfile->Enable(1); + m_recfile->SetValue(wxString(sOpt, wxConvUTF8)); + } else { + m_usename->SetValue(0); + m_recfile->Enable(0); + m_recfile->SetValue(wxT("uses service URL")); + } + + m_select->Append(wxT("General")); + m_select->Append(wxT("MPEG-4 Systems")); + m_select->Append(wxT("Media Decoders")); + m_select->Append(wxT("Compositor")); + m_select->Append(wxT("Renderer 2D")); + m_select->Append(wxT("Renderer 3D")); + m_select->Append(wxT("Video Output")); + m_select->Append(wxT("Audio Output")); + m_select->Append(wxT("Text Engine")); + m_select->Append(wxT("File Download")); + m_select->Append(wxT("Real-Time Streaming")); + m_select->Append(wxT("Streaming Cache")); + + sOpt = gf_cfg_get_key(cfg, "General", "ConfigPanel"); + m_sel = sOpt ? atoi(sOpt) : 0; + if (m_sel>11) m_sel=11; + m_select->SetSelection(m_sel); + + DoSelect(); +} + +BEGIN_EVENT_TABLE(wxGPACControl, wxDialog) + EVT_BUTTON(ID_APPLY, wxGPACControl::Apply) + EVT_COMBOBOX(ID_SELECT, wxGPACControl::OnSetSelection) + EVT_CHECKBOX(ID_FORCE_AUDIO, wxGPACControl::ForceAudio) + EVT_COMBOBOX(ID_AUDIO_DRIVER, wxGPACControl::OnSetAudioDriver) + EVT_BUTTON(ID_FONT_DIR, wxGPACControl::FontDir) + EVT_BUTTON(ID_CACHE_DIR, wxGPACControl::CacheDir) + EVT_CHECKBOX(ID_PROGRESSIVE, wxGPACControl::OnProgressive) + EVT_CHECKBOX(ID_USE_PROXY, wxGPACControl::OnUseProxy) + EVT_CHECKBOX(ID_RTP_OVER_RTSP, wxGPACControl::RTPoverRTSP) + EVT_CHECKBOX(ID_RTSP_REBUFFER, wxGPACControl::Rebuffer) + EVT_COMBOBOX(ID_RTSP_PORT, wxGPACControl::OnSetRTSPPort) + EVT_CHECKBOX(ID_USE_FILENAME, wxGPACControl::OnUseFileName) + EVT_BUTTON(ID_RECORD_DIR, wxGPACControl::OnRecDir) +END_EVENT_TABLE() + + +wxGPACControl::~wxGPACControl() +{ + char str[20]; + sprintf(str, "%d", m_sel); + gf_cfg_set_key(m_pApp->m_user.config, "General", "ConfigPanel", str); +} + + +void wxGPACControl::DoSelect() +{ + + /*hide everything*/ + s_main->Show(s_general, false); + s_main->Show(s_mpeg4, false); + s_main->Show(s_mdec, false); + s_main->Show(s_rend, false); + s_main->Show(s_rend2d, false); + s_main->Show(s_rend3d, false); + s_main->Show(s_video, false); + s_main->Show(s_audio, false); + s_main->Show(s_font, false); + s_main->Show(s_dnld, false); + s_main->Show(s_stream, false); + s_main->Show(s_rec, false); + switch (m_sel) { + case 0: s_main->Show(s_general, true); break; + case 1: s_main->Show(s_mpeg4, true); break; + case 2: s_main->Show(s_mdec, true); break; + case 3: s_main->Show(s_rend, true); break; + case 4: s_main->Show(s_rend2d, true); break; + case 5: s_main->Show(s_rend3d, true); break; + case 6: s_main->Show(s_video, true); break; + case 7: s_main->Show(s_audio, true); break; + case 8: s_main->Show(s_font, true); break; + case 9: s_main->Show(s_dnld, true); break; + case 10: s_main->Show(s_stream, true); break; + case 11: s_main->Show(s_rec, true); break; + } + SetSizer(s_main); + s_main->Fit(this); + //s_main->Layout(); + return; + +} + +void wxGPACControl::OnSetSelection(wxCommandEvent &WXUNUSED(event)) +{ + m_sel = m_select->GetSelection(); + DoSelect(); +} + +void wxGPACControl::FontDir(wxCommandEvent &WXUNUSED(event)) +{ + wxDirDialog dlg(this); + dlg.SetPath(m_fontdir->GetLabel()); + if (dlg.ShowModal() == wxID_OK) { + m_fontdir->SetLabel(dlg.GetPath()); + } +} +void wxGPACControl::CacheDir(wxCommandEvent &WXUNUSED(event)) +{ + wxDirDialog dlg(this); + dlg.SetPath(m_cachedir->GetLabel()); + if (dlg.ShowModal() == wxID_OK) { + m_cachedir->SetLabel(dlg.GetPath()); + } +} + +void wxGPACControl::OnProgressive(wxCommandEvent &WXUNUSED(event)) +{ + m_sax_duration->Enable(m_progressive->GetValue() ? 1 : 0); +} + +void wxGPACControl::OnUseProxy(wxCommandEvent &WXUNUSED(event)) +{ + m_proxy_name->Enable(m_use_proxy->GetValue() ? 1 : 0); +} + +void wxGPACControl::RTPoverRTSP(wxCommandEvent &WXUNUSED(event)) +{ + m_reorder->Enable(m_rtsp->GetValue() ? 0 : 1); +} + +void wxGPACControl::Rebuffer(wxCommandEvent &WXUNUSED(event)) +{ + if (m_dorebuffer->GetValue()) { + m_rebuffer->Enable(); + } else { + m_rebuffer->Disable(); + } +} + +void wxGPACControl::OnSetRTSPPort(wxCommandEvent &WXUNUSED(event)) +{ + if (m_port->GetSelection() > 1) { + m_rtsp->Enable(0); + m_reorder->Enable(0); + } else { + m_rtsp->Enable(1); + m_reorder->Enable(1); + } +} + +void wxGPACControl::OnRecDir(wxCommandEvent &WXUNUSED(event)) +{ + wxDirDialog dlg(this); + dlg.SetPath(m_recdir->GetLabel()); + if (dlg.ShowModal() == wxID_OK) { + m_recdir->SetLabel(dlg.GetPath()); + } +} + +void wxGPACControl::OnUseFileName(wxCommandEvent &WXUNUSED(event)) +{ + if (m_usename->GetValue()) { + m_recfile->Enable(); + m_recfile->SetValue(wxT("record")); + } else { + m_recfile->Disable(); + m_recfile->SetValue(wxT("uses service URL")); + } +} + +void wxGPACControl::ForceAudio(wxCommandEvent &WXUNUSED(event)) +{ + if (m_forcecfg->GetValue()) { + m_nbbuf->Enable(); + m_buflen->Enable(); + } else { + m_nbbuf->Disable(); + m_buflen->Disable(); + } +} + +void wxGPACControl::OnSetAudioDriver(wxCommandEvent &WXUNUSED(event)) +{ +#ifdef WIN32 + if (strstr(m_audio->GetStringSelection().mb_str(wxConvUTF8), "DirectSound")) { + m_notifs->Enable(1); + } else { + m_notifs->Enable(0); + } +#endif +} + + + +void wxGPACControl::Apply(wxCommandEvent &WXUNUSED(event)) +{ + /*save options*/ + GF_Config *cfg = m_pApp->m_user.config; + + m_pApp->m_loop = m_loop->GetValue() ? 1 : 0; + gf_cfg_set_key(cfg, "General", "Loop", m_loop->GetValue() ? "yes" : "no"); + m_pApp->m_lookforsubs = m_lookforsubs->GetValue() ? 1 : 0; + gf_cfg_set_key(cfg, "General", "LookForSubtitles", m_lookforsubs->GetValue() ? "yes" : "no"); + m_pApp->m_console_off = m_noconsole->GetValue() ? 1 : 0; + gf_cfg_set_key(cfg, "General", "ConsoleOff", m_noconsole->GetValue() ? "yes" : "no"); + gf_cfg_set_key(cfg, "General", "ViewXMT", m_viewxmt->GetValue() ? "yes" : "no"); + + s32 sel = m_lang->GetSelection(); + u32 i=0; + while (GF_ISO639_Lang[i]) { + /*only use common languages (having both 2- and 3-char code names)*/ + if (GF_ISO639_Lang[i+2][0]) { + if (!sel) break; + sel--; + } + i+=3; + } + gf_cfg_set_key(cfg, "Systems", "LanguageName", GF_ISO639_Lang[i]); + gf_cfg_set_key(cfg, "Systems", "Language3CC", GF_ISO639_Lang[i+1]); + gf_cfg_set_key(cfg, "Systems", "Language2CC", GF_ISO639_Lang[i+2]); + + + sel = m_thread->GetSelection(); + gf_cfg_set_key(cfg, "Systems", "ThreadingPolicy", (sel==0) ? "Single" : ( (sel==1) ? "Multi" : "Free")); + gf_cfg_set_key(cfg, "Systems", "ForceSingleClock", m_singletime->GetValue() ? "yes" : "no"); + gf_cfg_set_key(cfg, "Systems", "AlwaysDrawBIFS", m_bifsalwaysdrawn->GetValue() ? "yes" : "no"); + + gf_cfg_set_key(cfg, "Systems", "DefAudioDec", m_decaudio->GetStringSelection().mb_str(wxConvUTF8)); + gf_cfg_set_key(cfg, "Systems", "DefVideoDec", m_decvideo->GetStringSelection().mb_str(wxConvUTF8)); + + + gf_cfg_set_key(cfg, "Compositor", "HighSpeed", m_fast->GetValue() ? "yes" : "no"); + gf_cfg_set_key(cfg, "Compositor", "ForceSceneSize", m_force_size->GetValue() ? "yes" : "no"); + + gf_cfg_set_key(cfg, "Compositor", "FrameRate", BIFSRates[m_fps->GetSelection()]); + sel = m_aa->GetSelection(); + gf_cfg_set_key(cfg, "Compositor", "AntiAlias", (sel==0) ? "None" : ( (sel==1) ? "Text" : "All")); + sel = m_draw_bounds->GetSelection(); + gf_cfg_set_key(cfg, "Compositor", "BoundingVolume", (sel==2) ? "AABB" : (sel==1) ? "Box" : "None"); + + Bool is_3D = m_use3D->GetValue() ? 1 : 0; + if (m_bWas3D != is_3D) { + /*FIXME*/ + } + gf_cfg_set_key(cfg, "Compositor", "Raster2D", m_graph->GetStringSelection().mb_str(wxConvUTF8)); + + gf_cfg_set_key(cfg, "Compositor", "DirectDraw", m_direct->GetValue() ? "yes" : "no"); + gf_cfg_set_key(cfg, "Compositor", "ScalableZoom", m_scalable->GetValue() ? "yes" : "no"); + gf_cfg_set_key(cfg, "Compositor", "DisableYUV", m_noyuv->GetValue() ? "yes" : "no"); + + gf_cfg_set_key(cfg, "Compositor", "RasterOutlines", m_raster_outlines->GetValue() ? "yes" : "no"); + gf_cfg_set_key(cfg, "Compositor", "EmulatePOW2", m_emulpow2->GetValue() ? "yes" : "no"); + gf_cfg_set_key(cfg, "Compositor", "PolygonAA", m_polyaa->GetValue() ? "yes" : "no"); + gf_cfg_set_key(cfg, "Compositor", "DisableRectExt", m_norectext->GetValue() ? "yes" : "no"); + gf_cfg_set_key(cfg, "Compositor", "BitmapCopyPixels", m_copypixels->GetValue() ? "yes" : "no"); + gf_cfg_set_key(cfg, "Compositor", "BackFaceCulling", m_nobackcull->GetValue() ? "Off" : "On"); + + sel = m_wire->GetSelection(); + gf_cfg_set_key(cfg, "Compositor", "Wireframe", (sel==2) ? "WireOnSolid" : ( (sel==1) ? "WireOnly" : "WireNone" ) ); + sel = m_normals->GetSelection(); + gf_cfg_set_key(cfg, "Compositor", "DrawNormals", (sel==2) ? "PerVertex" : ( (sel==1) ? "PerFace" : "Never" ) ); + + gf_cfg_set_key(cfg, "Video", "SwitchResolution", m_switchres->GetValue() ? "yes" : "no"); + gf_cfg_set_key(cfg, "Video", "UseHardwareMemory", m_usehwmem->GetValue() ? "yes" : "no"); + gf_cfg_set_key(cfg, "Video", "DriverName", m_video->GetStringSelection().mb_str(wxConvUTF8)); + + + gf_cfg_set_key(cfg, "Audio", "ForceConfig", m_forcecfg->GetValue() ? "yes" : "no"); + gf_cfg_set_key(cfg, "Audio", "NoResync", m_noresync->GetValue() ? "yes" : "no"); + gf_cfg_set_key(cfg, "Audio", "DisableMultiChannel", m_nomulitch->GetValue() ? "yes" : "no"); + + gf_cfg_set_key(cfg, "Audio", "NumBuffers", wxString::Format(wxT("%d"), m_nbbuf->GetValue()).mb_str(wxConvUTF8) ); + gf_cfg_set_key(cfg, "Audio", "TotalDuration", wxString::Format(wxT("%d"), m_buflen->GetValue()).mb_str(wxConvUTF8) ); + gf_cfg_set_key(cfg, "Audio", "DriverName", m_audio->GetStringSelection().mb_str(wxConvUTF8)); +#ifdef WIN32 + if (m_notifs->IsEnabled()) + gf_cfg_set_key(cfg, "Audio", "DisableNotification", m_notifs->GetValue() ? "yes" : "no"); +#endif + + gf_cfg_set_key(cfg, "FontEngine", "FontReader", m_font->GetStringSelection().mb_str(wxConvUTF8)); + gf_cfg_set_key(cfg, "FontEngine", "FontDirectory", m_fontdir->GetLabel().mb_str(wxConvUTF8)); + switch (m_texturemode->GetSelection()) { + case 2: gf_cfg_set_key(cfg, "Compositor", "TextureTextMode", "Always"); break; + case 1: gf_cfg_set_key(cfg, "Compositor", "TextureTextMode", "Never"); break; + default: gf_cfg_set_key(cfg, "Compositor", "TextureTextMode", "Default"); break; + } + + gf_cfg_set_key(cfg, "Downloader", "CleanCache", m_cleancache->GetValue() ? "yes" : "no"); + gf_cfg_set_key(cfg, "Downloader", "RestartFiles", m_restartcache->GetValue() ? "yes" : "no"); + gf_cfg_set_key(cfg, "SAXLoader", "Progressive", m_progressive->GetValue() ? "yes" : "no"); + gf_cfg_set_key(cfg, "SAXLoader", "MaxDuration", m_sax_duration->GetLabel().mb_str(wxConvUTF8)); + gf_cfg_set_key(cfg, "General", "CacheDirectory", m_cachedir->GetLabel().mb_str(wxConvUTF8)); + + + Bool force_rtsp = 0; + switch (m_port->GetSelection()) { + case 3: + gf_cfg_set_key(cfg, "Streaming", "DefaultPort", "8080"); + force_rtsp = 1; + break; + case 2: + gf_cfg_set_key(cfg, "Streaming", "DefaultPort", "80"); + force_rtsp = 1; + break; + case 1: + gf_cfg_set_key(cfg, "Streaming", "DefaultPort", "7070"); + break; + default: + gf_cfg_set_key(cfg, "Streaming", "DefaultPort", "554"); + break; + } + + if (force_rtsp) { + gf_cfg_set_key(cfg, "Streaming", "RTPoverRTSP", "yes"); + } else { + gf_cfg_set_key(cfg, "Streaming", "RTPoverRTSP", m_rtsp->GetValue() ? "yes" : "no"); + if (!m_rtsp->GetValue()) gf_cfg_set_key(cfg, "Streaming", "ReorderSize", m_dorebuffer->GetValue() ? "30" : "0"); + } + + gf_cfg_set_key(cfg, "Streaming", "RTSPTimeout", m_timeout->GetValue().mb_str(wxConvUTF8)); + gf_cfg_set_key(cfg, "Network", "BufferLength", m_buffer->GetValue().mb_str(wxConvUTF8)); + if (m_dorebuffer->GetValue()) { + gf_cfg_set_key(cfg, "Network", "RebufferLength", m_rebuffer->GetValue().mb_str(wxConvUTF8)); + } else { + gf_cfg_set_key(cfg, "Network", "RebufferLength", "0"); + } + + gf_cfg_set_key(cfg, "StreamingCache", "KeepExistingFiles", m_overwrite->GetValue() ? "no" : "yes"); + if (m_usename->GetValue()) { + gf_cfg_set_key(cfg, "StreamingCache", "BaseFileName", m_recfile->GetValue().mb_str(wxConvUTF8)); + } else { + gf_cfg_set_key(cfg, "StreamingCache", "BaseFileName", NULL); + } + gf_cfg_set_key(cfg, "StreamingCache", "RecordDirectory", m_recdir->GetLabel().mb_str(wxConvUTF8)); + + + gf_term_set_option(m_pApp->m_term, GF_OPT_RELOAD_CONFIG, 1); +} + diff --git a/applications/osmo4_wx/wxGPACControl.h b/applications/osmo4_wx/wxGPACControl.h new file mode 100644 index 0000000..043d902 --- /dev/null +++ b/applications/osmo4_wx/wxGPACControl.h @@ -0,0 +1,137 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Osmo4 wxWidgets GUI + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + */ + +#ifndef _OPTIONS_H +#define _OPTIONS_H + +#include "wx/wxprec.h" + +#ifndef WX_PRECOMP + #include "wx/wx.h" +#endif + +#include +#include + +enum +{ + ID_SELECT = 1000, + ID_APPLY, + + ID_MAKE_DEF, + ID_FORCE_AUDIO, + ID_AUDIO_DRIVER, + ID_FONT_DIR, + ID_CACHE_DIR, + ID_PROGRESSIVE, + ID_RTSP_PORT, + ID_RTP_OVER_RTSP, + ID_RTSP_REBUFFER, + ID_RECORD_DIR, + ID_USE_FILENAME, + ID_USE_PROXY, +}; + +class wxOsmo4Frame; +class wxGPACControl : public wxDialog +{ +public: + wxGPACControl(wxWindow *parent); + virtual ~wxGPACControl(); + +private: + DECLARE_EVENT_TABLE() + + wxOsmo4Frame *m_pApp; + + wxComboBox *m_select; + Bool m_bWas3D; + + void Apply(wxCommandEvent &event); + void OnSetSelection(wxCommandEvent &event); + void ForceAudio(wxCommandEvent &event); + void OnSetAudioDriver(wxCommandEvent &event); + void FontDir(wxCommandEvent &event); + void CacheDir(wxCommandEvent &event); + void OnProgressive(wxCommandEvent &event); + void OnUseProxy(wxCommandEvent &event); + void RTPoverRTSP(wxCommandEvent &event); + void Rebuffer(wxCommandEvent &event); + void OnSetRTSPPort(wxCommandEvent &event); + void OnUseFileName(wxCommandEvent &event); + void OnRecDir(wxCommandEvent &event); + void DoSelect(); + s32 m_sel; + void SetYUVLabel(); + + wxBoxSizer *s_header, *s_main, *s_general, *s_mpeg4, *s_mdec, *s_rend, *s_rend2d, *s_rend3d, *s_audio, *s_video, *s_font, *s_dnld, *s_stream, *s_rec; + + /*general section*/ + wxCheckBox *m_loop, *m_lookforsubs, *m_noconsole, *m_viewxmt; + /*MPEG-4 systems*/ + wxCheckBox *m_bifsalwaysdrawn, *m_singletime; + wxComboBox *m_lang, *m_thread; + /*media decoders*/ + wxComboBox *m_decaudio, *m_decvideo; + /*Rendering*/ + wxComboBox *m_fps, *m_aa, *m_draw_bounds; + wxCheckBox *m_use3D, *m_fast, *m_force_size; + /*Renderer 2D*/ + wxComboBox *m_graph; + wxCheckBox *m_noyuv, *m_direct, *m_scalable; + wxStaticText *m_yuvtxt; + /*Renderer 3D*/ + wxCheckBox *m_raster_outlines, *m_polyaa, *m_nobackcull, *m_emulpow2, *m_norectext, *m_copypixels; + wxComboBox *m_wire, *m_normals; + /*video*/ + wxComboBox *m_video; + wxCheckBox *m_switchres, *m_usehwmem; + /*audio*/ + wxSpinCtrl *m_nbbuf, *m_buflen; + wxComboBox *m_audio; + wxCheckBox *m_forcecfg, *m_noresync, *m_nomulitch; +#ifdef WIN32 + wxCheckBox *m_notifs; +#endif + /*font*/ + wxComboBox *m_font; + wxButton *m_fontdir; + wxComboBox *m_texturemode; + /*file download*/ + wxButton *m_cachedir; + wxCheckBox *m_cleancache, *m_restartcache, *m_progressive, *m_use_proxy; + wxTextCtrl *m_sax_duration, *m_proxy_name; + /*streaming*/ + wxComboBox *m_port; + wxCheckBox *m_rtsp, *m_reorder, *m_dorebuffer; + wxTextCtrl *m_timeout, *m_buffer, *m_rebuffer; + /*file recorder*/ + wxButton *m_recdir; + wxCheckBox *m_overwrite, *m_usename; + wxTextCtrl *m_recfile; +}; + +#endif + diff --git a/applications/osmo4_wx/wxOsmo4.cpp b/applications/osmo4_wx/wxOsmo4.cpp new file mode 100644 index 0000000..b25b199 --- /dev/null +++ b/applications/osmo4_wx/wxOsmo4.cpp @@ -0,0 +1,2347 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Osmo4 wxWidgets GUI + * + * GPAC is gf_free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + */ + + +#include "wxOsmo4.h" +#include "wxGPACControl.h" +#include "fileprops.h" +#include +#include +#include +#include +#include + + +IMPLEMENT_APP(wxOsmo4App) + +#include "osmo4.xpm" + +#include + + + + + +#include +#include + +#include "toolbar.xpm" + +#include "Playlist.h" + +#ifdef WIN32 +#define FRAME_H 140 +#else +#define FRAME_H 110 +#endif + + +wxString get_pref_browser(GF_Config *cfg) +{ + const char *sOpt = gf_cfg_get_key(cfg, "General", "Browser"); + if (sOpt) return wxString(sOpt, wxConvUTF8); + return wxEmptyString; +/* +#ifdef __WXMAC__ + return wxT("safari"); +#else +#ifdef WIN32 + return wxT("explorer.exe"); +#else + return wxT("mozilla"); +#endif +#endif*/ +} + + +IMPLEMENT_DYNAMIC_CLASS(wxGPACEvent, wxEvent ) + +wxGPACEvent::wxGPACEvent(wxWindow* win) +{ + SetEventType(GPAC_EVENT); + SetEventObject(win); + gpac_evt.type = 0; + to_url = wxT(""); +} +wxEvent *wxGPACEvent::Clone() const +{ + wxGPACEvent *evt = new wxGPACEvent((wxWindow *) m_eventObject); + evt->to_url = to_url; + evt->gpac_evt = gpac_evt; + return evt; +} + +#include + +/*open file dlg*/ +BEGIN_EVENT_TABLE(OpenURLDlg, wxDialog) + EVT_BUTTON(ID_URL_GO, OpenURLDlg::OnGo) +END_EVENT_TABLE() + +OpenURLDlg::OpenURLDlg(wxWindow *parent, GF_Config *cfg) + : wxDialog(parent, -1, wxString(wxT("Enter remote presentation location"))) +{ +#ifndef WIN32 + SetSize(430, 35); +#else + SetSize(430, 55); +#endif + Centre(); + m_url = new wxComboBox(this, -1, wxT(""), wxDefaultPosition, wxDefaultSize, 0, NULL, wxCB_DROPDOWN); + m_url->SetSize(0, 2, 340, 18, wxSIZE_AUTO); + m_go = new wxButton(this, ID_URL_GO, wxT("Go !")); +#ifndef WIN32 + m_go->SetSize(344, 2, 20, 18, wxSIZE_AUTO); +#else + m_go->SetSize(364, 2, 30, 18, wxSIZE_AUTO); +#endif + m_urlVal = wxT(""); + + m_cfg = cfg; + + const char *sOpt; + u32 i=0; + + while (1) { + sOpt = gf_cfg_get_key_name(m_cfg, "RecentFiles", i); + if (!sOpt) break; + m_url->Append(wxString(sOpt, wxConvUTF8) ); + i++; + } +} + +#define MAX_LAST_FILES 20 +void UpdateLastFiles(GF_Config *cfg, const char *URL) +{ + u32 nb_entries; + gf_cfg_set_key(cfg, "RecentFiles", URL, NULL); + gf_cfg_insert_key(cfg, "RecentFiles", URL, "", 0); + /*remove last entry if needed*/ + nb_entries = gf_cfg_get_key_count(cfg, "RecentFiles"); + if (nb_entries>MAX_LAST_FILES) { + gf_cfg_set_key(cfg, "RecentFiles", gf_cfg_get_key_name(cfg, "RecentFiles", nb_entries-1), NULL); + } +} + +void OpenURLDlg::OnGo(wxCommandEvent& event) +{ + m_urlVal = m_url->GetValue(); + UpdateLastFiles(m_cfg, m_urlVal.mb_str(wxConvUTF8)); + EndModal(wxID_OK); +} +/*end open file dlg*/ + +#ifdef WIN32 +u32 get_sys_col(int idx) +{ + u32 res; + DWORD val = GetSysColor(idx); + res = (val)&0xFF; res<<=8; + res |= (val>>8)&0xFF; res<<=8; + res |= (val>>16)&0xFF; + return res; +} +#endif + +static void wxOsmo4_progress_cbk(const void *usr, const char *title, u64 done, u64 total) +{ + if (!total) return; + wxOsmo4Frame *app = (wxOsmo4Frame *)usr; + s32 prog = (s32) ( (100 * (u64)done) / total); + if (app->m_last_prog < prog) { + app->m_last_prog = prog; + + if (prog<100) { + /*appears to crash wxWidgets / X11 when refreshing the text too often*/ + if (app->m_LastStatusTime + 200 > gf_sys_clock()) return; + char msg[1024]; + sprintf(msg, "%s %02d %%)", title, prog); + //app->SetStatus(wxString(msg, wxConvUTF8)); + } else { + app->SetStatus(wxT("Ready")); + app->m_last_prog = -1; + } + } +} + +Bool GPAC_EventProc(void *ptr, GF_Event *evt) +{ + wxCommandEvent event; + wxOsmo4Frame *app = (wxOsmo4Frame *)ptr; + + switch (evt->type) { + case GF_EVENT_DURATION: + app->m_duration = (u32) (evt->duration.duration*1000); + app->m_can_seek = evt->duration.can_seek; + if (app->m_duration<1100) app->m_can_seek = 0; + app->m_pProg->Enable(app->m_can_seek ? 1 : 0); + app->m_pPlayList->SetDuration((u32) evt->duration.duration); + break; + case GF_EVENT_MESSAGE: + { + const char *servName; + if (!evt->message.service || !strcmp(evt->message.service, app->m_pPlayList->GetURL().mb_str(wxConvUTF8))) { + servName = "main service"; + } else { + servName = evt->message.service; + } + if (!evt->message.message) return 0; + + if (evt->message.error) { + app->SetStatus(wxString(evt->message.message, wxConvUTF8) + wxT(" (") + wxString(servName, wxConvUTF8) + wxT(")") ); + if (!app->m_connected) app->m_pPlayList->SetDead(); + } + else if (!app->m_console_off) { + if (strstr(evt->message.message, "100 %")) { + app->SetStatus(wxT("")); + } else { + app->SetStatus(wxString(evt->message.message, wxConvUTF8) ); + } + } + +#if 0 + /*log*/ + if (evt->message.error) + ::wxLogMessage(wxString(evt->message.message, wxConvUTF8) + wxT(" (") + wxString(servName, wxConvUTF8) + wxT(") ") + wxString(gf_error_to_string(evt->message.error), wxConvUTF8) ); + else + ::wxLogMessage(wxString(evt->message.message, wxConvUTF8) + wxT(" (") + wxString(servName, wxConvUTF8) + wxT(")")); +#endif + } + break; + case GF_EVENT_PROGRESS: + { + const char *sTitle; + if (evt->progress.progress_type==0) sTitle = (char *)"Buffer"; + else if (evt->progress.progress_type==1) sTitle = (char *)"Download"; + else if (evt->progress.progress_type==2) sTitle = (char *)"Import"; + gf_set_progress(sTitle, evt->progress.done, evt->progress.total); + } + break; + case GF_EVENT_KEYDOWN: + if (app->m_can_seek && (evt->key.flags & GF_KEY_MOD_ALT)) { + s32 res; + switch (evt->key.key_code) { + case GF_KEY_LEFT: + res = gf_term_get_time_in_ms(app->m_term) - 5*app->m_duration/100; + if (res<0) res=0; + gf_term_play_from_time(app->m_term, res, 0); + break; + case GF_KEY_RIGHT: + res = gf_term_get_time_in_ms(app->m_term) + 5*app->m_duration/100; + if ((u32) res>=app->m_duration) res = 0; + gf_term_play_from_time(app->m_term, res, 0); + break; + case GF_KEY_DOWN: + res = gf_term_get_time_in_ms(app->m_term) - 60000; + if (res<0) res=0; + gf_term_play_from_time(app->m_term, res, 0); + break; + case GF_KEY_UP: + res = gf_term_get_time_in_ms(app->m_term) + 60000; + if ((u32) res>=app->m_duration) res = 0; + gf_term_play_from_time(app->m_term, res, 0); + break; + } + } else if (evt->key.flags & GF_KEY_MOD_CTRL) { + switch (evt->key.key_code) { + case GF_KEY_LEFT: + app->m_pPlayList->PlayPrev(); + break; + case GF_KEY_RIGHT: + app->m_pPlayList->PlayNext(); + break; + } + } else { + switch (evt->key.key_code) { + case GF_KEY_HOME: + gf_term_set_option(app->m_term, GF_OPT_NAVIGATION_TYPE, 1); + break; + case GF_KEY_ESCAPE: + if (gf_term_get_option(app->m_term, GF_OPT_FULLSCREEN)) + gf_term_set_option(app->m_term, GF_OPT_FULLSCREEN, 0); + break; + default: + { + wxGPACEvent wxevt(app); + wxevt.gpac_evt = *evt; + app->AddPendingEvent(wxevt); + } + break; + } + } + break; + + case GF_EVENT_CONNECT: + { + wxGPACEvent wxevt(app); + wxevt.gpac_evt.type = GF_EVENT_CONNECT; + wxevt.gpac_evt.connect.is_connected = evt->connect.is_connected; + if (!evt->connect.is_connected) app->m_duration = 0; + app->AddPendingEvent(wxevt); + } + break; + case GF_EVENT_NAVIGATE: + { + wxGPACEvent wxevt(app); + wxevt.to_url = wxString(evt->navigate.to_url, wxConvUTF8); + wxevt.gpac_evt.type = evt->type; + app->AddPendingEvent(wxevt); + } + return 1; + case GF_EVENT_SET_CAPTION: + { + wxGPACEvent wxevt(app); + wxevt.to_url = wxString(evt->caption.caption, wxConvUTF8); + wxevt.gpac_evt.type = evt->type; + app->AddPendingEvent(wxevt); + } + return 1; + + case GF_EVENT_QUIT: + case GF_EVENT_VIEWPOINTS: + case GF_EVENT_STREAMLIST: + case GF_EVENT_SCENE_SIZE: +// case GF_EVENT_SIZE: + { + wxGPACEvent wxevt(app); + wxevt.gpac_evt = *evt; + app->AddPendingEvent(wxevt); + } + break; + case GF_EVENT_DBLCLICK: + gf_term_set_option(app->m_term, GF_OPT_FULLSCREEN, !gf_term_get_option(app->m_term, GF_OPT_FULLSCREEN)); + return 0; + case GF_EVENT_MOUSEDOWN: + if (!gf_term_get_option(app->m_term, GF_OPT_FULLSCREEN)) { +#ifdef __WXGTK__ + app->m_pVisual->SetFocus(); +#else + app->m_pView->SetFocus(); +#endif + } + break; + case GF_EVENT_AUTHORIZATION: + { + wxGPACEvent wxevt(app); + wxTextEntryDialog user_d (0, + wxT("Please set the user name for connection"), + wxString(evt->auth.site_url, wxConvUTF8), + wxString(evt->auth.user, wxConvUTF8)); + if (user_d.ShowModal() != wxID_OK) + return 0; + strncpy(evt->auth.user, user_d.GetValue().mb_str(wxConvUTF8), 50); + wxPasswordEntryDialog passwd_d(0, + wxT("Please enter password"), + wxString(evt->auth.site_url, wxConvUTF8), + wxString(evt->auth.password, wxConvUTF8)); + if (passwd_d.ShowModal() != wxID_OK) + return 0; + strncpy(evt->auth.password, passwd_d.GetValue().mb_str(wxConvUTF8), 50); + return 1; + } + case GF_EVENT_SYS_COLORS: +#ifdef WIN32 + evt->sys_cols.sys_colors[0] = get_sys_col(COLOR_ACTIVEBORDER); + evt->sys_cols.sys_colors[1] = get_sys_col(COLOR_ACTIVECAPTION); + evt->sys_cols.sys_colors[2] = get_sys_col(COLOR_APPWORKSPACE); + evt->sys_cols.sys_colors[3] = get_sys_col(COLOR_BACKGROUND); + evt->sys_cols.sys_colors[4] = get_sys_col(COLOR_BTNFACE); + evt->sys_cols.sys_colors[5] = get_sys_col(COLOR_BTNHIGHLIGHT); + evt->sys_cols.sys_colors[6] = get_sys_col(COLOR_BTNSHADOW); + evt->sys_cols.sys_colors[7] = get_sys_col(COLOR_BTNTEXT); + evt->sys_cols.sys_colors[8] = get_sys_col(COLOR_CAPTIONTEXT); + evt->sys_cols.sys_colors[9] = get_sys_col(COLOR_GRAYTEXT); + evt->sys_cols.sys_colors[10] = get_sys_col(COLOR_HIGHLIGHT); + evt->sys_cols.sys_colors[11] = get_sys_col(COLOR_HIGHLIGHTTEXT); + evt->sys_cols.sys_colors[12] = get_sys_col(COLOR_INACTIVEBORDER); + evt->sys_cols.sys_colors[13] = get_sys_col(COLOR_INACTIVECAPTION); + evt->sys_cols.sys_colors[14] = get_sys_col(COLOR_INACTIVECAPTIONTEXT); + evt->sys_cols.sys_colors[15] = get_sys_col(COLOR_INFOBK); + evt->sys_cols.sys_colors[16] = get_sys_col(COLOR_INFOTEXT); + evt->sys_cols.sys_colors[17] = get_sys_col(COLOR_MENU); + evt->sys_cols.sys_colors[18] = get_sys_col(COLOR_MENUTEXT); + evt->sys_cols.sys_colors[19] = get_sys_col(COLOR_SCROLLBAR); + evt->sys_cols.sys_colors[20] = get_sys_col(COLOR_3DDKSHADOW); + evt->sys_cols.sys_colors[21] = get_sys_col(COLOR_3DFACE); + evt->sys_cols.sys_colors[22] = get_sys_col(COLOR_3DHIGHLIGHT); + evt->sys_cols.sys_colors[23] = get_sys_col(COLOR_3DLIGHT); + evt->sys_cols.sys_colors[24] = get_sys_col(COLOR_3DSHADOW); + evt->sys_cols.sys_colors[25] = get_sys_col(COLOR_WINDOW); + evt->sys_cols.sys_colors[26] = get_sys_col(COLOR_WINDOWFRAME); + evt->sys_cols.sys_colors[27] = get_sys_col(COLOR_WINDOWTEXT); + return 1; +#else + memset(evt->sys_cols.sys_colors, 0, sizeof(u32)*28); + return 1; +#endif + } + return 0; +} + + + +bool wxOsmo4App::OnInit() +{ +#ifdef __WXGTK__ + XSynchronize((Display *) wxGetDisplay(), 1); +#endif + wxFrame *frame = new wxOsmo4Frame(); + frame->Show(TRUE); + SetTopWindow(frame); + return true; +} + + +class myDropfiles : public wxFileDropTarget +{ +public: + myDropfiles() : wxFileDropTarget() {} + virtual bool OnDropFiles(wxCoord x, wxCoord y, const wxArrayString& filenames); + wxOsmo4Frame *m_pMain; +}; + +bool myDropfiles::OnDropFiles(wxCoord x, wxCoord y, const wxArrayString& filenames) +{ + u32 count = filenames.GetCount(); + + if (count==1) { + const char *ext = strrchr(filenames.Item(0).mb_str(wxConvUTF8) , '.'); + /*if playing and sub d&d, open sub in current presentation*/ + if (m_pMain->m_connected && ext && ( !stricmp(ext, ".srt") || !stricmp(ext, ".sub") || !stricmp(ext, ".ttxt") || !stricmp(ext, ".xml") ) ) { + m_pMain->AddSubtitle(filenames.Item(0).mb_str(wxConvUTF8) , 1); + return TRUE; + } + } + + for (u32 i=0; im_pPlayList->QueueURL(filenames.Item(i)); + + m_pMain->m_pPlayList->RefreshList(); + m_pMain->m_pPlayList->PlayNext(); + return TRUE; +} + +bool GPACLogs::OnFrameClose(wxFrame *frame) +{ + Show(FALSE); + return 0; +} + +void wxOsmo4Frame::ShowViewWindow(Bool do_show) +{ + m_pView->Show(do_show ? 1 : 0); +#ifdef __WXGTK__ + //m_pView->Show(0); +#endif +} + +#ifdef __WXGTK__ +extern "C" { +#ifdef __WXGTK20__ + int gdk_x11_drawable_get_xid( void * ); + void *gdk_x11_drawable_get_xdisplay( void * ); +#endif + void *gtk_widget_get_parent_window( void * ); +} +#endif + + +void wxOsmo4Frame::CheckVideoOut() +{ + const char *sOpt = gf_cfg_get_key(m_user.config, "Video", "DriverName"); + void *os_handle = NULL; + void *os_display = NULL; + /*build a child window for embed display*/ + if (sOpt && stricmp(sOpt, "SDL Video Output")) { + if (m_user.os_window_handler) return; + m_bExternalView = 0; + +#ifdef __WXGTK__ + GtkWidget* widget = m_pVisual->GetHandle(); + +#ifdef __WXGTK20__ + os_handle = (void *) gdk_x11_drawable_get_xid(gtk_widget_get_parent_window(widget)); +#else + os_handle = (void *)*(int *)( (char *)gtk_widget_get_parent_window(widget) + 2 * sizeof(void *) ); +#endif + +#elif defined (WIN32) + os_handle = m_pView->GetHandle(); +#endif + if (os_handle) { + m_user.os_window_handler = os_handle; + m_user.os_display = os_display; + ShowViewWindow(1); + m_pView->SetSize(320, 240); + SetSize(wxSize(320, 240+FRAME_H)); + SetWindowStyle(wxDEFAULT_FRAME_STYLE); + DoLayout(320, 240); + return; + } + } + /*we're using SDL, don't use SDL hack*/ + m_bExternalView = 1; + m_user.os_window_handler = 0; + m_user.os_display = NULL; + SetSize(wxSize(320,FRAME_H)); + m_pView->SetSize(0, 0); + ShowViewWindow(0); + DoLayout(); + SetWindowStyle(wxDEFAULT_FRAME_STYLE & ~(wxMAXIMIZE_BOX | wxRESIZE_BORDER)); +} + +static void wxOsmo4_do_log(void *cbk, u32 level, u32 tool, const char *fmt, va_list list) +{ + wxOsmo4Frame *osmo = (wxOsmo4Frame *)cbk; + + if (osmo->m_logs) { + vfprintf(osmo->m_logs, fmt, list); + fflush(osmo->m_logs); + } else { + ::wxVLogMessage(wxString(fmt, wxConvUTF8), list); + } +} + + +Bool wxOsmo4Frame::LoadTerminal() +{ + m_term = NULL; + memset(&m_user, 0, sizeof(GF_User)); + + /*locate exec dir for cfg file*/ + wxPathList pathList; + wxString currentDir(wxGetCwd()); + wxString abs_gpac_path = wxT(""); + char *gpac_cfg, *sep; + + ::wxLogMessage(wxT("Looking for GPAC configuration file")); + + /*load config*/ + Bool first_launch = 0; + m_user.config = gf_cfg_init(NULL, &first_launch); + + if (!m_user.config) { + wxMessageDialog(NULL, wxT("Cannot open GPAC configuration file"), wxT("Init error"), wxOK); + return 0; + } + + gpac_cfg = gf_cfg_get_filename(m_user.config); + sep = strrchr(gpac_cfg, '/'); + if (!sep) sep = strrchr(gpac_cfg, '\\'); + if (sep) sep[0] = 0; + strcpy(szAppPath, gpac_cfg); + if (sep) sep[0] = '/'; + gf_free(gpac_cfg); + + /*check log file*/ + const char *str = gf_cfg_get_key(m_user.config, "General", "LogFile"); + if (str) m_logs = fopen(str, "wt"); + gf_log_set_callback(this, wxOsmo4_do_log); + + /*set log level*/ + gf_log_set_tools_levels( gf_cfg_get_key(m_user.config, "General", "Logs") ); + + gf_sys_init(0); + + ::wxLogMessage(wxT("GPAC configuration file opened - looking for modules")); + + m_user.modules = gf_modules_new(str, m_user.config); + /*initial launch*/ + if (!m_user.modules || !gf_modules_get_count(m_user.modules)) { + wxMessageDialog(NULL, wxT("No modules available - system cannot work"), wxT("Fatal Error"), wxOK).ShowModal(); + if (m_user.modules) gf_modules_del(m_user.modules); + gf_cfg_del(m_user.config); + m_user.config = NULL; + return 0; + } + + if (first_launch) { + u32 i; + for (i=0; iCanHandleURL(ifce, "test.test"); + gf_modules_close_interface((GF_BaseInterface *) ifce); + } + } + } + + + + ::wxLogMessage(wxT("%d modules found:"), gf_modules_get_count(m_user.modules)); + for (u32 i=0; im_pMain = this; + SetDropTarget(droptarget); + m_pLogs = new GPACLogs(this); + m_bGrabbed = 0; + + /*new menu bar*/ + wxMenuBar *b = new wxMenuBar(); + /*file*/ + wxMenu *menu = new wxMenu(); + menu->Append(GWX_FILE_OPEN, wxT("&Open File\tCtrl+O"), wxT("Open local presentation")); + menu->Append(GWX_FILE_OPEN_URL, wxT("&Open URL\tCtrl+U"), wxT("Open remote presentation")); + menu->AppendSeparator(); + menu->Append(FILE_PROPERTIES, wxT("&Properties\tCtrl+I"), wxT("Show presentation properties")); + menu->Enable(FILE_PROPERTIES, 0); + wxMenu *smenu = new wxMenu(); + smenu->Append(ID_MCACHE_ENABLE, wxT("&Enable"), wxT("Turns Recorder On/Off")); + smenu->Append(ID_MCACHE_STOP, wxT("&Stop"), wxT("Stops recording and saves")); + smenu->Append(ID_MCACHE_ABORT, wxT("&Abort"), wxT("Stops recording and discards")); + menu->Append(0, wxT("&Streaming Cache"), smenu); + menu->AppendSeparator(); + menu->Append(FILE_COPY, wxT("&Copy\tCtrl+C"), wxT("Copy selected text")); + menu->Append(FILE_PASTE, wxT("&Paste\tCtrl+V"), wxT("Copy selected text")); + menu->AppendSeparator(); + menu->Append(FILE_QUIT, wxT("E&xit"), wxT("Quit the application")); + b->Append(menu, wxT("&File")); + /*view*/ + menu = new wxMenu(); + vp_list = new wxMenu(); + menu->Append(0, wxT("&Viewpoint"), vp_list); + smenu = new wxMenu(); + smenu->Append(ID_HEADLIGHT, wxT("Headlight"), wxT("Turns headlight on/off"), wxITEM_CHECK); + smenu->AppendSeparator(); + smenu->Append(ID_NAVIGATE_NONE, wxT("None"), wxT("Disables Navigation"), wxITEM_CHECK); + smenu->Append(ID_NAVIGATE_WALK, wxT("Walk"), wxT("Walk Navigation"), wxITEM_CHECK); + smenu->Append(ID_NAVIGATE_FLY, wxT("Fly"), wxT("Fly Navigation"), wxITEM_CHECK); + smenu->Append(ID_NAVIGATE_EXAMINE, wxT("Examine"), wxT("Examine Navigation"), wxITEM_CHECK); + smenu->Append(ID_NAVIGATE_PAN, wxT("Pan"), wxT("Pan Navigation"), wxITEM_CHECK); + smenu->Append(ID_NAVIGATE_SLIDE, wxT("Slide"), wxT("Slide Navigation"), wxITEM_CHECK); + smenu->Append(ID_NAVIGATE_ORBIT, wxT("Orbit"), wxT("Orbit Navigation"), wxITEM_CHECK); + smenu->Append(ID_NAVIGATE_GAME, wxT("Game"), wxT("Game Navigation"), wxITEM_CHECK); + smenu->AppendSeparator(); + wxMenu *ssmenu = new wxMenu(); + ssmenu->Append(ID_COLLIDE_NONE, wxT("None"), wxT("No Collision detection"), wxITEM_CHECK); + ssmenu->Append(ID_COLLIDE_REG, wxT("Regular"), wxT("Regular Collision detection"), wxITEM_CHECK); + ssmenu->Append(ID_COLLIDE_DISP, wxT("Displacement"), wxT("Collision detecion with camera displacement"), wxITEM_CHECK); + smenu->Append(0, wxT("&Collision"), ssmenu); + smenu->Append(ID_GRAVITY, wxT("Gravity"), wxT("Turns gravity on/off"), wxITEM_CHECK); + smenu->AppendSeparator(); + smenu->Append(ID_NAVIGATE_RESET, wxT("Reset"), wxT("Reset Navigation")); + + menu->Append(0, wxT("&Navigation"), smenu); + menu->AppendSeparator(); + menu->Append(VIEW_FULLSCREEN, wxT("&Fullscreen"), wxT("Toggles Fullscreen"), wxITEM_CHECK); + menu->Append(VIEW_ORIGINAL, wxT("&Original Size"), wxT("Restore original size")); + smenu = new wxMenu(); + smenu->Append(VIEW_AR_KEEP, wxT("Keep Original\tCtrl+1"), wxT("Keep original aspect ratio"), wxITEM_CHECK); + smenu->Append(VIEW_AR_FILL, wxT("Fill Screen\tCtrl+2"), wxT("Stretch presentation to fill screen"), wxITEM_CHECK); + smenu->Append(VIEW_AR_43, wxT("Ratio 4/3\tCtrl+3"), wxT("Force aspect ratio to 4/3"), wxITEM_CHECK); + smenu->Append(VIEW_AR_169, wxT("Ratio 16/9\tCtrl+4"), wxT("Force aspect ratio to 16/9"), wxITEM_CHECK); + menu->Append(0, wxT("&Aspect Ratio"), smenu); + smenu->Check(VIEW_AR_KEEP, 1); + menu->AppendSeparator(); + menu->Append(VIEW_OPTIONS, wxT("&Options"), wxT("View Options")); + menu->AppendSeparator(); + menu->Append(VIEW_RTI, wxT("&Resource Usage"), wxT("View Resource Usage"), wxITEM_CHECK); + menu->Append(VIEW_LOGS, wxT("&Logs"), wxT("View GPAC logs")); + b->Append(menu, wxT("&View")); + + /*play*/ + menu = new wxMenu(); + sel_menu = new wxMenu(); + sel_menu->Append(0, wxT("&Audio"), new wxMenu()); + sel_menu->Append(0, wxT("&Video"), new wxMenu()); + sel_menu->Append(0, wxT("&Subtitles"), new wxMenu()); + sel_menu->AppendSeparator(); + sel_menu->Append(ID_ADD_SUB, wxT("&Add Subtitle"), wxT("Adds subtitle")); + menu->Append(ID_STREAM_MENU, wxT("&Streams Selection"), sel_menu); + chap_menu = new wxMenu(); + menu->Append(ID_CHAPTER_MENU, wxT("&Chapters"), chap_menu); + + menu->AppendSeparator(); + menu->Append(VIEW_PLAYLIST, wxT("&Playlist\tCtrl+L"), wxT("Show navigation history as playlist"), wxITEM_CHECK); + menu->Append(ID_CLEAR_NAV, wxT("&Clear History"), wxT("Clear navigation history")); + menu->AppendSeparator(); + menu->Append(FILE_PLAY, wxT("&Play/Pause\tCtrl+P"), wxT("Play/Pause/Resume Presentation")); + menu->Append(FILE_STEP, wxT("&Step-by-Step\tCtrl+S"), wxT("Play/Pause/Resume Presentation")); + menu->Append(FILE_STOP, wxT("&Stop"), wxT("Stop Presentation")); + menu->AppendSeparator(); + menu->Append(FILE_RELOAD_CONFIG, wxT("&Reload Config\tCtrl+R"), wxT("Reload Configuration File")); + menu->Append(FILE_RELOAD, wxT("&Reload File\tCtrl+R"), wxT("Reload Presentation")); + b->Append(menu, wxT("&Play")); + + menu = new wxMenu(); + menu->Append(APP_SHORTCUTS, wxT("&Shortcuts"), wxT("Show keyboard shortcuts")); + menu->Append(APP_NAV_KEYS, wxT("&Navigation Keys"), wxT("Show navigation keys")); + menu->AppendSeparator(); + menu->Append(APP_ABOUT, wxT("&About"), wxT("Display information and copyright")); + b->Append(menu, wxT("&?")); + + SetMenuBar(b); + + m_pStatusbar = CreateStatusBar(1, 0, -1, wxT("statusBar")); + ws[0] = 60; + ws[1] = 70; + ws[2] = -1; + m_pStatusbar->SetFieldsCount(3, ws); + + SetStatusBarPane(2); + wxColour foreCol = m_pStatusbar->GetBackgroundColour(); + SetBackgroundColour(foreCol); + + + m_pTimer = new wxTimer(); + m_pTimer->SetOwner(this, ID_CTRL_TIMER); + m_bGrabbed = 0; + + /*create toolbar*/ + m_pToolBar = CreateToolBar(wxTB_FLAT|wxTB_HORIZONTAL); + m_pOpenFile = new wxBitmap(tool_open_file); + m_pPrev = new wxBitmap(tool_prev); + m_pNext = new wxBitmap(tool_next); + m_pPlay = new wxBitmap(tool_play); + m_pPause = new wxBitmap(tool_pause); + m_pStep = new wxBitmap(tool_step); + m_pStop = new wxBitmap(tool_stop); + m_pInfo = new wxBitmap(tool_info); + m_pConfig = new wxBitmap(tool_config); + m_pSW2D = new wxBitmap(tool_sw_2d); + m_pSW3D = new wxBitmap(tool_sw_3d); + + m_pToolBar->AddTool(GWX_FILE_OPEN, wxT(""), *m_pOpenFile, wxT("Open File")); + m_pToolBar->AddSeparator(); + m_pPrevBut = new wxMenuButton(m_pToolBar, FILE_PREV, *m_pPrev); + m_pPrevBut->SetToolTip(wxT("Previous Location")); + m_pToolBar->AddControl(m_pPrevBut); + m_pNextBut = new wxMenuButton(m_pToolBar, FILE_NEXT, *m_pNext); + m_pNextBut->SetToolTip(wxT("Next Location")); + m_pToolBar->AddControl(m_pNextBut); + + m_pToolBar->AddSeparator(); + m_pToolBar->AddTool(FILE_PLAY, wxT(""), *m_pPlay, wxT("Play/Pause File")); + m_pToolBar->AddTool(FILE_STEP, wxT(""), *m_pStep, wxT("Step-by-Step Mode")); + m_pToolBar->AddTool(FILE_STOP, wxT(""), *m_pStop, wxT("Stop File")); + m_pToolBar->AddSeparator(); + m_pToolBar->AddTool(FILE_PROPERTIES, wxT(""), *m_pInfo, wxT("Show File Information")); + m_pToolBar->AddSeparator(); + m_pToolBar->AddTool(VIEW_OPTIONS, wxT(""), *m_pConfig, wxT("GPAC Configuration")); + m_pToolBar->AddTool(SWITCH_RENDER, wxT(""), *m_pSW3D, wxT("Switch 2D/3D Renderers")); + + m_pToolBar->Realize(); + + m_Address = new wxMyComboBox(this, ID_ADDRESS, wxT(""), wxPoint(50, 0), wxSize(80, 20)); + wxStaticText *add_text = new wxStaticText(this, -1, wxT("URL"), wxPoint(0, 0), wxSize(40, 20)); + add_text->SetBackgroundColour(foreCol); + + m_pAddBar = new wxBoxSizer(wxHORIZONTAL); + m_pAddBar->Add(add_text, 0, wxALIGN_TOP); + m_pAddBar->Add(m_Address, 2, wxALIGN_CENTER|wxEXPAND|wxADJUST_MINSIZE); + m_pAddBar->SetMinSize(80, 32); + m_pAddBar->Layout(); + + m_pProg = new wxSlider(this, ID_SLIDER, 0, 0, 1000, wxPoint(0, 22), wxSize(80, 22), wxSL_HORIZONTAL|wxSUNKEN_BORDER); + m_pProg->Enable(0); + m_pProg->Show(); + m_pProg->SetBackgroundColour(foreCol); + + m_pView = new wxWindow(this, -1, wxDefaultPosition, wxDefaultSize, wxNO_BORDER); + m_pView->SetBackgroundColour(wxColour(wxT("BLACK"))); +#ifdef __WXGTK__ + m_pVisual = new wxWindow(m_pView, -1, wxDefaultPosition, wxDefaultSize, wxNO_BORDER); + m_pVisual->SetBackgroundColour(wxColour(wxT("BLACK"))); +#endif + + m_pPlayList = new wxPlaylist(this); + m_pPlayList->SetIcon(wxIcon(osmo4)); + m_pPlayList->Hide(); + Raise(); + Show(); + + m_connected = 0; + if (!LoadTerminal()) { + Close(TRUE); + return; + } + + + + if (m_bExternalView) SetWindowStyle(wxDEFAULT_FRAME_STYLE & ~(wxMAXIMIZE_BOX | wxRESIZE_BORDER)); + DoLayout(320, 240); + UpdateRenderSwitch(); + + const char *sOpt = gf_cfg_get_key(m_user.config, "General", "ConsoleOff"); + m_console_off = (sOpt && !stricmp(sOpt, "yes")) ? 1 : 0; + sOpt = gf_cfg_get_key(m_user.config, "General", "Loop"); + m_loop = (sOpt && !stricmp(sOpt, "yes")) ? 1 : 0; + sOpt = gf_cfg_get_key(m_user.config, "General", "LookForSubtitles"); + m_lookforsubs = (sOpt && !stricmp(sOpt, "yes")) ? 1 : 0; + gf_term_set_option(m_term, GF_OPT_AUDIO_VOLUME, 100); + + ReloadURLs(); + Raise(); + m_pStatusbar->SetStatusText(wxT("Ready"), 2); + m_LastStatusTime = 0; + + m_pPrevBut->Refresh(); + m_pNextBut->Refresh(); + + wxOsmo4App &app = wxGetApp(); + if (app.argc>1) { + m_pPlayList->QueueURL(wxString(app.argv[1])); + m_pPlayList->RefreshList(); + m_pPlayList->PlayNext(); + } else { + char sPL[GF_MAX_PATH]; + strcpy((char *) sPL, szAppPath); +#ifdef WIN32 + strcat(sPL, "gpac_pl.m3u"); +#else + strcat(sPL, ".gpac_pl.m3u"); +#endif + m_pPlayList->OpenPlaylist(wxString(sPL, wxConvUTF8) ); + const char *sOpt = gf_cfg_get_key(m_user.config, "General", "PLEntry"); + if (sOpt) { + m_pPlayList->m_cur_entry = atoi(sOpt); + if (m_pPlayList->m_cur_entry>=(s32)gf_list_count(m_pPlayList->m_entries)) + m_pPlayList->m_cur_entry = -1; + } + + sOpt = gf_cfg_get_key(m_user.config, "General", "StartupFile"); + if (sOpt) { + gf_term_connect(m_term, sOpt); + m_bStartupFile = 1; + } + } + + sOpt = gf_cfg_get_key(m_user.config, "Audio", "DriverName"); + + if (!strcmp(sOpt, "No Audio Output Available")) { + ::wxLogMessage(wxT("WARNING: no audio output availble - make sure no other program is locking the sound card")); + SetStatus(wxT("No audio ouput available")); + + } else { + SetStatus(wxT("Ready")); + } +} + +wxOsmo4Frame::~wxOsmo4Frame() +{ + vp_list = NULL; + sel_menu = NULL; + + if (m_user.modules) gf_modules_del(m_user.modules); + gf_sys_close(); + if (m_user.config) gf_cfg_del(m_user.config); + + if (m_chapters_start) gf_free(m_chapters_start); + if (m_pView) delete m_pView; + + //m_pToolBar->RemoveTool(FILE_PREV); + //m_pToolBar->RemoveTool(FILE_NEXT); + + delete m_pPrevBut; + delete m_pNextBut; + delete m_pPlayList; + delete m_pTimer; + delete m_pOpenFile; + delete m_pPrev; + delete m_pNext; + delete m_pPlay; + delete m_pPause; + delete m_pStep; + delete m_pStop; + delete m_pInfo; + delete m_pConfig; + delete m_pSW2D; + delete m_pSW3D; +} + + +BEGIN_EVENT_TABLE(wxOsmo4Frame, wxFrame) + EVT_CLOSE(wxOsmo4Frame::OnCloseApp) + EVT_MENU(GWX_FILE_OPEN, wxOsmo4Frame::OnFileOpen) + EVT_MENU(GWX_FILE_OPEN_URL, wxOsmo4Frame::OnFileOpenURL) + EVT_MENU(FILE_RELOAD_CONFIG, wxOsmo4Frame::OnFileReloadConfig) + EVT_MENU(FILE_RELOAD, wxOsmo4Frame::OnFileReload) + EVT_MENU(FILE_PROPERTIES, wxOsmo4Frame::OnFileProperties) + EVT_MENU(FILE_QUIT, wxOsmo4Frame::OnFileQuit) + EVT_MENU(VIEW_FULLSCREEN, wxOsmo4Frame::OnFullScreen) + EVT_MENU(VIEW_OPTIONS, wxOsmo4Frame::OnOptions) + EVT_MENU(VIEW_AR_KEEP, wxOsmo4Frame::OnViewARKeep) + EVT_MENU(VIEW_AR_FILL, wxOsmo4Frame::OnViewARFill) + EVT_MENU(VIEW_AR_169, wxOsmo4Frame::OnViewAR169) + EVT_MENU(VIEW_AR_43, wxOsmo4Frame::OnViewAR43) + EVT_MENU(VIEW_ORIGINAL, wxOsmo4Frame::OnViewOriginal) + EVT_MENU(VIEW_PLAYLIST, wxOsmo4Frame::OnPlaylist) + EVT_UPDATE_UI(VIEW_PLAYLIST, wxOsmo4Frame::OnUpdatePlayList) + EVT_MENU(FILE_COPY, wxOsmo4Frame::OnFileCopy) + EVT_UPDATE_UI(FILE_COPY, wxOsmo4Frame::OnUpdateFileCopy) + EVT_MENU(FILE_PASTE, wxOsmo4Frame::OnFilePaste) + EVT_UPDATE_UI(FILE_PASTE, wxOsmo4Frame::OnUpdateFilePaste) + + EVT_MENU(ID_CLEAR_NAV, wxOsmo4Frame::OnClearNav) + EVT_UPDATE_UI(ID_STREAM_MENU, wxOsmo4Frame::OnUpdateStreamMenu) + EVT_UPDATE_UI(ID_CHAPTER_MENU, wxOsmo4Frame::OnUpdateChapterMenu) + EVT_MENU(ID_ADD_SUB, wxOsmo4Frame::OnAddSub) + + EVT_MENU(ID_MCACHE_ENABLE, wxOsmo4Frame::OnCacheEnable) + EVT_UPDATE_UI(ID_MCACHE_ENABLE, wxOsmo4Frame::OnUpdateCacheEnable) + EVT_MENU(ID_MCACHE_STOP, wxOsmo4Frame::OnCacheStop) + EVT_MENU(ID_MCACHE_ABORT, wxOsmo4Frame::OnCacheAbort) + EVT_UPDATE_UI(ID_MCACHE_STOP, wxOsmo4Frame::OnUpdateCacheAbort) + EVT_UPDATE_UI(ID_MCACHE_ABORT, wxOsmo4Frame::OnUpdateCacheAbort) + + + EVT_MENU(APP_SHORTCUTS, wxOsmo4Frame::OnShortcuts) + EVT_MENU(APP_NAV_KEYS, wxOsmo4Frame::OnNavInfo) + EVT_MENU(APP_ABOUT, wxOsmo4Frame::OnAbout) + EVT_GPACEVENT(wxOsmo4Frame::OnGPACEvent) + EVT_TIMER(ID_CTRL_TIMER, wxOsmo4Frame::OnTimer) + EVT_COMMAND_SCROLL(ID_SLIDER, wxOsmo4Frame::OnSlide) + EVT_MENU(VIEW_LOGS, wxOsmo4Frame::OnLogs) + EVT_MENU(VIEW_RTI, wxOsmo4Frame::OnRTI) + + EVT_MENUBUTTON_OPEN(FILE_PREV, wxOsmo4Frame::OnFilePrevOpen) + EVT_MENUBUTTON_OPEN(FILE_NEXT, wxOsmo4Frame::OnFileNextOpen) + EVT_MENU(FILE_PREV, wxOsmo4Frame::OnNavPrev) + EVT_UPDATE_UI(FILE_PREV, wxOsmo4Frame::OnUpdateNavPrev) + EVT_MENU_RANGE(ID_NAV_PREV_0, ID_NAV_PREV_9, wxOsmo4Frame::OnNavPrevMenu) + EVT_MENU(FILE_NEXT, wxOsmo4Frame::OnNavNext) + EVT_UPDATE_UI(FILE_NEXT, wxOsmo4Frame::OnUpdateNavNext) + EVT_MENU_RANGE(ID_NAV_NEXT_0, ID_NAV_NEXT_9, wxOsmo4Frame::OnNavNextMenu) + + EVT_TOOL(FILE_PLAY, wxOsmo4Frame::OnFilePlay) + EVT_TOOL(FILE_STEP, wxOsmo4Frame::OnFileStep) + EVT_TOOL(FILE_STOP, wxOsmo4Frame::OnFileStop) + EVT_TOOL(SWITCH_RENDER, wxOsmo4Frame::OnRenderSwitch) + + EVT_COMBOBOX(ID_ADDRESS, wxOsmo4Frame::OnURLSelect) + + EVT_MENU_RANGE(ID_SELSTREAM_0, ID_SELSTREAM_9, wxOsmo4Frame::OnStreamSel) + EVT_UPDATE_UI_RANGE(ID_SELSTREAM_0, ID_SELSTREAM_9, wxOsmo4Frame::OnUpdateStreamSel) + + EVT_MENU_RANGE(ID_SETCHAP_FIRST, ID_SETCHAP_LAST, wxOsmo4Frame::OnChapterSel) + EVT_UPDATE_UI_RANGE(ID_SETCHAP_FIRST, ID_SETCHAP_LAST, wxOsmo4Frame::OnUpdateChapterSel) + + EVT_MENU_RANGE(ID_VIEWPOINT_FIRST, ID_VIEWPOINT_LAST, wxOsmo4Frame::OnViewport) + EVT_UPDATE_UI_RANGE(ID_VIEWPOINT_FIRST, ID_VIEWPOINT_LAST, wxOsmo4Frame::OnUpdateViewport) + + EVT_MENU_RANGE(ID_NAVIGATE_NONE, ID_NAVIGATE_GAME, wxOsmo4Frame::OnNavigate) + EVT_UPDATE_UI_RANGE(ID_NAVIGATE_NONE, ID_NAVIGATE_GAME, wxOsmo4Frame::OnUpdateNavigation) + EVT_MENU(ID_NAVIGATE_RESET, wxOsmo4Frame::OnNavigateReset) + + EVT_MENU_RANGE(ID_COLLIDE_NONE, ID_COLLIDE_DISP, wxOsmo4Frame::OnCollide) + EVT_UPDATE_UI_RANGE(ID_COLLIDE_NONE, ID_COLLIDE_DISP, wxOsmo4Frame::OnUpdateCollide) + + EVT_MENU(ID_HEADLIGHT, wxOsmo4Frame::OnHeadlight) + EVT_UPDATE_UI(ID_HEADLIGHT, wxOsmo4Frame::OnUpdateHeadlight) + EVT_MENU(ID_GRAVITY, wxOsmo4Frame::OnGravity) + EVT_UPDATE_UI(ID_GRAVITY, wxOsmo4Frame::OnUpdateGravity) + + EVT_UPDATE_UI(FILE_PROPERTIES, wxOsmo4Frame::OnUpdateNeedsConnect) + EVT_UPDATE_UI(FILE_RELOAD, wxOsmo4Frame::OnUpdateNeedsConnect) + EVT_UPDATE_UI(FILE_PLAY, wxOsmo4Frame::OnUpdatePlay) + EVT_UPDATE_UI(FILE_STOP, wxOsmo4Frame::OnUpdateNeedsConnect) + EVT_UPDATE_UI(FILE_STEP, wxOsmo4Frame::OnUpdateNeedsConnect) + EVT_UPDATE_UI(VIEW_ORIGINAL, wxOsmo4Frame::OnUpdateNeedsConnect) + EVT_UPDATE_UI(VIEW_FULLSCREEN, wxOsmo4Frame::OnUpdateFullScreen) + EVT_UPDATE_UI(VIEW_AR_KEEP, wxOsmo4Frame::OnUpdateAR) + EVT_UPDATE_UI(VIEW_AR_FILL, wxOsmo4Frame::OnUpdateAR) + EVT_UPDATE_UI(VIEW_AR_169, wxOsmo4Frame::OnUpdateAR) + EVT_UPDATE_UI(VIEW_AR_43, wxOsmo4Frame::OnUpdateAR) + + EVT_SIZE(wxOsmo4Frame::OnSize) +END_EVENT_TABLE() + +void wxOsmo4Frame::DoLayout(u32 v_width, u32 v_height) +{ + wxPoint pos; + if (!m_Address || !m_pProg) return; + + int t_h = m_pToolBar->GetSize().y; + int a_h = m_pAddBar->GetSize().y; + int p_h = m_pProg->GetSize().y; + + if (m_bExternalView) { + if (v_width && v_height) { + m_orig_width = v_width; + m_orig_height = v_height; + } + SetClientSize(320, a_h+p_h+t_h); + m_pAddBar->SetDimension(0,0, 320, a_h); + m_pProg->SetSize(0, t_h+a_h, 320, p_h, 0); + return; + } + + if (v_width && v_height) { + m_orig_width = v_width; + m_orig_height = v_height; + v_height += a_h + p_h + t_h; + SetClientSize(v_width, v_height); + m_pView->SetSize(0, a_h+t_h, v_width, v_height, 0); + m_pAddBar->SetDimension(0, t_h, v_width, a_h); + m_pProg->SetSize(0, v_height - p_h, v_width, p_h, 0); + } + wxSize s = GetClientSize(); + s.y -= a_h + p_h + t_h; + if (m_pView) { + m_pView->SetSize(0, a_h+t_h, s.x, s.y, 0); + m_pAddBar->SetDimension(0, 0, s.x, a_h); + m_pAddBar->SetDimension(0, 0, s.x, a_h); + m_pAddBar->Layout(); + m_pProg->SetSize(0, s.y+t_h+a_h, s.x, p_h, 0); + if (m_term) gf_term_set_size(m_term, s.x, s.y); + } +} + +void wxOsmo4Frame::OnSize(wxSizeEvent &event) +{ + DoLayout(); +} + +void wxOsmo4Frame::OnCloseApp(wxCloseEvent &WXUNUSED(event)) +{ + if (m_term) gf_term_del(m_term); + m_term = NULL; + Destroy(); +} + + +wxString wxOsmo4Frame::GetFileFilter() +{ + u32 keyCount, i; + wxString sFiles, sSupportedFiles, sExts; + + /*force MP4 and 3GP files at beginning to make sure they are selected (Win32 bug with too large filters)*/ + sSupportedFiles = wxT("All Known Files|*.m3u;*.pls;*.mp4;*.3gp;*.3g2"); + sExts = wxT(""); + sFiles = wxT(""); + keyCount = gf_cfg_get_key_count(m_user.config, "MimeTypes"); + for (i=0; i=0) continue; + /*if same extensions for # mime types skip (don't polluate the file list)*/ + if (sExts.Find(wxString(szKeyList, wxConvUTF8) )>=0) continue; + + sExts += wxString(szKeyList, wxConvUTF8); + sExts += wxT(" "); + sFiles += wxString(sDesc, wxConvUTF8); + sFiles += wxT("|"); + + wxString sOpt = wxString(szKeyList, wxConvUTF8); + while (1) { + wxString ext = sOpt.BeforeFirst(' '); + if (ext.Find('.')<0) { + if (first) first = 0; + else sFiles += wxT(";"); + sFiles += wxT("*."); + sFiles += ext; + wxString sext = ext; + sext += wxT(";"); + if (sSupportedFiles.Find(sext)<0) { + sSupportedFiles += wxT(";*."); + sSupportedFiles += ext; + } + } + if (sOpt==ext) break; + wxString rem = ext + wxT(" "); + sOpt.Replace(rem, wxT(""), TRUE); + } + sFiles += wxT("|"); + } + sSupportedFiles += wxT("|"); + sSupportedFiles += sFiles; + sSupportedFiles += wxT("M3U Playlists|*.m3u|ShoutCast Playlists|*.pls|All Files|*.*||"); + return sSupportedFiles; +} + +void wxOsmo4Frame::OnFileOpen(wxCommandEvent & WXUNUSED(event)) +{ + wxFileDialog dlg(this, wxT("Select file(s)"), wxT(""), wxT(""), GetFileFilter(), wxOPEN | wxMULTIPLE | wxCHANGE_DIR /*| wxHIDE_READONLY*/); + + if (dlg.ShowModal() != wxID_OK) return; + + wxArrayString stra; + dlg.GetPaths(stra); + if (stra.GetCount() == 1) { + m_pPlayList->Truncate(); + } else { + m_pPlayList->Clear(); + } + for (u32 i=0; iQueueURL(stra[i]); + + m_pPlayList->RefreshList(); + m_pPlayList->PlayNext(); +} + +void wxOsmo4Frame::OnFileOpenURL(wxCommandEvent & WXUNUSED(event)) +{ + OpenURLDlg dlg(this, m_user.config); + if (dlg.ShowModal()==wxID_OK) { + m_pPlayList->Truncate(); + m_pPlayList->QueueURL(dlg.m_urlVal); + m_pPlayList->RefreshList(); + m_pPlayList->PlayNext(); + } +} + +void wxOsmo4Frame::OnFileProperties(wxCommandEvent & WXUNUSED(event)) +{ + wxFileProps dlg(this); + dlg.SetIcon(wxIcon(osmo4)); + dlg.ShowModal(); +} + +void wxOsmo4Frame::OnFileReload(wxCommandEvent & WXUNUSED(event)) +{ + gf_term_disconnect(m_term); + m_connected = 0; + DoConnect(); +} + +void wxOsmo4Frame::OnFileReloadConfig(wxCommandEvent & WXUNUSED(event)) +{ + gf_term_set_option(m_term, GF_OPT_RELOAD_CONFIG, 1); +} + +void wxOsmo4Frame::OnFileQuit(wxCommandEvent & WXUNUSED(event)) +{ + Close(FALSE); +} + +void wxOsmo4Frame::OnViewOriginal(wxCommandEvent & WXUNUSED(event)) +{ + if (!m_bExternalView) { + DoLayout(m_orig_width, m_orig_height); + } else { + gf_term_set_option(m_term, GF_OPT_ORIGINAL_VIEW, 1); + } +} + +void wxOsmo4Frame::OnOptions(wxCommandEvent & WXUNUSED(event)) +{ + wxGPACControl dlg(this); + dlg.SetIcon(wxIcon(osmo4)); + dlg.ShowModal(); +} + +void wxOsmo4Frame::DoConnect() +{ + //if (m_connected) { gf_term_disconnect(m_term); m_connected = 0; } + + wxString url = m_pPlayList->GetURL(); + m_Address->SetValue(url); +#ifdef __WXGTK__ + m_pVisual->SetFocus(); +#else + m_pView->SetFocus(); +#endif + wxString txt = wxT("Osmo4 - "); + txt += m_pPlayList->GetDisplayName(); + SetTitle(txt); + m_bStartupFile = 0; + gf_term_connect(m_term, url.mb_str(wxConvUTF8)); +} + +void wxOsmo4Frame::OnLogs(wxCommandEvent & WXUNUSED(event)) +{ + m_pLogs->Show(); +} + +void wxOsmo4Frame::OnUpdateNeedsConnect(wxUpdateUIEvent &event) +{ + event.Enable(m_connected ? 1 : 0); +} + +void wxOsmo4Frame::OnUpdatePlay(wxUpdateUIEvent &event) +{ + event.Enable( (m_connected || m_pPlayList->HasValidEntries()) ? 1 : 0); +} + +void wxOsmo4Frame::OnUpdateFullScreen(wxUpdateUIEvent &event) +{ + if (m_connected) { + event.Enable(1); + event.Check(gf_term_get_option(m_term, GF_OPT_FULLSCREEN) ? 1 : 0); + } else { + event.Enable(0); + } +} + +void wxOsmo4Frame::OnFullScreen(wxCommandEvent & WXUNUSED(event)) +{ + Bool isFS = gf_term_get_option(m_term, GF_OPT_FULLSCREEN) ? 1 : 0; + gf_term_set_option(m_term, GF_OPT_FULLSCREEN, isFS ? 0 : 1); +} + +void wxOsmo4Frame::OnViewARKeep(wxCommandEvent & WXUNUSED(event)) +{ + gf_term_set_option(m_term, GF_OPT_ASPECT_RATIO, GF_ASPECT_RATIO_KEEP); +} +void wxOsmo4Frame::OnViewARFill(wxCommandEvent & WXUNUSED(event)) +{ + gf_term_set_option(m_term, GF_OPT_ASPECT_RATIO, GF_ASPECT_RATIO_FILL_SCREEN); +} +void wxOsmo4Frame::OnViewAR169(wxCommandEvent & WXUNUSED(event)) +{ + gf_term_set_option(m_term, GF_OPT_ASPECT_RATIO, GF_ASPECT_RATIO_16_9); +} +void wxOsmo4Frame::OnViewAR43(wxCommandEvent & WXUNUSED(event)) +{ + gf_term_set_option(m_term, GF_OPT_ASPECT_RATIO, GF_ASPECT_RATIO_4_3); +} + +void wxOsmo4Frame::OnUpdateAR(wxUpdateUIEvent &event) +{ + if (!m_connected) { + event.Enable(0); + return; + } + event.Enable(1); + u32 val = gf_term_get_option(m_term, GF_OPT_ASPECT_RATIO); + if ((event.GetId() == VIEW_AR_FILL) && (val==GF_ASPECT_RATIO_FILL_SCREEN)) + event.Check(1); + else if ((event.GetId() == VIEW_AR_KEEP) && (val==GF_ASPECT_RATIO_KEEP)) + event.Check(1); + else if ((event.GetId() == VIEW_AR_169) && (val==GF_ASPECT_RATIO_16_9)) + event.Check(1); + else if ((event.GetId() == VIEW_AR_43) && (val==GF_ASPECT_RATIO_4_3)) + event.Check(1); + else event.Check(0); +} + +void wxOsmo4Frame::OnShortcuts(wxCommandEvent & WXUNUSED(event)) +{ + wxMessageDialog dlg(this, + wxT("Shortcuts with focus on main frame:\n") + wxT("Open File: Ctrl + O\n") + wxT("Show File Information: Ctrl + I\n") + wxT("Reload File: Ctrl + R\n") + wxT("Pause/Resume File: Ctrl + P\n") + wxT("Step by Step: Ctrl + S\n") + wxT("Fullscreen On/Off: Alt + Return\n") + wxT("View Playlist: Ctrl + L\n") + wxT("Aspect Ratio Normal: Ctrl + 1\n") + wxT("Aspect Ratio Fill: Ctrl + 2\n") + wxT("Aspect Ratio 4/3: Ctrl + 3\n") + wxT("Aspect Ratio 16/9: Ctrl + 4\n") + wxT("\n") + wxT("Shortcuts with focus on video frame:\n") + wxT("Seek +5% into presentation: Alt + right arrow\n") + wxT("Seek -5% into presentation: Alt + left arrow\n") + wxT("Seek +1min into presentation: Alt + up arrow\n") + wxT("Seek -1min into presentation: Alt + down arrow\n") + wxT("Next Playlist Entry: Ctrl + right arrow\n") + wxT("Prev Playlist Entry: Ctrl + left arrow\n") + + , wxT("Shortcuts Available on Osmo4") + , wxOK); + + dlg.ShowModal(); +} + +void wxOsmo4Frame::OnNavInfo(wxCommandEvent & WXUNUSED(event)) +{ + wxMessageDialog dlg(this, + wxT("* Walk & Fly modes:\n") + wxT("\tH move: H pan - V move: Z-translate - V move+CTRL or Wheel: V pan - Right Click (Walk only): Jump\n") + wxT("\tleft/right: H pan - left/right+CTRL: H translate - up/down: Z-translate - up/down+CTRL: V pan\n") + wxT("* Pan mode:\n") + wxT("\tH move: H pan - V move: V pan - V move+CTRL or Wheel: Z-translate\n") + wxT("\tleft/right: H pan - left/right+CTRL: H translate - up/down: V pan - up/down+CTRL: Z-translate\n") + wxT("* Slide mode:\n") + wxT("\tH move: H translate - V move: V translate - V move+CTRL or Wheel: Z-translate\n") + wxT("\tleft/right: H translate - left/right+CTRL: H pan - up/down: V translate - up/down+CTRL: Z-translate\n") + wxT("* Examine & Orbit mode:\n") + wxT("\tH move: Y-Axis rotate - H move+CTRL: No move - V move: X-Axis rotate - V move+CTRL or Wheel: Z-translate\n") + wxT("\tleft/right: Y-Axis rotate - left/right+CTRL: H translate - up/down: X-Axis rotate - up/down+CTRL: Y-translate\n") + wxT("* VR mode:\n") + wxT("\tH move: H pan - V move: V pan - V move+CTRL or Wheel: Camera Zoom\n") + wxT("\tleft/right: H pan - up/down: V pan - up/down+CTRL: Camera Zoom\n") + wxT("* Game mode (press END to escape):\n") + wxT("\tH move: H pan - V move: V pan\n") + wxT("\tleft/right: H translate - up/down: Z-translate\n") + wxT("\n") + wxT("* All 3D modes: CTRL+PGUP/PGDOWN will zoom in/out camera (field of view) \n") + wxT("\n") + wxT("*Slide Mode in 2D:\n") + wxT("\tH move: H translate - V move: V translate - V move+CTRL: zoom\n") + wxT("\tleft/right: H translate - up/down: V translate - up/down+CTRL: zoom\n") + wxT("*Examine Mode in 2D (3D renderer only):\n") + wxT("\tH move: Y-Axis rotate - V move: X-Axis rotate\n") + wxT("\tleft/right: Y-Axis rotate - up/down: X-Axis rotate\n") + wxT("\n") + wxT("HOME: reset navigation to last viewpoint (2D or 3D navigation)\n") + wxT("SHIFT key in all modes: fast movement\n") + + , wxT("3D navigation keys (\'H\'orizontal and \'V\'ertical) used in GPAC") + , wxOK ); + dlg.ShowModal(); +} + + +/*open file dlg*/ +class AboutDlg : public wxDialog { +public: + AboutDlg(wxWindow *parent); + +private: + wxStaticText *m_info; + wxButton *m_close; + void OnClose(wxCommandEvent& event); + DECLARE_EVENT_TABLE() +}; + +BEGIN_EVENT_TABLE(AboutDlg, wxDialog) + EVT_BUTTON(ID_ABOUT_CLOSE, AboutDlg::OnClose) +END_EVENT_TABLE() + +AboutDlg::AboutDlg(wxWindow *parent) + : wxDialog(parent, -1, wxString(wxT("GPAC/Osmo4 V ")wxT(GPAC_FULL_VERSION))) +{ + SetSize(220, 320); + Centre(); + wxBoxSizer *sizer = new wxBoxSizer(wxVERTICAL); + + m_info = new wxStaticText(this, -1, wxT("http://gpac.sourceforge.net"), wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE); + sizer->Add(m_info, 1, wxEXPAND|wxADJUST_MINSIZE, 0); + m_close = new wxButton(this, ID_ABOUT_CLOSE, wxT("Close"), wxDefaultPosition, wxSize(120, 20)); + sizer->Add(m_close, 0, wxEXPAND, 0); + + SetIcon(wxIcon(osmo4)); + m_info->SetLabel( + wxT("Osmo4 Player\n") + wxT("GPAC Multimedia Framework\n") + wxT("\n") + wxT("This program is gf_free software and may\n") + wxT("be distributed according to the terms\n") + wxT("of the GNU Lesser General Public License\n") + wxT("\n") + wxT("Copyright (c) Jean Le Feuvre 2000-2005\n") + wxT("(c) ENST 2005-200X\n") + wxT("All Rights Reserved\n") + wxT("http://gpac.sourceforge.net\n") + wxT("\n") + wxT(" ** With Many Thanks To ** \n\n") + wxT("Mozilla SpiderMonkey (JavaScript)\n") + wxT("The FreeType Project\n") + wxT("The PNG Group, The I.J.G.\n") + wxT("FFMPEG, FAAD, XVID, MAD\n") + ); + + SetSizer(sizer); + sizer->Fit(this); +} +void AboutDlg::OnClose(wxCommandEvent& WXUNUSED(event)) +{ + Close(FALSE); +} + +void wxOsmo4Frame::OnAbout(wxCommandEvent & WXUNUSED(event)) +{ + AboutDlg dlg(this); + dlg.ShowModal(); +} + + +void wxOsmo4Frame::OnGPACEvent(wxGPACEvent &event) +{ + wxString cmd; + wxCommandEvent evt; + if (!m_term) return; + + switch (event.gpac_evt.type) { + case GF_EVENT_NAVIGATE: + if (gf_term_is_supported_url(m_term, event.to_url.mb_str(wxConvUTF8), 1, 0)) { + char *str = gf_url_concatenate(m_pPlayList->GetURL().mb_str(wxConvUTF8), event.to_url.mb_str(wxConvUTF8)); + if (str) { + m_pPlayList->Truncate(); + m_pPlayList->QueueURL(wxString(str, wxConvUTF8)); + m_pPlayList->RefreshList(); + gf_free(str); + m_pPlayList->PlayNext(); + } + return; + } + cmd = get_pref_browser(m_user.config); + if (cmd.IsEmpty()){ + cmd += wxT(" "); + cmd += event.to_url; + wxExecute(cmd); + } else { +#ifdef wxLaunchDefaultBrowser + wxLaunchDefaultBrowser(event.to_url); +#endif + } + break; + case GF_EVENT_QUIT: + Close(TRUE); + break; + case GF_EVENT_SET_CAPTION: + SetTitle(event.to_url); + break; + case GF_EVENT_CONNECT: + BuildStreamList(0); + ConnectAcknowledged(event.gpac_evt.connect.is_connected); + break; + case GF_EVENT_KEYDOWN: + if (!(event.gpac_evt.key.flags & GF_KEY_MOD_CTRL)) return; + switch (event.gpac_evt.key.key_code) { + case GF_KEY_R: + gf_term_set_option(m_term, GF_OPT_REFRESH, 1); + break; + case GF_KEY_P: + OnFilePlay(evt); + break; + case GF_KEY_S: + OnFileStep(evt); + break; + } + break; + case GF_EVENT_SCENE_SIZE: + m_orig_width = event.gpac_evt.size.width; + m_orig_height = event.gpac_evt.size.height; + case GF_EVENT_SIZE: + if (! gf_term_get_option(m_term, GF_OPT_FULLSCREEN)) { + DoLayout(event.gpac_evt.size.width, event.gpac_evt.size.height); + } + break; + case GF_EVENT_VIEWPOINTS: + BuildViewList(); + break; + case GF_EVENT_STREAMLIST: + BuildStreamList(0); + break; + } +} + + +static wxString format_time(u32 duration, u32 timescale) +{ + u32 h, m, s; + Float time = duration; + time /= timescale; + time *= 1000; + h = (u32) (time / 1000 / 3600); + m = (u32) (time / 1000 / 60 - h*60); + s = (u32) (time / 1000 - h*3600 - m*60); + return wxString::Format(wxT("%02d:%02d:%02d"), h, m, s); +} + +void wxOsmo4Frame::SetStatus(wxString str) +{ + //m_pStatusbar->SetStatusText(str, 2); + m_LastStatusTime = gf_sys_clock(); +} + +#define RTI_REFRESH_MS 500 +void wxOsmo4Frame::OnRTI(wxCommandEvent & event) +{ + m_bViewRTI = event.IsChecked(); + if (m_bViewRTI) { + if (!m_pTimer->IsRunning()) m_pTimer->Start(RTI_REFRESH_MS, 0); + } else if (!m_connected && m_pTimer->IsRunning()) { + m_LastStatusTime = 0; + m_pStatusbar->SetStatusText(wxT("Ready"), 2); + m_pTimer->Stop(); + } +} + +void wxOsmo4Frame::OnTimer(wxTimerEvent& WXUNUSED(event)) +{ + wxString str; + u32 now; + if (m_LastStatusTime) { + now = gf_sys_clock(); + if (now > 1000+m_LastStatusTime) { + m_LastStatusTime = 0; + m_pStatusbar->SetStatusText(wxT("Ready"), 2); + } + } + + if (m_bViewRTI) { + GF_SystemRTInfo rti; + if (!gf_sys_get_rti(RTI_REFRESH_MS, &rti, 0)) return; + if (rti.gpac_memory) rti.process_memory = rti.gpac_memory; + + str = wxString::Format(wxT("CPU %02d (%02d) - Mem %d kB" ), + rti.total_cpu_usage, rti.process_cpu_usage, rti.gpac_memory/1024); + + m_pStatusbar->SetStatusText(str, 2); + } + if (!m_connected) return; + + now = gf_term_get_time_in_ms(m_term); + if (!now) return; + + if (!m_duration) { + str = format_time(now, 1000); + m_pStatusbar->SetStatusText(str); + str = wxString::Format(wxT("FPS %.2f"), gf_term_get_framerate(m_term, 0)); + m_pStatusbar->SetStatusText(str, 1); + return; + } +#ifdef __WXGTK__ + if (m_bGrabbed) { + u32 now = gf_sys_clock() - m_last_grab_time; + if (now>200) { + m_bGrabbed = 0; + Double res = (Double) m_last_grab_pos; + res /= 1000; + res *= m_duration; + if (gf_term_get_option(m_term, GF_OPT_PLAY_STATE)==GF_STATE_PAUSED) { + gf_term_set_option(m_term, GF_OPT_PLAY_STATE, GF_STATE_PLAYING); + m_bToReset = 0; + } + gf_term_play_from_time(m_term, (u32) res, 0); + return; + } + } +#endif + + if (!m_bGrabbed) { + if ((now >= m_duration + 500) && gf_term_get_option(m_term, GF_OPT_IS_FINISHED)) { + m_pPlayList->PlayNext(); + } else { + Double val = now * 1000; + val /= m_duration; + m_pProg->SetValue((val<=1000) ? (u32) val : 1000); + + if (0) { + str = format_time(m_duration-now, 1000); + } else { + str = format_time(now, 1000); + } + m_pStatusbar->SetStatusText(str); + str = wxString::Format(wxT("FPS %.2f"), gf_term_get_framerate(m_term, 0)); + m_pStatusbar->SetStatusText(str, 1); + } + } +} + +void wxOsmo4Frame::ConnectAcknowledged(Bool bOk) +{ + if (bOk) { + m_pTimer->Start(RTI_REFRESH_MS, 0); + m_connected = 1; + m_bToReset = 0; + UpdatePlay(); + BuildChapterList(0); + } else { + BuildChapterList(1); + if (!m_connected) { + UpdatePlay(); + m_pTimer->Stop(); + //m_pProg->Enable(0); + } + } +} + +void wxOsmo4Frame::OnFilePlay(wxCommandEvent & WXUNUSED(event)) +{ + wxCommandEvent evt; + if (m_connected) { + if (gf_term_get_option(m_term, GF_OPT_PLAY_STATE)==GF_STATE_PAUSED) { + gf_term_set_option(m_term, GF_OPT_PLAY_STATE, GF_STATE_PLAYING); + if (m_bToReset) { + m_pTimer->Start(100, 0); + gf_term_play_from_time(m_term, 0, 0); + } + m_bToReset = 0; + UpdatePlay(); + } else { + gf_term_set_option(m_term, GF_OPT_PLAY_STATE, GF_STATE_PAUSED); + UpdatePlay(); + } + } else { + m_pPlayList->Play(); + } +} + +void wxOsmo4Frame::OnFileStep(wxCommandEvent & WXUNUSED(event)) +{ + wxCommandEvent evt; + gf_term_set_option(m_term, GF_OPT_PLAY_STATE, GF_STATE_STEP_PAUSE); + UpdatePlay(); +} + +void wxOsmo4Frame::OnFileStop(wxCommandEvent &WXUNUSED(event)) +{ + Stop(); +} + +void wxOsmo4Frame::Stop() +{ + if (gf_term_get_option(m_term, GF_OPT_PLAY_STATE)==GF_STATE_PLAYING) { + gf_term_set_option(m_term, GF_OPT_PLAY_STATE, GF_STATE_PAUSED); + } + m_bToReset = 1; + m_pTimer->Stop(); + m_pProg->SetValue(0); + UpdatePlay(); +} + +void wxOsmo4Frame::OnSlide(wxScrollEvent &event) +{ + if (!m_duration) return; + + /*wxSlider on GTK is buggy, so track a release timeout*/ +#ifdef __WXGTK__ + m_last_grab_time = gf_sys_clock(); + m_bGrabbed = 1; + m_last_grab_pos = event.GetPosition(); + Double now = (Double) m_last_grab_pos; + now /= 1000; + now *= m_duration; + wxString str = format_time((u32) (now), 1000); + m_pStatusbar->SetStatusText(str); + if (!m_pTimer->IsRunning()) m_pTimer->Start(100, 0); +#else + s32 type = event.GetEventType(); + if (type == wxEVT_SCROLL_THUMBTRACK) { + m_bGrabbed = 1; + Double now = (Double) event.GetPosition(); + now /= 1000; + now *= m_duration; + wxString str = format_time((u32) (now), 1000); + m_pStatusbar->SetStatusText(str); + } + else if (m_bGrabbed){ + m_bGrabbed = 0; + Double res = (Double) m_pProg->GetValue(); + res /= 1000; + res *= m_duration; + if (gf_term_get_option(m_term, GF_OPT_PLAY_STATE)==GF_STATE_PAUSED) { + gf_term_set_option(m_term, GF_OPT_PLAY_STATE, GF_STATE_PLAYING); + m_bToReset = 0; + if (!m_pTimer->IsRunning()) m_pTimer->Start(100, 0); + } + gf_term_play_from_time(m_term, (u32) res, 0); + } +#endif +} + + +void wxOsmo4Frame::BuildViewList() +{ + if (!vp_list || !m_connected) return; + + while (vp_list->GetMenuItemCount()) { + wxMenuItem* it = vp_list->FindItemByPosition(0); + vp_list->Delete(it); + } + + s32 id = ID_VIEWPOINT_FIRST; + nb_viewpoints = 0; + while (1) { + const char *szName; + Bool bound; + GF_Err e = gf_term_get_viewpoint(m_term, nb_viewpoints+1, &szName, &bound); + if (e) break; + if (szName) { + vp_list->AppendCheckItem(id+nb_viewpoints, wxString(szName, wxConvUTF8) ); + } else { + vp_list->AppendCheckItem(id+nb_viewpoints, wxString::Format(wxT("Viewpoint #%d"), nb_viewpoints+1) ); + } + nb_viewpoints++; + } +} + +void wxOsmo4Frame::OnViewport(wxCommandEvent & event) +{ + u32 ID = event.GetId() - ID_VIEWPOINT_FIRST; + gf_term_set_viewpoint(m_term, ID+1, NULL); +} + +void wxOsmo4Frame::OnUpdateViewport(wxUpdateUIEvent & event) +{ + u32 ID = event.GetId() - ID_VIEWPOINT_FIRST; + const char *szName; + Bool bound; + gf_term_get_viewpoint(m_term, ID+1, &szName, &bound); + event.Enable(1); + if (bound) event.Check(1); +} + +void wxOsmo4Frame::OnNavigate(wxCommandEvent & event) +{ + switch (event.GetId()) { + case ID_NAVIGATE_NONE: gf_term_set_option(m_term, GF_OPT_NAVIGATION, GF_NAVIGATE_NONE); break; + case ID_NAVIGATE_WALK: gf_term_set_option(m_term, GF_OPT_NAVIGATION, GF_NAVIGATE_WALK); break; + case ID_NAVIGATE_FLY: gf_term_set_option(m_term, GF_OPT_NAVIGATION, GF_NAVIGATE_FLY); break; + case ID_NAVIGATE_EXAMINE: gf_term_set_option(m_term, GF_OPT_NAVIGATION, GF_NAVIGATE_EXAMINE); break; + case ID_NAVIGATE_PAN: gf_term_set_option(m_term, GF_OPT_NAVIGATION, GF_NAVIGATE_PAN); break; + case ID_NAVIGATE_SLIDE: gf_term_set_option(m_term, GF_OPT_NAVIGATION, GF_NAVIGATE_SLIDE); break; + case ID_NAVIGATE_ORBIT: gf_term_set_option(m_term, GF_OPT_NAVIGATION, GF_NAVIGATE_ORBIT); break; + case ID_NAVIGATE_GAME: gf_term_set_option(m_term, GF_OPT_NAVIGATION, GF_NAVIGATE_GAME); break; + } +} +void wxOsmo4Frame::OnNavigateReset(wxCommandEvent & WXUNUSED(event)) +{ + gf_term_set_option(m_term, GF_OPT_NAVIGATION_TYPE, 0); +} +void wxOsmo4Frame::OnUpdateNavigation(wxUpdateUIEvent & event) +{ + u32 ID = event.GetId(); + event.Enable(0); + if (!m_connected) return; + u32 type = gf_term_get_option(m_term, GF_OPT_NAVIGATION_TYPE); + bool enable = type ? 1 : 0; + + u32 mode = gf_term_get_option(m_term, GF_OPT_NAVIGATION); + /*common 2D/3D modes*/ + if (ID==ID_NAVIGATE_NONE) { event.Enable(enable); event.Check(mode ? 0 : 1); } + else if (ID==ID_NAVIGATE_EXAMINE) { event.Enable(enable); event.Check((mode==GF_NAVIGATE_EXAMINE) ? 1 : 0); } + else if (ID==ID_NAVIGATE_SLIDE) { event.Enable(enable); event.Check((mode==GF_NAVIGATE_SLIDE) ? 1 : 0); } + + if (type==GF_NAVIGATE_TYPE_2D) return; + event.Enable(enable); + if (ID==ID_NAVIGATE_WALK) event.Check((mode==GF_NAVIGATE_WALK) ? 1 : 0); + else if (ID==ID_NAVIGATE_FLY) event.Check((mode==GF_NAVIGATE_FLY) ? 1 : 0); + else if (ID==ID_NAVIGATE_PAN) event.Check((mode==GF_NAVIGATE_PAN) ? 1 : 0); + else if (ID==ID_NAVIGATE_ORBIT) event.Check((mode==GF_NAVIGATE_ORBIT) ? 1 : 0); + else if (ID==ID_NAVIGATE_GAME) event.Check((mode==GF_NAVIGATE_GAME) ? 1 : 0); +} + +void wxOsmo4Frame::OnRenderSwitch(wxCommandEvent &WXUNUSED(event)) +{ + const char *opt = gf_cfg_get_key(m_user.config, "Compositor", "ForceOpenGL"); + Bool use_gl = (opt && !stricmp(opt, "yes")) ? 1 : 0; + gf_cfg_set_key(m_user.config, "Compositor", "ForceOpenGL", use_gl ? "no" : "yes"); + + gf_term_set_option(m_term, GF_OPT_USE_OPENGL, !use_gl); + + UpdateRenderSwitch(); +} + +void wxOsmo4Frame::UpdateRenderSwitch() +{ + const char *opt = gf_cfg_get_key(m_user.config, "Compositor", "ForceOpenGL"); + m_pToolBar->RemoveTool(SWITCH_RENDER); + if (opt && !stricmp(opt, "yes")) + m_pToolBar->InsertTool(12, SWITCH_RENDER, *m_pSW2D, wxNullBitmap, FALSE, NULL, wxT("2D Rasterizer")); + else + m_pToolBar->InsertTool(12, SWITCH_RENDER, *m_pSW3D, wxNullBitmap, FALSE, NULL, wxT("OpenGL Rendering")); + +#ifdef WIN32 + /*there's a display bug with the menubtn, remove and reinsert*/ + m_pToolBar->RemoveTool(FILE_PREV); + m_pToolBar->RemoveTool(FILE_NEXT); + m_pToolBar->InsertControl(2, m_pPrevBut); + m_pToolBar->InsertControl(3, m_pNextBut); +#endif + m_pToolBar->Realize(); +} + +void wxOsmo4Frame::UpdatePlay() +{ + m_pToolBar->RemoveTool(FILE_PLAY); + if (m_connected) { + if (gf_term_get_option(m_term, GF_OPT_PLAY_STATE)==GF_STATE_PAUSED) + m_pToolBar->InsertTool(5, FILE_PLAY, *m_pPlay, wxNullBitmap, FALSE, NULL, wxT("Pause File")); + else + m_pToolBar->InsertTool(5, FILE_PLAY, *m_pPause, wxNullBitmap, FALSE, NULL, wxT("Play File")); + } else { + m_pToolBar->InsertTool(5, FILE_PLAY, *m_pPlay, wxNullBitmap, FALSE, NULL, wxT("Pause File")); + } + +#ifdef WIN32 + /*there's a display bug with the menubtn, remove and reinsert*/ + m_pToolBar->RemoveTool(FILE_PREV); + m_pToolBar->RemoveTool(FILE_NEXT); + m_pToolBar->InsertControl(2, m_pPrevBut); + m_pToolBar->InsertControl(3, m_pNextBut); +#endif + m_pToolBar->Realize(); +} + +void wxOsmo4Frame::OnCollide(wxCommandEvent & event) +{ + u32 ID = event.GetId(); + if (ID==ID_COLLIDE_NONE) gf_term_set_option(m_term, GF_OPT_COLLISION, GF_COLLISION_NONE); + else if (ID==ID_COLLIDE_REG) gf_term_set_option(m_term, GF_OPT_COLLISION, GF_COLLISION_NORMAL); + else if (ID==ID_COLLIDE_DISP) gf_term_set_option(m_term, GF_OPT_COLLISION, GF_COLLISION_DISPLACEMENT); +} +void wxOsmo4Frame::OnUpdateCollide(wxUpdateUIEvent & event) +{ + u32 ID = event.GetId(); + event.Enable(0); + if (!m_connected) return; + event.Enable(1); + u32 mode = gf_term_get_option(m_term, GF_OPT_COLLISION); + if (ID==ID_COLLIDE_NONE) { event.Check((mode==GF_COLLISION_NONE) ? 1 : 0); } + else if (ID==ID_COLLIDE_REG) { event.Check((mode==GF_COLLISION_NORMAL) ? 1 : 0); } + else if (ID==ID_COLLIDE_DISP) { event.Check((mode==GF_COLLISION_DISPLACEMENT) ? 1 : 0); } +} + +void wxOsmo4Frame::OnHeadlight(wxCommandEvent &WXUNUSED(event)) +{ + Bool val = !gf_term_get_option(m_term, GF_OPT_HEADLIGHT); + gf_term_set_option(m_term, GF_OPT_HEADLIGHT, val); +} +void wxOsmo4Frame::OnUpdateHeadlight(wxUpdateUIEvent & event) +{ + event.Enable(0); + if (!m_connected) return; + u32 type = gf_term_get_option(m_term, GF_OPT_NAVIGATION_TYPE); + if (type!=GF_NAVIGATE_TYPE_3D) return; + + event.Enable(1); + event.Check(gf_term_get_option(m_term, GF_OPT_HEADLIGHT) ? 1 : 0); +} +void wxOsmo4Frame::OnGravity(wxCommandEvent & WXUNUSED(event)) +{ + Bool val = gf_term_get_option(m_term, GF_OPT_GRAVITY) ? 0 : 1; + gf_term_set_option(m_term, GF_OPT_GRAVITY, val); +} +void wxOsmo4Frame::OnUpdateGravity(wxUpdateUIEvent & event) +{ + event.Enable(0); + if (!m_connected) return; + u32 type = gf_term_get_option(m_term, GF_OPT_NAVIGATION_TYPE); + if (type!=GF_NAVIGATE_TYPE_3D) return; + type = gf_term_get_option(m_term, GF_OPT_NAVIGATION); + if (type != GF_NAVIGATE_WALK) return; + event.Enable(1); + event.Check(gf_term_get_option(m_term, GF_OPT_GRAVITY) ? 1 : 0); +} + + +BEGIN_EVENT_TABLE(wxMyComboBox, wxComboBox) + EVT_KEY_UP(wxMyComboBox::OnKeyUp) +END_EVENT_TABLE() + +void wxMyComboBox::OnKeyUp(wxKeyEvent &event) +{ + if (event.GetKeyCode()==WXK_RETURN) { + event.Skip(); + wxCommandEvent evt; + evt.SetEventType(wxEVT_COMMAND_COMBOBOX_SELECTED); + evt.SetEventObject(this); + evt.SetId(GetId()); + GetParent()->AddPendingEvent(evt); + } +} + + +void wxOsmo4Frame::ReloadURLs() +{ + const char *sOpt; + u32 i=0; + + m_Address->Clear(); + while (1) { + sOpt = gf_cfg_get_key_name(m_user.config, "RecentFiles", i); + if (!sOpt) break; + m_Address->Append(wxString(sOpt, wxConvUTF8) ); + i++; + } +} + +void wxOsmo4Frame::SelectionReady() +{ + wxString urlVal = m_Address->GetValue(); + if (urlVal.Find(wxT("://"))>0) { + UpdateLastFiles(m_user.config, urlVal.mb_str(wxConvUTF8)); + ReloadURLs(); + } + m_pPlayList->Truncate(); + m_pPlayList->QueueURL(urlVal); + m_pPlayList->RefreshList(); + m_pPlayList->PlayNext(); +} + +void wxOsmo4Frame::OnURLSelect(wxCommandEvent &WXUNUSED(event)) +{ + SelectionReady(); +} + +void wxOsmo4Frame::OnPlaylist(wxCommandEvent &WXUNUSED(event)) +{ + assert(m_pPlayList); + m_pPlayList->Show(m_pPlayList->IsShown() ? 0 : 1); +} + +void wxOsmo4Frame::OnUpdatePlayList(wxUpdateUIEvent & event) +{ + event.Enable(1); + event.Check(m_pPlayList->IsShown() ? 1 : 0); +} + +void wxOsmo4Frame::OnFilePrevOpen(wxNotifyEvent & event) +{ + u32 count = gf_list_count(m_pPlayList->m_entries); + u32 start = m_pPlayList->m_cur_entry - 1; + wxMenu *popup = new wxMenu(); + + for (u32 i=0; i<10; i++) { + if (i > start) break; + if (start - i >= count) break; + PLEntry *ple = (PLEntry *) gf_list_get(m_pPlayList->m_entries, start - i); + popup->Append(ID_NAV_PREV_0 + i, wxString(ple->m_disp_name, wxConvUTF8) ); + } + m_pPrevBut->AssignMenu(popup); +} + +void wxOsmo4Frame::OnFileNextOpen(wxNotifyEvent & event) +{ + u32 count = gf_list_count(m_pPlayList->m_entries); + wxMenu *popup = new wxMenu(); + u32 start = m_pPlayList->m_cur_entry + 1; + for (u32 i=0; i<10; i++) { + if (start + i >= count) break; + PLEntry *ple = (PLEntry *) gf_list_get(m_pPlayList->m_entries, start + i); + popup->Append(ID_NAV_NEXT_0 + i, wxString(ple->m_disp_name, wxConvUTF8) ); + } + m_pNextBut->AssignMenu(popup); +} + +void wxOsmo4Frame::OnNavPrev(wxCommandEvent &WXUNUSED(event)) +{ + if (m_pPlayList->m_cur_entry<=0) return; + m_pPlayList->PlayPrev(); +} +void wxOsmo4Frame::OnUpdateNavPrev(wxUpdateUIEvent & event) +{ + if (m_pPlayList->m_cur_entry<=0) event.Enable(0); + else event.Enable(TRUE); +} +void wxOsmo4Frame::OnNavPrevMenu(wxCommandEvent &event) +{ + u32 ID = event.GetId() - ID_NAV_PREV_0; + s32 prev = m_pPlayList->m_cur_entry - ID; + if (prev>=0) { + m_pPlayList->m_cur_entry = prev; + m_pPlayList->PlayPrev(); + } +} +void wxOsmo4Frame::OnNavNext(wxCommandEvent &WXUNUSED(event)) +{ + /*don't play if last could trigger playlist loop*/ + if ((m_pPlayList->m_cur_entry<0) || (gf_list_count(m_pPlayList->m_entries) == 1 + (u32) m_pPlayList->m_cur_entry)) return; + m_pPlayList->PlayNext(); +} +void wxOsmo4Frame::OnUpdateNavNext(wxUpdateUIEvent & event) +{ + if (m_pPlayList->m_cur_entry<0) event.Enable(0); + else if ((u32) m_pPlayList->m_cur_entry + 1 == gf_list_count(m_pPlayList->m_entries) ) event.Enable(0); + else event.Enable(1); +} + +void wxOsmo4Frame::OnNavNextMenu(wxCommandEvent &event) +{ + u32 ID = event.GetId() - ID_NAV_NEXT_0; + s32 next = m_pPlayList->m_cur_entry + ID; + if (next < (s32) gf_list_count(m_pPlayList->m_entries) ) { + m_pPlayList->m_cur_entry = next; + m_pPlayList->PlayNext(); + } +} + +void wxOsmo4Frame::OnClearNav(wxCommandEvent &WXUNUSED(event)) +{ + m_pPlayList->ClearButPlaying(); +} + + +void wxOsmo4Frame::BuildStreamList(Bool reset_only) +{ + u32 nb_subs; + wxMenu *pMenu; + + pMenu = sel_menu->FindItemByPosition(0)->GetSubMenu(); + while (pMenu->GetMenuItemCount()) { + wxMenuItem* it = pMenu->FindItemByPosition(0); + pMenu->Delete(it); + } + pMenu = sel_menu->FindItemByPosition(1)->GetSubMenu(); + while (pMenu->GetMenuItemCount()) { + wxMenuItem* it = pMenu->FindItemByPosition(0); + pMenu->Delete(it); + } + pMenu = sel_menu->FindItemByPosition(2)->GetSubMenu(); + while (pMenu->GetMenuItemCount()) { + wxMenuItem* it = pMenu->FindItemByPosition(0); + pMenu->Delete(it); + } + + if (reset_only) { + m_bFirstStreamListBuild = 1; + return; + } + + if (!gf_term_get_option(m_term, GF_OPT_CAN_SELECT_STREAMS)) return; + + nb_subs = 0; + GF_ObjectManager *root_od = gf_term_get_root_object(m_term); + if (!root_od) return; + u32 count = gf_term_get_object_count(m_term, root_od); + + for (u32 i=0; iFindItemByPosition(0)->GetSubMenu(); + if (!info.owns_service) sprintf(szLabel, "Audio #"LLU, (u64)pMenu->GetMenuItemCount() + 1); + pMenu->AppendCheckItem(ID_SELSTREAM_0 +i, wxString(szLabel, wxConvUTF8)); + break; + case GF_STREAM_VISUAL: + pMenu = sel_menu->FindItemByPosition(1)->GetSubMenu(); + if (!info.owns_service) sprintf(szLabel, "Video #"LLU, (u64)pMenu->GetMenuItemCount() + 1); + pMenu->AppendCheckItem(ID_SELSTREAM_0 +i, wxString(szLabel, wxConvUTF8)); + break; + case GF_STREAM_TEXT: + nb_subs ++; + pMenu = sel_menu->FindItemByPosition(2)->GetSubMenu(); + if (!info.owns_service) sprintf(szLabel, "Subtitle #"LLU, (u64)pMenu->GetMenuItemCount() + 1); + pMenu->AppendCheckItem(ID_SELSTREAM_0 +i, wxString(szLabel, wxConvUTF8)); + break; + } + } + if (m_bFirstStreamListBuild) { + m_bFirstStreamListBuild = 0; + if (!nb_subs && m_lookforsubs) LookForSubtitles(); + } +} + +void wxOsmo4Frame::OnStreamSel(wxCommandEvent & event) +{ + GF_ObjectManager *root_od = gf_term_get_root_object(m_term); + if (!root_od) return; + u32 ID = event.GetId() - ID_SELSTREAM_0; + GF_ObjectManager *odm = gf_term_get_object(m_term, root_od, ID); + gf_term_select_object(m_term, odm); +} + +void wxOsmo4Frame::OnUpdateStreamSel(wxUpdateUIEvent & event) +{ + GF_ObjectManager *root_od = gf_term_get_root_object(m_term); + if (!root_od) return; + u32 ID = event.GetId() - ID_SELSTREAM_0; + + GF_ObjectManager *odm = gf_term_get_object(m_term, root_od, ID); + if (!odm) return; + + GF_MediaInfo info; + gf_term_get_object_info(m_term, odm, &info); + event.Enable(1); + event.Check(info.status ? 1 : 0); +} + +void wxOsmo4Frame::OnUpdateStreamMenu(wxUpdateUIEvent & event) +{ + if (!m_connected || !gf_term_get_option(m_term, GF_OPT_CAN_SELECT_STREAMS)) { + event.Enable(0); + } else { + event.Enable(1); + } +} + +void wxOsmo4Frame::OnAddSub(wxCommandEvent &WXUNUSED(event)) +{ + wxFileDialog dlg(this, wxT("Add Subtitle"), wxT(""), wxT(""), wxT("All Subtitles|*.srt;*.ttxt|SRT Subtitles|*.srt|3GPP TimedText|*.ttxt|"), wxOPEN | wxCHANGE_DIR /* | wxHIDE_READONLY*/); + + if (dlg.ShowModal() == wxID_OK) { + AddSubtitle(dlg.GetPath().mb_str(wxConvUTF8), 1); + } + +} + +void wxOsmo4Frame::AddSubtitle(const char *fileName, Bool auto_play) +{ + gf_term_add_object(m_term, fileName, auto_play); +} + +static Bool subs_enum_dir_item(void *cbck, char *item_name, char *item_path) +{ + wxOsmo4Frame *_this = (wxOsmo4Frame*)cbck; + _this->AddSubtitle(item_path, 0); + return 0; +} + +void wxOsmo4Frame::LookForSubtitles() +{ + char dir[GF_MAX_PATH]; + const char *url = m_pPlayList->GetURL().mb_str(wxConvUTF8); + strcpy(dir, url); + char *sep = strrchr(dir, '\\'); + if (!sep) strcpy(dir, ::wxGetCwd().mb_str(wxConvUTF8)); + else sep[0] = 0; + + gf_enum_directory(dir, 0, subs_enum_dir_item, this, "ttxt;srt"); +} + +void wxOsmo4Frame::OnCacheEnable(wxCommandEvent &WXUNUSED(event)) +{ + u32 state = gf_term_get_option(m_term, GF_OPT_MEDIA_CACHE); + if (state==GF_MEDIA_CACHE_DISABLED) { + gf_term_set_option(m_term, GF_OPT_MEDIA_CACHE, GF_MEDIA_CACHE_ENABLED); + } else if (state==GF_MEDIA_CACHE_DISABLED) { + gf_term_set_option(m_term, GF_OPT_MEDIA_CACHE, GF_MEDIA_CACHE_DISABLED); + } +} + +void wxOsmo4Frame::OnCacheStop(wxCommandEvent &WXUNUSED(event)) +{ + gf_term_set_option(m_term, GF_OPT_MEDIA_CACHE, GF_MEDIA_CACHE_DISABLED); +} + +void wxOsmo4Frame::OnCacheAbort(wxCommandEvent &WXUNUSED(event)) +{ + gf_term_set_option(m_term, GF_OPT_MEDIA_CACHE, GF_MEDIA_CACHE_DISCARD); +} + +void wxOsmo4Frame::OnUpdateCacheEnable(wxUpdateUIEvent & event) +{ + u32 state = gf_term_get_option(m_term, GF_OPT_MEDIA_CACHE); + switch (state) { + case GF_MEDIA_CACHE_ENABLED: + event.Enable(1); + event.SetText(wxT("Enabled")); + break; + case GF_MEDIA_CACHE_RUNNING: + event.SetText(wxT("Running")); + event.Enable(0); + break; + case GF_MEDIA_CACHE_DISABLED: + event.SetText(wxT("Disabled")); + break; + } +} + +void wxOsmo4Frame::OnUpdateCacheAbort(wxUpdateUIEvent & event) +{ + u32 state = gf_term_get_option(m_term, GF_OPT_MEDIA_CACHE); + event.Enable( (state==GF_MEDIA_CACHE_RUNNING) ? 1 : 0); +} + + + +void wxOsmo4Frame::BuildChapterList(Bool reset_only) +{ + GF_MediaInfo odi; + + while (chap_menu->GetMenuItemCount()) { + wxMenuItem* it = chap_menu->FindItemByPosition(0); + chap_menu->Delete(it); + } + if (m_chapters_start) gf_free(m_chapters_start); + m_chapters_start = NULL; + m_num_chapters = 0; + if (reset_only) return; + + GF_ObjectManager *root_od = gf_term_get_root_object(m_term); + if (!root_od) return; + if (gf_term_get_object_info(m_term, root_od, &odi) != GF_OK) return; + + u32 count = gf_list_count(odi.od->OCIDescriptors); + m_num_chapters = 0; + for (u32 i=0; iOCIDescriptors, i); + if (seg->tag != GF_ODF_SEGMENT_TAG) continue; + + if (seg->SegmentName && strlen((const char *)seg->SegmentName)) { + strcpy(szLabel, (const char *) seg->SegmentName); + } else { + sprintf(szLabel, "Chapter %02d", m_num_chapters+1); + } + chap_menu->AppendCheckItem(ID_SETCHAP_FIRST + m_num_chapters, wxString(szLabel, wxConvUTF8)); + + m_chapters_start = (Double *) gf_realloc(m_chapters_start, sizeof(Double)*(m_num_chapters+1)); + m_chapters_start[m_num_chapters] = seg->startTime; + m_num_chapters++; + } + + /*get any service info*/ + NetInfoCommand com; + if (!m_bStartupFile && gf_term_get_service_info(m_term, root_od, &com) == GF_OK) { + wxString title = wxT(""); + if (com.track_info) { title.Format(wxT("%02d "), (u32) (com.track_info>>16) ); } + if (com.artist) { title.Append(wxString(com.artist, wxConvUTF8)); title += wxT(" "); } + if (com.name) { title.Append(wxString(com.name, wxConvUTF8)); title += wxT(" "); } + if (com.album) { title += wxT("("); title.Append(wxString(com.album, wxConvUTF8)); title += wxT(")"); } + + if (title.length()) SetTitle(title); + } + +} + +void wxOsmo4Frame::OnChapterSel(wxCommandEvent & event) +{ + GF_ObjectManager *root_od = gf_term_get_root_object(m_term); + if (!root_od) return; + u32 ID = event.GetId() - ID_SETCHAP_FIRST; + gf_term_play_from_time(m_term, (u32) (1000*m_chapters_start[ID]), 0); +} + +void wxOsmo4Frame::OnUpdateChapterSel(wxUpdateUIEvent & event) +{ + Double now; + Bool is_current; + u32 ID = event.GetId() - ID_SETCHAP_FIRST; + + now = gf_term_get_time_in_ms(m_term); + now /= 1000; + + is_current = 0; + if (m_chapters_start[ID]<=now) { + if (ID+1now) is_current = 1; + } else { + is_current = 1; + } + } + event.Enable(1); + event.Check(is_current ? 1 : 0); +} + +void wxOsmo4Frame::OnUpdateChapterMenu(wxUpdateUIEvent & event) +{ + if (!m_connected || !m_num_chapters) { + event.Enable(0); + } else { + event.Enable(1); + } +} + +void wxOsmo4Frame::OnFileCopy(wxCommandEvent &event) +{ + const char *text = gf_term_get_text_selection(m_term, 0); + if (!text) return; + if (!wxTheClipboard->Open()) return; + + wxTheClipboard->SetData( new wxTextDataObject( wxString(text, wxConvUTF8)) ); + wxTheClipboard->Close(); +} + +void wxOsmo4Frame::OnUpdateFileCopy(wxUpdateUIEvent &event) +{ + if (gf_term_get_text_selection(m_term, 1)!=NULL) { + event.Enable(1); + } else { + event.Enable(0); + } +} + +void wxOsmo4Frame::OnFilePaste(wxCommandEvent &event) +{ + if (!wxTheClipboard->Open()) return; + if (wxTheClipboard->IsSupported( wxDF_TEXT )) { + wxTextDataObject data; + wxTheClipboard->GetData(data); + gf_term_paste_text(m_term, data.GetText().mb_str(wxConvUTF8), 0); + } + wxTheClipboard->Close(); +} + +void wxOsmo4Frame::OnUpdateFilePaste(wxUpdateUIEvent &event) +{ + Bool ok = 0; + if (wxTheClipboard->Open()) { + if (wxTheClipboard->IsSupported( wxDF_TEXT )) { + if (gf_term_paste_text(m_term, NULL, 1)==GF_OK) { + ok = 1; + } + } + wxTheClipboard->Close(); + } + event.Enable(ok ? 1 : 0); +} + diff --git a/applications/osmo4_wx/wxOsmo4.h b/applications/osmo4_wx/wxOsmo4.h new file mode 100644 index 0000000..6c2b463 --- /dev/null +++ b/applications/osmo4_wx/wxOsmo4.h @@ -0,0 +1,390 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Osmo4 wxWidgets GUI + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + */ + +#ifndef _WXOSMO4_H +#define _WXOSMO4_H + +/* +we need to force X to work in sync mode when we use embedded view... +include first to avoid Bool type redef between X11 and gpac +*/ +#ifdef __WXGTK__ +#include +#endif + +#include "wx/wxprec.h" + +#ifndef WX_PRECOMP +#include "wx/wx.h" +#endif + +#include +#include +#include +#include "menubtn.h" + +/*include gpac AFTER wx in case we override malloc/realloc/free for mem tracking*/ +#include +#include + +class wxOsmo4App : public wxApp +{ +public: + virtual bool OnInit(); +}; + +DECLARE_APP(wxOsmo4App) + +class wxOsmo4Frame; +class wxPlaylist; + +class GPACLogs : public wxLogWindow { +public: + GPACLogs(wxFrame *parent) : wxLogWindow(parent, wxT("GPAC Logs"), FALSE, FALSE) { + m_pMain = (wxOsmo4Frame *) parent; + } + virtual bool OnFrameClose(wxFrame *frame); + +private: + wxOsmo4Frame *m_pMain; +}; + +#define MAX_VIEWPOINTS 50 + +// Menu commands +enum +{ + GWX_FILE_OPEN = wxID_HIGHEST, + GWX_FILE_OPEN_URL, + FILE_RELOAD, + FILE_RELOAD_CONFIG, + FILE_PLAY, + FILE_STEP, + FILE_STOP, + FILE_PREV, + FILE_NEXT, + FILE_PROPERTIES, + FILE_COPY, + FILE_PASTE, + TERM_RELOAD, + FILE_QUIT, + VIEW_FULLSCREEN, + VIEW_ORIGINAL, + VIEW_AR_KEEP, + VIEW_AR_FILL, + VIEW_AR_43, + VIEW_AR_169, + VIEW_OPTIONS, + VIEW_LOGS, + VIEW_RTI, + VIEW_PLAYLIST, + SWITCH_RENDER, + APP_SHORTCUTS, + APP_NAV_KEYS, + APP_ABOUT, + ID_ADDRESS, + ID_URL_GO, + ID_ABOUT_CLOSE, + ID_CLEAR_NAV, + ID_STREAM_MENU, + ID_CHAPTER_MENU, + ID_ADD_SUB, + + ID_MCACHE_ENABLE, + ID_MCACHE_STOP, + ID_MCACHE_ABORT, + + ID_CTRL_TIMER, + ID_SLIDER, + + ID_TREE_VIEW, + ID_OD_TIMER, + ID_VIEW_SG, + ID_VIEW_WI, + ID_VIEW_SEL, + + + ID_HEADLIGHT, + ID_NAVIGATE_NONE, + ID_NAVIGATE_WALK, + ID_NAVIGATE_FLY, + ID_NAVIGATE_EXAMINE, + ID_NAVIGATE_SLIDE, + ID_NAVIGATE_PAN, + ID_NAVIGATE_ORBIT, + ID_NAVIGATE_GAME, + ID_NAVIGATE_RESET, + + ID_COLLIDE_NONE, + ID_COLLIDE_REG, + ID_COLLIDE_DISP, + ID_GRAVITY, + + ID_PL_OPEN, + ID_PL_SAVE, + ID_PL_ADD_FILE, + ID_PL_ADD_URL, + ID_PL_ADD_DIR, + ID_PL_ADD_DIR_REC, + ID_PL_REM_FILE, + ID_PL_REM_ALL, + ID_PL_REM_DEAD, + ID_PL_UP, + ID_PL_DOWN, + ID_PL_RANDOMIZE, + ID_PL_REVERSE, + ID_PL_SEL_REV, + ID_PL_SORT_TITLE, + ID_PL_SORT_FILE, + ID_PL_SORT_DUR, + ID_PL_PLAY, + + + /*reserve IDs for viewpoint menu*/ + ID_VIEWPOINT_FIRST, + ID_VIEWPOINT_LAST = ID_VIEWPOINT_FIRST + MAX_VIEWPOINTS, + + /*reserve IDs for navigation menus*/ + ID_NAV_PREV_0, + ID_NAV_PREV_9 = ID_NAV_PREV_0 + 10, + ID_NAV_NEXT_0, + ID_NAV_NEXT_9 = ID_NAV_NEXT_0 + 10, + /*reserve IDs for stream selection menus*/ + ID_SELSTREAM_0, + ID_SELSTREAM_9 = ID_SELSTREAM_0 + 10, + + /*reserve IDs for chapter selection menus*/ + ID_SETCHAP_FIRST, + ID_SETCHAP_LAST = ID_SELSTREAM_0 + 200, +}; + +wxString get_pref_browser(GF_Config *cfg); + +class wxGPACEvent : public wxEvent +{ +public: + wxGPACEvent( wxWindow* win = (wxWindow*) NULL ); + void CopyObject( wxObject& obj ) const; + virtual wxEvent *Clone() const; + + wxString to_url; + GF_Event gpac_evt; + + DECLARE_DYNAMIC_CLASS(wxGPACEvent) +}; +typedef void (wxEvtHandler::*GPACEventFunction)(wxGPACEvent&); +DEFINE_EVENT_TYPE(GPAC_EVENT) + +#define EVT_GPACEVENT(func) DECLARE_EVENT_TABLE_ENTRY(GPAC_EVENT, -1, -1, (wxObjectEventFunction) (wxEventFunction) (GPACEventFunction) & func, (wxObject*) NULL), + +class OpenURLDlg : public wxDialog { +public: + OpenURLDlg(wxWindow *parent, GF_Config *cfg); + wxString m_urlVal; +private: + wxButton *m_go; + wxComboBox *m_url; + GF_Config *m_cfg; + void OnGo(wxCommandEvent& event); + DECLARE_EVENT_TABLE() +}; + +class wxMyComboBox : public wxComboBox +{ +public: + wxMyComboBox(wxWindow* parent, wxWindowID id, const wxString& value = wxT(""), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize) + : wxComboBox(parent, id, value, pos, size, 0, NULL, wxCB_DROPDOWN) + {} + +private: + DECLARE_EVENT_TABLE() + + void OnKeyUp(wxKeyEvent &event); +}; + +class wxOsmo4Frame : public wxFrame { +public: + wxOsmo4Frame(); + virtual ~wxOsmo4Frame(); + + char szAppPath[GF_MAX_PATH]; + + u32 m_duration; + wxString the_next_url; + GF_Terminal *m_term; + GF_User m_user; + Bool m_connected, m_can_seek, m_console_off, m_loop, m_lookforsubs; + + void DoConnect(); + + void ConnectAcknowledged(Bool bOk); + void SetStatus(wxString str); + + void OnFilePlay(wxCommandEvent &event); + void OnFileStep(wxCommandEvent &event); + void OnFileStop(wxCommandEvent &event); + wxString GetFileFilter(); + + void BuildViewList(); + void BuildStreamList(Bool reset_only); + void BuildChapterList(Bool reset_only); + + void AddSubtitle(const char *fileName, Bool auto_play); + + wxWindow *m_pView; + +#ifdef __WXGTK__ + u32 m_last_grab_time, m_last_grab_pos; + wxWindow *m_pVisual; +#endif + wxSlider *m_pProg; + wxPlaylist *m_pPlayList; + + void DoLayout(u32 v_width = 0, u32 v_height = 0); + s32 m_last_prog; + + FILE *m_logs; + u32 m_log_level, m_log_tools; + u32 m_LastStatusTime; + +protected: + +private: + DECLARE_EVENT_TABLE() + + void OnCloseApp(wxCloseEvent &event); + void OnSize(wxSizeEvent &event); + + void OnFileOpen(wxCommandEvent &event); + void OnFileOpenURL(wxCommandEvent &event); + void OnFileReload(wxCommandEvent &event); + void OnFileReloadConfig(wxCommandEvent & event); + void OnFileProperties(wxCommandEvent &event); + void OnFileQuit(wxCommandEvent &event); + void OnFullScreen(wxCommandEvent &event); + void OnOptions(wxCommandEvent &event); + void OnViewARKeep(wxCommandEvent &event); + void OnViewARFill(wxCommandEvent &event); + void OnViewAR169(wxCommandEvent &event); + void OnViewAR43(wxCommandEvent &event); + void OnViewOriginal(wxCommandEvent &event); + void OnPlaylist(wxCommandEvent &event); + void OnShortcuts(wxCommandEvent &event); + void OnNavInfo(wxCommandEvent &event); + void OnAddSub(wxCommandEvent &event); + void OnAbout(wxCommandEvent &event); + Bool LoadTerminal(); + void OnGPACEvent(wxGPACEvent &event); + void OnTimer(wxTimerEvent& event); + void OnSlide(wxScrollEvent &event); + void OnRelease(wxScrollEvent &event); + void OnLogs(wxCommandEvent & event); + void OnRTI(wxCommandEvent & event); + void OnUpdatePlay(wxUpdateUIEvent &event); + void OnUpdateNeedsConnect(wxUpdateUIEvent &event); + void OnUpdateFullScreen(wxUpdateUIEvent &event); + void OnUpdateAR(wxUpdateUIEvent &event); + void OnViewport(wxCommandEvent & event); + void OnUpdateViewport(wxUpdateUIEvent & event); + void OnNavigate(wxCommandEvent & event); + void OnNavigateReset(wxCommandEvent & event); + void OnUpdateNavigation(wxUpdateUIEvent & event); + void OnRenderSwitch(wxCommandEvent &event); + void OnCollide(wxCommandEvent & event); + void OnUpdateCollide(wxUpdateUIEvent & event); + void OnHeadlight(wxCommandEvent & event); + void OnUpdateHeadlight(wxUpdateUIEvent & event); + void OnGravity(wxCommandEvent & event); + void OnUpdateGravity(wxUpdateUIEvent & event); + void OnURLSelect(wxCommandEvent &event); + void OnUpdatePlayList(wxUpdateUIEvent & event); + void OnFilePrevOpen(wxNotifyEvent & event); + void OnFileNextOpen(wxNotifyEvent & event); + void OnNavPrev(wxCommandEvent &event); + void OnUpdateNavPrev(wxUpdateUIEvent & event); + void OnNavPrevMenu(wxCommandEvent &event); + void OnNavNext(wxCommandEvent &event); + void OnUpdateNavNext(wxUpdateUIEvent & event); + void OnNavNextMenu(wxCommandEvent &event); + void OnClearNav(wxCommandEvent &event); + void OnStreamSel(wxCommandEvent &event); + void OnUpdateStreamSel(wxUpdateUIEvent & event); + void OnUpdateStreamMenu(wxUpdateUIEvent & event); + void OnChapterSel(wxCommandEvent &event); + void OnUpdateChapterSel(wxUpdateUIEvent & event); + void OnUpdateChapterMenu(wxUpdateUIEvent & event); + + void SelectionReady(); + void ReloadURLs(); + void LookForSubtitles(); + + void OnCacheEnable(wxCommandEvent &event); + void OnCacheStop(wxCommandEvent &event); + void OnCacheAbort(wxCommandEvent &event); + void OnUpdateCacheEnable(wxUpdateUIEvent & event); + void OnUpdateCacheAbort(wxUpdateUIEvent & event); + + void OnFileCopy(wxCommandEvent &event); + void OnUpdateFileCopy(wxUpdateUIEvent &event); + void OnFilePaste(wxCommandEvent &event); + void OnUpdateFilePaste(wxUpdateUIEvent &event); + + + void CheckVideoOut(); + + wxMenuBar* m_pMenubar; + wxStatusBar* m_pStatusbar; + wxTimer *m_pTimer; + GPACLogs *m_pLogs; + wxBoxSizer *m_pAddBar; + + Bool m_bGrabbed, m_bToReset, m_bFirstStreamListBuild; + wxBitmap *m_pOpenFile, *m_pPrev, *m_pNext, *m_pPlay, *m_pPause, *m_pStep, *m_pStop, *m_pInfo, *m_pConfig, *m_pSW2D, *m_pSW3D; + wxMenuButton *m_pPrevBut, *m_pNextBut; + wxToolBar *m_pToolBar; + wxMyComboBox *m_Address; + + wxMenu *vp_list; + wxMenu *sel_menu; + wxMenu *chap_menu; + void Stop(); + + s32 nb_viewpoints; + + void UpdateRenderSwitch(); + void UpdatePlay(); + + u32 m_orig_width, m_orig_height; + + u32 m_num_chapters; + Double *m_chapters_start; + Bool m_bExternalView, m_bViewRTI, m_bStartupFile; + + void ShowViewWindow(Bool do_show); +}; + + +#endif //_WXOSMO4_H + diff --git a/applications/osmo4_wx/wxOsmo4.rc b/applications/osmo4_wx/wxOsmo4.rc new file mode 100644 index 0000000..3e839b8 --- /dev/null +++ b/applications/osmo4_wx/wxOsmo4.rc @@ -0,0 +1,72 @@ +//Microsoft Developer Studio generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// French (France) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_FRA) +#ifdef _WIN32 +LANGUAGE LANG_FRENCH, SUBLANG_FRENCH +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_OSMO_ICON ICON DISCARDABLE "../../doc/osmo4.ico" +#endif // French (France) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/applications/osmozilla/Makefile b/applications/osmozilla/Makefile deleted file mode 100644 index 869f2be..0000000 --- a/applications/osmozilla/Makefile +++ /dev/null @@ -1,112 +0,0 @@ -include ../../config.mak - -vpath %.cpp $(SRC_PATH)/applications/osmozilla - -ifeq ($(CONFIG_WIN32),yes) -USER_NAME=root -else -USER_NAME=$(shell whoami) -ifeq ($(USER_NAME), root) -else -MOZILLA_DIR=local -endif -endif - -CFLAGS=$(CPPFLAGS) $(XUL_CFLAGS) -I"$(SRC_PATH)/include" - -ifeq ($(DEBUGBUILD), yes) -CFLAGS+=-g -LDFLAGS+=-g -endif - -ifeq ($(GPROFBUILD), yes) -CFLAGS+=-pg -LDFLAGS+=-pg -endif - -ifeq ($(CONFIG_WIN32),yes) -CFLAGS+=-DXP_WIN -else -ifeq ($(CONFIG_DARWIN),yes) -CFLAGS+=-DXP_MAC -else -CFLAGS+=-DXP_UNIX -DMOZ_X11 -endif -endif - -CFLAGS+=-DNPBASIC_EXPORTS -DMOZILLA_STRICT_API -DXPCOM_GLUE - - -LINKLIBS=-L../../bin/gcc -lgpac - -OBJS=osmozilla.o osmo_npapi.o - -SRCS := $(OBJS:.o=.cpp) - - -LIB=nposmozilla.$(DYN_LIB_SUFFIX) -ifeq ($(CONFIG_WIN32),yes) -LINKLIBS+=-lwinmm -lgdi32 -LDFLAGS+=--export-all-symbols -endif - -all: $(LIB) - -$(LIB): $(OBJS) -ifeq ($(CONFIG_WIN32),yes) - windres osmozilla.rc osmoz.o - $(CXX) $(SHFLAGS) $(LDFLAGS) -o ../../bin/gcc/$@ $(OBJS) osmoz.o $(LINKLIBS) - cp "$(SRC_PATH)/applications/osmozilla/nsIOsmozilla.xpt_w32" ../../bin/gcc/nposmozilla.xpt - chmod +w ../../bin/gcc/nposmozilla.xpt -else - $(CXX) $(SHFLAGS) $(LDFLAGS) $(OBJS) $(LINKLIBS) -o ../../bin/gcc/$@ - cp "$(SRC_PATH)/applications/osmozilla/nsIOsmozilla.xpt_linux" ../../bin/gcc/nposmozilla.xpt - chmod +w ../../bin/gcc/nposmozilla.xpt -endif - @echo $(USER_ROOT) - -%.o: %.cpp - $(CXX) $(CFLAGS) -c -o $@ $< - -clean: - rm -f $(OBJS) ../../bin/gcc/$(LIB) ../../bin/gcc/nposmozilla.xpt -ifeq ($(CONFIG_WIN32),yes) - rm -f osmoz.o -endif - -install: -ifeq ($(MOZILLA_DIR), local) -ifeq ($(USER_NAME), root) - @echo "*** Root cannot install local mozilla plugins! ***" - @echo "*** Exit root mode and reinstall mozilla plugin! ***" -else - $(MAKE) $(LIB) - install -D -m 755 ../../bin/gcc/$(LIB) "$(HOME)/.mozilla/plugins/$(LIB)" - install -D -m 755 ../../bin/gcc/nposmozilla.xpt "$(HOME)/.mozilla/components/nposmozilla.xpt" -endif -else - install -D -m 755 ../../bin/gcc/$(LIB) "$(MOZILLA_DIR)/components/$(LIB)" - install -D -m 755 ../../bin/gcc/nposmozilla.xpt "$(MOZILLA_DIR)/components/nposmozilla.xpt" -endif - -uninstall: -ifeq ($(MOZILLA_DIR), local) -ifeq ($(USER_NAME), root) -else - rm -rf "$(HOME)/.mozilla/plugins/$(LIB)" - rm -rf "$(HOME)/.mozilla/components/nposmozilla.xpt" -endif -else - rm -rf "$(MOZILLA_DIR)/components/$(LIB)" - rm -rf "$(MOZILLA_DIR)/components/nposmozilla.xpt" -endif - -dep: depend - -depend: - rm -f .depend - $(CC) -MM $(CFLAGS) $(SRCS) 1>.depend - -distclean: clean - rm -f Makefile.bak .depend - diff --git a/applications/osmozilla/nsIOsmozilla.h b/applications/osmozilla/nsIOsmozilla.h deleted file mode 100644 index 15869d4..0000000 --- a/applications/osmozilla/nsIOsmozilla.h +++ /dev/null @@ -1,125 +0,0 @@ -/* -* DO NOT EDIT. THIS FILE IS GENERATED FROM nsIOsmozilla.idl -*/ - -#ifndef __gen_nsIOsmozilla_h__ -#define __gen_nsIOsmozilla_h__ - -#include "osmo_npapi.h" - -#ifdef GECKO_XPCOM - -#ifndef __gen_nsISupports_h__ -#include "nsISupports.h" -#endif - -/* For IDL files that don't want to include root IDL files. */ -#ifndef NS_NO_VTABLE -#define NS_NO_VTABLE -#endif - -/* starting interface: nsIOsmozilla */ -#define NS_IOSMOZILLA_IID_STR "d2d536a0-b6fc-11d5-9d10-0060b0fbd80b" - -#define NS_IOSMOZILLA_IID \ - {0xd2d536a0, 0xb6fc, 0x11d5, \ - { 0x9d, 0x10, 0x00, 0x60, 0xb0, 0xfb, 0xd8, 0x0b }} - -class NS_NO_VTABLE nsIOsmozilla : public nsISupports { -public: - - NS_DEFINE_STATIC_IID_ACCESSOR(NS_IOSMOZILLA_IID) - - /* void Pause (); */ - NS_IMETHOD Pause(void) = 0; - - /* void Play (); */ - NS_IMETHOD Play(void) = 0; - - /* void Stop (); */ - NS_IMETHOD Stop(void) = 0; - - /* void Update (in string type, in string commands); */ - NS_IMETHOD Update(const char *type, const char *commands) = 0; - -}; - -/* Use this macro when declaring classes that implement this interface. */ -#define NS_DECL_NSIOSMOZILLA \ - NS_IMETHOD Pause(void); \ - NS_IMETHOD Play(void); \ - NS_IMETHOD Stop(void); \ - NS_IMETHOD Update(const char *type, const char *commands); - -/* Use this macro to declare functions that forward the behavior of this interface to another object. */ -#define NS_FORWARD_NSIOSMOZILLA(_to) \ - NS_IMETHOD Pause(void) { return _to Pause(); } \ - NS_IMETHOD Play(void) { return _to Play(); } \ - NS_IMETHOD Stop(void) { return _to Stop(); } \ - NS_IMETHOD Update(const char *type, const char *commands) { return _to Update(type, commands); } - -/* Use this macro to declare functions that forward the behavior of this interface to another object in a safe way. */ -#define NS_FORWARD_SAFE_NSIOSMOZILLA(_to) \ - NS_IMETHOD Pause(void) { return !_to ? NS_ERROR_NULL_POINTER : _to->Pause(); } \ - NS_IMETHOD Play(void) { return !_to ? NS_ERROR_NULL_POINTER : _to->Play(); } \ - NS_IMETHOD Stop(void) { return !_to ? NS_ERROR_NULL_POINTER : _to->Stop(); } \ - NS_IMETHOD Update(const char *type, const char *commands) { return !_to ? NS_ERROR_NULL_POINTER : _to->Update(type, commands); } - -#if 0 -/* Use the code below as a template for the implementation class for this interface. */ - -/* Header file */ -class nsOsmozilla : public nsIOsmozilla -{ -public: - NS_DECL_ISUPPORTS - NS_DECL_NSIOSMOZILLA - - nsOsmozilla(); - virtual ~nsOsmozilla(); - /* additional members */ -}; - -/* Implementation file */ -NS_IMPL_ISUPPORTS1(nsOsmozilla, nsIOsmozilla) - -nsOsmozilla::nsOsmozilla() -{ - /* member initializers and constructor code */ -} - -nsOsmozilla::~nsOsmozilla() -{ - /* destructor code */ -} - -/* void Pause (); */ -NS_IMETHODIMP nsOsmozilla::Pause() -{ - return NS_ERROR_NOT_IMPLEMENTED; -} - -/* void Play (); */ -NS_IMETHODIMP nsOsmozilla::Play() -{ - return NS_ERROR_NOT_IMPLEMENTED; -} - -/* void Stop (); */ -NS_IMETHODIMP nsOsmozilla::Stop() -{ - return NS_ERROR_NOT_IMPLEMENTED; -} - -/* void Update (in string type, in string commands); */ -NS_IMETHODIMP nsOsmozilla::Update(const char *type, const char *commands) -{ - return NS_ERROR_NOT_IMPLEMENTED; -} - -/* End of implementation class template. */ -#endif - -#endif //GECKO_XPCOM - -#endif /* __gen_nsIOsmozilla_h__ */ diff --git a/applications/osmozilla/nsIOsmozilla.idl b/applications/osmozilla/nsIOsmozilla.idl deleted file mode 100644 index 338fb96..0000000 --- a/applications/osmozilla/nsIOsmozilla.idl +++ /dev/null @@ -1,9 +0,0 @@ -#include "nsISupports.idl" - -[scriptable, uuid(d2d536a0-b6fc-11d5-9d10-0060b0fbd8bn)] -interface nsIOsmozilla : nsISupports { - void Pause(); - void Play(); - void Stop(); - void Update(in string type, in string commands); -}; diff --git a/applications/osmozilla/nsIOsmozilla.xpt_linux b/applications/osmozilla/nsIOsmozilla.xpt_linux deleted file mode 100644 index 1c96fbb3cbf5e69ed61129a611f127121875f92a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 180 zcmazDaQ64*3aKne^~p@)<&t7#VqjumVAul0N-XaA`q7eo;v=1CZ@soSR>jnUj+U(#`@>-XaA`q7eo;v=1CZ@soSR>jnUj+U(#`@> -#include -#endif - -NPNetscapeFuncs *sBrowserFunctions = NULL; - -NPError Osmozilla_GetURL(NPP instance, const char *url, const char *target) -{ - if (!sBrowserFunctions) return NPERR_INVALID_FUNCTABLE_ERROR; - return sBrowserFunctions->geturl(instance, url, target); -} - -void Osmozilla_SetStatus(NPP instance, const char *message) -{ - if (!sBrowserFunctions) return; - sBrowserFunctions->status(instance, message); -} - -#ifndef GECKO_XPCOM -void Osmozilla_InitScripting(Osmozilla *osmo); -#endif - -NPError NPOsmozilla_New(NPMIMEType pluginType, NPP instance, uint16_t mode, int16_t argc, char* argn[], char* argv[], NPSavedData* saved) -{ - Osmozilla *osmo; - NPError rv = NPERR_NO_ERROR; - if(instance == NULL) - return NPERR_INVALID_INSTANCE_ERROR; - - - osmo = (Osmozilla *) malloc(sizeof(Osmozilla)); - memset(osmo, 0, sizeof(Osmozilla)); - - osmo->np_instance = instance; - // associate the plugin instance object with NPP instance - instance->pdata = (void *)osmo; - - Osmozilla_Initialize(osmo, argc, argn, argv); - -#ifndef GECKO_XPCOM - Osmozilla_InitScripting(osmo); -#endif - - return rv; -} - -// here is the place to clean up and destroy the nsPluginInstance object -NPError NPOsmozilla_Destroy (NPP instance, NPSavedData** save) -{ - NPError rv = NPERR_NO_ERROR; - Osmozilla *osmozilla; - if(instance == NULL) - return NPERR_INVALID_INSTANCE_ERROR; - - osmozilla = (Osmozilla*)instance->pdata; - if (osmozilla != NULL) { - Osmozilla_Shutdown(osmozilla); -#ifdef GECKO_XPCOM - NPOsmozilla_ShutdownScript(osmozilla); -#else - if (osmozilla->script_obj) sBrowserFunctions->releaseobject(osmozilla->script_obj); - osmozilla->script_obj = NULL; -#endif - - free(osmozilla); - } - instance->pdata = NULL; - return rv; -} - -// during this call we know when the plugin window is ready or -// is about to be destroyed so we can do some gui specific -// initialization and shutdown -NPError NPOsmozilla_SetWindow (NPP instance, NPWindow* pNPWindow) -{ - Osmozilla *osmozilla; - void *os_wnd_handle, *os_wnd_display; - NPError rv = NPERR_NO_ERROR; - - if (!instance || !instance->pdata) return NPERR_INVALID_INSTANCE_ERROR; - if (pNPWindow == NULL) return NPERR_GENERIC_ERROR; - osmozilla = (Osmozilla *)instance->pdata; - - // window just created - if (!osmozilla->window_set) { - if (pNPWindow->window == NULL) return NPERR_GENERIC_ERROR; - -#ifdef XP_WIN - os_wnd_handle = pNPWindow->window; - os_wnd_display = NULL; -#elif defined(XP_MAXOS) - os_wnd_handle = pNPWindow->window; - os_wnd_display = NULL; -#elif defined(XP_UNIX) - os_wnd_handle = pNPWindow->window; - /*HACK - although we don't use the display in the X11 plugin, this is used to signal that - the user is mozilla and prevent some X11 calls crashing the browser in file playing mode - (eg, "firefox myfile.mp4" )*/ - os_wnd_display =((NPSetWindowCallbackStruct *)pNPWindow->ws_info)->display; -#endif - - if (! Osmozilla_SetWindow(osmozilla, os_wnd_handle, os_wnd_display, pNPWindow->width, pNPWindow->height) ) { - return NPERR_MODULE_LOAD_FAILED_ERROR; - } - - } - -#if 0 - // window goes away - if((pNPWindow->window == NULL) && plugin->isInitialized()) - return plugin->SetWindow(pNPWindow); - - // window resized? - if(plugin->isInitialized() && (pNPWindow->window != NULL)) - return plugin->SetWindow(pNPWindow); - - // this should not happen, nothing to do - if((pNPWindow->window == NULL) && !plugin->isInitialized()) - return plugin->SetWindow(pNPWindow); -#endif - - return rv; -} - -NPError NPOsmozilla_NewStream(NPP instance, NPMIMEType type, NPStream* stream, NPBool seekable, uint16_t *stype) -{ - Osmozilla *osmozilla; - if(instance == NULL) - return NPERR_INVALID_INSTANCE_ERROR; - - osmozilla = (Osmozilla *)instance->pdata; - if(osmozilla== NULL) - return NPERR_GENERIC_ERROR; - - Osmozilla_ConnectTo(osmozilla, stream->url); - *stype = NP_SEEK; - return NPERR_NO_ERROR; -} - -NPINT32 NPOsmozilla_WriteReady (NPP instance, NPStream *stream) -{ - return 0x0fffffff; -} - -NPINT32 NPOsmozilla_Write (NPP instance, NPStream *stream, NPINT32 offset, NPINT32 len, void *buffer) -{ - return len; -} - -NPError NPOsmozilla_DestroyStream (NPP instance, NPStream *stream, NPError reason) -{ - return NPERR_NO_ERROR; -} - -void NPOsmozilla_StreamAsFile (NPP instance, NPStream* stream, const char* fname) -{ -} - -void NPOsmozilla_Print (NPP instance, NPPrint* printInfo) -{ - Osmozilla *osmozilla; - if(instance == NULL) - return; - - osmozilla = (Osmozilla *)instance->pdata; - if(osmozilla== NULL) - return; - - Osmozilla_Print(osmozilla, (printInfo->mode == NP_EMBED) ? 1 : 0, printInfo->print.embedPrint.platformPrint, - printInfo->print.embedPrint.window.x, printInfo->print.embedPrint.window.y, - printInfo->print.embedPrint.window.width, printInfo->print.embedPrint.window.height); -} - -void NPOsmozilla_URLNotify(NPP instance, const char* url, NPReason reason, void* notifyData) -{ - return; -} - -NPError NPOsmozilla_GetValue(NPP instance, NPPVariable variable, void *value) -{ - NPError rv = NPERR_NO_ERROR; - Osmozilla *osmozilla; - if(instance == NULL) - return NPERR_INVALID_INSTANCE_ERROR; - - osmozilla = (Osmozilla *)instance->pdata; - if(osmozilla== NULL) return NPERR_GENERIC_ERROR; - - switch (variable) { -#ifdef GECKO_XPCOM - case NPPVpluginScriptableInstance: - rv = NPOsmozilla_GetPeer(osmozilla, value); - break; - - case NPPVpluginScriptableIID: - rv = NPOsmozilla_GetPeerIID(osmozilla, value); - break; -#else - - case NPPVpluginScriptableNPObject: - sBrowserFunctions->retainobject(osmozilla->script_obj); - * (void **)value = osmozilla->script_obj; - break; - -#endif - case NPPVpluginNameString : - *(const char**)value = "Osmozilla/GPAC plugin for NPAPI"; - break; - default: - break; - } - - return rv; -} - - - -NPError NPOsmozilla_SetValue(NPP instance, NPNVariable variable, void *value) -{ - return NPERR_NO_ERROR; -} - -int16_t NPOsmozilla_HandleEvent(NPP instance, void* event) -{ - /*we hacked the proc*/ - return 0; -} - - -NPError OSCALL NP_Shutdown() -{ -#ifdef GECKO_XPCOM - NPOsmozilla_ReleaseServiceManager(); -#endif - return NPERR_NO_ERROR; -} - -static NPError fillPluginFunctionTable(NPPluginFuncs* aNPPFuncs) -{ - if(aNPPFuncs == NULL) - return NPERR_INVALID_FUNCTABLE_ERROR; - - // Set up the plugin function table that Netscape will use to - // call us. Netscape needs to know about our version and size - // and have a UniversalProcPointer for every function we implement. - - aNPPFuncs->version = (NP_VERSION_MAJOR << 8) | NP_VERSION_MINOR; - aNPPFuncs->newp = NPOsmozilla_New; - aNPPFuncs->destroy = NPOsmozilla_Destroy; - aNPPFuncs->setwindow = NPOsmozilla_SetWindow; - aNPPFuncs->newstream = NPOsmozilla_NewStream; - aNPPFuncs->destroystream = NPOsmozilla_DestroyStream; - aNPPFuncs->asfile = NPOsmozilla_StreamAsFile; - aNPPFuncs->writeready = NPOsmozilla_WriteReady; - aNPPFuncs->write = NPOsmozilla_Write; - aNPPFuncs->print = NPOsmozilla_Print; - aNPPFuncs->event = NPOsmozilla_HandleEvent; - aNPPFuncs->urlnotify = NPOsmozilla_URLNotify; - aNPPFuncs->getvalue = NPOsmozilla_GetValue; - aNPPFuncs->setvalue = NPOsmozilla_SetValue; - return NPERR_NO_ERROR; -} - - -static NPError NS_PluginInitialize() -{ -#ifdef GECKO_XPCOM - NPOsmozilla_GetServiceManager(); -#endif - return NPERR_NO_ERROR; -} - -// get values per plugin -NPError NS_PluginGetValue(NPPVariable aVariable, void *aValue) -{ - NPError err = NPERR_NO_ERROR; - switch (aVariable) { - case NPPVpluginNameString: - *((char **)aValue) = (char *) "Osmozilla"; - break; - case NPPVpluginDescriptionString: - *((char **)aValue) = Osmozilla_GetVersion(); - break; - default: - err = NPERR_INVALID_PARAM; - break; - } - return err; -} - - - - -#define GPAC_PLUGIN_MIMETYPES \ - "audio/mpeg:mp2,mp3,mpga,mpega:MP3 Music;" \ - "audio/x-mpeg:mp2,mp3,mpga,mpega:MP3 Music;" \ - "audio/amr:amr,awb:AMR Audio;" \ - "audio/mp4:mp4,mpg4,mpeg4,m4a:MPEG-4 Audio;" \ - "audio/aac:aac:MPEG-4 AAC Music;" \ - "audio/aacp:aac:MPEG-4 AACPlus Music;" \ - "audio/basic:snd,au:Basic Audio;" \ - "audio/x-wav:wav:WAV Audio;" \ - "audio/3gpp:3gp,3gpp:3GPP/MMS Music;" \ - "audio/3gpp2:3g2,3gp2:3GPP2/MMS Music;" \ - "video/mpeg:mpg,mpeg,mpe,mpv2:MPEG Video;" \ - "video/x-mpeg:mpg,mpeg,mpe,mpv2:MPEG Video;" \ - "video/mpeg-system:mpg,mpeg,mpe,vob,mpv2:MPEG Video;" \ - "video/x-mpeg-system:mpg,mpeg,mpe,vob,mpv2:MPEG Video;" \ - "video/avi:avi:AVI Video;" \ - "video/quicktime:mov,qt:QuickTime Movies;" \ - "video/x-ms-asf:asf,asx:Windows Media Video;" \ - "video/x-ms-wmv:wmv:Windows Media;" \ - "video/mp4:mp4,mpg4:MPEG-4 Video;" \ - "video/3gpp:3gp,3gpp:3GPP/MMS Video;" \ - "video/3gpp2:3g2,3gp2:3GPP2/MMS Video;" \ - "image/jpeg:jpeg,jpg:JPEG Images;" \ - "image/png:png:PNG Images;" \ - "image/bmp:bmp:MS Bitmap Images;" \ - "image/svg+xml:svg,svg.gz,svgz:SVG Document;" \ - "image/x-svgm:svgm:SVGM Document;" \ - "x-subtitle/srt:srt:SRT SubTitles;" \ - "x-subtitle/sub:sub:SUB SubTitles;" \ - "x-subtitle/ttxt:ttxt:GPAC 3GPP TimedText;" \ - "model/vrml:wrl,wrl.gz:VRML World;" \ - "model/x3d+vrml:x3dv,x3dv.gz,x3dvz:X3D/VRML World;" \ - "model/x3d+xml:x3d,x3d.gz,x3dz:X3D/XML World;" \ - "application/ogg:ogg:Ogg Media;" \ - "application/x-ogg:ogg:Ogg Media;" \ - "application/x-bt:bt,bt.gz,btz:MPEG-4 Text (BT);" \ - "application/x-xmt:xmt,xmt.gz,xmtz:MPEG-4 Text (XMT);" \ - "application/mp4:mp4,mpg4:MPEG-4 Movies;" \ - "application/sdp:sdp:Streaming Media Session;" \ - /* explicit plugin call */ \ - "application/x-gpac::GPAC plugin;" \ - -char * NP_GetMIMEDescription(void) -{ - return (char *) GPAC_PLUGIN_MIMETYPES; -} - - -NPError NP_GetValue(void *future, NPPVariable aVariable, void *aValue) -{ - return NS_PluginGetValue(aVariable, aValue); -} - - -#if defined(XP_WIN) || defined(XP_MACOS) - -NPError OSCALL NP_Initialize(NPNetscapeFuncs* aNPNFuncs) -{ - sBrowserFunctions = aNPNFuncs; - - return NS_PluginInitialize(); -} - -NPError OSCALL NP_GetEntryPoints(NPPluginFuncs* aNPPFuncs) -{ - return fillPluginFunctionTable(aNPPFuncs); -} - - -#elif defined(XP_UNIX) - -NPError OSCALL NP_Initialize(NPNetscapeFuncs* aNPNFuncs, NPPluginFuncs* aNPPFuncs) -{ - NPError rv; - sBrowserFunctions = aNPNFuncs; - rv = fillPluginFunctionTable(aNPPFuncs); - if(rv != NPERR_NO_ERROR) - return rv; - - return NS_PluginInitialize(); -} -#endif - - -#ifdef GECKO_XPCOM - - -#include -#include -#include -#include -#include - -#include "nsIOsmozilla.h" - -#include "osmozilla.h" - - -nsIServiceManager *gServiceManager = NULL; - - -// We must implement nsIClassInfo because it signals the -// Mozilla Security Manager to allow calls from JavaScript. -// helper class to implement all necessary nsIClassInfo method stubs -// and to set flags used by the security system - -class nsClassInfoMixin : public nsIClassInfo -{ - // These flags are used by the DOM and security systems to signal that - // JavaScript callers are allowed to call this object's scritable methods. - NS_IMETHOD GetFlags(PRUint32 *aFlags) - {*aFlags = nsIClassInfo::PLUGIN_OBJECT | nsIClassInfo::DOM_OBJECT; - return NS_OK;} - - NS_IMETHOD GetImplementationLanguage(PRUint32 *aImplementationLanguage) - {*aImplementationLanguage = nsIProgrammingLanguage::CPLUSPLUS; - return NS_OK;} - - // The rest of the methods can safely return error codes... - NS_IMETHOD GetInterfaces(PRUint32 *count, nsIID * **array) - {return NS_ERROR_NOT_IMPLEMENTED;} - NS_IMETHOD GetHelperForLanguage(PRUint32 language, nsISupports **_retval) - {return NS_ERROR_NOT_IMPLEMENTED;} - NS_IMETHOD GetContractID(char * *aContractID) - {return NS_ERROR_NOT_IMPLEMENTED;} - NS_IMETHOD GetClassDescription(char * *aClassDescription) - {return NS_ERROR_NOT_IMPLEMENTED;} - NS_IMETHOD GetClassID(nsCID * *aClassID) - {return NS_ERROR_NOT_IMPLEMENTED;} - NS_IMETHOD GetClassIDNoAlloc(nsCID *aClassIDNoAlloc) - {return NS_ERROR_NOT_IMPLEMENTED;} -}; - - -class nsOsmozillaPeer : public nsIOsmozilla , public nsClassInfoMixin -{ -public: - nsOsmozillaPeer(Osmozilla *osmo); - virtual ~nsOsmozillaPeer(); - - // methods from nsISupports - NS_IMETHOD QueryInterface(const nsIID & aIID, void **aInstancePtr); - NS_IMETHOD_(nsrefcnt) AddRef(); - NS_IMETHOD_(nsrefcnt) Release(); - -public: - NS_DECL_NSIOSMOZILLA - void SetInstance(Osmozilla *osmo); - -protected: - nsrefcnt mRefCnt; - Osmozilla *mPlugin; -}; - - -static NS_DEFINE_IID(kIZillaPluginIID, NS_IOSMOZILLA_IID); -static NS_DEFINE_IID(kIClassInfoIID, NS_ICLASSINFO_IID); -static NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID); - -nsOsmozillaPeer::nsOsmozillaPeer(Osmozilla *osmo) -{ - mPlugin=osmo; - mRefCnt = 0; -} - -nsOsmozillaPeer::~nsOsmozillaPeer() -{ -} -// Notice that we expose our claim to implement nsIClassInfo. -//NS_IMPL_ISUPPORTS2(nsOsmozillaPeer, nsITestPlugin, nsIClassInfo) - -// the following method will be callable from JavaScript -NS_IMETHODIMP nsOsmozillaPeer::Pause() { - Osmozilla_Pause(mPlugin); - return NS_OK; -} -NS_IMETHODIMP nsOsmozillaPeer::Play() { - Osmozilla_Play(mPlugin); - return NS_OK; -} -NS_IMETHODIMP nsOsmozillaPeer::Stop() { - Osmozilla_Stop(mPlugin); - return NS_OK; -} - -NS_IMETHODIMP nsOsmozillaPeer::Update(const char *type, const char *commands) -{ - Osmozilla_Update(mPlugin, type, commands); - return NS_OK; -} - -void nsOsmozillaPeer::SetInstance(Osmozilla *osmo) -{ - mPlugin = osmo; -} - -NS_IMETHODIMP_(nsrefcnt) nsOsmozillaPeer::AddRef() -{ - ++mRefCnt; - return mRefCnt; -} - -NS_IMETHODIMP_(nsrefcnt) nsOsmozillaPeer::Release() -{ - --mRefCnt; - if (mRefCnt == 0) { - delete this; - return 0; - } - return mRefCnt; -} - -// here nsOsmozillaPeer should return three interfaces it can be asked for by their iid's -// static casts are necessary to ensure that correct pointer is returned -NS_IMETHODIMP nsOsmozillaPeer::QueryInterface(const nsIID & aIID, - void **aInstancePtr) -{ - if (!aInstancePtr) - return NS_ERROR_NULL_POINTER; - - if (aIID.Equals(kIZillaPluginIID)) { - *aInstancePtr = NS_STATIC_CAST(nsIOsmozilla *, this); - AddRef(); - return NS_OK; - } - - if (aIID.Equals(kIClassInfoIID)) { - *aInstancePtr = NS_STATIC_CAST(nsIClassInfo *, this); - AddRef(); - return NS_OK; - } - - if (aIID.Equals(kISupportsIID)) { - *aInstancePtr = NS_STATIC_CAST(nsISupports *, (NS_STATIC_CAST (nsIOsmozilla *, this))); - AddRef(); - return NS_OK; - } - return NS_NOINTERFACE; -} - - -extern NPNetscapeFuncs *sBrowserFunctions; - -void NPOsmozilla_GetServiceManager() -{ - // this is probably a good place to get the service manager - // note that Mozilla will add reference, so do not forget to release - nsISupports *sm = NULL; - - if (!sBrowserFunctions) return; - sBrowserFunctions->getvalue(NULL, NPNVserviceManager, &sm); - - // Mozilla returns nsIServiceManager so we can use it directly; doing QI on - // nsISupports here can still be more appropriate in case something is changed - // in the future so we don't need to do casting of any sort. - if (sm) { - sm->QueryInterface(NS_GET_IID(nsIServiceManager), (void **) &gServiceManager); - NS_RELEASE(sm); - } -} - -void NPOsmozilla_ReleaseServiceManager() -{ -#ifdef GECKO_XPCOM - // we should release the service manager - NS_IF_RELEASE(gServiceManager); - gServiceManager = NULL; -#endif -} - -void NPOsmozilla_ShutdownScript(Osmozilla *osmo) -{ - nsOsmozillaPeer *peer = (nsOsmozillaPeer *) osmo->scriptable_peer; - if (peer != NULL) { - peer->SetInstance(NULL); - NS_IF_RELEASE(peer); - } -} - - -NPError NPOsmozilla_GetPeer(Osmozilla *osmo, void *value) -{ - if (!osmo->scriptable_peer) { - osmo->scriptable_peer = new nsOsmozillaPeer(osmo); - if (!osmo->scriptable_peer) return NPERR_OUT_OF_MEMORY_ERROR; - NS_ADDREF( (nsOsmozillaPeer *) osmo->scriptable_peer); - } - - NS_ADDREF( (nsOsmozillaPeer *)osmo->scriptable_peer); - *(nsISupports **) value = (nsISupports *) osmo->scriptable_peer; - return NPERR_NO_ERROR; -} - -NPError NPOsmozilla_GetPeerIID(Osmozilla *osmo, void *value) -{ - static nsIID scriptableIID = NS_IOSMOZILLA_IID; - if (!sBrowserFunctions) return NPERR_OUT_OF_MEMORY_ERROR; - - nsIID *ptr = (nsIID *) sBrowserFunctions->memalloc( sizeof(nsIID) ); - if (! ptr) return NPERR_OUT_OF_MEMORY_ERROR; - - *ptr = scriptableIID; - *(nsIID **) value = ptr; - return NPERR_NO_ERROR; -} - -#else - -#define kOSMOZILLA_ID_METHOD_PLAY 0 -#define kOSMOZILLA_ID_METHOD_PAUSE 1 -#define kOSMOZILLA_ID_METHOD_STOP 2 -#define kOSMOZILLA_ID_METHOD_UPDATE 3 - -#define kOSMOZILLA_NUM_METHODS 4 - -NPIdentifier v_OSMOZILLA_MethodIdentifiers[kOSMOZILLA_NUM_METHODS]; -const NPUTF8 * v_OSMOZILLA_MethodNames[kOSMOZILLA_NUM_METHODS] = { - "Play", - "Pause", - "Stop", - "Update" -}; - -NPClass osmozilla_script_class; - -typedef struct { - NPClass *_class; - uint32_t referenceCount; - Osmozilla *osmo; -} OsmozillaObject; - -NPObject *OSMOZILLA_Allocate(NPP npp, NPClass *theClass) -{ - OsmozillaObject *obj = NULL; - - sBrowserFunctions->getstringidentifiers(v_OSMOZILLA_MethodNames, kOSMOZILLA_NUM_METHODS, v_OSMOZILLA_MethodIdentifiers); - obj = (OsmozillaObject *)malloc(sizeof(OsmozillaObject)); - obj->osmo = (Osmozilla *) npp->pdata; - return (NPObject *)obj; -} - -void OSMOZILLA_Deallocate(NPObject* obj) -{ - free(obj); - return; -} - -void OSMOZILLA_Invalidate(NPObject* obj) -{ - return; -} - -bool OSMOZILLA_HasMethod(NPObject* obj, NPIdentifier name) -{ - int i = 0; - while (i < kOSMOZILLA_NUM_METHODS) { - if ( name == v_OSMOZILLA_MethodIdentifiers[i] ) { - return 1; - } - i++; - } - return 0; -} - -bool OSMOZILLA_Invoke(NPObject* obj, NPIdentifier name, const NPVariant* args, uint32_t argCount, NPVariant* result) -{ - OsmozillaObject *npo = (OsmozillaObject *)obj; - if (!npo->osmo) return 0; - if (name == v_OSMOZILLA_MethodIdentifiers[kOSMOZILLA_ID_METHOD_PLAY]) { - Osmozilla_Play(npo->osmo); - return 1; - } - if (name == v_OSMOZILLA_MethodIdentifiers[kOSMOZILLA_ID_METHOD_PAUSE]) { - Osmozilla_Pause(npo->osmo); - return 1; - } - if (name == v_OSMOZILLA_MethodIdentifiers[kOSMOZILLA_ID_METHOD_STOP]) { - Osmozilla_Stop(npo->osmo); - return 1; - } - if (name == v_OSMOZILLA_MethodIdentifiers[kOSMOZILLA_ID_METHOD_UPDATE]) { - const char *mime = NULL; - const char *update = NULL; - if (argCount==2) { - mime = (args[0].type==NPVariantType_String) ? args[0].value.stringValue.UTF8Characters : NULL; - update = (args[1].type==NPVariantType_String) ? args[1].value.stringValue.UTF8Characters : NULL; - } - if (!update) return 0; - Osmozilla_Update(npo->osmo, mime, update); - return 1; - } - return 0; -} - -bool OSMOZILLA_InvokeDefault(NPObject *npobj, const NPVariant *args, uint32_t argCount, NPVariant *result) -{ - return 1; -} - -bool OSMOZILLA_HasProperty(NPObject* obj, NPIdentifier name) -{ - bool result = 0; - /*nothing exposed yet*/ - return result; -} - -bool OSMOZILLA_GetProperty(NPObject* obj, NPIdentifier name, NPVariant* result) -{ - return 1; -} - -bool OSMOZILLA_SetProperty(NPObject *obj, NPIdentifier name, const NPVariant *value) -{ - return 1; -} - -bool OSMOZILLA_RemoveProperty(NPObject *npobj, NPIdentifier name) -{ - return 1; -} - -bool OSMOZILLA_Enumerate(NPObject *npobj, NPIdentifier **value, uint32_t *count) -{ - return 1; -} - -void Osmozilla_InitScripting(Osmozilla *osmo) -{ - osmozilla_script_class.allocate = OSMOZILLA_Allocate; - osmozilla_script_class.deallocate = OSMOZILLA_Deallocate; - osmozilla_script_class.invalidate = OSMOZILLA_Invalidate; - osmozilla_script_class.hasMethod = OSMOZILLA_HasMethod; - osmozilla_script_class.invoke = OSMOZILLA_Invoke; - osmozilla_script_class.invokeDefault = OSMOZILLA_InvokeDefault; - osmozilla_script_class.hasProperty = OSMOZILLA_HasProperty; - osmozilla_script_class.getProperty = OSMOZILLA_GetProperty; - osmozilla_script_class.setProperty = OSMOZILLA_SetProperty; - osmozilla_script_class.removeProperty = OSMOZILLA_RemoveProperty; - osmozilla_script_class.enumerate = OSMOZILLA_Enumerate; - - /*create script object*/ - osmo->script_obj = sBrowserFunctions->createobject(osmo->np_instance, &osmozilla_script_class); - -} - -#endif //GECKO_XPCOM - diff --git a/applications/osmozilla/osmo_npapi.h b/applications/osmozilla/osmo_npapi.h deleted file mode 100644 index cd2c3bf..0000000 --- a/applications/osmozilla/osmo_npapi.h +++ /dev/null @@ -1,133 +0,0 @@ -/* -* GPAC - Multimedia Framework C SDK -* -* Copyright (c) ENST 2000-200X -* All rights reserved -* -* This file is part of GPAC / Osmozilla NPAPI plugin -* -* GPAC is free software; you can redistribute it and/or modify -* it under the terms of the GNU Lesser General Public License as published by -* the Free Software Foundation; either version 2, or (at your option) -* any later version. -* -* GPAC is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU Lesser General Public License for more details. -* -* You should have received a copy of the GNU Lesser General Public -* License along with this library; see the file COPYING. If not, write to -* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. -* -*/ - -#ifndef _OSMO_NPAPI_H_ -#define _OSMO_NPAPI_H_ - - -#ifdef WIN32 -#include -#ifndef __cplusplus -typedef BOOL bool; -#endif //__cplusplus -#endif - -#include "npapi.h" - -/*check this with gecko 1.9.2*/ -#if (NP_VERSION_MINOR < 20) -#define GECKO_XPCOM -#endif - -#ifdef GECKO_XPCOM -#include "npupp.h" - -#ifndef uint16_t -typedef uint16 uint16_t; -#endif - -#ifndef int16_t -typedef int16 int16_t; -#endif - -#define NPINT32 int32 - -#else - -#include "npfunctions.h" - -#define NPINT32 int32_t - -#endif - -#ifdef XP_UNIX -#include -#endif //XP_UNIX - - -#ifndef HIBYTE -#define HIBYTE(i) (i >> 8) -#endif - -#ifndef LOBYTE -#define LOBYTE(i) (i & 0xff) -#endif - -/*functions callbacks to browser*/ -NPError Osmozilla_GetURL(NPP instance, const char *url, const char *target); -void Osmozilla_SetStatus(NPP instance, const char *message); - - -/* -Plugins functions exposed to browser -*/ -NPError NPOsmozilla_New(NPMIMEType pluginType, NPP instance, uint16_t mode, int16_t argc, char* argn[], char* argv[], NPSavedData* saved); -NPError NPOsmozilla_Destroy(NPP instance, NPSavedData** save); -NPError NPOsmozilla_SetWindow(NPP instance, NPWindow* window); -NPError NPOsmozilla_NewStream(NPP instance, NPMIMEType type, NPStream* stream, NPBool seekable, uint16_t* stype); -NPError NPOsmozilla_DestroyStream(NPP instance, NPStream* stream, NPError reason); -NPINT32 NPOsmozilla_WriteReady(NPP instance, NPStream* stream); -NPINT32 NPOsmozilla_Write(NPP instance, NPStream* stream, NPINT32 offset, NPINT32 len, void* buffer); -void NPOsmozilla_StreamAsFile(NPP instance, NPStream* stream, const char* fname); -void NPOsmozilla_Print(NPP instance, NPPrint* platformPrint); -int16_t NPOsmozilla_HandleEvent(NPP instance, void* event); -void NPOsmozilla_URLNotify(NPP instance, const char* url, NPReason reason, void* notifyData); -NPError NPOsmozilla_GetValue(NPP instance, NPPVariable variable, void *result); -NPError NPOsmozilla_SetValue(NPP instance, NPNVariable variable, void *value); - - -/* -Functions called by browser -*/ - -typedef struct __tag_osmozilla Osmozilla; - -/*base functions*/ -int Osmozilla_Initialize(Osmozilla *osmo, signed short argc, char* argn[], char* argv[]); -void Osmozilla_Shutdown(Osmozilla *osmo); -int Osmozilla_SetWindow(Osmozilla *osmozilla, void *os_wnd_handle, void *os_wnd_display, unsigned int width, unsigned int height); -void Osmozilla_ConnectTo(Osmozilla *osmozilla, const char *url); -void Osmozilla_Print(Osmozilla *osmozilla, unsigned int is_embed, void *os_print_dc, unsigned int target_x, unsigned int target_y, unsigned int target_width, unsigned int target_height); -char *Osmozilla_GetVersion(); - - -/*scripting functions*/ -void Osmozilla_Play(Osmozilla *osmo); -void Osmozilla_Pause(Osmozilla *osmo); -void Osmozilla_Stop(Osmozilla *osmo); -void Osmozilla_Update(Osmozilla *osmo, const char *type, const char *commands); - - - -#ifdef GECKO_XPCOM - -void NPOsmozilla_GetServiceManager(); -void NPOsmozilla_ReleaseServiceManager(); -void NPOsmozilla_ShutdownScript(Osmozilla *osmo); -NPError NPOsmozilla_GetPeer(Osmozilla *osmo, void *value); -NPError NPOsmozilla_GetPeerIID(Osmozilla *osmo, void *value); - -#endif //GECKO_XPCOM - -#endif //_NPPLAT_H_ diff --git a/applications/osmozilla/osmozilla.cpp b/applications/osmozilla/osmozilla.cpp deleted file mode 100644 index 784fd6e..0000000 --- a/applications/osmozilla/osmozilla.cpp +++ /dev/null @@ -1,499 +0,0 @@ -/* -* GPAC - Multimedia Framework C SDK -* -* Copyright (c) ENST 2000-200X -* All rights reserved -* -* This file is part of GPAC / Osmozilla NPAPI plugin -* -* GPAC is free software; you can redistribute it and/or modify -* it under the terms of the GNU Lesser General Public License as published by -* the Free Software Foundation; either version 2, or (at your option) -* any later version. -* -* GPAC is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU Lesser General Public License for more details. -* -* You should have received a copy of the GNU Lesser General Public -* License along with this library; see the file COPYING. If not, write to -* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. -* -*/ - -#include "osmozilla.h" - -#ifdef XP_WIN -#include -#endif - -#include -#include -#include - - -short Osmozilla_GetURL(NPP instance, const char *url, const char *target); -void Osmozilla_SetStatus(NPP instance, const char *message); - - -void Osmozilla_Shutdown(Osmozilla *osmo) -{ - if (osmo->url) gf_free(osmo->url); - osmo->url = NULL; - if (osmo->term) { - GF_Terminal *a_term = osmo->term; - osmo->term = NULL; - gf_term_del(a_term); - } - if (osmo->user->modules) gf_modules_del(osmo->user->modules); - if (osmo->user->config) gf_cfg_del(osmo->user->config); - gf_free(osmo->user); - osmo->user = NULL; -} - -static void osmozilla_do_log(void *cbk, u32 level, u32 tool, const char *fmt, va_list list) -{ - FILE *logs = (FILE *) cbk; - vfprintf(logs, fmt, list); - fflush(logs); -} - - -Bool Osmozilla_EventProc(void *opaque, GF_Event *evt) -{ - char msg[1024]; - Osmozilla *osmo = (Osmozilla *)opaque; - if (!osmo->term) return 0; - - switch (evt->type) { - case GF_EVENT_MESSAGE: - if (!evt->message.message) return 0; - if (evt->message.error) - sprintf((char *)msg, "GPAC: %s (%s)", evt->message.message, gf_error_to_string(evt->message.error)); - else - sprintf((char *)msg, "GPAC: %s", evt->message.message); - - Osmozilla_SetStatus(osmo->np_instance, msg); - break; - case GF_EVENT_PROGRESS: - if (evt->progress.done == evt->progress.total) { - Osmozilla_SetStatus(osmo->np_instance, ""); - } else { - char *szTitle = (char *)""; - if (evt->progress.progress_type==0) szTitle = (char *)"Buffer "; - else if (evt->progress.progress_type==1) szTitle = (char *)"Download "; - else if (evt->progress.progress_type==2) szTitle = (char *)"Import "; - sprintf(msg, "(GPAC) %s: %02.2f", szTitle, (100.0*evt->progress.done) / evt->progress.total); - Osmozilla_SetStatus(osmo->np_instance, msg); - } - break; - - /*IGNORE any scene size, just work with the size allocated in the parent doc*/ - case GF_EVENT_SCENE_SIZE: - gf_term_set_size(osmo->term, osmo->width, osmo->height); - break; - /*window has been resized (full-screen plugin), resize*/ - case GF_EVENT_SIZE: - osmo->width = evt->size.width; - osmo->height = evt->size.height; - gf_term_set_size(osmo->term, osmo->width, osmo->height); - break; - case GF_EVENT_CONNECT: - osmo->is_connected = evt->connect.is_connected; - break; - case GF_EVENT_DURATION: - osmo->can_seek = evt->duration.can_seek; - osmo->duration = evt->duration.duration; - break; - case GF_EVENT_DBLCLICK: - gf_term_set_option(osmo->term, GF_OPT_FULLSCREEN, !gf_term_get_option(osmo->term, GF_OPT_FULLSCREEN)); - break; - case GF_EVENT_NAVIGATE_INFO: - strcpy(msg, evt->navigate.to_url); - Osmozilla_SetStatus(osmo->np_instance, msg); - break; - case GF_EVENT_NAVIGATE: - if (gf_term_is_supported_url(osmo->term, evt->navigate.to_url, 1, osmo->disable_mime)) { - gf_term_navigate_to(osmo->term, evt->navigate.to_url); - return 1; - } else { - u32 i; - char *target = (char *)"_self"; - - for (i=0; inavigate.param_count; i++) { - if (!strcmp(evt->navigate.parameters[i], "_parent")) target = (char *)"_parent"; - else if (!strcmp(evt->navigate.parameters[i], "_blank")) target = (char *)"_blank"; - else if (!strcmp(evt->navigate.parameters[i], "_top")) target = (char *)"_top"; - else if (!strcmp(evt->navigate.parameters[i], "_new")) target = (char *)"_new"; - else if (!strnicmp(evt->navigate.parameters[i], "_target=", 8)) target = (char *) evt->navigate.parameters[i]+8; - } - Osmozilla_GetURL(osmo->np_instance, evt->navigate.to_url, target); - return 1; - } - break; - } - return 0; -} - -int Osmozilla_Initialize(Osmozilla *osmo, signed short argc, char* argn[], char* argv[]) -{ - const char *str; - int i; - u32 log_level, log_tools; - - osmo->auto_start = 1; - - /*options sent from plugin*/ - for(i=0;iauto_start = 0; - - else if (!stricmp(argn[i],"src") ) { - if (osmo->url) gf_free(osmo->url); - osmo->url = gf_strdup(argv[i]); - } - else if (!stricmp(argn[i],"use3d") && (!stricmp(argv[i], "true") || !stricmp(argv[i], "yes") ) ) { - osmo->use_3d = 1; - } - else if (!stricmp(argn[i],"loop") && (!stricmp(argv[i], "true") || !stricmp(argv[i], "yes") ) ) { - osmo->loop = 1; - } - else if (!stricmp(argn[i],"aspectratio")) { - osmo->aspect_ratio = GF_ASPECT_RATIO_KEEP; - if (!stricmp(argv[i], "keep")) osmo->aspect_ratio = GF_ASPECT_RATIO_KEEP; - else if (!stricmp(argv[i], "16:9")) osmo->aspect_ratio = GF_ASPECT_RATIO_16_9; - else if (!stricmp(argv[i], "4:3")) osmo->aspect_ratio = GF_ASPECT_RATIO_4_3; - else if (!stricmp(argv[i], "fill")) osmo->aspect_ratio = GF_ASPECT_RATIO_FILL_SCREEN; - } - } - - /*URL is not absolute, request new stream to mozilla - we don't pass absolute URLs since some may not be - handled by gecko */ - if (osmo->url) { - Bool absolute_url = 0; - if (strstr(osmo->url, "://")) absolute_url = 1; - else if (osmo->url[0] == '/') { - FILE *test = gf_f64_open(osmo->url, "rb"); - if (test) { - absolute_url = 1; - fclose(test); - } - } - else if ((osmo->url[1] == ':') && ((osmo->url[2] == '\\') || (osmo->url[2] == '/'))) absolute_url = 1; - - if (!absolute_url) { - char *url = osmo->url; - osmo->url = NULL; - Osmozilla_GetURL(osmo->np_instance, url, NULL); - gf_free(url); - } - } - - GF_SAFEALLOC(osmo->user, GF_User); - osmo->user->config = gf_cfg_init(NULL, NULL); - /*need to have a valid cfg file for now*/ - if (!osmo->user->config) { - gf_free(osmo->user); - osmo->user = NULL; -#ifdef WIN32 - MessageBox(NULL, "GPAC CONFIGURATION FILE NOT FOUND OR INVALID", "OSMOZILLA FATAL ERROR", MB_OK); -#else - fprintf(stdout, "OSMOZILLA FATAL ERROR\nGPAC CONFIGURATION FILE NOT FOUND OR INVALID\n"); -#endif - return 0; - } - - str = gf_cfg_get_key(osmo->user->config, "General", "ModulesDirectory"); - osmo->user->modules = gf_modules_new(str, osmo->user->config); - if (!gf_modules_get_count(osmo->user->modules)) { - if (osmo->user->modules) gf_modules_del(osmo->user->modules); - gf_free(osmo->user); - osmo->user = NULL; -#ifdef WIN32 - MessageBox(NULL, "GPAC MODULES NOT FOUND", "OSMOZILLA FATAL ERROR", MB_OK); -#else - fprintf(stdout, "OSMOZILLA FATAL ERROR\nGPAC MODULES NOT FOUND\n"); -#endif - return 0; - } - - osmo->user->opaque = osmo; - osmo->user->EventProc = Osmozilla_EventProc; - - /*always fetch mime ? Check with anchor examples*/ - osmo->disable_mime = 0; - str = gf_cfg_get_key(osmo->user->config, "General", "NoMIMETypeFetch"); - if (str && !strcmp(str, "yes")) osmo->disable_mime = 0; - /*check log file*/ - str = gf_cfg_get_key(osmo->user->config, "General", "LogFile"); - if (str) { - osmo->logs = gf_f64_open(str, "wt"); - if (osmo->logs) gf_log_set_callback(osmo->logs, osmozilla_do_log); - } - - /*setup logs*/ - log_level = gf_log_parse_level(gf_cfg_get_key(osmo->user->config, "General", "LogLevel")); - if (!log_level) - fprintf(stdout, "Osmozilla: invalid log level specified\n"); - gf_log_set_level(log_level); - log_tools = gf_log_parse_tools(gf_cfg_get_key(osmo->user->config, "General", "LogTools")); - if (!log_tools) - fprintf(stdout, "Osmozilla: invalid log tools specified\n"); - gf_log_set_tools(log_tools); - - fprintf(stdout, "Osmozilla initialized\n"); - - return 1; -} - -int Osmozilla_SetWindow(Osmozilla *osmo, void *os_wnd_handle, void *os_wnd_display, unsigned int width, unsigned int height) -{ - const char *gui; - if (osmo->window_set) { - osmo->width = width; - osmo->height = height; - if (osmo->is_connected) gf_term_set_size(osmo->term, osmo->width, osmo->height); - return 1; - } - if (!os_wnd_handle) return 0; - - osmo->width = width; - osmo->height = height; - - osmo->user->os_window_handler = os_wnd_handle; - osmo->user->os_display = os_wnd_display; - - /*Everything is now setup, create the terminal*/ - fprintf(stdout, "Creating Osmozilla terminal\n"); - osmo->term = gf_term_new(osmo->user); - if (!osmo->term) return 0; - fprintf(stdout, "Osmozilla terminal created\n"); - - gf_term_set_option(osmo->term, GF_OPT_ASPECT_RATIO, osmo->aspect_ratio); - osmo->window_set = 1; - -#ifdef XP_WIN - SetFocus((HWND)os_wnd_handle); -#endif - - /*stream not ready*/ - if (!osmo->url || !osmo->auto_start) { - fprintf(stdout, "Osmozilla ready - not connecting to %s yet\n", osmo->url); - return 1; - } - - /*connect from 0 and pause if not autoplay*/ - gui = gf_cfg_get_key(osmo->user->config, "General", "StartupFile"); - if (gui) { - gf_cfg_set_key(osmo->user->config, "Temp", "BrowserMode", "yes"); - gf_cfg_set_key(osmo->user->config, "Temp", "GUIStartupFile", osmo->url); - gf_term_connect(osmo->term, gui); - } else { - gf_term_connect(osmo->term, osmo->url); - } - fprintf(stdout, "Osmozilla connected to %s\n", osmo->url); - return 1; -} - -char *Osmozilla_GetVersion() -{ - return (char *) "GPAC Plugin " GPAC_FULL_VERSION " for NPAPI compatible Web Browsers. For more information go to GPAC website"; -} - -void Osmozilla_ConnectTo(Osmozilla *osmo, const char *url) -{ - fprintf(stdout, "Osmozilla connecting to %s\n", url); - if (osmo->url) gf_free(osmo->url); - osmo->url = gf_strdup(url); - - /*connect from 0 and pause if not autoplay*/ - if (osmo->auto_start) { - const char *gui = gf_cfg_get_key(osmo->user->config, "General", "StartupFile"); - if (gui) { - gf_cfg_set_key(osmo->user->config, "Temp", "BrowserMode", "yes"); - gf_cfg_set_key(osmo->user->config, "Temp", "GUIStartupFile", url); - gf_term_connect(osmo->term, gui); - } else { - gf_term_connect(osmo->term, url); - } - } - fprintf(stdout, "Osmozilla connected to %s\n", url); -} - -void Osmozilla_Pause(Osmozilla *osmo) -{ - if (osmo->term) { - if (gf_term_get_option(osmo->term, GF_OPT_PLAY_STATE) == GF_STATE_PAUSED) { - gf_term_set_option(osmo->term, GF_OPT_PLAY_STATE, GF_STATE_PLAYING); - } else { - gf_term_set_option(osmo->term, GF_OPT_PLAY_STATE, GF_STATE_PAUSED); - } - } -} - -void Osmozilla_Play(Osmozilla *osmo) -{ - if (!osmo->is_connected) { - if (osmo->url) gf_term_connect(osmo->term, (const char *) osmo->url); - } else { - gf_term_set_option(osmo->term, GF_OPT_PLAY_STATE, GF_STATE_PLAYING); - } -} - -void Osmozilla_Stop(Osmozilla *osmo) -{ - if (osmo->term) gf_term_disconnect(osmo->term); -} - -#ifdef XP_WIN -PBITMAPINFO CreateBitmapInfoStruct(GF_VideoSurface *pfb) -{ - PBITMAPINFO pbmi; - WORD cClrBits; - - cClrBits = 32; - - pbmi = (PBITMAPINFO) LocalAlloc(LPTR, - sizeof(BITMAPINFOHEADER)); - - pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); - pbmi->bmiHeader.biWidth = pfb->width; - pbmi->bmiHeader.biHeight = 1; - pbmi->bmiHeader.biPlanes = 1; - pbmi->bmiHeader.biBitCount = cClrBits; - - pbmi->bmiHeader.biCompression = BI_RGB; - pbmi->bmiHeader.biSizeImage = ((pbmi->bmiHeader.biWidth * cClrBits +31) & ~31) /8 - * pbmi->bmiHeader.biHeight; - pbmi->bmiHeader.biClrImportant = 0; - return pbmi; -} -#endif - -void Osmozilla_Print(Osmozilla *osmo, unsigned int is_embed, void *os_print_dc, unsigned int target_x, unsigned int target_y, unsigned int target_width, unsigned int target_height) -{ - if (is_embed) { -#ifdef XP_MACOS - /* - os_print_dc contains a THPrint reference on MacOS - */ - } -#endif // XP_MACOS -#ifdef XP_UNIX - /* - os_print_dc contains a NPPrintCallbackStruct on Unix and - the plug-in location and size in the NPWindow are in page coordinates (720/ inch), but the printer requires point coordinates (72/inch) - */ -#endif // XP_UNIX -#ifdef XP_WIN - /* - The coordinates for the window rectangle are in TWIPS format. - This means that you need to convert the x-y coordinates using the Windows API call DPtoLP when you output text - */ - GF_VideoSurface fb; - u32 xsrc, ysrc; - u16 src_16; - char *src; - float deltay; - int ysuiv = 0; - char *ligne; - BITMAPINFO *infoSrc; - HDC pDC = (HDC)os_print_dc; - /*lock the source buffer */ - gf_term_get_screen_buffer(osmo->term, &fb); - infoSrc = CreateBitmapInfoStruct(&fb); - deltay = (float)target_height/(float)fb.height; - ligne = (char *) LocalAlloc(GMEM_FIXED, fb.width*4); - for (ysrc=0; ysrc> 8) & 0xf8; - dst[2] += dst[2]>>5; - dst[1] = (src_16 >> 3) & 0xfc; - dst[1] += dst[1]>>6; - dst[0] = (src_16 << 3) & 0xf8; - dst[0] += dst[0]>>5; - src+=2; - break; - case GF_PIXEL_RGB_555: - src_16 = * (u16 *)src; - dst[2] = (src_16 >> 7) & 0xf8; - dst[2] += dst[2]>>5; - dst[1] = (src_16 >> 2) & 0xf8; - dst[1] += dst[1]>>5; - dst[0] = (src_16 << 3) & 0xf8; - dst[0] += dst[0]>>5; - src+=2; - break; - } - dst += 4; - } - ycrt = ysuiv; - ysuiv = (u32) ( ((float)ysrc+1.0)*deltay); - delta = ysuiv-ycrt; - StretchDIBits( - pDC, target_x, target_y, target_width, - delta, - 0, 0, fb.width, 1, - ligne, infoSrc, DIB_RGB_COLORS, SRCCOPY); - } - - /*unlock GPAC frame buffer */ - gf_term_release_screen_buffer(osmo->term, &fb); - /* gf_free temporary objects */ - GlobalFree(ligne); - LocalFree(infoSrc); -#endif // XP_WIN - - return; -} - -/*TODO - this is full print, present the print dialog and manage the print*/ -} - -void Osmozilla_Update(Osmozilla *osmo, const char *type, const char *commands) -{ - if (osmo->term) { - GF_Err e = gf_term_scene_update(osmo->term, (char *) type, (char *) commands); - if (e) { - char szMsg[1024]; - sprintf((char *)szMsg, "GPAC: Error applying update (%s)", gf_error_to_string(e) ); - Osmozilla_SetStatus(osmo->np_instance, szMsg); - } - } -} - diff --git a/applications/osmozilla/osmozilla.def b/applications/osmozilla/osmozilla.def deleted file mode 100644 index 5d5bbb1..0000000 --- a/applications/osmozilla/osmozilla.def +++ /dev/null @@ -1,6 +0,0 @@ -LIBRARY nposmozilla - -EXPORTS - NP_GetEntryPoints @1 - NP_Initialize @2 - NP_Shutdown @3 diff --git a/applications/osmozilla/osmozilla.h b/applications/osmozilla/osmozilla.h deleted file mode 100644 index 5929d97..0000000 --- a/applications/osmozilla/osmozilla.h +++ /dev/null @@ -1,71 +0,0 @@ -/* -* GPAC - Multimedia Framework C SDK -* -* Copyright (c) ENST 2000-200X -* All rights reserved -* -* This file is part of GPAC / Osmozilla NPAPI plugin -* -* GPAC is free software; you can redistribute it and/or modify -* it under the terms of the GNU Lesser General Public License as published by -* the Free Software Foundation; either version 2, or (at your option) -* any later version. -* -* GPAC is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU Lesser General Public License for more details. -* -* You should have received a copy of the GNU Lesser General Public -* License along with this library; see the file COPYING. If not, write to -* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. -* -*/ - -#ifndef __OSMOZILLA_H__ -#define __OSMOZILLA_H__ - - -/*DO NOT INCLUDE ANY GPAC FILE IN THIS HEADER, IT CAUSES TYPE REDEFINITION CONFLICT ON OSX*/ -typedef struct _tag_terminal GF_Terminal; -typedef struct _tag_user GF_User; - -#include - - -typedef struct _NPP *NPP; - -typedef struct __tag_osmozilla -{ - /*plugiun & window info*/ - NPP np_instance; - int window_set; - unsigned int height, width; - - /*GPAC term*/ - GF_User *user; - GF_Terminal *term; - - /*general options*/ - char loop, auto_start, is_connected, use_3d, disable_mime; - unsigned int aspect_ratio; - - /*the URL we are connected to*/ - char *url; - /*timing info of current url*/ - double duration; - char can_seek; - - /*log file if any*/ - FILE *logs; - -#ifdef GECKO_XPCOM - void *scriptable_peer; -#else - struct NPObject *script_obj; -#endif - -} Osmozilla; - - -#endif // __OSMOZILLA_H__ diff --git a/applications/osmozilla/osmozilla.png b/applications/osmozilla/osmozilla.png deleted file mode 100644 index 6f66a17b1edda041995dfdee6d4baae64b040a71..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 121239 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 Gxb=Tf0re~Z diff --git a/applications/osmozilla/osmozilla.rc b/applications/osmozilla/osmozilla.rc deleted file mode 100644 index 9fc460b..0000000 --- a/applications/osmozilla/osmozilla.rc +++ /dev/null @@ -1,128 +0,0 @@ -//Microsoft Developer Studio generated resource script. -// -#include "resource.h" - -#define APSTUDIO_READONLY_SYMBOLS -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 2 resource. -// -#include "winresrc.h" - -/*do not include setup.h*/ -#define _GF_SETUP_H_ -#include - -///////////////////////////////////////////////////////////////////////////// -#undef APSTUDIO_READONLY_SYMBOLS - -///////////////////////////////////////////////////////////////////////////// -// Neutral resources - -#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_NEU) -#ifdef _WIN32 -LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL -#pragma code_page(1252) -#endif //_WIN32 - -#ifndef _MAC -///////////////////////////////////////////////////////////////////////////// -// -// Version -// - -VS_VERSION_INFO VERSIONINFO - FILEVERSION 1,0,1,0 - PRODUCTVERSION 1,0,1,0 - FILEFLAGSMASK 0x3fL -#ifdef _DEBUG - FILEFLAGS 0x1L -#else - FILEFLAGS 0x0L -#endif - FILEOS 0x40004L - FILETYPE 0x2L - FILESUBTYPE 0x0L -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "040904e4" - BEGIN - VALUE "Comments", "\0" - VALUE "CompanyName", " \0" - VALUE "FileDescription", "Osmozilla allows playback of many media and rich media files. For more information, visit gpac.sourceforge.net\0" - VALUE "FileExtents", "*|aac|wrl,wrl.gz|x3dv,x3dv.gz,x3dvz|x3d,x3d.gz,x3dz|svg,svg.gz,svgz|mpg,mpeg,mp2,mpa,mpe,mpv2|asf,wma,wmv,asx,asr|avi|mp4,mpg4|mp4|m4a|3gp,3gpp|3gp,3gpp|3g2,3gp2|3g2,3gp2|mp2,mp3,mpga,mpega|ogg|sdp\0" - VALUE "FileOpenName", "GPAC Plugin|AAC Music|VRML World|X3D/VRML World|X3D/XML World|SVG Document|MPEG Video|WindowsMedia Movies|AVI Movies|MPEG-4 Videos|MPEG-4 Movies|MPEG-4 Music|3GPP Movies|3GPP Music|3GPP2 Movies|3GPP2 Music|MP3 Music|OGG Movies|SDP Session\0" - VALUE "FileVersion", GPAC_VERSION"-rev"GPAC_SVN_REVISION"\0" - VALUE "InternalName", "nposmozilla\0" - VALUE "LegalCopyright", "Copyright © GPAC 2005-2007\0" - VALUE "LegalTrademarks", "\0" - VALUE "MIMEType", "application/x-gpac|audio/aac|model/vrml|model/x3d+vrml|model/x3d+xml|image/svg+xml|video/mpeg|video/x-ms-asf|video/avi|video/mp4|application/mp4|audio/mp4|video/3gpp|audio/3gpp|video/3gpp2|audio/3gpp2|audio/mpeg|application/ogg|application/sdp\0" - VALUE "OriginalFilename", "nposmozilla.dll\0" - VALUE "PrivateBuild", "\0" - VALUE "ProductName", "Osmozilla - GPAC Plugin for Mozilla\0" - VALUE "ProductVersion", GPAC_VERSION"-rev"GPAC_SVN_REVISION"\0" - VALUE "SpecialBuild", "\0" - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x409, 1252 - END -END - -#endif // !_MAC - -#endif // Neutral resources -///////////////////////////////////////////////////////////////////////////// - - -///////////////////////////////////////////////////////////////////////////// -// English (U.S.) resources - -#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) -#ifdef _WIN32 -LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US -#pragma code_page(1252) -#endif //_WIN32 - -#ifdef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// TEXTINCLUDE -// - -1 TEXTINCLUDE DISCARDABLE -BEGIN - "resource.h\0" -END - -2 TEXTINCLUDE DISCARDABLE -BEGIN - "#include ""winresrc.h""\r\n" - "\0" -END - -3 TEXTINCLUDE DISCARDABLE -BEGIN - "\r\n" - "\0" -END - -#endif // APSTUDIO_INVOKED - -#endif // English (U.S.) resources -///////////////////////////////////////////////////////////////////////////// - - - -#ifndef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 3 resource. -// - - -///////////////////////////////////////////////////////////////////////////// -#endif // not APSTUDIO_INVOKED - diff --git a/applications/osmozilla/readme.txt b/applications/osmozilla/readme.txt deleted file mode 100644 index 7a61b5c..0000000 --- a/applications/osmozilla/readme.txt +++ /dev/null @@ -1,8 +0,0 @@ -to regenerate plugin interface from IDL: - -xpidl -m header -I path_to\gecko-sdk\xpcom\idl nsIOsmozilla.idl -xpidl -m typelib -I path_to\gecko-sdk\xpcom\idl nsIOsmozilla.idl -xpt_link nposmozilla.xpt nsIOsmozilla.xpt - -This MUST be done for win32 and linux OSs independently, an .xpt file generated on one OS is not compatible with another OS... -Please keep the w32 file "nsIOsmozilla.xpt_w32" and the linux one "nsIOsmozilla.xpt_linux" to bear with makefiles... \ No newline at end of file diff --git a/applications/osmozilla/resource.h b/applications/osmozilla/resource.h deleted file mode 100644 index 8d3b519..0000000 --- a/applications/osmozilla/resource.h +++ /dev/null @@ -1,21 +0,0 @@ -//{{NO_DEPENDENCIES}} -// Microsoft Developer Studio generated include file. -// Used by osmozilla.rc -// -#define IDD_MAIN 101 -#define IDC_BUTTON_GO 1002 -#define IDC_STATIC_UA 1003 -#define IDC_BUTTON1 1005 -#define IDC_BUTTON_DONT 1005 - -// Next default values for new objects -// -#ifdef APSTUDIO_INVOKED -#ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NO_MFC 1 -#define _APS_NEXT_RESOURCE_VALUE 102 -#define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 1006 -#define _APS_NEXT_SYMED_VALUE 101 -#endif -#endif diff --git a/applications/testapps/broadcaster/Makefile b/applications/testapps/broadcaster/Makefile index 42d847b..2bc1e0e 100644 --- a/applications/testapps/broadcaster/Makefile +++ b/applications/testapps/broadcaster/Makefile @@ -21,13 +21,12 @@ endif #common obj OBJS=RTP_serv_generator.o RTP_serv_packetizer.o RTP_serv_sender.o broadcaster.o sdp_generator.o debug.o -LDFLAGS += -L../../../bin/gcc -LIBS=-lgpac +LINKFLAGS=-L../../../bin/gcc -lgpac all: broadcaster broadcaster: $(OBJS) *.h - $(CC) -o $(APPNAME) $(OBJS) $(LIBS) $(LDFLAGS) + $(CC) $(LDFLAGS) -o $(APPNAME) $(OBJS) $(LINKFLAG) .c.o: $(CC) -Wall -g -c $(CFLAGS) $*.c diff --git a/applications/testapps/broadcaster/broadcaster.c b/applications/testapps/broadcaster/broadcaster.c index 3b8349c..e3a00f0 100644 --- a/applications/testapps/broadcaster/broadcaster.c +++ b/applications/testapps/broadcaster/broadcaster.c @@ -213,7 +213,7 @@ u32 tcp_server(void *par) fprintf(stderr, "Error opening temp file for the configuration\n"); exit(1); } - ret = fwrite(buffer, 1, byte_read, fp); + ret = gf_fwrite(buffer, 1, byte_read, fp); fclose(fp); /* parsing config info */ @@ -240,7 +240,7 @@ u32 tcp_server(void *par) GF_Err e = gf_sk_receive(conn_socket, temp, sizeof(temp), 0, &byte_read); if (e == GF_OK) { - fwrite(temp, 1, byte_read, fp); + gf_fwrite(temp, 1, byte_read, fp); } else if (e==GF_IP_NETWORK_EMPTY) { num_retry--; if (!num_retry) @@ -309,8 +309,6 @@ int main (const int argc, const char** argv) /* init gpac lib */ gf_sys_init(); - gf_log_set_level(GF_LOG_ERROR); - gf_log_set_tools(GF_LOG_NETWORK|GF_LOG_RTP|GF_LOG_SCENE|GF_LOG_PARSER|GF_LOG_AUTHOR|GF_LOG_CODING|GF_LOG_SCRIPT); GF_SAFEALLOC(conf, CONF_Data); diff --git a/applications/testapps/broadcaster/sdp_generator.c b/applications/testapps/broadcaster/sdp_generator.c index 0cd7f89..02a1b6b 100644 --- a/applications/testapps/broadcaster/sdp_generator.c +++ b/applications/testapps/broadcaster/sdp_generator.c @@ -22,16 +22,16 @@ int sdp_generator(PNC_CallbackData *data, char *ip_dest, char *sdp_fmt) exit(1); } - ret = fwrite("v=0\n", 1, 4, fp); + ret = gf_fwrite("v=0\n", 1, 4, fp); sprintf(temp, "o=GpacBroadcaster 3326096807 1117107880000 IN IP%d %s\n", gf_net_is_ipv6(ip_dest) ? 6 : 4, ip_dest); - ret = fwrite(temp, 1, strlen(temp), fp); + ret = gf_fwrite(temp, 1, strlen(temp), fp); - ret = fwrite("s=MPEG4Broadcaster\n", 1, 19, fp); + ret = gf_fwrite("s=MPEG4Broadcaster\n", 1, 19, fp); sprintf(temp, "c=IN IP%d %s\n", gf_net_is_ipv6(ip_dest) ? 6 : 4, ip_dest); - ret = fwrite(temp, 1, strlen(temp), fp); + ret = gf_fwrite(temp, 1, strlen(temp), fp); - ret = fwrite("t=0 0\n", 1, 6, fp); + ret = gf_fwrite("t=0 0\n", 1, 6, fp); codec = (GF_SceneEngine *) data->codec; if (codec) { @@ -45,21 +45,21 @@ int sdp_generator(PNC_CallbackData *data, char *ip_dest, char *sdp_fmt) free(buffer); sprintf(temp, "a=mpeg4-iod:\"data:application/mpeg4-iod;base64,%s\"\n", buf64); - ret = fwrite(temp, 1, strlen(temp), fp); + ret = gf_fwrite(temp, 1, strlen(temp), fp); } sprintf(temp, "m=application %d RTP/AVP 96\n", port); - ret = fwrite(temp, 1, strlen(temp), fp); + ret = gf_fwrite(temp, 1, strlen(temp), fp); - ret = fwrite("a=rtpmap:96 mpeg4-generic/1000\n", 1, 31, fp); + ret = gf_fwrite("a=rtpmap:96 mpeg4-generic/1000\n", 1, 31, fp); if (esd) { sprintf(temp, "a=mpeg4-esid:%d\n", esd->ESID); - ret = fwrite(temp, 1, strlen(temp), fp); + ret = gf_fwrite(temp, 1, strlen(temp), fp); } sprintf(temp, "%s\n", sdp_fmt); - ret = fwrite(temp, 1, strlen(temp), fp); + ret = gf_fwrite(temp, 1, strlen(temp), fp); fflush(fp); fclose(fp); dprintf(DEBUG_sdp_generator, "SDP file generated in broadcaster.sdp\n"); diff --git a/applications/testapps/dmbrs/main.c b/applications/testapps/dmbrs/main.c index 0755e4e..7650492 100644 --- a/applications/testapps/dmbrs/main.c +++ b/applications/testapps/dmbrs/main.c @@ -26,13 +26,13 @@ void save_ts(char *filename, unsigned char *data) { FILE *ts_out = fopen(filename,"a+b"); - fwrite(data, 1, 188, ts_out); + gf_fwrite(data, 1, 188, ts_out); fclose(ts_out); } void save_rs_0(char *filename, unsigned char *data) { FILE *rs_out = fopen(filename,"a+b"); - fwrite(data, 1, 204, rs_out); + gf_fwrite(data, 1, 204, rs_out); fclose(rs_out); } @@ -219,7 +219,7 @@ void main(int argc, char **argv) for (j = 1; j <188; j++) { buffer[j] = rand();//j; } - fwrite(buffer, 1, 188, in); + gf_fwrite(buffer, 1, 188, in); } } fclose(in); diff --git a/applications/testapps/mpedemux/main.c b/applications/testapps/mpedemux/main.c index f176754..c6bc692 100644 --- a/applications/testapps/mpedemux/main.c +++ b/applications/testapps/mpedemux/main.c @@ -81,9 +81,6 @@ int main(int argc, char **argv) return GF_OK; } - gf_log_set_level(GF_LOG_ERROR); - gf_log_set_tools(GF_LOG_CONTAINER); - GF_SAFEALLOC(mpedemux, MPEDemux); mpedemux->ts_demux = gf_m2ts_demux_new(); mpedemux->ts_demux->on_event = mpedemux_on_event; diff --git a/applications/testapps/mpeg2ts/main.c b/applications/testapps/mpeg2ts/main.c index ae79bfc..3e948a5 100644 --- a/applications/testapps/mpeg2ts/main.c +++ b/applications/testapps/mpeg2ts/main.c @@ -32,7 +32,7 @@ void on_m2ts_event(GF_M2TS_Demuxer *ts, u32 evt_type, void *par) case GF_M2TS_EVT_PES_PCK: pck = par; if (dest && (dump_pid == pck->stream->pid)) { - fwrite(pck->data, pck->data_len, 1, dest); + gf_fwrite(pck->data, pck->data_len, 1, dest); } //fprintf(stdout, "PES(%d): DTS "LLD" PTS" LLD" RAP %d size %d\n", pck->stream->pid, pck->DTS, pck->PTS, pck->rap, pck->data_len); diff --git a/applications/udptsseg/main.c b/applications/udptsseg/main.c index 33ede4f..b372b65 100644 --- a/applications/udptsseg/main.c +++ b/applications/udptsseg/main.c @@ -131,8 +131,6 @@ int main(int argc, char **argv) /* gpac init */ /*****************/ gf_sys_init(0); - gf_log_set_level(GF_LOG_ERROR); - gf_log_set_tools(0xFFFFFFFF); /*****************/ /* parsing of the arguments */ @@ -256,7 +254,7 @@ int main(int argc, char **argv) if (ts_output_file != NULL) { u32 now = gf_sys_clock(); if (towrite) { - fwrite(input_buffer, 1, towrite, ts_output_file); + gf_fwrite(input_buffer, 1, towrite, ts_output_file); if (towrite < leftinbuffer) { fprintf(stderr, "Warning: wrote %d bytes, keeping %d bytes\n", towrite, (leftinbuffer-towrite)); memmove(input_buffer, input_buffer+towrite, leftinbuffer-towrite); diff --git a/bin/w32_rel/nsis_install/gpac_installer.nsi b/bin/w32_rel/nsis_install/gpac_installer.nsi deleted file mode 100644 index 961e0c1..0000000 --- a/bin/w32_rel/nsis_install/gpac_installer.nsi +++ /dev/null @@ -1,738 +0,0 @@ -BGGradient - -XPStyle on -WindowIcon on -Icon "..\..\..\..\doc\osmo4.ico" -UninstallIcon "..\..\..\..\doc\osmo4.ico" - -!define GPAC_VERSION 0.4.6 -!define /date RELDATE "%Y%m%d_%H%M" - -!define GPAC_ROOT ..\..\..\.. - -Name "GPAC Framework ${GPAC_VERSION}" -OutFile "GPAC.Framework.Setup-${RELDATE}.exe" - - -InstallDir "$PROGRAMFILES\GPAC" -InstallDirRegKey HKCU "SOFTWARE\GPAC" "InstallDir" - - -LicenseText "GPAC Licence" -LicenseData "${GPAC_ROOT}\COPYING" - -DirText "This will install the GPAC Framework on your computer. Choose a directory" - -InstType Normal - - -ComponentText "This will install the GPAC Framework on your computer. Select which optional things you want installed." - - -Function FctWriteRegStrAuth - ;local var - Push $0 - Push $R0 - Push $R1 - Push $R2 - Push $R3 - ;pop function arguments - Exch 5 - Pop $R3 - Exch 5 - Pop $R2 - Exch 5 - Pop $R1 - Exch 5 - Pop $R0 - - ;test if calling HKCR - StrCmp $R0 "HKCR" +1 +3 - WriteRegStr HKCR $R1 $R2 $R3 - goto lbl_end - - #has current user admin privileges? - userInfo::getAccountType - Pop $0 - StrCmp $0 "Admin" lbl_admin lbl_not_admin - - lbl_admin: - WriteRegStr HKLM $R1 $R2 $R3 - goto lbl_end - - lbl_not_admin: - WriteRegStr HKCU $R1 $R2 $R3 - - lbl_end: - Pop $R3 - Pop $R2 - Pop $R1 - Pop $R0 - Pop $0 -FunctionEnd - -!macro WriteRegStrAuth HKREG SUBREG ENTRY VALUESTR - Push "${HKREG}" - Push "${SUBREG}" - Push "${ENTRY}" - Push "${VALUESTR}" - Call FctWriteRegStrAuth -!macroend - -!define WriteRegStrAuth "!insertmacro WriteRegStrAuth" - - -Function un.FctDeleteRegKeyAuth - ;local var - Push $0 - Push $R0 - Push $R1 - ;pop function arguments - Exch 3 - Pop $R1 - Exch 3 - Pop $R0 - - ;test if calling HKCR - StrCmp $R0 "HKCR" +1 +3 - DeleteRegKey HKCR $R1 - goto lbl_end - - #has current user admin privileges? - userInfo::getAccountType - Pop $0 - StrCmp $0 "Admin" lbl_admin lbl_not_admin - - lbl_admin: - DeleteRegKey HKLM $R1 - goto lbl_end - - lbl_not_admin: - DeleteRegKey HKCU $R1 - - lbl_end: - Pop $0 - Pop $R1 - Pop $R0 -FunctionEnd - -!macro DeleteRegKeyAuth HKREG SUBREG - Push "${HKREG}" - Push "${SUBREG}" - Call un.FctDeleteRegKeyAuth -!macroend - -!define DeleteRegKeyAuth "!insertmacro DeleteRegKeyAuth" - - -Function InsertGDIPLUS - Push $R0 - Push $R1 - ReadRegStr $R0 HKLM "SOFTWARE\Microsoft\Windows NT\CurrentVersion" CurrentVersion - StrCmp $R0 "" 0 lbl_winnt - - ;NOT NT - ReadRegStr $R0 HKLM SOFTWARE\Microsoft\Windows\CurrentVersion VersionNumber - - StrCpy $R1 $R0 1 - ; win95, NOT SUPPORTED - StrCmp $R1 '4' 0 lbl_err_95 - StrCpy $R1 $R0 3 - StrCmp $R1 '4.0' lbl_err_95 - ;winME or 98 otherwise - StrCmp $R1 '4.9' lbl_add lbl_add - -lbl_err_nt: - MessageBox MB_OK "Microsoft GDI+ cannot be installed on NT 3 Systems" - Goto lbl_done - -lbl_err_95: - MessageBox MB_OK "Microsoft GDI+ cannot be installed on Windows 95 and older Systems" - Goto lbl_done - -lbl_winnt: - StrCpy $R1 $R0 1 - StrCmp $R1 '3' lbl_err_nt - StrCmp $R1 '4' lbl_add - StrCpy $R1 $R0 3 - StrCmp $R1 '5.0' lbl_add ;2000 - StrCmp $R1 '5.1' lbl_xp ;XP - StrCmp $R1 '5.2' lbl_done ;.NET server - -lbl_add: - File ".\Gdiplus.dll" - -lbl_xp: - File "..\gm_gdip_raster.dll" - -lbl_done: -FunctionEnd - -;osmo4 install -Section "Osmo4/GPAC Player" - SectionIn RO - SetOutPath $INSTDIR - - File /oname=ReadMe.txt "${GPAC_ROOT}\README" - File /oname=License.txt "${GPAC_ROOT}\COPYING" - File /oname=Changelog.txt "${GPAC_ROOT}\Changelog" - File "${GPAC_ROOT}\doc\configuration.html" - File "${GPAC_ROOT}\doc\gpac.mp4" - - File "..\Osmo4.exe" - File "..\..\..\..\doc\osmo4.ico" - File "..\libgpac.dll" - File "..\gm_dummy_in.dll" - File "..\gm_dx_hw.dll" - File "..\js32.dll" - File "..\gm_gpac_js.dll" - - ;create default cache - SetOutPath $INSTDIR\cache - - SetOutPath $INSTDIR - - ${WriteRegStrAuth} HKCU "SOFTWARE\GPAC" "InstallDir" "$INSTDIR" - ${WriteRegStrAuth} HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\Osmo4" "DisplayName" "Osmo4/GPAC (remove only)" - ${WriteRegStrAuth} HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\Osmo4" "UninstallString" "$INSTDIR\uninstall.exe" - WriteUninstaller "uninstall.exe" - -SectionEnd - -SubSection "GPAC Plugins" - - -; -; 2 install modes, normal one and full one - -Section "MPEG-4 BIFS Decoder" - SectionIn 1 - File "..\gm_bifs_dec.dll" -SectionEnd - -Section "MPEG-4 ODF Decoder" - SectionIn 1 - File "..\gm_odf_dec.dll" -SectionEnd - -Section "MPEG-4 LASeR Decoder" - SectionIn 1 - File "..\gm_laser_dec.dll" -SectionEnd - -Section "MPEG-4 SAF Demultiplexer" - SectionIn 1 - File "..\gm_saf_in.dll" -SectionEnd - -Section "Generic Scene Description File Loader" - SectionIn 1 - File "..\gm_ctx_load.dll" -SectionEnd - -Section "Image Package (PNG, JPEG, BMP)" - SectionIn 1 - File "..\gm_img_in.dll" -SectionEnd - -Section "AAC Audio support (FAAD decoder, AAC files and Radios)" - SectionIn 1 - File "..\gm_aac_in.dll" -SectionEnd - -Section "MP3 Audio support (MAD decoder, MP3 files and Radios)" - SectionIn 1 - File "..\gm_mp3_in.dll" -SectionEnd - -Section "AC3 Audio support (A52 decoder, AC3 files and Radios)" - SectionIn 1 - File "..\gm_ac3_in.dll" -SectionEnd - -Section "FFMPEG Reader and Decoder" - SectionIn 1 - File "..\gm_ffmpeg_in.dll" - File "..\avcodec-52.dll" - File "..\avformat-52.dll" - File "..\avutil-50.dll" - File "..\swscale-0.dll" -SectionEnd - -Section "XviD Video Decoder" - SectionIn 1 - File "..\gm_xvid_dec.dll" -SectionEnd - -;Section "3GPP AMR NB & WB Speech Decoder" -; SectionIn 1 -; File "..\gm_amr_float_dec.dll" -;SectionEnd - -Section "Subtitle & TimedText Support" - SectionIn 1 - File "..\gm_timedtext.dll" -SectionEnd - -Section "MP4 and 3GPP File Reader" - SectionIn 1 - File "..\gm_isom_in.dll" -SectionEnd - -Section "MPEG-2 TS Reader" - SectionIn 1 - File "..\gm_mpegts_in.dll" -SectionEnd - -Section "Real-Time Streaming (RTP/RTSP/RTP) Support" - SectionIn 1 - File "..\gm_rtp_in.dll" -SectionEnd - -Section "Progressive SVG Support" - SectionIn 1 - File "..\gm_svg_in.dll" -SectionEnd - - -Section "GDI+ Rasterizer" - SectionIn 1 - call InsertGDIPLUS -SectionEnd - -Section "GPAC 2D Rasterizer" - SectionIn 1 - File "..\gm_soft_raster.dll" -SectionEnd - -Section "FreeType Font Outliner" - SectionIn 1 - File "..\gm_ft_font.dll" -SectionEnd - -Section "Windows MME Audio Output" - SectionIn 1 - File "..\gm_wav_out.dll" -SectionEnd - -Section "Xiph Ogg Reader - Vorbis and Theora Decoders" - SectionIn 1 - File "..\gm_ogg.dll" -SectionEnd - -;Section "UPnP Support" -; SectionIn 1 -; File "..\gm_platinum.dll" -;SectionEnd - -;Section "Widget Manager" -; SectionIn 1 -; File "..\gm_widgetman.dll" -;SectionEnd - -SubSectionEnd - -SubSection "Osmo4 Shortcuts" - -Section "Add Start Menu Shortcuts" - SectionIn 1 - #has current user admin privileges? - userInfo::getAccountType - Pop $0 - StrCmp $0 "Admin" +1 +2 - SetShellVarContext all - CreateDirectory "$SMPROGRAMS\Osmo4" - CreateShortCut "$SMPROGRAMS\Osmo4\Uninstall.lnk" "$INSTDIR\uninstall.exe" "" "$INSTDIR\uninstall.exe" 0 - CreateShortCut "$SMPROGRAMS\Osmo4\Osmo4.lnk" "$INSTDIR\Osmo4.exe" "" "$INSTDIR\Osmo4.exe" 0 - CreateShortCut "$SMPROGRAMS\Osmo4\Readme.lnk" "$INSTDIR\ReadMe.txt" - CreateShortCut "$SMPROGRAMS\Osmo4\License.lnk" "$INSTDIR\License.txt" - CreateShortCut "$SMPROGRAMS\Osmo4\History.lnk" "$INSTDIR\changelog.txt" - CreateShortCut "$SMPROGRAMS\Osmo4\Configuration Info.lnk" "$INSTDIR\configuration.html" -SectionEnd - -Section "Add shortcut to QuickLaunch" - SectionIn 1 - CreateShortCut "$QUICKLAUNCH\Osmo4.lnk" "$INSTDIR\Osmo4.exe" "" "$INSTDIR\Osmo4.exe" 0 -SectionEnd - -Section "Add shortcut to Desktop" - SectionIn 1 - CreateShortCut "$DESKTOP\Osmo4.lnk" "$INSTDIR\Osmo4.exe" "" "$INSTDIR\Osmo4.exe" 0 -SectionEnd - -!define SHCNE_ASSOCCHANGED 0x08000000 -!define SHCNF_IDLIST 0 - -Section "Make Osmo4 the default MPEG-4 Player" - SectionIn 1 - ;write file association - ${WriteRegStrAuth} HKCR GPAC\mp4\DefaultIcon "" "$INSTDIR\Osmo4.ico, 0" - ${WriteRegStrAuth} HKCR GPAC\mp4\Shell\open\command "" "$INSTDIR\Osmo4.exe %L" - ${WriteRegStrAuth} HKCR .mp4 "" "GPAC\mp4" - !system 'shell32.dll::SHChangeNotify(i, i, i, i) v (${SHCNE_ASSOCCHANGED}, ${SHCNF_IDLIST}, 0, 0)' - -SectionEnd - -Section "Associate 3GPP files (3GP) with Osmo4" - SectionIn 1 - ;write file association - ${WriteRegStrAuth} HKCR GPAC\3gp\DefaultIcon "" "$INSTDIR\Osmo4.ico, 0" - ${WriteRegStrAuth} HKCR GPAC\3gp\Shell\open\command "" "$INSTDIR\Osmo4.exe %L" - ${WriteRegStrAuth} HKCR .3gp "" "GPAC\3gp" - !system 'shell32.dll::SHChangeNotify(i, i, i, i) v (${SHCNE_ASSOCCHANGED}, ${SHCNF_IDLIST}, 0, 0)' -SectionEnd - -Section "Associate 3GPP2 files (3G2) with Osmo4" - SectionIn 1 - ;write file association - ${WriteRegStrAuth} HKCR GPAC\3g2\DefaultIcon "" "$INSTDIR\Osmo4.ico, 0" - ${WriteRegStrAuth} HKCR GPAC\3g2\Shell\open\command "" "$INSTDIR\Osmo4.exe %L" - ${WriteRegStrAuth} HKCR .3g2 "" "GPAC\3g2" - !system 'shell32.dll::SHChangeNotify(i, i, i, i) v (${SHCNE_ASSOCCHANGED}, ${SHCNF_IDLIST}, 0, 0)' -SectionEnd - -SubSectionEnd - - -Section "MP4Box (Command-line MPEG-4 tool)" - SectionIn 1 - SetOutPath $INSTDIR - File "..\MP4Box.exe" - - Push $INSTDIR - Call AddToPath -SectionEnd - - -!define HK_MOZ "SOFTWARE\MozillaPlugins\@gpac/osmozilla,version=1.0" - -Section "Osmozilla (GPAC Plugin for Mozilla)" - SectionIn 1 - SetOutPath $INSTDIR - File "..\nposmozilla.dll" - File "..\nposmozilla.xpt" - - ${WriteRegStrAuth} HKCU "SOFTWARE\MozillaPlugins\@gpac/osmozilla,version=1.0" "Path" "$INSTDIR\nposmozilla.dll" - ${WriteRegStrAuth} HKCU "SOFTWARE\MozillaPlugins\@gpac/osmozilla,version=1.0" "XPTPath" "$INSTDIR\nposmozilla.xpt" - ${WriteRegStrAuth} HKCU "SOFTWARE\MozillaPlugins\@gpac/osmozilla,version=1.0" "Version" "${GPAC_VERSION}" - ${WriteRegStrAuth} HKCU "SOFTWARE\MozillaPlugins\@gpac/osmozilla,version=1.0" "Vendor" "GPAC" - ${WriteRegStrAuth} HKCU "SOFTWARE\MozillaPlugins\@gpac/osmozilla,version=1.0" "Description" "GPAC plugin" - ${WriteRegStrAuth} HKCU "SOFTWARE\MozillaPlugins\@gpac/osmozilla,version=1.0" "ProductName" "Osmozilla" -SectionEnd - - -Section "GPAX (GPAC ActiveX Control)" - SectionIn 1 - SetOutPath $INSTDIR - File "..\GPAX.dll" - RegDLL "$INSTDIR\GPAX.dll" -SectionEnd - - -Section "MP4Client (GPAC Command-line client/grabber)" - SectionIn 1 - SetOutPath $INSTDIR - File "..\MP4Client.exe" -SectionEnd - - -Function .onInstSuccess -; MessageBox MB_YESNO "GPAC Framework installation complete. Do you want to launch the Osmo4 player?" IDNO NoLaunch -; Exec $INSTDIR\Osmo4.exe -; NoLaunch: -FunctionEnd - - - - - -; uninstall stuff - -UninstallText "This will uninstall OSMO4/GPAC from your computer. Hit next to continue." - -; special uninstall section. -Section "Uninstall" - ; remove registry keys - ${DeleteRegKeyAuth} HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\Osmo4" - ${DeleteRegKeyAuth} HKCU "SOFTWARE\GPAC" - ${DeleteRegKeyAuth} HKCU "SOFTWARE\MozillaPlugins\@gpac/osmozilla,version=1.0" - ${DeleteRegKeyAuth} HKCR GPAC\mp4\DefaultIcon - ${DeleteRegKeyAuth} HKCR GPAC\mp4\shell\open\command - ${DeleteRegKeyAuth} HKCR GPAC\mp4 - ${DeleteRegKeyAuth} HKCR .mp4 - ${DeleteRegKeyAuth} HKCR GPAC\3gp\DefaultIcon - ${DeleteRegKeyAuth} HKCR GPAC\3gp\shell\open\command - ${DeleteRegKeyAuth} HKCR GPAC\3gp - ${DeleteRegKeyAuth} HKCR .3gp - ${DeleteRegKeyAuth} HKCR GPAC\3g2\DefaultIcon - ${DeleteRegKeyAuth} HKCR GPAC\3g2\shell\open\command - ${DeleteRegKeyAuth} HKCR GPAC\3g2 - ${DeleteRegKeyAuth} HKCR .3g2 - ${DeleteRegKeyAuth} HKCR GPAC - - Delete $INSTDIR\cache\*.* - RMDir "$INSTDIR\cache" - Delete $INSTDIR\*.* - RMDir "$INSTDIR" - UnRegDLL "$INSTDIR\GPAX.dll" - Push $INSTDIR - Call un.RemoveFromPath - #has current user admin privileges? - userInfo::getAccountType - Pop $0 - StrCmp $0 "Admin" +1 +2 - SetShellVarContext all - Delete "$SMPROGRAMS\Osmo4\*.*" - RMDir "$SMPROGRAMS\Osmo4" - Delete "$QUICKLAUNCH\Osmo4.lnk" - Delete "$DESKTOP\Osmo4.lnk" - -SectionEnd - -;path modif functions -!verbose 3 -!include "WinMessages.NSH" -!verbose 4 - -; AddToPath - Adds the given dir to the search path. -; Input - head of the stack -; Note - Win9x systems requires reboot - -Function AddToPath - Exch $0 - Push $1 - Push $2 - Push $3 - - # don't add if the path doesn't exist - IfFileExists $0 "" AddToPath_done - - ReadEnvStr $1 PATH - Push "$1;" - Push "$0;" - Call StrStr - Pop $2 - StrCmp $2 "" "" AddToPath_done - Push "$1;" - Push "$0\;" - Call StrStr - Pop $2 - StrCmp $2 "" "" AddToPath_done - GetFullPathName /SHORT $3 $0 - Push "$1;" - Push "$3;" - Call StrStr - Pop $2 - StrCmp $2 "" "" AddToPath_done - Push "$1;" - Push "$3\;" - Call StrStr - Pop $2 - StrCmp $2 "" "" AddToPath_done - - Call IsNT - Pop $1 - StrCmp $1 1 AddToPath_NT - ; Not on NT - StrCpy $1 $WINDIR 2 - FileOpen $1 "$1\autoexec.bat" a - FileSeek $1 -1 END - FileReadByte $1 $2 - IntCmp $2 26 0 +2 +2 # DOS EOF - FileSeek $1 -1 END # write over EOF - FileWrite $1 "$\r$\nSET PATH=%PATH%;$3$\r$\n" - FileClose $1 - SetRebootFlag true - Goto AddToPath_done - - AddToPath_NT: - ReadRegStr $1 HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment" "PATH" - StrCpy $2 $1 1 -1 # copy last char - StrCmp $2 ";" 0 +2 # if last char == ; - StrCpy $1 $1 -1 # remove last char - StrCmp $1 "" AddToPath_NTdoIt - StrCpy $0 "$1;$0" - AddToPath_NTdoIt: - WriteRegExpandStr HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment" "PATH" $0 - - ReadRegStr $1 HKCU "Environment" "PATH" - StrCpy $2 $1 1 -1 # copy last char - StrCmp $2 ";" 0 +2 # if last char == ; - StrCpy $1 $1 -1 # remove last char - StrCmp $1 "" AddToPath_NTdoIt2 - StrCpy $0 "$1;$0" - AddToPath_NTdoIt2: - WriteRegExpandStr HKCU "Environment" "PATH" $0 - SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000 - - AddToPath_done: - Pop $3 - Pop $2 - Pop $1 - Pop $0 -FunctionEnd - -; RemoveFromPath - Remove a given dir from the path -; Input: head of the stack - -Function un.RemoveFromPath - Exch $0 - Push $1 - Push $2 - Push $3 - Push $4 - Push $5 - Push $6 - - IntFmt $6 "%c" 26 # DOS EOF - - Call un.IsNT - Pop $1 - StrCmp $1 1 unRemoveFromPath_NT - ; Not on NT - StrCpy $1 $WINDIR 2 - FileOpen $1 "$1\autoexec.bat" r - GetTempFileName $4 - FileOpen $2 $4 w - GetFullPathName /SHORT $0 $0 - StrCpy $0 "SET PATH=%PATH%;$0" - Goto unRemoveFromPath_dosLoop - - unRemoveFromPath_dosLoop: - FileRead $1 $3 - StrCpy $5 $3 1 -1 # read last char - StrCmp $5 $6 0 +2 # if DOS EOF - StrCpy $3 $3 -1 # remove DOS EOF so we can compare - StrCmp $3 "$0$\r$\n" unRemoveFromPath_dosLoopRemoveLine - StrCmp $3 "$0$\n" unRemoveFromPath_dosLoopRemoveLine - StrCmp $3 "$0" unRemoveFromPath_dosLoopRemoveLine - StrCmp $3 "" unRemoveFromPath_dosLoopEnd - FileWrite $2 $3 - Goto unRemoveFromPath_dosLoop - unRemoveFromPath_dosLoopRemoveLine: - SetRebootFlag true - Goto unRemoveFromPath_dosLoop - - unRemoveFromPath_dosLoopEnd: - FileClose $2 - FileClose $1 - StrCpy $1 $WINDIR 2 - Delete "$1\autoexec.bat" - CopyFiles /SILENT $4 "$1\autoexec.bat" - Delete $4 - Goto unRemoveFromPath_done - - unRemoveFromPath_NT: - ReadRegStr $1 HKCU "SYSTEM\CurrentControlSet\Control\Session Manager\Environment" "PATH" - StrCpy $5 $1 1 -1 # copy last char - StrCmp $5 ";" +2 # if last char != ; - StrCpy $1 "$1;" # append ; - Push $1 - Push "$0;" - Call un.StrStr ; Find `$0;` in $1 - Pop $2 ; pos of our dir - StrCmp $2 "" unRemoveFromPath_done - ; else, it is in path - # $0 - path to add - # $1 - path var - StrLen $3 "$0;" - StrLen $4 $2 - StrCpy $5 $1 -$4 # $5 is now the part before the path to remove - StrCpy $6 $2 "" $3 # $6 is now the part after the path to remove - StrCpy $3 $5$6 - - StrCpy $5 $3 1 -1 # copy last char - StrCmp $5 ";" 0 +2 # if last char == ; - StrCpy $3 $3 -1 # remove last char - - WriteRegExpandStr HKCU "SYSTEM\CurrentControlSet\Control\Session Manager\Environment" "PATH" $3 - SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000 - - unRemoveFromPath_done: - Pop $6 - Pop $5 - Pop $4 - Pop $3 - Pop $2 - Pop $1 - Pop $0 -FunctionEnd - -########################################### -# Utility Functions # -########################################### - -; IsNT -; no input -; output, top of the stack = 1 if NT or 0 if not -; -; Usage: -; Call IsNT -; Pop $R0 -; ($R0 at this point is 1 or 0) - -!macro IsNT un -Function ${un}IsNT - Push $0 - ReadRegStr $0 HKLM "SOFTWARE\Microsoft\Windows NT\CurrentVersion" CurrentVersion - StrCmp $0 "" 0 IsNT_yes - ; we are not NT. - Pop $0 - Push 0 - Return - - IsNT_yes: - ; NT!!! - Pop $0 - Push 1 -FunctionEnd -!macroend -!insertmacro IsNT "" -!insertmacro IsNT "un." - -; StrStr -; input, top of stack = string to search for -; top of stack-1 = string to search in -; output, top of stack (replaces with the portion of the string remaining) -; modifies no other variables. -; -; Usage: -; Push "this is a long ass string" -; Push "ass" -; Call StrStr -; Pop $R0 -; ($R0 at this point is "ass string") - -!macro StrStr un -Function ${un}StrStr -Exch $R1 ; st=haystack,old$R1, $R1=needle - Exch ; st=old$R1,haystack - Exch $R2 ; st=old$R1,old$R2, $R2=haystack - Push $R3 - Push $R4 - Push $R5 - StrLen $R3 $R1 - StrCpy $R4 0 - ; $R1=needle - ; $R2=haystack - ; $R3=len(needle) - ; $R4=cnt - ; $R5=tmp - loop: - StrCpy $R5 $R2 $R3 $R4 - StrCmp $R5 $R1 done - StrCmp $R5 "" done - IntOp $R4 $R4 + 1 - Goto loop -done: - StrCpy $R1 $R2 "" $R4 - Pop $R5 - Pop $R4 - Pop $R3 - Pop $R2 - Exch $R1 -FunctionEnd -!macroend -!insertmacro StrStr "" -!insertmacro StrStr "un." - diff --git a/bin/win32/release/nsis_install/gpac_installer.nsi b/bin/win32/release/nsis_install/gpac_installer.nsi index 128a87c..367006e 100644 --- a/bin/win32/release/nsis_install/gpac_installer.nsi +++ b/bin/win32/release/nsis_install/gpac_installer.nsi @@ -437,6 +437,7 @@ Section "MP4Box" SecMP4B SectionIn 1 SetOutPath $INSTDIR File "..\MP4Box.exe" + File "..\MP42TS.exe" Push $INSTDIR Call AddToPath diff --git a/configure b/configure index dbe7d88..feca36b 100755 --- a/configure +++ b/configure @@ -1,9 +1,11 @@ #!/bin/sh # -# GPAC MPEG-4 SDK configure script (c) 2003-2005 Jean Le Feuvre -# inspired from ffmpeg configure by Fabrice Bellard (c) 2000-2002 +# GPAC MPEG-4 SDK configure script (c) 2003-2005 Jean Le Feuvre (c) 2005-20XX Telecom ParisTech +# inspired from ffmpeg configure by Fabrice Bellard (c) 2000-2002 #set -v -# set temporary file name + + +#set temporary file name if test ! -z "$TMPDIR" ; then TMPDIR1="${TMPDIR}" elif test ! -z "$TEMPDIR" ; then @@ -12,6 +14,7 @@ else TMPDIR1="/tmp" fi + #thanks to ffmpeg for this for v in "$@"; do r="${v#*=}" @@ -28,7 +31,8 @@ TMPE="${TMPDIR1}/gpac-conf-${RANDOM}-$$-${RANDOM}" TMPO="${TMPDIR1}/gpac-conf-${RANDOM}-$$-${RANDOM}.o" TMPS="${TMPDIR1}/gpac-conf-${RANDOM}-$$-${RANDOM}.S" -# default parameters + +#default parameters DESTDIR="" prefix="/usr/local" mandir="" @@ -43,6 +47,7 @@ ar="ar" ranlib="ranlib" make="make" strip="strip" +pkg_config="pkg-config" readelf="readelf" install="${INSTALL:=install}" instflags="${INSTFLAGS:=-p}" @@ -61,6 +66,7 @@ js_lib="-ljs" lm_lib="" has_mingw_directx="no" has_js="no" +has_platinum="no" has_ft="no" has_jpeg="no" has_png="no" @@ -138,6 +144,10 @@ disable_isoff_frag="no" disable_streaming="no" enable_depth_compositor="no" enable_renoir="no" +has_avcap="no" +avcap_cflags="" +avcap_ldflags="-lavcap" +has_opensvc="no" win32="no" mingw32="no" @@ -162,7 +172,9 @@ OSS_LDFLAGS="" INSTFLAGS="" is_64="no" -#Configure Usage + + +#configure usage if test x"$1" = x"-h" -o x"$1" = x"--help" ; then cat << EOF @@ -199,11 +211,16 @@ GPAC configuration options: --disable-opt disable GCC optimizations --disable-ipv6 disable IPV6 support --disable-wx disable wxWidgets support + --disable-platinum disable Platinum UPnP support + --disable-alsa disable Alsa audio --disable-oss-audio disable OSS audio + --enable-jack enable Jack audio + --disable-jack disable Jack audio + --enable-pulseaudio enable Pulse audio + --disable-pulseaudio disable Pulse audio --disable-x11-shm disable X11 shared memory support --disable-x11-xv disable X11 Xvideo support --enable-fixed-point enable fixed-point math - --force-fixed-point force fixed-point math without changing gpac math.h header --enable-tinygl enable TinyGL support --enable-joystick enable joystick support --disable-ssl disable OpenSSL support @@ -211,6 +228,7 @@ GPAC configuration options: --enable-amr-nb enable AMR NB library --enable-amr-wb enable AMR WB library --enable-amr enable both AMR NB and WB libraries + --enable-static-bin GPAC static build --static-mp4box configure for static linking of MP4Box. --enable-depth enables depth handling in the compositor @@ -230,6 +248,7 @@ Configuration options for libgpac - all options can be enabled with --enable-opt --disable-m2ps disable MPEG2 PS --disable-m2ts disable MPEG2 TS --disable-m2ts-mux disable MPEG2 TS Multiplexer + --disable-dvb4linux disable dvb4linux support --disable-parsers disable AV parsers --disable-import disable media importers --disable-export disable media exporters @@ -251,6 +270,7 @@ Configuration options for libgpac - all options can be enabled with --enable-opt Extra libraries configuration. You can turn a libray off or force using the local version in gpac/extra_lib/ --use-js=OPT force SpiderMonkey ECMAScript OPT=[no,local] --use-ft=OPT force FreeType OPT=[no,local] + --use-zlib=OPT force ZLIB OPT=[no,local] --use-jpeg=OPT force JPEG OPT=[no,local] --use-png=OPT force PNG OPT=[no,local] --use-faad=OPT force FAAD OPT=[no,local] @@ -261,6 +281,7 @@ Extra libraries configuration. You can turn a libray off or force using the loca --use-vorbis=OPT force vorbis OPT=[no,system,local] --use-theora=OPT force theora OPT=[no,system,local] --use-openjpeg=OPT force openjpeg OPT=[no,system,local] + --use-a52=OPT force a52 (ac3) OPT=[no,system,local] NOTE: The object files are build at the place where configure is launched EOF @@ -289,6 +310,12 @@ for opt do ;; --cpu=*) cpu=`echo $opt | cut -d '=' -f 2` ;; + --enable-debug) debuginfo="yes"; no_gcc_opt="yes" + ;; + --disable-opt) no_gcc_opt="yes" + ;; + --enable-pic) want_pic="yes"; + ;; esac done @@ -352,7 +379,7 @@ case "$cpu" in ;; esac -# Checking for CFLAGS +#checking for CFLAGS if test -z "$CFLAGS"; then CFLAGS="" fi @@ -365,6 +392,7 @@ if test "$mingw32" = "yes" ; then cross_prefix="" fi + cc="${cross_prefix}${cc}" #for ccache cc="${cc}" @@ -372,9 +400,16 @@ cpp="${cross_prefix}${cpp}" ar="${cross_prefix}${ar}" ranlib="${cross_prefix}${ranlib}" strip="${cross_prefix}${strip}" +pkg_config="${cross_prefix}${pkg_config}" -# find source path +#check pkg_config +if ! $pkg_config --version >/dev/null 2>&1 ; then + echo "$pkg_config not found, configure may detect wrong libraries" +fi + + +#find source path source_path="`echo $0 | sed -e 's#/configure##'`" source_path_used="yes" if test -z "$source_path" -o "$source_path" = "." ; then @@ -383,7 +418,9 @@ if test -z "$source_path" -o "$source_path" = "." ; then else source_path="`cd \"$source_path\"; pwd`" fi -# OS specific + + +#OS specific targetos=`uname -s` case $targetos in BeOS) @@ -402,8 +439,8 @@ case $targetos in esac SHFLAGS=-nostart - # no need for libm, but the inet stuff - # Check for BONE + #no need for libm, but the inet stuff + #check for BONE if (echo $BEINCLUDES|grep 'headers/be/bone' >/dev/null); then extralibs="-lbind -lsocket" else @@ -417,11 +454,12 @@ case $targetos in readelf="greadelf" LDFLAGS="${opt#--extra-ldflags=}" instflags="" - # check for 64-bit -cat > $TMPC << EOF + #check for 64-bit + cat > $TMPC << EOF #include int main( void ) { return 0; } EOF + CFLAGS_NO_LTO=$(echo ${CFLAGS} | sed -e 's/\ -flto[-A-Za-z0-9=]*//g') $cc ${CFLAGS_NO_LTO} -o $TMPO $TMPC 2>/dev/null && $($readelf -h $TMPO | grep "Class:.*ELF64$" >/dev/null 2>&1) if test $? -eq 0; then @@ -458,11 +496,11 @@ EOF xul_flags=-DXP_MAC if test -d /sw/bin ; then alt_macosx_dir="/sw" - CFLAGS="-I/sw/include $CFLAGS" + CFLAGS_DIR="-I/sw/include" LDFLAGS="-L/sw/lib $LDFLAGS" elif test -d /opt/local/bin ; then alt_macosx_dir="/opt/local" - CFLAGS="-I/opt/local/include $CFLAGS" + CFLAGS_DIR="-I/opt/local/include" LDFLAGS="-L/opt/local/lib $LDFLAGS" fi cc="cc" @@ -519,7 +557,8 @@ EOF *) ;; esac -# Defines directory for binaries and libs (ex. for TinyGL) + +#defines directory for binaries and libs (ex. for TinyGL) target_bin_dir="" if test "$cross_prefix" = "" ; then target_bin_dir=`${cc} -v 2>&1 | sed -n '2p' | awk ' {print $2}'`-${cc_orig} @@ -527,12 +566,14 @@ else target_bin_dir=${cross_prefix}${cc_orig} fi + #if test "$source_path_used" = "yes" ; then mkdir -p extra_lib mkdir -p extra_lib/lib mkdir -p extra_lib/lib/gcc #fi + #OK check for all local & systems lib local_inc=$source_path/extra_lib/include local_lib=extra_lib/lib/gcc @@ -543,6 +584,7 @@ cat > $TMPC << EOF #include int main( void ) { return 0; } EOF +CFLAGS="$CFLAGS -Wall" if $cc -o $TMPO $TMPC -fno-strict-aliasing 2> /dev/null ; then CFLAGS="$CFLAGS -fno-strict-aliasing" fi @@ -551,22 +593,55 @@ if $cc -o $TMPO $TMPC -lz -Wno-pointer-sign 2> /dev/null ; then CFLAGS="$CFLAGS -Wno-pointer-sign" fi + +#GCC opt +if test "$no_gcc_opt" = "no"; then + if $cc --version | grep 'sbox-arm-linux-gcc (GCC) 3.4.4' + then + echo "Detected buggy arm GCC (diablo), using -O2" + CFLAGS="-O2 $CFLAGS" + else + CFLAGS="-O3 $CFLAGS" + fi +fi + + +#GCC PIC +if test "$want_pic" = "yes" ; then + CFLAGS="$CFLAGS -fPIC -DPIC" + CPPFLAGS="$CPPFLAGS -fPIC -DPIC" +fi + + +#force use of cflags with cc +cc_naked=$cc +cpp_naked=$cpp +cc="$cc $CFLAGS" +cpp="$cpp $CPPFLAGS" + + #look for zlib cat > $TMPC << EOF #include -int main( void ) { if (zlibVersion() != ZLIB_VERSION) { puts("zlib version differs !!!"); return 1; } return 0; } +int main( void ) { if (strcmp(zlibVersion(), ZLIB_VERSION)) { puts("zlib version differs !!!"); return 1; } return 0; } EOF has_zlib="no" -if $cc -o $TMPO $TMPC -lz 2> /dev/null ; then - has_zlib="system" -elif $cc -o $TMPO $TMPC -I"$local_inc/zlib" -L$local_lib -lz 2> /dev/null ; then +if test "$cross_prefix" = "" ; then + if $cc -o $TMPO $TMPC -lz $LDFLAGS 2> /dev/null ; then + has_zlib="system" + fi +fi +if test "$has_zlib" = "no" ; then + if $cc -o $TMPO $TMPC -I"$local_inc/zlib" -L$local_lib -lz 2> /dev/null ; then has_zlib="local" -else + else echo "error: zlib not found on system or in local libs" exit 1 + fi fi + #check dlopen cat > $TMPC << EOF #include @@ -575,7 +650,7 @@ EOF if $cc -o $TMPE $TMPC > /dev/null 2>&1 ; then dlopen="yes" -elif $cc -o $TMPE $TMPC -ldl > /dev/null 2>&1 ; then +elif $cc -o $TMPE $TMPC $LDFLAGS -ldl > /dev/null 2>&1 ; then GPAC_SH_FLAGS="$GPAC_SH_FLAGS -ldl" else if test "$win32" = "no"; then @@ -585,6 +660,7 @@ else fi + #look for spidermonkey JS support #spidermonkey test for new API @@ -602,14 +678,27 @@ EOF has_js="local" #dc added else - - if pkg-config --exists mozilla-js 2> /dev/null ; then - js_flags=`pkg-config --cflags mozilla-js` - js_lib_pkg=`pkg-config --libs mozilla-js` - if $cpp -o $TMPO $TMPCPP $js_flags $js_lib_pkg -lpthread 2> /dev/null ; then + #try pkg-config + if $pkg_config --exists mozilla-js 2> /dev/null ; then + js_flags=`$pkg_config --cflags mozilla-js` + js_lib_pkg=`$pkg_config --libs mozilla-js` + if $cpp -o $TMPO $TMPCPP $js_flags $js_lib_pkg $LDFLAGS -lpthread 2> /dev/null ; then has_js="system" - js_lib=`pkg-config --libs mozilla-js` + js_lib=`$pkg_config --libs mozilla-js` fi + #try firefox folders (starting at ubuntu 11.10, no pkg-config) + elif ls -d /usr/lib/firefox* > /dev/null 2>&1 ; then + firefox_version=`cd /usr/lib ; ls -d firefox* | grep -v addons | grep -v devel ; cd - > /dev/null` + for i in $firefox_version ; do + if test "$has_js" = "no" ; then + js_inc="/usr/include/$i" + js_flags="-DXP_UNIX -I$js_inc" + js_lib="-L/usr/lib/$i/ -lxul -lmozsqlite3 -lmozalloc -lnss3" + if $cpp -o $TMPO $TMPCPP $js_flags $js_lib 2> /dev/null ; then + has_js="$i" + fi + fi + done fi if test "$has_js" = "no" ; then @@ -620,10 +709,10 @@ EOF has_js="prefix" #dc added end else - if $cpp -o $TMPO $TMPCPP $js_flags -ljs -lpthread 2> /dev/null ; then + if $cpp -o $TMPO $TMPCPP $js_flags $LDFLAGS -ljs -lpthread 2> /dev/null ; then js_inc="/usr/include" has_js="system" - elif $cpp -o $TMPO $TMPCPP -DXP_UNIX -I$alt_macosx_dir/include/js -L$alt_macosx_dir/lib -ljs -lpthread 2> /dev/null ; then + elif $cpp -o $TMPO $TMPCPP -DXP_UNIX -I$alt_macosx_dir/include/js -L$alt_macosx_dir/lib $LDFLAGS -ljs -lpthread 2> /dev/null ; then has_js="system" js_flags="-DXP_UNIX -I$alt_macosx_dir/include/js" js_lib="-L$alt_macosx_dir/lib -ljs" @@ -632,14 +721,14 @@ EOF #debian spidermonkey (smjs) js_flags="-DXP_UNIX -I/usr/include/smjs" js_inc="/usr/include/smjs" - if $cpp -o $TMPO $TMPCPP $js_flags -lsmjs -lpthread 2> /dev/null ; then + if $cpp -o $TMPO $TMPCPP $js_flags $LDFLAGS -lsmjs -lpthread 2> /dev/null ; then has_js="system" js_lib="-lsmjs" else #debian spidermonkey (mozjs) js_flags="-DXP_UNIX -I/usr/include/mozjs" js_inc="/usr/include/mozjs" - if $cpp -o $TMPO $TMPCPP $js_flags -lmozjs -lpthread 2> /dev/null ; then + if $cpp -o $TMPO $TMPCPP $js_flags $LDFLAGS -lmozjs -lpthread 2> /dev/null ; then has_js="system" js_lib="-lmozjs" fi @@ -680,19 +769,19 @@ EOF has_js="prefix" #dc added end else - if pkg-config --exists mozilla-js 2> /dev/null ; then - js_flags=`pkg-config --cflags mozilla-js` - js_lib_pkg=`pkg-config --libs mozilla-js` - if $cc -o $TMPO $TMPC $js_flags $js_lib_pkg -lpthread 2> /dev/null ; then + if $pkg_config --exists mozilla-js 2> /dev/null ; then + js_flags=`$pkg_config --cflags mozilla-js` + js_lib_pkg=`$pkg_config --libs mozilla-js` + if $cc -o $TMPO $TMPC $js_flags $js_lib_pkg $LDFLAGS -lpthread 2> /dev/null ; then has_js="system" - js_lib=`pkg-config --libs mozilla-js` + js_lib=`$pkg_config --libs mozilla-js` fi fi if test "$has_js" = "no" ; then - if $cc -o $TMPO $TMPC $js_flags -ljs 2> /dev/null ; then + if $cc -o $TMPO $TMPC $js_flags $LDFLAGS -ljs 2> /dev/null ; then js_inc="/usr/include" has_js="system" - elif $cc -o $TMPO $TMPC -DXP_UNIX -I$alt_macosx_dir/include/js -L$alt_macosx_dir/lib -ljs 2> /dev/null ; then + elif $cc -o $TMPO $TMPC -DXP_UNIX -I$alt_macosx_dir/include/js -L$alt_macosx_dir/lib $LDFLAGS -ljs 2> /dev/null ; then has_js="system" js_flags="-DXP_UNIX -I$alt_macosx_dir/include/js" js_lib="-L$alt_macosx_dir/lib -ljs" @@ -701,14 +790,14 @@ EOF #debian spidermonkey (smjs) js_flags="-DXP_UNIX -I/usr/include/smjs" js_inc="/usr/include/smjs" - if $cc -o $TMPO $TMPC $js_flags -lsmjs 2> /dev/null ; then + if $cc -o $TMPO $TMPC $js_flags $LDFLAGS -lsmjs 2> /dev/null ; then has_js="system" js_lib="-lsmjs" else #debian spidermonkey (mozjs) js_flags="-DXP_UNIX -I/usr/include/mozjs" js_inc="/usr/include/mozjs" - if $cc -o $TMPO $TMPC $js_flags -lmozjs 2> /dev/null ; then + if $cc -o $TMPO $TMPC $js_flags $LDFLAGS -lmozjs 2> /dev/null ; then has_js="system" js_lib="-lmozjs" fi @@ -721,7 +810,6 @@ EOF fi - if test "$has_js" != "no" ; then if test "$linux" = "yes" ; then if test "$cpu" != "sh4"; then @@ -745,6 +833,79 @@ fi #end JS test + +#look for platinum support +cat > $TMPC << EOF +#include +int main( void ) { return 0; } +EOF +if $cpp -o $TMPO $TMPC -I$local_inc/platinum $LDFLAGS -L$local_lib -lPlatinum -lPltMediaServer -lPltMediaConnect -lPltMediaRenderer -lNeptune -lZlib 2> /dev/null ; then + has_platinum="yes" +fi + + +#look for avcap support +avcap_cflags="" +avcap_ldflags="-lavcap" + +cat > $TMPC << EOF +#include +using namespace avcap; +int main( void ) { + const DeviceCollector::DeviceList& dl = DEVICE_COLLECTOR::instance().getDeviceList(); + DeviceDescriptor* dd = 0; + for (DeviceCollector::DeviceList::const_iterator i = dl.begin(); i != dl.end(); i++) { + dd = *i; + std::cout << dd->getName().c_str() << "\n"; + } + return 0; +} +EOF +if $cpp -o $TMPO $TMPC $LDFLAGS $avcap_cflags $avcap_ldflags 2> /dev/null ; then + has_avcap="yes" +else + if test "$darwin" = "yes" ; then + avcap_cflags="-I$local_inc -I$local_inc/avcap/osx" + avcap_ldflags="${wl}-flat_namespace -lavcap -lpthread -framework QuickTime -framework QuartzCore" + else + avcap_cflags="-I$local_inc -I$local_inc/avcap/linux" + avcap_ldflags="-lavcap -lpthread" + fi + if $cpp -o $TMPO $TMPC $avcap_cflags $LDFLAGS -L$local_lib $avcap_ldflags 2> /dev/null ; then + has_avcap="yes" + avcap_ldflags="-L../../$local_lib $avcap_ldflags" + fi +fi + + +#look for opensvc support + +if test "$darwin" = "yes" ; then +osvc_cflags="-I/usr/include -I/usr/local/include" +osvc_ldflags="-L/usr/lib -L/usr/local/lib -lOpenSVCDec" +else +osvc_cflags="" +osvc_ldflags="-lOpenSVCDec" +fi + +cat > $TMPC << EOF +#include +int main( void ) { return 0; } +EOF +if $cpp -o $TMPO $TMPC $osvc_cflags $LDFLAGS $osvc_ldflags 2> /dev/null ; then + has_opensvc="yes" +else +osvc_cflags="-I$local_inc" +osvc_ldflags="-lOpenSVCDec" + +if $cpp -o $TMPO $TMPC $osvc_cflags $LDFLAGS -L$local_lib $osvc_ldflags 2> /dev/null ; then + has_opensvc="yes" + osvc_ldflags="-L../../$local_lib $osvc_ldflags" +fi + +fi + + #look for freetype support cat > $TMPC << EOF #include @@ -753,30 +914,25 @@ cat > $TMPC << EOF #include FT_OUTLINE_H int main( void ) { return 0; } EOF -if test "$cross_prefix" != "" ; then +if test "$cross_prefix" = "" ; then ft_cflags="-I$prefix/include " ft_lflags="-L$prefix/lib -lfreetype" - if $cc -o $TMPO $TMPC $ft_cflags $ft_lflags 2> /dev/null ; then + if $cc $CFLAGS_DIR -o $TMPO $TMPC $ft_cflags $ft_lflags $LDFLAGS 2> /dev/null ; then has_ft="system" else - ft_cflags="-I$local_inc/freetype" - ft_lflags="-L$local_lib -lfreetype" - if $cc -o $TMPO $TMPC $ft_cflags $ft_lflags 2> /dev/null ; then - has_ft="local" + ft_cflags="`freetype-config --cflags`" + ft_lflags="`freetype-config --libs`" + if $cc -o $TMPO $TMPC $ft_cflags $ft_lflags $LDFLAGS 2> /dev/null ; then + has_ft="system" fi fi -else +fi +if test "$has_ft" = "no" ; then if test "`which freetype-config`" != ""; then - ft_cflags="`freetype-config --cflags`" - ft_lflags="`freetype-config --libs`" + ft_cflags="-I$local_inc/freetype" + ft_lflags="-L$local_lib -lfreetype" if $cc -o $TMPO $TMPC $ft_cflags $ft_lflags 2> /dev/null ; then - has_ft="system" - else - ft_cflags="-I$local_inc/freetype" - ft_lflags="-L$local_lib -lfreetype" - if $cc -o $TMPO $TMPC $ft_cflags $ft_lflags 2> /dev/null ; then - has_ft="local" - fi + has_ft="local" fi fi fi @@ -799,11 +955,12 @@ else LINK_SSL="-lssl -lcrypto" fi -if $cc -o $TMPO $TMPC $LINK_SSL 2> /dev/null ; then +if $cc -o $TMPO $TMPC $LINK_SSL $LDFLAGS 2> /dev/null ; then has_ssl="yes" fi + #look for JPEG support cat > $TMPC << EOF #include @@ -811,44 +968,37 @@ cat > $TMPC << EOF int main( void ) { return 0; } EOF -if test "$cross_prefix" != "" ; then - if test "`which $prefix/bin/jpeg-config`" != ""; then +if test "$cross_prefix" = "" ; then + if $cc -o $TMPO $TMPC $LDFLAGS -ljpeg 2> /dev/null ; then + has_jpeg="system" + elif test "$alt_macosx_dir" != "" ; then + if cc -o $TMPO $TMPC -I$alt_macosx_dir/include -L$alt_macosx_dir/lib $LDFLAGS -ljpeg 2> /dev/null ; then + has_jpeg="system" + fi + elif test "`which $prefix/bin/jpeg-config`" != ""; then jpeg_cflags="`$prefix/bin/jpeg-config --cflags`" jpeg_lflags="`$prefix/bin/jpeg-config --libs`" - if $cc -o $TMPO $TMPC $jpeg_cflags $jpeg_lflags 2> /dev/null ; then + if $cc -o $TMPO $TMPC $jpeg_cflags $jpeg_lflags $LDFLAGS 2> /dev/null ; then has_jpeg="system" - else - jpeg_cflags="-I$local_inc/jpeg" - jpeg_lflags="-L$local_lib -ljpeg" - if $cc -o $TMPO $TMPC $jpeg_cflags $jpeg_lflags 2> /dev/null ; then - has_jpeg="local" - fi fi else jpeg_cflags="-I$prefix/include" jpeg_lflags="-L$prefix/lib -ljpeg" - if $cc -o $TMPO $TMPC $jpeg_cflags $jpeg_lflags 2> /dev/null ; then + if $cc -o $TMPO $TMPC $jpeg_cflags $jpeg_lflags $LDFLAGS 2> /dev/null ; then has_jpeg="system" - else - jpeg_cflags="-I$local_inc/jpeg" - jpeg_lflags="-L$local_lib -ljpeg" - if $cc -o $TMPO $TMPC $jpeg_cflags $jpeg_lflags 2> /dev/null ; then - has_jpeg="local" - fi fi fi -else - if $cc -o $TMPO $TMPC -ljpeg 2> /dev/null ; then - has_jpeg="system" - elif test "$alt_macosx_dir" != "" ; then - if cc -o $TMPO $TMPC -I$alt_macosx_dir/include -L$alt_macosx_dir/lib -ljpeg 2> /dev/null ; then - has_jpeg="system" - fi - elif $cc -o $TMPO $TMPC -I$local_inc/jpeg -L$local_lib -ljpeg 2> /dev/null ; then +fi +if test "$has_jpeg" = "no" ; then + jpeg_cflags="-I$local_inc/jpeg" + jpeg_lflags="-L$local_lib -ljpeg" + if $cc -o $TMPO $TMPC $jpeg_cflags $jpeg_lflags 2> /dev/null ; then has_jpeg="local" fi fi + + #look for OpenJPEG support cat > $TMPC << EOF #include @@ -856,64 +1006,76 @@ cat > $TMPC << EOF int main( void ) { return 0; } EOF -if $cc -o $TMPO $TMPC -lopenjpeg 2> /dev/null ; then - has_openjpeg="system" -elif test "$alt_macosx_dir" != "" ; then - if cc -o $TMPO $TMPC -I$alt_macosx_dir/include -L$alt_macosx_dir/lib -ljpeg 2> /dev/null ; then +if test "$cross_prefix" = "" ; then + if $cc -o $TMPO $TMPC $LDFLAGS -lopenjpeg 2> /dev/null ; then has_openjpeg="system" + elif test "$alt_macosx_dir" != "" ; then + if cc -o $TMPO $TMPC -I$alt_macosx_dir/include -L$alt_macosx_dir/lib $LDFLAGS -ljpeg 2> /dev/null ; then + has_openjpeg="system" + fi + fi +fi + +if test "$has_openjpeg" = "no" ; then + if $cc -o $TMPO $TMPC -I$local_inc/openjpeg -L$local_lib -lopenjpeg 2> /dev/null ; then + has_openjpeg="local" fi -elif $cc -o $TMPO $TMPC -I$local_inc/openjpeg -L$local_lib -lopenjpeg 2> /dev/null ; then - has_openjpeg="local" fi + + #look for PNG support cat > $TMPC << EOF #include int main( void ) { return 0; } EOF -if test "$cross_prefix" != "" ; then +if test "$cross_prefix" = "" ; then png_cflags="-I$prefix/include" png_lflags="-L$prefix/lib -lpng -lz" #-nostdlib prevents from searching standard compiler libraries #if $cc -o $TMPO $TMPC -nostdlib $png_cflags $png_lflags 2> /dev/null ; then - if $cc -o $TMPO $TMPC $png_cflags $png_lflags 2> /dev/null ; then + if $cc -o $TMPO $TMPC $png_cflags $png_lflags $LDFLAGS 2> /dev/null ; then has_png="system" - else - png_cflags="-I$local_inc/png" - png_lflags="-L$local_lib -lpng" - if $cc -o $TMPO $TMPC $png_cflags $png_lflags 2> /dev/null ; then - has_png="local" - fi - fi -else - if $cc -o $TMPO $TMPC -lpng -lz 2> /dev/null ; then + elif $cc -o $TMPO $TMPC $LDFLAGS -lpng -lz 2> /dev/null ; then has_png="system" elif test "$alt_macosx_dir" != "" ; then - if $cc -o $TMPO $TMPC -I$alt_macosx_dir/include -L$alt_macosx_dir/lib -lpng 2> /dev/null ; then + if $cc -o $TMPO $TMPC -I$alt_macosx_dir/include -L$alt_macosx_dir/lib $LDFLAGS -lpng 2> /dev/null ; then has_png="system" fi - elif $cc -o $TMPO $TMPC -I$local_inc/png -L$local_lib -lpng 2> /dev/null ; then + fi +fi +if test "$has_png" = "no" ; then + if $cc -o $TMPO $TMPC -I$local_inc/png -L$local_lib -lpng 2> /dev/null ; then has_png="local" fi fi + + #look for MAD support cat > $TMPC << EOF #include int main( void ) { return 0; } EOF -if $cc -o $TMPO $TMPC -lmad 2> /dev/null ; then - has_mad="system" -elif test "$alt_macosx_dir" != "" ; then - if $cc -o $TMPO $TMPC -I$alt_macosx_dir/include -L$alt_macosx_dir/lib -lmad 2> /dev/null ; then +if test "$cross_prefix" = "" ; then + if $cc -o $TMPO $TMPC $LDFLAGS -lmad 2> /dev/null ; then has_mad="system" + elif test "$alt_macosx_dir" != "" ; then + if $cc -o $TMPO $TMPC -I$alt_macosx_dir/include -L$alt_macosx_dir/lib $LDFLAGS -lmad 2> /dev/null ; then + has_mad="system" + fi + fi +fi +if test "$has_mad" = "no" ; then + if $cc -o $TMPO $TMPC -I$local_inc/mad -L$local_lib -lmad 2> /dev/null ; then + has_mad="local" fi -elif $cc -o $TMPO $TMPC -I$local_inc/mad -L$local_lib -lmad 2> /dev/null ; then - has_mad="local" fi + + #look for A52DEC support cat > $TMPC << EOF #define uint32_t unsigned int @@ -923,15 +1085,22 @@ cat > $TMPC << EOF int main( void ) { return 0; } EOF -if $cc -o $TMPO $TMPC -la52 2> /dev/null ; then - has_a52="system" -elif test "$alt_macosx_dir" != "" ; then - if $cc -o $TMPO $TMPC -I$alt_macosx_dir/include -L$alt_macosx_dir/lib -la52 2> /dev/null ; then +if test "$cross_prefix" = "" ; then + if $cc -o $TMPO $TMPC $LDFLAGS -la52 2> /dev/null ; then has_a52="system" + elif test "$alt_macosx_dir" != "" ; then + if $cc -o $TMPO $TMPC -I$alt_macosx_dir/include -L$alt_macosx_dir/lib $LDFLAGS -la52 2> /dev/null ; then + has_a52="system" + fi fi -elif $cc -o $TMPO $TMPC -I$local_inc -L$local_lib -la52 2> /dev/null ; then - has_a52="local" fi +if test "$has_a52" = "no" ; then + if $cc -o $TMPO $TMPC -I$local_inc -L$local_lib -la52 2> /dev/null ; then + has_a52="local" + fi +fi + + #look for XVID support cat > $TMPC << EOF @@ -939,47 +1108,57 @@ cat > $TMPC << EOF int main( void ) { return 0; } EOF -if test "$cross_prefix" != "" ; then - if $cc -o $TMPO $TMPC -I$prefix/include -L$prefix/lib -lxvidcore -lpthread 2> /dev/null ; then +if test "$cross_prefix" = "" ; then + if $cc -o $TMPO $TMPC -I$prefix/include -L$prefix/lib $LDFLAGS -lxvidcore -lpthread 2> /dev/null ; then has_xvid="system" - elif $cc -o $TMPO $TMPC -I$local_inc/xvid -L$local_lib -lxvidcore -lpthread 2> /dev/null ; then - has_xvid="local" - fi -else - if $cc -o $TMPO $TMPC -lxvidcore -lpthread 2> /dev/null ; then + elif $cc -o $TMPO $TMPC $LDFLAGS -lxvidcore -lpthread 2> /dev/null ; then has_xvid="system" elif test "$alt_macosx_dir" != "" ; then - if $cc -o $TMPO $TMPC -I$alt_macosx_dir/include -L$alt_macosx_dir/lib -lxvidcore -lpthread 2> /dev/null ; then + if $cc -o $TMPO $TMPC -I$alt_macosx_dir/include -L$alt_macosx_dir/lib $LDFLAGS -lxvidcore -lpthread 2> /dev/null ; then has_xvid="system" fi - elif $cc -o $TMPO $TMPC -I$local_inc/xvid -L$local_lib -lxvidcore -lpthread 2> /dev/null ; then + fi +fi +if test "$has_xvid" = "no" ; then + if $cc -o $TMPO $TMPC -I$local_inc/xvid -L$local_lib -lxvidcore -lpthread 2> /dev/null ; then has_xvid="local" fi fi + + #look for FAAD support cat > $TMPC << EOF #include int main( void ) { return 0; } EOF -if $cc -o $TMPO $TMPC -lfaad -lm 2> /dev/null ; then - has_faad="system" -elif test "$alt_macosx_dir" != "" ; then - if $cc -o $TMPO $TMPC -I$alt_macosx_dir/include -L$alt_macosx_dir/lib -lfaad 2> /dev/null ; then +if test "$cross_prefix" = "" ; then + if $cc -o $TMPO $TMPC $LDFLAGS -lfaad -lm 2> /dev/null ; then has_faad="system" + elif test "$alt_macosx_dir" != "" ; then + if $cc -o $TMPO $TMPC -I$alt_macosx_dir/include -L$alt_macosx_dir/lib $LDFLAGS -lfaad 2> /dev/null ; then + has_faad="system" + fi fi -elif $cc -o $TMPO $TMPC -I$local_inc/faad -L$local_lib -lfaad -lm 2> /dev/null ; then - has_faad="local" fi +if test "$has_faad" = "no" ; then + if $cc -o $TMPO $TMPC -I$local_inc/faad -L$local_lib -lfaad -lm 2> /dev/null ; then + has_faad="local" + fi +fi + #look for FFMPEG support -if pkg-config --exists libavcodec libavformat libswscale 2> /dev/null ; then - ffmpeg_cflags=`pkg-config --cflags libavcodec libavformat libswscale libavutil` - ffmpeg_lflags=`pkg-config --libs libavcodec libavformat libswscale libavutil` +if $pkg_config --exists libavcodec libavformat libswscale 2> /dev/null ; then + ffmpeg_cflags=`$pkg_config --cflags libavcodec libavformat libswscale libavutil` + ffmpeg_lflags=`$pkg_config --libs libavcodec libavformat libswscale libavutil` has_ffmpeg="system" +else + ffmpeg_cflags="" + ffmpeg_lflags="-lz -lavcodec -lavformat" fi cat > $TMPC << EOF @@ -987,7 +1166,7 @@ cat > $TMPC << EOF int main( void ) { return 0; } EOF -if $cc -o $TMPO $TMPC $ffmpeg_cflags $ffmpeg_lflags 2> /dev/null ; then +if $cc -o $TMPO $TMPC $ffmpeg_cflags $ffmpeg_lflags $LDFLAGS 2> /dev/null ; then old_ffmpeg_inc="no" else old_ffmpeg_inc="yes" @@ -996,34 +1175,53 @@ else #include int main( void ) { return 0; } EOF + fi -if test "$cross_prefix" != "" ; then - if $cc -o $TMPO $TMPC -I$prefix/include -L$prefix/lib -lz -lavcodec -lavformat -lavutil 2> /dev/null ; then +if test "$cross_prefix" = "" ; then + if $cc -o $TMPO $TMPC -I$prefix/include -L$prefix/lib $ffmpeg_lflags $LDFLAGS 2> /dev/null ; then has_ffmpeg="system" ffmpeg_cflags="-I$prefix/include" - ffmpeg_lflags="-L$prefix/lib -lz -lavcodec -lavformat -lavutil" - else - if $cc -o $TMPO $TMPC -I$local_inc -L$local_lib -lz -lavcodec -lavformat 2> /dev/null ; then - has_ffmpeg="local" - ffmpeg_cflags="-I$local_inc/include" - ffmpeg_lflags="-L$local_lib/lib -lz -lavcodec -lavformat -lavutil" - echo OK - fi - fi -else - if $cc -o $TMPO $TMPC -lz -lavcodec -lavformat 2> /dev/null ; then + ffmpeg_lflags="-L$prefix/lib $ffmpeg_lflags" + elif $cc -o $TMPO $TMPC $ffmpeg_lflags $LDFLAGS 2> /dev/null ; then has_ffmpeg="system" elif test "$alt_macosx_dir" != "" ; then - if $cc -o $TMPO $TMPC -I$alt_macosx_dir/include -L$alt_macosx_dir/lib -lz -lavcodec -lavformat 2> /dev/null ; then + if $cc -o $TMPO $TMPC -I$alt_macosx_dir/include -L$alt_macosx_dir/lib $ffmpeg_lflags $LDFLAGS 2> /dev/null ; then has_ffmpeg="system" ffmpeg_cflags="-I$alt_macosx_dir/include" - ffmpeg_lflags="-L$alt_macosx_dir/lib -lz -lavcodec -lavformat" + ffmpeg_lflags="-L$alt_macosx_dir/lib $ffmpeg_lflags" fi - elif $cc -o $TMPO $TMPC -I$local_inc -L$local_lib -lz -lavcodec -lavformat 2> /dev/null ; then + fi +fi +if test "$has_ffmpeg" = "no" ; then + if $cc -o $TMPO $TMPC -I$local_inc -L$local_lib $ffmpeg_lflags 2> /dev/null ; then has_ffmpeg="local" ffmpeg_cflags="-I$local_inc" - ffmpeg_lflags="-L$local_lib -lz -lavcodec -lavformat -lavutil" + ffmpeg_lflags="-L$local_lib $ffmpeg_lflags" + fi +fi + + + +#look for MAD support +has_freenect="no" +cat > $TMPC << EOF +#include +int main( void ) { return 0; } +EOF + +if test "$cross_prefix" = "" ; then + if $cc -o $TMPO $TMPC $LDFLAGS -lfreenect 2> /dev/null ; then + has_freenect="system" + elif test "$alt_macosx_dir" != "" ; then + if $cc -o $TMPO $TMPC -I$alt_macosx_dir/include -L$alt_macosx_dir/lib $LDFLAGS -lfreenect 2> /dev/null ; then + has_freenect="system" + fi + fi +fi +if test "$has_freenect" = "no" ; then + if $cc -o $TMPO $TMPC -I$local_inc/mad -L$local_lib -lfreenect 2> /dev/null ; then + has_freenect="local" fi fi @@ -1034,42 +1232,46 @@ cat > $TMPC << EOF int main( void ) { return 0; } EOF -if $cc -o $TMPO $TMPC -lvorbis 2> /dev/null ; then +if $cc -o $TMPO $TMPC $LDFLAGS -lvorbis 2> /dev/null ; then has_vorbis="system" elif test "$alt_macosx_dir" != "" ; then - if $cc -o $TMPO $TMPC -I$alt_macosx_dir/include -L$alt_macosx_dir/lib -lvorbis 2> /dev/null ; then + if $cc -o $TMPO $TMPC -I$alt_macosx_dir/include -L$alt_macosx_dir/lib $LDFLAGS -lvorbis 2> /dev/null ; then has_vorbis="system" fi elif $cc -o $TMPO $TMPC -I$local_inc -L$local_lib -lvorbis -lm 2> /dev/null ; then has_vorbis="local" fi + + #look for theora support cat > $TMPC << EOF #include int main( void ) { return 0; } EOF -if $cc -o $TMPO $TMPC -ltheora 2> /dev/null ; then +if $cc -o $TMPO $TMPC $LDFLAGS -ltheora 2> /dev/null ; then has_theora="system" elif test "$alt_macosx_dir" != "" ; then - if $cc -o $TMPO $TMPC -I$alt_macosx_dir/include -L$alt_macosx_dir/lib -ltheora -logg 2> /dev/null ; then + if $cc -o $TMPO $TMPC -I$alt_macosx_dir/include -L$alt_macosx_dir/lib $LDFLAGS -ltheora -logg 2> /dev/null ; then has_theora="system" fi elif $cc -o $TMPO $TMPC -I$local_inc -L$local_lib -ltheora -logg -lm 2> /dev/null ; then has_theora="local" fi + + #look for OGG support cat > $TMPC << EOF #include int main( void ) { return 0; } EOF -if $cc -o $TMPO $TMPC -logg 2> /dev/null ; then +if $cc -o $TMPO $TMPC $LDFLAGS -logg 2> /dev/null ; then has_ogg="system" elif test "$alt_macosx_dir" != "" ; then - if $cc -o $TMPO $TMPC -I$alt_macosx_dir/include -L$alt_macosx_dir/lib -logg 2> /dev/null ; then + if $cc -o $TMPO $TMPC -I$alt_macosx_dir/include -L$alt_macosx_dir/lib $LDFLAGS -logg 2> /dev/null ; then has_ogg="system" fi elif $cc -o $TMPO $TMPC -I$local_inc -L$local_lib -logg -lm 2> /dev/null ; then @@ -1080,15 +1282,15 @@ else fi -#look for OSS support +#look for OSS support if test "$darwin" = "yes" ; then - cat > $TMPC << EOF #include int main( void ) { return 0; } EOF - if $cc -o $TMPO $TMPC -DLIBOSS_INTERNAL -I$alt_macosx_dir/include/ -I$alt_macosx_dir/include/liboss -L$alt_macosx_dir/lib -loss 2> /dev/null ; then + + if $cc -o $TMPO $TMPC -DLIBOSS_INTERNAL -I$alt_macosx_dir/include/ -I$alt_macosx_dir/include/liboss -L$alt_macosx_dir/lib -loss $LDFLAGS 2> /dev/null ; then has_oss_audio="SYS" OSS_CFLAGS="-DLIBOSS_INTERNAL -I$alt_macosx_dir/include/ -I$alt_macosx_dir/include/liboss" OSS_LDFLAGS="-L$alt_macosx_dir/lib -loss" @@ -1114,7 +1316,8 @@ EOF #include int main( void ) { return 0; } EOF - if $cc -o $TMPO $TMPC 2> /dev/null ; then + + if $cc -o $TMPO $TMPC $LDFLAGS 2> /dev/null ; then has_oss_audio="YES" fi fi @@ -1122,6 +1325,7 @@ EOF fi + #look for wxWidgets support has_wx="no" wx_too_old="no" @@ -1167,18 +1371,19 @@ EOF fi #end wx test -# If svg isn't disabled +#if svg isn't disabled if test "$disable_svg" != "yes"; then - # Then we check libxm2 presence via pkg-config - if pkg-config libxml-2.0 --exists > /dev/null 2>&1 ; then - libxml2_cflags=`pkg-config libxml-2.0 --cflags` - libxml2_lib_flags=`pkg-config libxml-2.0 --libs` + #then check libxm2 presence via pkg-config + if $pkg_config libxml-2.0 --exists > /dev/null 2>&1 ; then + libxml2_cflags=`$pkg_config libxml-2.0 --cflags` + libxml2_lib_flags=`$pkg_config libxml-2.0 --libs` has_libxml2="yes" fi fi -# look for IPv6 + +#look for IPv6 cat > $TMPC << EOF #include #include @@ -1193,11 +1398,13 @@ IN6_IS_ADDR_MULTICAST( (struct in6_addr *) 0); } EOF -if $cc -o $TMPE $TMPC > /dev/null 2>&1 ; then +if $cc -o $TMPE $TMPC $LDFLAGS > /dev/null 2>&1 ; then has_ipv6="yes" fi -# look for DVB4linux + + +#look for DVB4linux cat > $TMPC << EOF #include #include @@ -1205,11 +1412,13 @@ int main( void ) { } EOF -if $cc -o $TMPE $TMPC > /dev/null 2>&1 ; then +if $cc -o $TMPE $TMPC $LDFLAGS > /dev/null 2>&1 ; then has_dvb4linux="yes" fi -# look for XMLRPC + + +#look for XMLRPC cat > $TMPC << EOF #include #include @@ -1218,58 +1427,70 @@ int main( void ) { } EOF -if $cc -o $TMPE $TMPC > /dev/null 2>&1 ; then +if $cc -o $TMPE $TMPC $LDFLAGS > /dev/null 2>&1 ; then has_xmlrpc="yes" fi -# look for alsa + + +#look for alsa cat > $TMPC << EOF -#include +#include int main( void ) { } EOF -if $cc -o $TMPE $TMPC > /dev/null 2>&1 ; then +if $cc -o $TMPE $TMPC $LDFLAGS > /dev/null 2>&1 ; then has_alsa="yes" fi -# look for pulseaudio + + +#look for pulseaudio cat > $TMPC << EOF #include int main( void ) { } EOF -if $cc -o $TMPE $TMPC > /dev/null 2>&1 ; then + +if $cc -o $TMPE $TMPC $LDFLAGS > /dev/null 2>&1 ; then has_pulseaudio="yes" fi + + #look for jack cat > $TMPC << EOF #include int main( void ) { } EOF -if $cc -o $TMPE $TMPC > /dev/null 2>&1 ; then +if $cc -o $TMPE $TMPC $LDFLAGS > /dev/null 2>&1 ; then has_jack="yes" fi + + #look for directfb support cat > $TMPC << EOF -#include +#include int main( void ) { return 0; } EOF -directfb_inc="/opt/STM/STLinux-2.3/devkit/sh4/target/usr/include/directfb" +directfb_inc="/usr/include/directfb" directfb_lib="-ldirectfb -lfusion -ldirect" -if $cc -o $TMPO $TMPC -I$directfb_inc -L$directfb_lib 2> /dev/null ; then +if $cc -o $TMPO $TMPC -I$directfb_inc -L$directfb_lib $LDFLAGS 2> /dev/null ; then has_directfb="yes" fi + + #look for X11 shared memory support cat > $TMPC << EOF #include int main( void ) { return 0; } EOF -if $cc -o $TMPO $TMPC -I$X11_PATH/include -L$X11_PATH/lib 2> /dev/null ; then + +if $cc -o $TMPO $TMPC -I$X11_PATH/include -L$X11_PATH/lib $LDFLAGS 2> /dev/null ; then has_x11="yes" #look for X11 shared memory support @@ -1281,7 +1502,7 @@ if $cc -o $TMPO $TMPC -I$X11_PATH/include -L$X11_PATH/lib 2> /dev/null ; then int main( void ) { return 0; } EOF - if $cc -o $TMPO $TMPC -I$X11_PATH/include -L$X11_PATH/lib 2> /dev/null ; then + if $cc -o $TMPO $TMPC -I$X11_PATH/include -L$X11_PATH/lib $LDFLAGS 2> /dev/null ; then has_x11_shm="yes" fi @@ -1293,12 +1514,15 @@ EOF int main( void ) { return 0; } EOF - if $cc -o $TMPO $TMPC -I$X11_PATH/include -L$X11_PATH/lib 2> /dev/null ; then + if $cc -o $TMPO $TMPC -I$X11_PATH/include -L$X11_PATH/lib $LDFLAGS 2> /dev/null ; then has_x11_xv="yes" fi fi + + +#overwrite detection with manual settings for opt do case "$opt" in --sdl-cfg=*) sdl_path=`echo $opt | cut -d '=' -f 2`; sdl_local="yes" @@ -1329,22 +1553,18 @@ for opt do ;; --disable-jack=*) has_jack="no" ;; - --disable-alsa=*) has_alsa="no" - ;; - --enable-debug) debuginfo="yes"; no_gcc_opt="yes" + --disable-alsa) has_alsa="no" ;; --enable-gprof) gprof_build="yes"; ;; --enable-static-bin) static_build="yes"; ;; - --enable-pic) want_pic="yes"; - ;; - --disable-opt) no_gcc_opt="yes" - ;; --disable-ipv6) has_ipv6="no" ;; --disable-wx) has_wx="no" ;; + --disable-platinum) has_platinum="no" + ;; --disable-oss-audio) has_oss_audio="no" ;; --disable-x11-shm) has_x11_shm="no" @@ -1383,10 +1603,12 @@ for opt do ;; --use-ffmpeg=*) has_ffmpeg=${opt#--use-ffmpeg=} ;; + --use-freenect=*) has_freenect=${opt#--use-freenect=} + ;; --use-png=*) tmp_has_png=${opt#--use-png=} if test "$tmp_has_png" = "system" ; then if test "$has_png" != "system" ; then - if test "$cross_prefix" != "" ; then + if test "$cross_prefix" != "" ; then echo echo "WARNING: PNG has been forced to system, but we are cross-compiling, it will have to be on target" echo @@ -1399,6 +1621,22 @@ for opt do fi has_png=$tmp_has_png ;; + --use-zlib=*) tmp_has_zlib=${opt#--use-zlib=} + if test "$tmp_has_zlib" = "system" ; then + if test "$has_zlib" != "system" ; then + if test "$cross_prefix" != "" ; then + echo + echo "WARNING: ZLIB has been forced to system, but we are cross-compiling, it will have to be on target" + echo + else + echo + echo "WARNING!! : ZLIB has been forced to system even though it hasn't been found in this host" + echo + fi + fi + fi + has_png=$tmp_has_png + ;; --use-ogg=*) has_ogg=${opt#--use-ogg=} ;; --use-vorbis=*) has_vorbis=${opt#--use-vorbis=} @@ -1412,7 +1650,7 @@ for opt do --enable-pulseaudio=*) has_pulseaudio="yes" ;; - --disable-all) has_pulseaudio="no"; disable_3d="yes"; disable_svg="yes"; disable_vrml="yes"; disable_od="yes"; disable_bifs="yes"; disable_bifs_enc="yes"; disable_laser="yes"; disable_beng="yes"; disable_qtvr="yes"; disable_avi="yes"; disable_ogg="yes"; disable_m2ps="yes"; disable_m2ts="yes"; disable_m2ts_mux="yes"; disable_parsers="yes"; disable_import="yes"; disable_export="yes"; disable_swf="yes"; disable_scene_stats="yes"; disable_scene_dump="yes"; disable_scene_encode="yes"; disable_loader_isoff="yes"; disable_od_dump="yes"; disable_mcrypt="yes"; disable_isoff="yes"; disable_isoff_write="yes"; disable_isoff_hint="yes"; disable_isoff_frag="yes"; disable_streaming="yes"; disable_x3d="yes"; disable_loader_bt="yes"; disable_loader_xmt="yes"; has_dvb4linux="no" + --disable-all) has_pulseaudio="no"; has_alsa="no"; disable_3d="yes"; disable_svg="yes"; disable_vrml="yes"; disable_od="yes"; disable_bifs="yes"; disable_bifs_enc="yes"; disable_laser="yes"; disable_beng="yes"; disable_qtvr="yes"; disable_avi="yes"; disable_ogg="yes"; disable_m2ps="yes"; disable_m2ts="yes"; disable_m2ts_mux="yes"; disable_parsers="yes"; disable_import="yes"; disable_export="yes"; disable_swf="yes"; disable_scene_stats="yes"; disable_scene_dump="yes"; disable_scene_encode="yes"; disable_loader_isoff="yes"; disable_od_dump="yes"; disable_mcrypt="yes"; disable_isoff="yes"; disable_isoff_write="yes"; disable_isoff_hint="yes"; disable_isoff_frag="yes"; disable_streaming="yes"; disable_x3d="yes"; disable_loader_bt="yes"; disable_loader_xmt="yes"; has_dvb4linux="no" ;; --disable-3d) disable_3d="yes" @@ -1475,6 +1713,8 @@ for opt do ;; --enable-m2ts-mux) disable_m2ts_mux="no" ;; + --disable-dvb4linux) has_dvb4linux="no" + ;; --disable-parsers) disable_parsers="yes" ;; --enable-parsers) disable_parsers="no" @@ -1542,14 +1782,15 @@ for opt do --disable-streaming) disable_streaming="yes" ;; --enable-streaming) disable_streaming="no" - ;; + ;; esac done -#look for OpenGL support or for TinyGL support + +#look for OpenGL support or for TinyGL support LINK3D="" INCL3D="" DarwinGL="no" @@ -1560,12 +1801,14 @@ if test "$darwin" = "yes" ; then #include int main( void ) { glEnable(GL_NORMALIZE); return 0; } EOF + else cat > $TMPC << EOF #include #include int main( void ) { glEnable(GL_NORMALIZE); return 0; } EOF + fi if test "$disable_3d" = "no" ; then @@ -1581,9 +1824,9 @@ if test "$disable_3d" = "no" ; then else LINK3D="-lGL -lGLU -lX11" fi - if $cc -o $TMPO $TMPC $LINK3D 2> /dev/null ; then + if $cc -o $TMPO $TMPC $LINK3D $LDFLAGS 2> /dev/null ; then has_opengl="yes" - elif $cc -o $TMPO $TMPC -I$X11_PATH/include -L$X11_PATH/lib 2> /dev/null ; then + elif $cc -o $TMPO $TMPC -I$X11_PATH/include -L$X11_PATH/lib $LDFLAGS 2> /dev/null ; then has_opengl="yes" INCL3D="-I$X11_PATH/include" LINK3D="-L$X11_PATH/lib $LINK3D" @@ -1597,8 +1840,9 @@ cat > $TMPC << EOF #include int main( void ) { int a ; a = TINYGL ; } EOF + if test "$enable_tinygl" = "yes" ;then - if $cc -o $TMPO $TMPC -lTinyGL 2> /dev/null ; then + if $cc -o $TMPO $TMPC $LDFLAGS -lTinyGL 2> /dev/null ; then has_tinygl="yes" has_opengl="yes" LINK3D="-lTinyGL" @@ -1606,24 +1850,25 @@ if test "$enable_tinygl" = "yes" ;then fi + #look for GECKO support cat > $TMPCPP << EOF #include int main( void ) { return 0; } EOF -if $cc -o $TMPO $TMPCPP -I$xulsdk_path $CFLAGS $LDFLAGS 2> /dev/null ; then +if $cc -o $TMPO $TMPCPP -I$xulsdk_path $LDFLAGS 2> /dev/null ; then has_xul="system" xul_flags="-I$xulsdk_path $xul_flags" else - if $cc -o $TMPO $TMPCPP $xul_flags -I$local_inc/gecko-sdk/include 2> /dev/null ; then + if $cc -o $TMPO $TMPCPP $xul_flags -I$local_inc/gecko-sdk/include $LDFLAGS 2> /dev/null ; then has_xul="local" xul_flags="-I$local_inc/gecko-sdk/include $xul_flags" else #xulrunner directories are sometimes included through js packages if test "$has_js" = "system" ;then - if $cpp -o $TMPO $TMPCPP $js_flags $js_lib_pkg 2> /dev/null ; then - xul_flags=`pkg-config --cflags mozilla-js` + if $cpp -o $TMPO $TMPCPP $js_flags $js_lib_pkg $LDFLAGS 2> /dev/null ; then + xul_flags=`$pkg_config --cflags mozilla-js` has_xul="system" fi fi @@ -1631,28 +1876,19 @@ else fi + #look for joystick support cat > $TMPC << EOF #include int main( void ) { return 0; } EOF if test "$enable_joystick" = "yes" ;then - if $cc -o $TMPO $TMPC 2> /dev/null ; then + if $cc -o $TMPO $TMPC $LDFLAGS 2> /dev/null ; then has_joystick="yes" fi fi -#GCC opt -if test "$no_gcc_opt" = "no"; then - if $cc --version | grep 'sbox-arm-linux-gcc (GCC) 3.4.4' - then - echo "Detected buggy arm GCC (diablo), using -O2" - CFLAGS="-O2 $CFLAGS" - else - CFLAGS="-O3 $CFLAGS" - fi -fi #look for DX support dx_path="system" @@ -1664,7 +1900,7 @@ if test "$win32" = "yes" ; then int main( void ) { return 0; } EOF - if $cc -o $TMPO $TMPC 2> /dev/null ; then + if $cc -o $TMPO $TMPC $LDFLAGS 2> /dev/null ; then has_mingw_directx="yes" else dx_path="$dxsdk_path" @@ -1676,6 +1912,7 @@ EOF fi + #look for SDL support sdl_too_old=no has_sdl=no @@ -1700,7 +1937,7 @@ EOF fi sdl_cflags=`$sdl_config --cflags` - if $cc -o $TMPO $sdl_cflags $TMPC $sdl_lib_flags > /dev/null 2>&1 ; then + if $cc -o $TMPO $sdl_cflags $TMPC $LDFLAGS $sdl_lib_flags > /dev/null 2>&1 ; then _sdlversion=`$sdl_config --version | sed 's/[^0-9]//g'` if test "$_sdlversion" -lt 121 ; then sdl_too_old=yes @@ -1713,6 +1950,8 @@ fi #end SDL check + +#look at endianess if test -z "$cross_prefix" ; then # big/little endian test @@ -1724,7 +1963,7 @@ return (*((uint8_t*)(&i))) == 0x67; } EOF - if $cc -o $TMPO $TMPC 2>/dev/null ; then + if $cc -o $TMPO $TMPC $LDFLAGS 2>/dev/null ; then $TMPO && bigendian="yes" else echo big/little endian test failed @@ -1740,6 +1979,8 @@ else fi + +#man dir if test x"$mandir" = x""; then mandir="${prefix}/man" fi @@ -1760,6 +2001,8 @@ if test "$cpu" = "sh4"; then fi fi + + #prepare for config.h writing TMPH="${TMPDIR1}/gpac-conf-${RANDOM}-$$-${RANDOM}.h" echo "/* Automatically generated by configure */" > $TMPH @@ -1767,7 +2010,6 @@ echo "#ifndef GF_CONFIG_H" >> $TMPH echo "#define GF_CONFIG_H" >> $TMPH echo "#define GPAC_CONFIGURATION \"$GPAC_CONFIGURATION\"" >> $TMPH - version="`grep '#define GPAC_VERSION ' \"$source_path/include/gpac/tools.h\" | cut -d '"' -f 2`" if which svnversion >/dev/null @@ -1782,8 +2024,8 @@ echo "" echo "** System Configuration" echo "Install prefix: $prefix" echo "Source path: $source_path" -echo "C compiler: $cc" -echo "C++ compiler: $cpp" +echo "C compiler: $cc_naked" +echo "C++ compiler: $cpp_naked" echo "make: $make" echo "CPU: $cpu" echo "Big Endian: $bigendian" @@ -1943,7 +2185,7 @@ if test "$win32" != "yes" ; then echo "PulseAudio Audio: $has_pulseaudio" echo "DirectFB support: $has_directfb" echo "X11 Shared Memory support: $has_x11_shm (path: $X11_PATH)" - echo "X11 XVideo support: $has_x11_xv" + echo "X11 XVideo support: $has_x11_xv" fi echo "SDL Support: $has_sdl" @@ -1988,6 +2230,8 @@ echo "FAAD: $has_faad" echo "XVID: $has_xvid" echo "FFMPEG: $has_ffmpeg" echo "Xiph OGG: $has_ogg" +echo "Platinum UPnP: $has_platinum" +echo "AVCap: $has_avcap" if test "$has_ogg" = "no"; then has_ogg="no" else @@ -1995,6 +2239,8 @@ else echo "Xiph Theora: $has_theora" fi echo "A52 (AC3): $has_a52" +echo "OpenSVCDecoder: $has_opensvc" +echo "Freenect: $has_freenect" if test "$enable_renoir" = "yes" ; then echo "Renoir enabled - make sure the driver libraries are present in modules/viren_out directory" @@ -2045,10 +2291,8 @@ if test "$gprof_build" = "yes"; then fi fi - -if test "$want_pic" = "yes" ; then - CFLAGS="$CFLAGS -fPIC -DPIC" - CPPFLAGS="$CPPFLAGS -fPIC -DPIC" +if test "$darwin" = "yes" ; then + CFLAGS="$CFLAGS_DIR $CFLAGS" fi ldir=`pwd` @@ -2068,7 +2312,7 @@ echo "moddir_path=$prefix/$libdir/gpac" >> config.mak echo "mandir=$mandir" >> config.mak echo "tinygl_target_bin_dir=$target_bin_dir" >> config.mak echo "MAKE=$make" >> config.mak -echo "CC=$cc" >> config.mak +echo "CC=$cc_naked" >> config.mak echo "AR=$ar" >> config.mak echo "RANLIB=$ranlib" >> config.mak echo "STRIP=$strip" >> config.mak @@ -2082,7 +2326,7 @@ echo "libdir=$libdir" >> config.mak #for cross-compilation -if test "$cross_prefix" != "" ; then +if test "$cross_prefix" != "" ; then echo "CROSS_COMPILING=yes" >> config.mak fi @@ -2162,6 +2406,10 @@ echo "CONFIG_JS=$has_js" >> config.mak if test "$has_js" = "no" ; then has_js="no" else + if test "$has_js" = "local" ; then + js_flags="-DXP_UNIX -I$local_inc/js" + js_lib="-ljs" + fi echo "JS_FLAGS=$js_flags" >> config.mak echo "JS_LIBS=$js_lib" >> config.mak echo "#define GPAC_HAS_SPIDERMONKEY" >> $TMPH @@ -2199,6 +2447,7 @@ echo "CONFIG_ALSA=$has_alsa" >> config.mak echo "CONFIG_JACK=$has_jack" >> config.mak echo "CONFIG_A52=$has_a52" >> config.mak echo "CONFIG_PULSEAUDIO=$has_pulseaudio" >> config.mak +echo "CONFIG_FREENECT=$has_freenect" >> config.mak echo "DISABLE_SVG=$disable_svg" >> config.mak echo "HAS_LIBXML2=$has_libxml2" >> config.mak @@ -2269,6 +2518,20 @@ if test "$has_wx" = "yes"; then echo "WX_LFLAGS=$wx_lflags" >> config.mak fi +echo "CONFIG_PLATINUM=$has_platinum" >> config.mak + +echo "CONFIG_AVCAP=$has_avcap" >> config.mak +if test "$has_avcap" = "yes" ; then + echo "AVCAP_CFLAGS=$avcap_cflags" >> config.mak + echo "AVCAP_LDFLAGS=$avcap_ldflags" >> config.mak +fi + +echo "CONFIG_OPENSVC=$has_opensvc" >> config.mak +if test "$has_opensvc" = "yes" ; then + echo "OSVC_CFLAGS=$osvc_cflags" >> config.mak + echo "OSVC_LDFLAGS=$osvc_ldflags" >> config.mak +fi + echo "MOZILLA_DIR=$moz_path" >> config.mak echo "CONFIG_XUL=$has_xul" >> config.mak @@ -2338,7 +2601,8 @@ echo "GPAC_ENST=$GPAC_ENST" >> config.mak echo "GPAC_ENST_INC=$GPAC_ENST" >> config.mak -# build tree in object directory if source path is different from current one + +#build tree in object directory if source path is different from current one if test "$source_path_used" = "yes" ; then echo "Creating compilation tree image" @@ -2394,27 +2658,43 @@ echo "LOCAL_INC_PATH=$local_inc" >> config.mak echo "#endif" >> $TMPH -# Do not overwrite config.h if unchanged to avoid superfluous rebuilds. -if ! cmp -s $TMPH config.h; then + +#do not overwrite config.h if unchanged to avoid superfluous rebuilds. +if ! cmp -s $TMPH config.h ; then rm -f config.h mv -f $TMPH config.h else echo "config.h is unchanged" fi + rm -f $TMPO $TMPC $TMPE $TMPS $TMPCPP $TMPH -if [ ! -d "./bin" ]; then + +if [ ! -d "./bin" ] ; then mkdir ./bin fi -if [ ! -d "./bin/gcc" ]; then +if [ ! -d "./bin/gcc" ] ; then mkdir ./bin/gcc fi -if [ ! -d "./bin/gcc/temp" ]; then +if [ ! -d "./bin/gcc/temp" ] ; then mkdir ./bin/gcc/temp fi echo '%.opic : %.c' >> config.mak echo ' $(CC) $(CFLAGS) $(PIC_CFLAGS) -c $< -o $@' >> config.mak +#pkg-config +echo "prefix=$prefix" > gpac.pc +echo "exec_prefix=\${prefix}" >> gpac.pc +echo "libdir=\${exec_prefix}/lib" >> gpac.pc +echo "includedir=\${exec_prefix}/include" >> gpac.pc +echo "" >> gpac.pc +echo "Name: gpac" >> gpac.pc +echo "Description: GPAC Multimedia Framework" >> gpac.pc +echo "URL: http://gpac.sourceforge.net" >> gpac.pc +echo "Version:$version" >> gpac.pc +echo "Cflags: -I\${prefix}/include/gpac" >> gpac.pc +echo "Libs: -L\${libdir} -lgpac" >> gpac.pc + echo "Done - type 'make help' for make info, 'make' to build" diff --git a/doc/configuration.html b/doc/configuration.html index 49025fe0f2093751bbb49f6d3cdef2bb31b2101b..b87b7e463754134ed6b07b356cf534abab7b2e96 100644 GIT binary patch literal 95134 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 literal 44316 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 CJQNTB diff --git a/doc/man/mp4box.1 b/doc/man/mp4box.1 index 00b9854..8f927e6 100644 --- a/doc/man/mp4box.1 +++ b/doc/man/mp4box.1 @@ -159,6 +159,9 @@ splits in files of desired maximum size. This will remove all MPEG-4 Systems med .B \-split-chunk start:end extracts a new file from specified start to end times (in seconds). This will remove all MPEG-4 Systems media. Alias: -splitx .TP +.B \-splitz start:end +same as -split-chunk option, but adjust end time to be juste before the last rap found in the chunk. +.TP .B \-split-rap start:end splits in files begining at each RAP. This will remove all MPEG-4 Systems media. Alias: -splitr .TP diff --git a/doc/man/mp4client.1 b/doc/man/mp4client.1 index c7e58e1..8ede331 100644 --- a/doc/man/mp4client.1 +++ b/doc/man/mp4client.1 @@ -28,6 +28,61 @@ removes script message, buffering and downloading status. .TP .B \-strict-error exits after the first error is reported. +.TP +.B \-log-file FILE +specifies where to write logs. Default is stdout. Same as -lf. +.TP +.B \-logs TOOLS +sets log tools and levels, formatted as a ':'-separated list of toolX[:toolZ]@level. By default all errors are logged. level can be one of quiet, error, warning, info or debug. tool can be: +.br +core: libgpac core +.br +coding: bitstream formats (audio, video, scene) +.br +container: container formats (ISO File, MPEG-2 TS, AVI, ...) +.br +network: network data exept RTP trafic +.br +rtp: rtp trafic +.br +author: authoring tools (hint, import, export) +.br +sync: terminal sync layer +.br +codec: terminal codec messages +.br +parser: scene parsers (svg, xmt, bt) and other +.br +media: terminal media object management +.br +scene: scene graph and scene manager +.br +script: scripting engine messages +.br +interact: interaction engine (events, scripts, etc) +.br +smil: SMIL timing engine +.br +compose: composition engine (2D, 3D, etc) +.br +mmio: Audio/Video HW I/O management +.br +rti: various run-time stats +.br +cache: HTTP cache subsystem +.br +audio: Audio renderer and mixers +.br +mem: GPAC memory tracker +.br +module: GPAC modules debugging +.br +mutex: mutex +.br +none: no tool logged +.br +all: all tools logged - other tools can be specified afterwards. +.br . .SH PLAYBACK OPTIONS A file can be controled during playback by typing one of the following key at prompt. diff --git a/extra_lib/include/OpenSVCDecoder/ControlLayer.h b/extra_lib/include/OpenSVCDecoder/ControlLayer.h new file mode 100644 index 0000000..789b2ec --- /dev/null +++ b/extra_lib/include/OpenSVCDecoder/ControlLayer.h @@ -0,0 +1,45 @@ +/***************************************************************************** + * + * SVC4DSP developped in IETR image lab + * + * + * + * Médéric BLESTEL + * Mickael RAULET + * http://www.ietr.org/ + * + * + * + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * + * $Id$ + * + **************************************************************************/ + +#define WINEXPORT + +#ifdef TCPMP +#ifndef POCKET_PC +#include "windows.h" +#undef WINEXPORT +#define WINEXPORT WINAPI +#endif +#endif + + +void getLayer(int *num_layer); diff --git a/extra_lib/include/OpenSVCDecoder/ParseAU.h b/extra_lib/include/OpenSVCDecoder/ParseAU.h new file mode 100644 index 0000000..c9119ad --- /dev/null +++ b/extra_lib/include/OpenSVCDecoder/ParseAU.h @@ -0,0 +1,39 @@ +/***************************************************************************** +* +* SVC4DSP developped in IETR image lab +* +* +* +* Médéric BLESTEL +* Mickael RAULET +* http://www.ietr.org/ +* +* +* +* +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +* +* +* $Id$ +* +**************************************************************************/ + + + +void ParseAuPlayers(void *PlayerStruct, const unsigned char *buf, int buf_size, int nal_length_size, int is_avc); +int GetDqIdMax(const unsigned char *buf, int buf_size, int nal_length_size, int *DqidTable, int is_avc); +int Geth264NalSize(const unsigned char *buf, int buf_size, int *bufindex); + diff --git a/extra_lib/include/OpenSVCDecoder/SVCDecoder_ietr_api.h b/extra_lib/include/OpenSVCDecoder/SVCDecoder_ietr_api.h new file mode 100644 index 0000000..4f3c7d0 --- /dev/null +++ b/extra_lib/include/OpenSVCDecoder/SVCDecoder_ietr_api.h @@ -0,0 +1,128 @@ +/***************************************************************************** +* +* SVC4DSP developped in IETR image lab +* +* +* +* Médéric BLESTEL +* Mickael RAULET +* http://www.ietr.org/ +* +* +* +* +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +* +* +* $Id$ +* +**************************************************************************/ + +#ifndef _SVCDecoder_ietr_api_h +#define _SVCDecoder_ietr_api_h + +#define WINEXPORT + +#include "SvcInterface.h" + +#ifndef POCKET_PC +#ifdef TCPMP +#include "windows.h" +#undef WINEXPORT +#define WINEXPORT WINAPI +#endif +#endif + +enum { + SVC_STATUS_ERROR = -1, + SVC_STATUS_OK = 0, // no error and no frame ready + SVC_IMAGE_READY = 1, // no error and image ready + SVC_GHOST_IMAGE = 2 // no image for chosen layer but image could be ready for other layers +}; + + +/*====================================================================================================== +int SVCDecoder_init(); +This method initializes the internal resources of the decoder +@return SVC_STATUS_OK if creation is successful +======================================================================================================*/ +int WINEXPORT SVCDecoder_init(void **PlayerStruct); + + +/*====================================================================================================== +void WINEXPORT UpdateLayer(int *DqIdTable, int *CurrDqId, int MaxDqId, int Command); +Use this method to change of quality, spatial or temporal scalability during the +decoding process. +The DqIdTable specifies the DqId of each layer present into the stream. +CurrDqId specifies the DqId of the current layer. +MaxDqId specifies the maximal DqId present into the access unit. +Command specifies the action to execute: +0: To switch down of spatial or quality scalability +1: To switch up of spatial or quality scalability +2: To switch up to the layer with the higthest DqId +3: To switch down of temporal scalability +4: To switch up of temproal scalability +@return nothing +=======================================================================================================*/ +void WINEXPORT UpdateLayer(int *DqIdTable, int *CurrDqId, int *TemporalCom, int *TemporalId, int MaxDqId, int Command); + + + +/*====================================================================================================== +void WINEXPORT SetCommandLayer(int *Command, int DqIdMax, int CurrDqId, int *TemporalCom, int TemporalId); +Use this method to generate the right command for the SVC decoder. +The Command is a 4 size table given to the decoder. +DqIdMax specifies the maximal DqId present into the access unit. +CurrDqId specifies the DqId of the current layer. +TemporalCom is the command to use concerning the temporal scalability. +0 -> do nothing +1 -> decode a lower temporal scalability. +2 -> decode a highter temporal scalability. +3 -> define a specific temporal scalability. (use TemporalId variable to set). +@return nothing +=======================================================================================================*/ +void WINEXPORT SetCommandLayer(int *Command, int DqIdMax, int CurrDqId, int *TemporalCom, int TemporalId); + + +/*====================================================================================================== +int decodeNAL( unsigned char* nal, int nal_length, OPENSVCFRAME *Frame, int DqIdMax); +Use this method to give a new NAL Unit to the decoder. The NAL content (no start code) +is stored at the address given by the argument pNAL and the length of the NAL is provided +by the argument nal_length. Memory of this nal buffer is managed by the application. +If the function returns the code SVC_IMAGE_READY, this means that an image is ready and in this case, +the arguments *Frame has been updated by the decoder and the image plans are contained at +the addresses pY, PU, pV. +The image stored at these location is an image centered in a rectangle of size (*pWidth + 32, *pHeight + 32) +for Y and ((*pWidth + 32) / 2, (*pHeight + 32) / 2)) for U and V. +Memory of these image buffers is managed by the decoder. +In case of a SVC stream, the largest DqIq of the Access Unit should be given. + +@param nal is the address of the buffer that contains the NAL unit +@param nal_length is the size of NAL Unit data +@param Frame Contains all parameters from the displayed picture +@param DQIdMax is the largest DQId of the current access unit +@return SVC_STATUS_OK or SVC_STATUS_ERROR or SVC_IMAGE_READY or SVC_GHOST_IMAGE +======================================================================================================*/ +int WINEXPORT decodeNAL(void *PlayerStruct, unsigned char* nal, int nal_length, OPENSVCFRAME *Frame, int *LayerCommand); + + +/*====================================================================================================== +int SVCDecoder_close(); +This method releases the internal resources of the decoder +@return SVC_STATUS_OK if creation is successfull +=======================================================================================================*/ +int WINEXPORT SVCDecoder_close(void *PlayerStruct); +#endif // SVCDecoder_ietr_api diff --git a/extra_lib/include/OpenSVCDecoder/SvcInterface.h b/extra_lib/include/OpenSVCDecoder/SvcInterface.h new file mode 100644 index 0000000..03f6664 --- /dev/null +++ b/extra_lib/include/OpenSVCDecoder/SvcInterface.h @@ -0,0 +1,59 @@ +/***************************************************************************** +* +* SVC4DSP developped in IETR image lab +* +* +* +* Médéric BLESTEL +* Mickael RAULET +* http://www.ietr.org/ +* +* +* +* +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +* +* +* $Id$ +* +**************************************************************************/ + +#ifndef SVCINTERFACE_H +#define SVCINTERFACE_H + +#define QoE_ + +#ifdef QoE +#include "SvcToQoE.h" +#endif + + +typedef struct{ + + int Width; //Output frame width + int Height; //Output frame height + unsigned char* pY[1]; //Output frame Y + unsigned char* pU[1]; //Output frame U + unsigned char* pV[1]; //Output frame V + +#ifdef QoE + SVCTOQOE *QoEData[1]; //QoE structure +#endif + +} OPENSVCFRAME; + + +#endif diff --git a/extra_lib/include/avcap/CaptureDevice.h b/extra_lib/include/avcap/CaptureDevice.h new file mode 100644 index 0000000..c0636b3 --- /dev/null +++ b/extra_lib/include/avcap/CaptureDevice.h @@ -0,0 +1,189 @@ +/* + * (c) 2005, 2008 Nico Pranke , Robin Luedtke + * + * This file is part of avcap. + * + * avcap is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * avcap is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with avcap. If not, see . + */ + +/* avcap is free for non-commercial use. + * To use it in commercial endeavors, please contact Nico Pranke + */ + + +#ifndef CAPTUREDEVICE_H_ +#define CAPTUREDEVICE_H_ + +#include +#include + +#include "avcap-export.h" +#include "DeviceDescriptor.h" + +//! This is the namespace which contains all classes of the avcap-library. + +namespace avcap +{ + // forward declarations + class ConnectorManager; + class ControlManager; + class CaptureManager; + class DeviceDescriptor; + class FormatManager; + + /*! \brief This class is an abstraction of a video capture device. + * + * It is the main entry point for an application to access the functionality of + * a capture device, i.e. to query capabilities and settings of the device, to + * manipulate them and to capture the video. + * + * The access to the functionality of a capture device is divided into different domains and is + * provided by so called managers. See the documentation of the various Manager-classes for + * the details of their usage. The managers and their responsibilities are: + * + *

    + *
  • CaptureManager: start/stop capture and register a capture-handler
  • + *
  • ConnectorManager: query available inputs/outputs of the device and get/set them
  • + *
  • ControlManager: query available controls of the device and get/set their values
  • + *
  • FormatManager: query and get/set available formats, + * their properties and resolutions associated with them
  • + *
+ * + * CaptureDevice-classes implementing the back-end for a certain capture-API must derive from this class and + * implement the abstract methods to provide the API-specific manager-classes. + * However, if you want to use avcap only, you can use one of the following implementations, + * representing the supported, existing devices (depending on the operating system avcap has + * been build on, not all of them may be available and/or not all methods of their managers are implemented): + * + *
    + *
  • V4L2_Device (Linux): devices that are supported by a Video4Linux2-API driver + * (Requires read/write access to the /dev/video* -file of the device)
  • + *
  • V4L1_Device (Linux): devices that are supported by a Video4Linux-API driver + * (Requires read/write access to the /dev/video* -file of the device)
  • + *
  • AVC_Device (Linux): support for AV/C-Devices (e.g. DV-Cams). + * (Requires: libiec61883, libavc1394, librom1394, libraw1394 and read/write access to /dev/raw1394)
  • + *
  • QT_Device (MAC OS X): capture from a device using the QuickTime SampleGrabber
  • + *
  • DS_Device (Win32): capture from a device using a DirectShow filter-graph
  • + *
+ * + * A concrete CaptureDevice-object must not be created manually. A unique instance can be obtained by calling + * the method DeviceDescriptor::getDevice() between successive calls to DeviceDescriptor::open() and close(). + * All available DeviceDescriptors representing the capture devices found on a system can be obtained by calling + * DeviceCollector::instance()->getDeviceList(). The CaptureDevice-object is owned by the DeviceDescriptor, + * so you must not delete the CaptureDevice instance. + * + */ + + class AVCAP_Export CaptureDevice + { + public: + //! Constructor + inline CaptureDevice() + {} + + //! Destructor + virtual inline ~CaptureDevice() + {} + + //! Return the descriptor of the device. + /*! \return The DeviceDescriptor-object.*/ + virtual const DeviceDescriptor* getDescriptor() = 0; + + //! Use this manager to start/stop capturing and to register a user defined CaptureHandler. + /*! \return The VidCapManager.*/ + virtual CaptureManager* getVidCapMgr() = 0; + + //! Use this manager to query available audio/video inputs/outputs and to select them. + /*! \return The ConnectorManager. */ + virtual ConnectorManager* getConnectorMgr() = 0; + + /*! Use this manager to query and to adjust the available controls of the device (e.g. + * brightness, contrast, saturation...). + * \return The ControlManager. */ + virtual ControlManager* getControlMgr() = 0; + + //! Use this manager to query the available formats, video standards and resolutions to select the desired ones. + /*! \return The FormatManager. */ + virtual FormatManager* getFormatMgr() = 0; + + private: + //! Open the device and do initialization. May fail, if already opened before. + /*! This method creates the managers. Don't use them before open() has been called. + * \return 0 if successful, -1 else + * */ + virtual int open() = 0; + + //! Close the device and do cleanup. + /*! All IOBuffers received by a capture-CallbackHandler should have called their + * release()-method, before close() is called to ensure propper cleanup. Usually this is guaranteed, + * if the buffer is released in the context of the capture-thread from within handleCapture(). + * All managers are destroyed by this method and are thus not available anymore after calling close(). + * \return 0 if successful, -1 else */ + virtual int close() = 0; + }; +} + +#endif // CAPTUREDEVICE_H_ + +/*! \mainpage avcap-library + * + * \section intro Introduction + * + * The avcap-library is a cross-API, cross-platform simple and easy to use C++ + * video capture library. It's aim is to provide a unified API for + * Linux, Windows and Mac OS X to capture video from appropriate hardware. It hides the + * system specific quirks and issues of different API's used on different systems to access video + * capture hardware and hopefully helps to write portable capture-applications. + * + * \subsection linux Linux + * Under GNU/Linux the avcap-library supports Video4Linux-Devices, Video4Linux2-Devices and AV/C-Devices (e.g. + * DV-Cams) as capture sources. Note that you need read/write permission to the /dev/video* files + * to use V4L(2)-Devices. Usually it is sufficient, if the user is a member of the group that owns this files + * (usually group 'video'). To capture from AV/C-Devices the user needs read/write permission to /dev/raw1394. Membership + * in the group 'disk' should be sufficient here. + * + * \subsection windows Win32 (Implementation by Robin Luedtke, Nico Pranke) + * The Windows-version is basically a class wrapper for the DirectShow API and thus + * supports only devices with a WDM (Windows driver model) or an old VFW (Video for + * windows) compliant capture device driver. Understanding the avcap Win32 implementation + * may be a little difficult because of the following reasons: + * First, DirectShow is based on the Windows COM (component object model), second, + * in some cases, DirectShow is a little confusing (e.g. some DirectShow functions have a strange behavior -- workarounds + * are inevitable). + * In addition to this, VFW, WDM and even WDM devices itself are handled differently by DirectShow. + * Third, some important documentation is missing in the DirectShow documentation.\n \n + * + * \subsection mac Mac OS X + * + * The implementation for Darwin uses the QuickTime SequenceGrabber-Component and has been tested with the built-in + * iSight, various USB-cams and DV-Cams. + * + * \section install Building and Installation + * + * See the INSTALL file. + * + * \section Usage + * + * For an example on how to use the avcap-library take a look at the captest-program and read the documentation of + * class CaptureDevice to have a good starting point. + * + * \section licence Licence + * + * (c) 2005-2008 Nico Pranke , Win32 implementation by Robin Luedtke \n\n + * + * For non-commercial use, avcap is distributed under the GNU General Public License version 2. Refer to the file "COPYING" for details. + * + * For commercial use, please contact Nico Pranke for licensing. +*/ + diff --git a/extra_lib/include/avcap/CaptureHandler.h b/extra_lib/include/avcap/CaptureHandler.h new file mode 100644 index 0000000..29c1105 --- /dev/null +++ b/extra_lib/include/avcap/CaptureHandler.h @@ -0,0 +1,58 @@ +/* + * (c) 2005, 2008 Nico Pranke , Robin Luedtke + * + * This file is part of avcap. + * + * avcap is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * avcap is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with avcap. If not, see . + */ + +/* avcap is free for non-commercial use. + * To use it in commercial endeavors, please contact Nico Pranke + */ + +#ifndef CAPTUREHANDLER_H_ +#define CAPTUREHANDLER_H_ + +#include "avcap-export.h" + +namespace avcap +{ + //! Abstract base class for capture handlers. + + /*! If an application wants to capture data, it must implement a CaptureHandler and must register it + * with the VidCapManager of the CaptureDevice. The VidCapManager will call handleCaptureEvent() + * always a new frame has been captured. If the buffer isn't used + * anymore the IOBuffer::release() method must be called in order to enable the + * VidCapManager to reuse or release the buffer. */ + + class AVCAP_Export CaptureHandler + { + public: + //! Consturctor + inline CaptureHandler() + {} + + //! Destructor + virtual inline ~CaptureHandler() + {} + + //! This method is called if a new frame has been captured by the VidCapManager. + /*! If the buffer isn't used anymore, then the method IOBuffer::release() must be + * called. + * \param io_buf The buffer containing the captured frame. */ + virtual void handleCaptureEvent(class IOBuffer* io_buf) = 0; + }; +} + +#endif // CAPTUREHANDLER_H_ diff --git a/extra_lib/include/avcap/CaptureManager.h b/extra_lib/include/avcap/CaptureManager.h new file mode 100644 index 0000000..4a39ae6 --- /dev/null +++ b/extra_lib/include/avcap/CaptureManager.h @@ -0,0 +1,132 @@ +/* + * (c) 2005, 2008 Nico Pranke , Robin Luedtke + * + * This file is part of avcap. + * + * avcap is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * avcap is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with avcap. If not, see . + */ + +/* avcap is free for non-commercial use. + * To use it in commercial endeavors, please contact Nico Pranke + */ + +#ifndef CAPTUREMANAGER_H_ +#define CAPTUREMANAGER_H_ + +#if !defined(_MSC_VER) && !defined(USE_PREBUILD_LIBS) +# include "avcap-config.h" +#endif + +#include "avcap-export.h" + +namespace avcap +{ + class CaptureHandler; + class IOBuffer; + + //! Abstract interface to access capture related tasks of a CaptureDevice. + + /*! An implementation of this class is provided by the API-specific CaptureDevice. + * The CaptureManager can be used by applications to register a CaptureHandler and + * to start/stop the capture. There is only one CaptureHandler at the same time. + * To distribute the captured data to more than one interested sink is the responsibility + * of the application. */ + + class AVCAP_Export CaptureManager + { + public: + enum + { + MAX_BUFFERS = 32, //!< The maximum number of IOBuffers. + DEFAULT_BUFFERS = 16 //!< The default number of used IOBuffers. + }; + + +#ifdef AVCAP_LINUX + enum IOMethod + { + IO_METHOD_NOCAP = 0, + IO_METHOD_READ, + IO_METHOD_MMAP, + IO_METHOD_USERPTR, + }; +#endif + + private: + CaptureHandler* mCaptureHandler; + + public: + //! Constructor + inline CaptureManager() : mCaptureHandler(0) + {} + + //! Destructor + virtual inline ~CaptureManager() + {} + + //! Do basic initialization after startup. + virtual int init() = 0; + + //! Called before object destruction. + virtual int destroy() = 0; + + //! Start capturing data. + virtual int startCapture() = 0; + + //! Stop capturing data. + virtual int stopCapture() = 0; + + //! Register a capture handler. + /*! Only one capture handler can be registered at the same time. + * The handlers CaptureHandler::handleCaptureEvent() method will be called, + * if new data has been captured. The ownership of the handler remains at the caller. + * He is responsible for removing and deleting the handler. + * \param handler The capture handler implementation.*/ + virtual inline void registerCaptureHandler(CaptureHandler *handler) + { mCaptureHandler = handler; } + + //! Remove the current capture handler. + /*! If a capture handler was registered before, then this handler will not be + * notified anymore if data has been captured. */ + virtual inline void removeCaptureHandler() + { mCaptureHandler = 0; } + + //! Get the current CaptureHandler. + /*! Return the capture handler currently registered with registerCaptureHandler() + * or 0, if no handler was registered before. + * \return pointer to the capture handler */ + virtual inline CaptureHandler* getCaptureHandler() + { return mCaptureHandler; } + + //! Returns the number of IOBuffers currently available. + /*! The CaptureManager usually waits to capture the next frame until an IOBuffer is available. + * The application is reponsible to release the IOBuffers to make it available to the capture manager. + * \return the number of IOBuffers. */ + virtual int getNumIOBuffers() = 0; + + private: + //! Dequeue the next buffer. + /*! \return the next buffer with captured data. */ + virtual IOBuffer* dequeue() = 0; + + //! Enqueue a buffer. + /*! \param buf the buffer that isn't used by the application anymore and that can be reused now. + * \return 0 success, -1 on failure*/ + virtual int enqueue(IOBuffer* buf) = 0; + + friend class IOBuffer; + }; +}; + +#endif // CAPTUREMANAGER_H_ diff --git a/extra_lib/include/avcap/Connector.h b/extra_lib/include/avcap/Connector.h new file mode 100644 index 0000000..cc318a7 --- /dev/null +++ b/extra_lib/include/avcap/Connector.h @@ -0,0 +1,117 @@ +/* + * (c) 2005, 2008 Nico Pranke , Robin Luedtke + * + * This file is part of avcap. + * + * avcap is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * avcap is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with avcap. If not, see . + */ + +/* avcap is free for non-commercial use. + * To use it in commercial endeavors, please contact Nico Pranke + */ + + +#ifndef CONNECTOR_H_ +#define CONNECTOR_H_ + +#if !defined(_MSC_VER) && !defined(USE_PREBUILD_LIBS) +# include "avcap-config.h" +#endif + +#ifdef AVCAP_LINUX +# include +# include +#endif + +#include +#include + +#include "avcap-export.h" + +namespace avcap +{ + // forward declaration + class DeviceDescriptor; + class Tuner; + + //! This class is the abstraction of a video/audio input or output. + + /*! It is used to describe available inputs and outputs of a device and + * to select them by means of the API-dependent CaptureDevices implementation of the ConnectorManager. + * The ConnectorManager queries all available connectors of a device and + * provides methods to set and get the currently used ones. + */ + + class AVCAP_Export Connector + { + public: +#ifdef AVCAP_LINUX + enum + { + INPUT_TYPE_TUNER = V4L2_INPUT_TYPE_TUNER + }; +#endif + + protected: + DeviceDescriptor* mDeviceDescriptor; + int mIndex; + int mAudioset; + int mType; + std::string mName; + + public: + //! The Constructor. Objects of this class are created by the ConnectorManager. + inline Connector(DeviceDescriptor *dd, int index, const std::string& name, int type=0, int audioset=0): + mDeviceDescriptor(dd), mIndex(index), mAudioset(audioset), mType(type), mName(name) + {} + + //! The Destructor. + virtual inline ~Connector() + {} + + //! Returns the unique index of the connector. + /*! \return The index. */ + inline int getIndex() const + { return mIndex; } + + //! Get mapping of audio inputs to video inputs. + /*! For devices which provide audio and video capturing, + * video inputs can correspond to zero or more audio inputs. The audio inputs + * are numbered from 0 to N-1, N <= 32. Each bit of the audioset corresponds + * to one input. For details, see the Video4Linux2 API Documentation. + * \n\nWin32: A video connector can correspond to only one audio connector + * (only one bit can be set at a time). */ + inline int getAudioset() const + { return mAudioset; } + + //! Get the tuner associated with the Connector. + /*! If a tuner is associated whith the connector (e.g. for TV-Tuner cards), then this method returns + * an object of class Tuner to access the tuner specific functionality. + * \return object of class Tuner or 0 if there is no tuner. */ + virtual inline Tuner* getTuner() + { return 0; } + + //! Provides a textual description of the connector. + /*! \return connector name. */ + inline const std::string& getName() const + { return mName; } + + //! Test, whether a tuner is associated with the connector or not. + /*! \return true, if tuner is associated, false otherwise. */ + virtual inline bool hasTuner() const + { return false; } + }; +}; + +#endif // CONNECTOR_H_ diff --git a/extra_lib/include/avcap/ConnectorManager.h b/extra_lib/include/avcap/ConnectorManager.h new file mode 100644 index 0000000..417ef15 --- /dev/null +++ b/extra_lib/include/avcap/ConnectorManager.h @@ -0,0 +1,151 @@ +/* + * (c) 2005, 2008 Nico Pranke , Robin Luedtke + * + * This file is part of avcap. + * + * avcap is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * avcap is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with avcap. If not, see . + */ + +/* avcap is free for non-commercial use. + * To use it in commercial endeavors, please contact Nico Pranke + */ + + +#ifndef CONNECTORMANAGER_H_ +#define CONNECTORMANAGER_H_ + +#include + +#include "Connector.h" +#include "Manager.h" +#include "avcap-export.h" + +namespace avcap +{ +class DeviceCollector; + + //! This class is the interface to query and select the available video/audio inputs/outputs. + + /*! This class manages STL-lists of objects of class Connector which describe + * an input or output of a capture device. Applications can get these lists and get/set + * the currently used connector of a special type. The methods to deal + * with outputs are only of partial interest for capturing but have been added + * for completeness. + * Application must use the API-dependent CaptureDevice-object to get an ConnectorManager. + * The default implementations of the mehtods in this class are a noop. + */ + + class AVCAP_Export ConnectorManager: public Manager + { + protected: + // Connector Lists + ListType mVideoInputs; + ListType mAudioInputs; + ListType mVideoOutputs; + ListType mAudioOutputs; + + public: + //! Construct the manager and query for available inputs and outputs for audio and video. + /*! The manager is usualy created by an CaptureDevice object. + * \param dd The DeviceDescriptor to acces the device. */ + ConnectorManager(DeviceDescriptor *dd); + + //! The destructor. */ + virtual ~ConnectorManager() = 0; + + //! Returns the Connector describing the currently used video input. + /*! The default-implementation returns 0. + * \return video input connector.*/ + virtual inline Connector* getVideoInput() + { return 0; } + + //! Sets the currently used video input. + /*! The default-implementation is a noop and returns -1. + * \param c The connector to use for the video input. + * \return 0, if succesful, -1 else*/ + virtual inline int setVideoInput(Connector* c) + { return -1; } + + //! Returns the Connector describing the currently used audio input. + /*! The default-implementation returns 0. + * \return audio input connector.*/ + virtual inline Connector* getAudioInput() + { return 0; } + + //! Sets the currently used audio input. + /*! The default-implementation is a noop and returns -1. + * \param c The connector to use for the audio input. + * \return 0, if succesful, -1 else*/ + virtual inline int setAudioInput(Connector* c) + { return -1; } + + //! Returns the Connector describing the currently used video output. + /*! The default-implementation returns 0. + * \return video output connector.*/ + virtual inline Connector* getVideoOutput() + { return 0; } + + //! Sets the currently used video output. + /*! The default-implementation is a noop and returns -1. + * \param c The connector to use for the video input. + * \return 0, if succesful, -1 else*/ + virtual inline int setVideoOutput(Connector* c) + { return -1; } + + //! Returns the Connector describing the currently used audio output. + /*! The default-implementation returns 0. + * \return audio output connector.*/ + virtual inline Connector* getAudioOutput() + { return 0; } + + //! Sets the currently used audio output. + /*! The default-implementation is a noop and returns -1. + * \param c The connector to use for the audio output. + * \return 0, if succesful, -1 else*/ + virtual inline int setAudioOutput(Connector* c) + { return -1; } + + //! Get the list of available video inputs of the device. + /*! \return STL-list of pointers to objects of type + * Connector describing the available video inputs. */ + inline const ListType& getVideoInputList() const + { return mVideoInputs; } + + //! Get the list of available audio inputs of the device. + /*! \return STL-list of pointers to objects of type + * Connector describing the available audio inputs. */ + inline const ListType& getAudioInputList() const + { return mAudioInputs; } + + //! Get the list of available video outputs of the device. + /*! \return STL-list of pointers to objects of type + * Connector describing the available video outputs. */ + inline const ListType& getVideoOutputList() const + { return mVideoOutputs; } + + //! Get the list of available audio outputs of the device. + /*! \return STL-list of pointers to objects of type + * Connector describing the available audio outputs. */ + inline const ListType& getAudioOutputList() const + { return mAudioOutputs; } + + //! This method is called after creation to query for video/audio in- and outputs. + virtual void query() = 0; + + private: + void clearList(ConnectorManager::ListType& list); + }; +} + +#endif //CONNECTORMANAGER_H_ diff --git a/extra_lib/include/avcap/ControlManager.h b/extra_lib/include/avcap/ControlManager.h new file mode 100644 index 0000000..ff4a253 --- /dev/null +++ b/extra_lib/include/avcap/ControlManager.h @@ -0,0 +1,82 @@ +/* + * (c) 2005, 2008 Nico Pranke , Robin Luedtke + * + * This file is part of avcap. + * + * avcap is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * avcap is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with avcap. If not, see . + */ + +/* avcap is free for non-commercial use. + * To use it in commercial endeavors, please contact Nico Pranke + */ + +#ifndef CONTROLMANAGER_H_ +#define CONTROLMANAGER_H_ + +#include +#include + +#include "Control_avcap.h" +#include "Manager.h" +#include "avcap-export.h" + +namespace avcap +{ + //! Abstract base for classes that manage the controls of a capture device. + + /*! Devices have typically a number of user-setable controls (e.g. brightness, hue,...). + * The number of controls, the type and possible values will vary from device to device. + * The ControlManager queries for available controls, their type and valid values. + * It provides a STL-List of Control-derived objects which represents the functonality of a + * device control. The concrete ControlManager may not be instantiated + * by the application but can be obtained from the CaptureDevice object. */ + + class AVCAP_Export ControlManager:public Manager + { + protected: + ListType mControls; + + public: + //! The constructor. + /*! \param dd The device descriptor to access the device. */ + ControlManager(DeviceDescriptor *dd); + + //! The destructor. + virtual ~ControlManager() = 0; + + //! Find a control by name. + /*! \param name The name of the control to find. + * \return Pointer to the control or 0, if no control was found. */ + Control* getControl(const std::string& name); + + //! Find a control by id. + /*! \param id The id of the control to find. + * \return Pointer to the control or 0, if no control was found. */ + Control* getControl(int id); + + //! Returns the STL-list of Control objects. + /*! \return The control list. */ + inline const ListType& getControlList() + { return (const ListType&) mControls; } + + //! Reset all controls to their default values,i.e. calls the reset()-method of all managed controls. + /*! \return 0 if successful, -1 else */ + virtual int resetAll(); + + virtual void query() = 0; + }; +} + + +#endif //CONTROLMANAGER_H_ diff --git a/extra_lib/include/avcap/Control_avcap.h b/extra_lib/include/avcap/Control_avcap.h new file mode 100644 index 0000000..47e05cb --- /dev/null +++ b/extra_lib/include/avcap/Control_avcap.h @@ -0,0 +1,206 @@ +/* + * (c) 2005, 2008 Nico Pranke , Robin Luedtke + * + * This file is part of avcap. + * + * avcap is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * avcap is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with avcap. If not, see . + */ + +/* avcap is free for non-commercial use. + * To use it in commercial endeavors, please contact Nico Pranke + */ + +#ifndef CONTROL_H_ +#define CONTROL_H_ + +#include +#include + +#include "avcap-export.h" +#include "Interval.h" + +namespace avcap { +// forward declaration +class DeviceDescriptor; + + //! Abstract Base class for all device controls. + + /*! Capture devices possess various controls (e.g. hue, saturation,...) of different type. + * This class provides the interface that all controls share. Objects + * derived from this class are managed by a ControlManager which is + * obtained by the concrete CaptureDevice object. + * A concrete control may expose an extended interface to provide additional functionality. + * Applications can use the getType()-method or RTTI to determine the type of the concrete control. */ + + class AVCAP_Export Control + { + public: + enum Type { + INTEGER_CONTROL = 0, + BOOL_CONTROL, + BUTTON_CONTROL, + MENU_CONTROL, + CTRLCLASS_CONTROL, + USERDEFINED_CONTROL + }; + + private: + Type mType; + + public: + //! Constructor + Control(Type t): mType(t) + {} + + //! Destructor + virtual ~Control() + {} + + //! Get the unique identifier of the control. + /*! \return id */ + virtual int getId() const = 0; + + //! Get the default value of the control. + /*! \return default value */ + virtual int getDefaultValue() const = 0; + + //! Get the name of the control. + /*! \return control name */ + virtual const std::string& getName() const = 0; + + //! Set the new value of the control. + /*! \param val : The new value. + * \return 0, if successful, -1 else */ + virtual int setValue(int val) = 0; + + //! Get the current value of the control. + /*! \return the value */ + virtual int getValue() const = 0; + + //! Set the value of the control to the default value. + /*! \return 0, if successful, -1 else */ + virtual int reset() = 0; + + //! Return the type of the control. + /* \return type */ + virtual inline Type getType() const + { return mType; } + + private: + Control() + {} + }; + + //! Abstraction of an Integer-valued control. + /*! Such controls additionally provide information about the range and step of it's values. */ + + class AVCAP_Export IntegerControl: public Control + { + public: + IntegerControl(): Control(Control::INTEGER_CONTROL) + {} + + virtual ~IntegerControl() + {} + + //! Get the interval describing the range and step of valid values for this control. + /*! \return interval */ + virtual const Interval& getInterval() const = 0; + }; + + //! Abstraction of a boolean-like control. + /*! Such controls provide no additional information. */ + + class AVCAP_Export BoolControl: public Control + { + public: + BoolControl(): Control(Control::BOOL_CONTROL) + {} + + virtual ~BoolControl() + {} + }; + + //! Abstraction of a button-like control. + /*! Such controls provide a convinience-method to trigger the buttons action. */ + + class AVCAP_Export ButtonControl: public Control + { + public: + ButtonControl(): Control(Control::BUTTON_CONTROL) + {} + + virtual ~ButtonControl() + {} + + //! Push the button. + /*! \return 0, if successful, -1 else */ + virtual int push() = 0; + }; + + //! Abstraction of a control describing the class of the successive controls. + /*! The sole purpose of such a control is to provide a name for the class of the controls + * following in the control-list. This name can be used for example to group the controls + * in a 'tabbed' user-interface (See V4L2-API spec, Extended Controls). */ + + class AVCAP_Export CtrlClassControl: public Control + { + public: + CtrlClassControl(): Control(Control::CTRLCLASS_CONTROL) + {} + + virtual ~CtrlClassControl() + {} + }; + + + //! A menu item. + struct MenuItem + { + //! The name of the item. + std::string name; + + //! The index to identify the item. + int index; + + public: + //! The constructor. + inline MenuItem(const std::string& n, int i): + name(n), index(i) + {} + }; + + //! Abstraction of a menu-like control. + /*! These controls provide a list of items, the user can chose from. */ + + class AVCAP_Export MenuControl: public Control + { + public: + //! Type of the item list. + typedef std::list ItemList; + + public: + MenuControl(): Control(Control::MENU_CONTROL) + {} + + virtual ~MenuControl() + {} + + //! Returns the STL-list of menu items associated with this control. + /*! \return the menu items. */ + virtual const ItemList& getItemList () = 0; + }; +} + +#endif // CONTROL_H_ diff --git a/extra_lib/include/avcap/DeviceCollector.h b/extra_lib/include/avcap/DeviceCollector.h new file mode 100644 index 0000000..0d02c66 --- /dev/null +++ b/extra_lib/include/avcap/DeviceCollector.h @@ -0,0 +1,131 @@ +/* + * (c) 2005, 2008 Nico Pranke , Robin Luedtke + * + * This file is part of avcap. + * + * avcap is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * avcap is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with avcap. If not, see . + */ + +/* avcap is free for non-commercial use. + * To use it in commercial endeavors, please contact Nico Pranke + */ + +#ifndef DEVICECOLLECTOR_H_ +#define DEVICECOLLECTOR_H_ + +#include +#include + +#include "singleton.h" + +#if !defined(_MSC_VER) && !defined(USE_PREBUILD_LIBS) +# include "avcap-config.h" +#endif + +#include "avcap-export.h" + +namespace avcap +{ +class DeviceDescriptor; +class CaptureDevice; + + //! This singleton queries the capture devices available on the system and provides a factory-method to create CaptureDevice-objects. + + /*! This class tests during instantiation (i.e. the first call to it's instance()-method), + * which capture devices are available on the system + * and provides an STL-list of DeviceDescriptor objects describing these devices. + * + * The following strategy to find capture devices in the system is applied: + * + * Linux: + *
    + *
  • All /dev/video* nodes are tested by default.
  • + *
  • If avcap has been compiled with HAS_AVC_SUPPORT defined, all IEEE 1394 AV/C-devices are tested.
  • + *
  • IEEE1394 digital camera support is planned but currently not implemented. + *
+ * Win32: + *
    + *
  • All devices in the CLSID_VideoInputDeviceCategory category (see DirectShow documentation) are tested.
  • + *
+ * Darwin: + *
    + *
  • All devices of the SequenceGrabber-Component device-list are tested.
  • + *
+ * + * Access the singleton instance via DEVICE_COLLECTOR::instance(). + **/ + + class AVCAP_Export DeviceCollector + { + public: + //! List type of the DeviceDescriptor object list. + typedef std::list DeviceList; + + private: + DeviceList mDeviceList; + + public: + //! Constructor + DeviceCollector(); + + //! Destructor + virtual ~DeviceCollector(); + + //! Returns the STL-list of DeviceDescriptor objects describing available capture devices. + /*! \return The descriptor list.*/ + inline const DeviceList& getDeviceList() const + { return (const DeviceList&) mDeviceList; } + + //! Linux only! Test, if the device with the given name can be opened and is a V4L1 or V4L2 capture device or not. + /*! If it is, a new DeviceDescriptor-object is created + * and stored in the device list, managed by the collector. + * \param name : the name of a device node (e.g. /dev/video0) + * \return true, if it is a V4L1-device, false else*/ + bool testDevice(const std::string& name); + + private: + +#ifdef AVCAP_LINUX + void query_V4L1_Devices(); + + void query_V4L2_Devices(); + + void query_ieee1394_Devices(); + + int test_V4L1_Device(const std::string& name); + + int test_V4L2_Device(const std::string& name); +#endif + +#ifdef AVCAP_OSX + void query_QT_Devices(); +#endif + +#ifdef AVCAP_WINDOWS + void query_DS_Devices(); + + int test_DS_Device(const std::string& name); + + bool getInstalledDeviceIDs(std::list &UniqueDeviceIDList); + +#endif +}; + +//! The DeviceCollector singleton. Access the singleton instance via DEVICE_COLLECTOR::instance(). +typedef Singleton DEVICE_COLLECTOR; +} + +/* \todo Add Linux-support for IEEE 1394 digital cameras. */ + +#endif // DEVICECOLLECTOR_H_ diff --git a/extra_lib/include/avcap/DeviceDescriptor.h b/extra_lib/include/avcap/DeviceDescriptor.h new file mode 100644 index 0000000..676831c --- /dev/null +++ b/extra_lib/include/avcap/DeviceDescriptor.h @@ -0,0 +1,196 @@ +/* + * (c) 2005, 2008 Nico Pranke , Robin Luedtke + * + * This file is part of avcap. + * + * avcap is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * avcap is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with avcap. If not, see . + */ + +/* avcap is free for non-commercial use. + * To use it in commercial endeavors, please contact Nico Pranke + */ + + +#ifndef DEVICEDESCRIPTOR_H_ +#define DEVICEDESCRIPTOR_H_ + +#include +#include + +#if !defined(_MSC_VER) && !defined(USE_PREBUILD_LIBS) +# include "avcap-config.h" +#endif + +#include "avcap-export.h" + +// IVTV driver name +#define DRIVER_IVTV "ivtv" + +namespace avcap +{ +class CaptureDevice; + + //! Objects of classes derived from this abstract base uniquely identify a capture device in a system. + + /*! It is used as an system independent description of a capture device. + * It provides the interface to access information about a device and the device itself. + * Special devices must inherit this class, e.g. V4L2_DeviceDescriptor or AVC_DeviceDescriptor. + * A list of objects derived from this class (one for each device) is provided + * by the DeviceCollector-singleton, which tries + * to determine all capture devices available on the system, so applications + * don't have to instantiate objects of these class explicitly. Objects of this + * class can be used to create a concrete CaptureDevice object by calling DEVICE_COLLECTOR::instance()->createDevice(). + * The class must be implemented for a concrete capture API/OS. + */ + + class AVCAP_Export DeviceDescriptor + { + public: +#ifdef AVCAP_LINUX + typedef int DEV_HANDLE_T; +#endif + +#ifdef AVCAP_OSX + typedef int DEV_HANDLE_T; +#endif + +#ifdef _WIN32 + //! Win32: Platform dependent device handle type for windows. (represents the DirectShow capture filter (always casted to a IBaseFilter COM-Interface). + /* To get the complete created filter graph call GetFilterGraphFromFilter() + declared in the "HelpFunc.h" header file. */ + typedef void* DEV_HANDLE_T; +#endif + + private: + const static std::string mEmptyString; + + public: + //! Constructor + DeviceDescriptor(); + + //! Destructor + virtual ~DeviceDescriptor() = 0; + + //! Open the underlying device. + /*! The CaptureDevice-Object returned by getDevice(), which is actually used to perform + * capturing is not valid before open() is called. + * \return 0 success, -1 on failure, e.g. open() has been already called before + */ + virtual int open() = 0; + + //! Close the underlying device. + /*! The CaptureDevice-Object returned by getDevice(), which is actually used to perform + * capturing, is not valid after close() is called. + * \return 0 success, -1 failure + */ + virtual int close() = 0; + + //! Returns the unique identifier of the device. + /*! \return unique identifier of device */ + virtual const std::string& getName() const = 0; + + //! Returns the name of the driver. + /*! The default implementation returns an empty string. + * \return driver */ + virtual inline const std::string& getDriver() const + { return mEmptyString; } + + //! Returns the name of the device + /*! The default implementation returns an empty string. + * \return name of the card */ + virtual inline const std::string& getCard() const + { return mEmptyString; } + + //! Returns a textual description of the device. + /*! The default implementation returns an empty string. + * \return name */ + virtual inline const std::string& getInfo() const + { return mEmptyString; } + + //! Returns the version number of the driver. + /*! The default implementation returns 0. + * \return version. */ + virtual inline int getVersion() const + { return 0; } + + //! Returns the version number of the driver as string. + /*! The default implementation returns an empty string. + * \return version string. */ + virtual inline const std::string& getVersionString() const + { return mEmptyString; } + + //! Returns the API-specific device handle used to reference the device. + /*! \return the device handle */ + virtual const DEV_HANDLE_T getHandle() const = 0; + + //! Device is an audio/video device. The default implementation returns false. + virtual inline bool isAVDev() const + { return false; } + + //! Device is capable to capture some data. The default implementation returns false. + virtual inline bool isVideoCaptureDev() const + { return false; } + + //! Device is a VBI device. The default implementation returns false. + virtual inline bool isVBIDev() const + { return false; } + + //! Device has a tuner. The default implementation returns false. + virtual inline bool isTuner() const + { return false; } + + //! Device is an audio device. The default implementation returns false. + virtual inline bool isAudioDev() const + { return false; } + + //! Device is a radio device. The default implementation returns false. + virtual inline bool isRadioDev() const + { return false; } + + //! Device supports video overlay. The default implementation returns false. + virtual inline bool isOverlayDev() const + { return false; } + + //! Device supports read/write IO-methods (linux specific, see V4L2 API Docu for further details). + /*! The default implementation returns false. */ + virtual inline bool isRWDev () const + { return false; } + + //! Device supports asynchroneous IO-methods (linux specific, see V4L2 API Docu for further details). + /*! The default implementation returns false. */ + virtual inline bool isAsyncIODev() const + { return false; } + + //! Device supports memory mapping IO-methods (linux specific, see V4L2 API Docu for further details). + /*! The default implementation returns false. */ + virtual inline bool isStreamingDev() const + { return false; } + + //! Factory-method to create a API-dependent CaptureDevice-object. + /*! Applications must not create their own + * instances of a CaptureDevice but use this method to access the proper + * API-dependent unique device-object. You can use this object anywhere between + * successive calls to open() and close(), i.e. it is not valid before open() and not after close(). + * The ownership of the object remains at the descriptor, so the caller + * must not delete the object after usage. + * Only one CaptureDevice-object will be created for each DeviceDescriptor, so + * multiple calls to getDevice() will always return the same object instance. + * + * \return the CaptureDevice-instance or 0, if not available. */ + virtual CaptureDevice* getDevice() = 0; + }; +} + + +#endif //DEVICEDESCRIPTOR_H_ diff --git a/extra_lib/include/avcap/FormatManager.h b/extra_lib/include/avcap/FormatManager.h new file mode 100644 index 0000000..b111c26 --- /dev/null +++ b/extra_lib/include/avcap/FormatManager.h @@ -0,0 +1,310 @@ +/* + * (c) 2005, 2008 Nico Pranke , Robin Luedtke + * + * This file is part of avcap. + * + * avcap is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * avcap is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with avcap. If not, see . + */ + +/* avcap is free for non-commercial use. + * To use it in commercial endeavors, please contact Nico Pranke + */ + +#ifndef FORMATMANAGER_H_ +#define FORMATMANAGER_H_ + +#include +#include + +#if !defined(_MSC_VER) && !defined(USE_PREBUILD_LIBS) +# include +# include "avcap-config.h" +#endif + +#include "Manager.h" +#include "avcap-export.h" + +#ifdef AVCAP_LINUX +# include +# include +#endif // AVCAP_LINUX + +#ifdef _WIN32 +typedef unsigned int uint32_t; +#endif + +/* taken from linux/videodev2.h but all FormatManagers use these fourcc-codes*/ + +#define FOURCC(a,b,c,d)\ + (((uint32_t)(a)<<0)|((uint32_t)(b)<<8)|((uint32_t)(c)<<16)|((uint32_t)(d)<<24)) + +#define PIX_FMT_RGB332 FOURCC('R','G','B','1') /* 8 RGB-3-3-2 */ +#define PIX_FMT_RGB555 FOURCC('R','G','B','O') /* 16 RGB-5-5-5 */ +#define PIX_FMT_RGB565 FOURCC('R','G','B','P') /* 16 RGB-5-6-5 */ +#define PIX_FMT_RGB555X FOURCC('R','G','B','Q') /* 16 RGB-5-5-5 BE */ +#define PIX_FMT_RGB565X FOURCC('R','G','B','R') /* 16 RGB-5-6-5 BE */ +#define PIX_FMT_BGR24 FOURCC('B','G','R','3') /* 24 BGR-8-8-8 */ +#define PIX_FMT_RGB24 FOURCC('R','G','B','3') /* 24 RGB-8-8-8 */ +#define PIX_FMT_BGR32 FOURCC('B','G','R','4') /* 32 BGR-8-8-8-8 */ +#define PIX_FMT_RGB32 FOURCC('R','G','B','4') /* 32 RGB-8-8-8-8 */ +#define PIX_FMT_GREY FOURCC('G','R','E','Y') /* 8 Greyscale */ +#define PIX_FMT_YVU410 FOURCC('Y','V','U','9') /* 9 YVU 4:1:0 */ +#define PIX_FMT_YVU420 FOURCC('Y','V','1','2') /* 12 YVU 4:2:0 */ +#define PIX_FMT_YUYV FOURCC('Y','U','Y','V') /* 16 YUV 4:2:2 */ +#define PIX_FMT_UYVY FOURCC('U','Y','V','Y') /* 16 YUV 4:2:2 */ +#define PIX_FMT_YUV422P FOURCC('4','2','2','P') /* 16 YVU422 planar */ +#define PIX_FMT_YUV411P FOURCC('4','1','1','P') /* 16 YVU411 planar */ +#define PIX_FMT_Y41P FOURCC('Y','4','1','P') /* 12 YUV 4:1:1 */ + +/* two planes -- one Y, one Cr + Cb interleaved */ +#define PIX_FMT_NV12 FOURCC('N','V','1','2') /* 12 Y/CbCr 4:2:0 */ +#define PIX_FMT_NV21 FOURCC('N','V','2','1') /* 12 Y/CrCb 4:2:0 */ + +/* The following formats are not defined in the V4L2 specification */ +#define PIX_FMT_YUV410 FOURCC('Y','U','V','9') /* 9 YUV 4:1:0 */ +#define PIX_FMT_YUV420 FOURCC('Y','U','1','2') /* 12 YUV 4:2:0 */ +#define PIX_FMT_I420 FOURCC('I','4','2','0') /* 12 identical to YU12 */ +#define PIX_FMT_YYUV FOURCC('Y','Y','U','V') /* 16 YUV 4:2:2 */ +#define PIX_FMT_HI240 FOURCC('H','I','2','4') /* 8 8-bit color */ +#define PIX_FMT_HM12 FOURCC('H','M','1','2') /* 8 YUV 4:1:1 16x16 macroblocks */ + +/* see http://www.siliconimaging.com/RGB%20Bayer.htm */ +#define PIX_FMT_SBGGR8 FOURCC('B','A','8','1') /* 8 BGBG.. GRGR.. */ + +/* compressed formats */ +#define PIX_FMT_MJPEG FOURCC('M','J','P','G') /* Motion-JPEG */ +#define PIX_FMT_JPEG FOURCC('J','P','E','G') /* JFIF JPEG */ +#define PIX_FMT_DV FOURCC('d','v','s','d') /* 1394 */ +#define PIX_FMT_MPEG FOURCC('M','P','E','G') /* MPEG-1/2/4 */ + +/* Vendor-specific formats */ +#define PIX_FMT_WNVA FOURCC('W','N','V','A') /* Winnov hw compress */ +#define PIX_FMT_SN9C10X FOURCC('S','9','1','0') /* SN9C10x compression */ +#define PIX_FMT_PWC1 FOURCC('P','W','C','1') /* pwc older webcam */ +#define PIX_FMT_PWC2 FOURCC('P','W','C','2') /* pwc newer webcam */ +#define PIX_FMT_ET61X251 FOURCC('E','6','2','5') /* ET61X251 compression */ + +namespace avcap +{ +class DeviceDescriptor; + + //! Description of the video standard. + struct AVCAP_Export VideoStandard + { +#ifdef AVCAP_LINUX + typedef v4l2_std_id STANDARD_ID_T; + enum { + PAL = V4L2_STD_PAL_B, + NTSC = V4L2_STD_NTSC_M, + SECAM = V4L2_STD_SECAM_B + }; +#endif + +#if defined (_WIN32) || defined (AVCAP_OSX) + typedef unsigned int STANDARD_ID_T; +#endif + + + std::string name; //!< The name of the standard. + STANDARD_ID_T id; //!< A unique identifier. + + //! Constructor + VideoStandard(const std::string& n, STANDARD_ID_T i): + name(n), + id(i) + {}; + }; + + //! The Resolution consists of a width and a height. + struct AVCAP_Export Resolution { + int width, height; + + Resolution(int w, int h): + width(w), + height(h) + {} + }; + + //! Description of a video format. + class AVCAP_Export Format + { + public: + typedef std::list ResolutionList_t; + private: + std::string mName; // A textual description. + uint32_t mFourcc; // The Four Character Code of the format. + ResolutionList_t mResList; + +#ifdef _WIN32 + void *mediatype; /* stores DirectShow-specific format description (only used internaly). + *< It is always casted to a AM_MEDIA_TYPE + * DirectShow structure; see DirectShow documentation */ +#endif + + public: + //! Constructor + inline Format(const std::string& n, uint32_t f): + mName(n), mFourcc(f) + {} + + //! Destructor + virtual ~Format(); + + //! Get the name of the format. + /*! \return name */ + inline const std::string& getName() const + { return mName; } + + //! Get the four character code of the format (see: www.fourcc.org). + /*! \return fourcc */ + inline uint32_t getFourcc() const + { return mFourcc; } + + //! Return a list of resolutions that are supported for this format. + /*! \return the resolutions.*/ + inline const ResolutionList_t& getResolutionList() const + { return mResList; } + + void addResolution(int w, int h); + +#ifdef _WIN32 + void* getMediaType() { return mediatype; } + + void setMediaType(void* mt) { mediatype = mt; } +#endif + }; + + //! Abstract base for classes that query and manage available formats, video-standards and resolutions of a capture device. + + /*! This class queries the formats, video-standards and resolutions provided by the device + * and allows applications to set them. + * The class provides a STL-list of Format-objects. + * Actualy changing the format may be deferred by the concrete implementation until it + * is really necessary, e.g. the capture begins, because advising the driver + * to change the format can be a quite time-consuming operation. + * Most of the methods in this class are implemented as a noop and are + * reimplemented by the derived class for a concrete capture API/OS, if the method is applicable. + */ + + class AVCAP_Export FormatManager: public Manager + { + public: + typedef std::list VideoStandardList; + + protected: + ListType mFormats; + int mWidth; + int mHeight; + int mBytesPerLine; +#if defined(AVCAP_LINUX) || defined (AVCAP_OSX) + unsigned int mCurrentFormat; +#endif +#ifdef _WIN32 + void *mCurrentFormat; // Always casted to a AM_MEDIA_TYPE DirectShow structure +#endif + unsigned long mImageSize; + bool mModified; + VideoStandardList mStandards; + + public: + //! The constructor. */ + FormatManager(DeviceDescriptor *dd); + + //! The destructor. */ + virtual ~FormatManager(); + + //! Returns the STL-list of Format objects describing the available formats. + /*! \return The format list.*/ + virtual inline const ListType& getFormatList() const + { return (const ListType&) mFormats; } + + //! Set the format to capture. + /*! \param fmt The new format. + * \return 0, if successful, -1 else */ + virtual int setFormat(Format *fmt); + + //! Set the format to capture. + /*! \param fourcc The four character code of the new format. + * \return 0, if successful, -1 else */ + virtual int setFormat(uint32_t fourcc); + + //! Get the current format. + /*! \return The format. */ + virtual Format* getFormat(); + + //! Set the image with and height. + /*! \param w : width + * \param h : height + * \return 0, if successful, -1 else */ + virtual int setResolution(int w, int h); + + //! Set the number of used bytes per scanline, if possible. + /*! \param bpl + * \return 0, if successful, -1 else */ + virtual int setBytesPerLine(int bpl); + + //! Returns the image with. + /*! \return width*/ + virtual int getWidth(); + + //! Returns the image height. + /*! \return height*/ + virtual int getHeight(); + + //! Returns the bytes per line. + /*! \return bpl*/ + virtual int getBytesPerLine(); + + //! Flushes the format, i.e. the driver is advised to apply the current format settings. + /*! \return 0, if successful, -1 else */ + virtual int flush(); + + //! The number of bytes that an image of the current size requires to be stored in memory, including padding. + /*! \return size */ + virtual size_t getImageSize(); + + //! Set the framerate. + /*! The default implementation returns -1 + * \param fps : the number of frames per second. + * \return 0 if successful, -1 on failure */ + virtual int setFramerate(int fps); + + //! Get the current framerate. + /*! The default implementation returns -1 + *! \return the frames per second */ + virtual int getFramerate(); + + //! Get the STL-list of avaliable video standards described by VideoStandard objects. + /*! \return standards list*/ + virtual inline const VideoStandardList& getVideoStandardList() const + { return (const VideoStandardList&) mStandards; } + + //! Get the currently used video standard. + /*! The default implementation returns 0 + * \return the current standard or 0, if not applicable */ + virtual const VideoStandard* getVideoStandard(); + + //! Set the video standard to use. + /*! Attention: not all video standards can be set in conjunction with each connector and format. + * The default implementation returns -1 + * \param std The new video standard. + * \return 0, if successful, -1 else */ + virtual int setVideoStandard(const VideoStandard* std); + + virtual void query() = 0; + }; +}; +#endif //FORMATMANAGER_H_ + + diff --git a/extra_lib/include/avcap/IOBuffer.h b/extra_lib/include/avcap/IOBuffer.h new file mode 100644 index 0000000..a8b379a --- /dev/null +++ b/extra_lib/include/avcap/IOBuffer.h @@ -0,0 +1,135 @@ +/* + * (c) 2005, 2008 Nico Pranke , Robin Luedtke + * + * This file is part of avcap. + * + * avcap is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * avcap is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with avcap. If not, see . + */ + +/* avcap is free for non-commercial use. + * To use it in commercial endeavors, please contact Nico Pranke + */ + + +#ifndef IOBUFFER_H_ +#define IOBUFFER_H_ + + +#include +#include +#include + +#if !defined(_MSC_VER) && !defined(USE_PREBUILD_LIBS) +# include +# include "avcap-config.h" +#endif + +#ifdef _WIN32 +# include +#endif + +#include "CaptureManager.h" +#include "avcap-export.h" + +namespace avcap +{ + //! The buffer to store captured data. + + /*! The class contains the captured data and provides additional information, + * e.g. sequence number, valid bytes and a capture timestamp. The data in the buffer + * may not correspond exactly to one frame, e.g. if the captured data is part + * of a stream (e.g. MPEG). + * */ + + class AVCAP_Export IOBuffer + { + public: + + //! Use-state of the buffer + enum State + { + STATE_USED = 0, //!> currently used + STATE_UNUSED, //!> currently unused + }; + + private: + CaptureManager *mMgr; + void* mPtr; + size_t mSize; + int mIndex; + int mState; + long mSequence; + size_t mValid; + struct timeval mTimestamp; + + public: + + //! Constructor + IOBuffer(CaptureManager* mgr, void* ptr, size_t size, int index = 0); + virtual ~IOBuffer(); + + //! Get the pointer to the frame data. + /*! \return the captured data. */ + inline void* getPtr() const + { return mPtr; } + + //! Returns the maximum number of bytes the buffer can contain. + /*! \return Size of buffer in bytes. */ + inline size_t getSize() const + { return mSize; } + + //! Return the sequence number of the frame. + /*! \return Sequence number */ + inline long getSequence() const + { return mSequence; } + + //! Returns the number of valid bytes in the buffer. + /*! \return Number of valid bytes */ + inline size_t getValidBytes() const + { return mValid; } + + //! Returns a timestamp in milliseconds. + /*! \return timestamp */ + unsigned long getTimestamp(); + + //! Must be called by the application after the buffer isn't used anymore to to enable its reutilization. + void release(); + + //! Get the index of the buffer. + /*! \return the buffer index. */ + inline int getIndex() const + { return mIndex; } + + //! Set the state of the buffer. + /*! This method should not be used by applications. + *! \param state : the new state */ + inline void setState(State state) + { mState = state; } + + //! Get the buffer usage state. + /*! \return the current buffer state. */ + inline State getState() const + { return (State) mState; } + + //! Set buffer parameters. + /*! This method should not be used by applications. + * \param valid : number of valid bytes in buffer + * \param state : the current buffer state + * \param ts : the timestamp the data was captured + * \param seq : the sequence number of the captured data */ + void setParams(const size_t valid, State state, struct timeval &ts, int seq); + }; +} + +#endif // IOBUFFER_H_ diff --git a/extra_lib/include/avcap/Interval.h b/extra_lib/include/avcap/Interval.h new file mode 100644 index 0000000..ac45118 --- /dev/null +++ b/extra_lib/include/avcap/Interval.h @@ -0,0 +1,56 @@ +/* + * (c) 2005, 2008 Nico Pranke , Robin Luedtke + * + * This file is part of avcap. + * + * avcap is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * avcap is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with avcap. If not, see . + */ + +/* avcap is free for non-commercial use. + * To use it in commercial endeavors, please contact Nico Pranke + */ + + +#ifndef INTERVAL_H_ +#define INTERVAL_H_ + +#include "avcap-export.h" + +namespace avcap +{ + //! An integer interval used by some Control-types. + class AVCAP_Export Interval + { + public: + //! The minimal value. + int min; + + //! The maximal value. + int max; + + //! The increment. + int step; + + //! The Constructor. + Interval( int min_, int max_, int step_ ) + { + min = min_; + max = max_; + step = step_; + }; + }; +}; + +#endif // INTERVAL_H_ + diff --git a/extra_lib/include/avcap/Manager.h b/extra_lib/include/avcap/Manager.h new file mode 100644 index 0000000..b1b2d17 --- /dev/null +++ b/extra_lib/include/avcap/Manager.h @@ -0,0 +1,67 @@ +/* + * (c) 2005, 2008 Nico Pranke , Robin Luedtke + * + * This file is part of avcap. + * + * avcap is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * avcap is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with avcap. If not, see . + */ + +/* avcap is free for non-commercial use. + * To use it in commercial endeavors, please contact Nico Pranke + */ + + +#ifndef MANAGER_H_ +#define MANAGER_H_ + +#include +#include + +namespace avcap +{ + class DeviceDescriptor; + + //! Abstract base class for Managers. + + /*! Classes that provide access to specific aspects of + * a device derive from this class. Managers usualy manage a number of + * objects of a specific type that abstract these aspects. + * The template parameter is used to define a STL list-type to + * store these objects.*/ + + template + class Manager + { + public: + //! The STL list-type to store the managed objects. + typedef std::list ListType; + + protected: + DeviceDescriptor *mDeviceDescriptor; + + public: + inline Manager(DeviceDescriptor* dd): + mDeviceDescriptor(dd) + {} + + virtual ~Manager() + {} + + /*! Called during initialisation by the CaptureDevice to query for + * the objects that the implementation of this class manages. */ + virtual void query() = 0; + }; +} + +#endif // MANAGER_H_ diff --git a/extra_lib/include/avcap/ProbeValues.h b/extra_lib/include/avcap/ProbeValues.h new file mode 100644 index 0000000..9a161bb --- /dev/null +++ b/extra_lib/include/avcap/ProbeValues.h @@ -0,0 +1,51 @@ +/* + * (c) 2005, 2008 Nico Pranke , Robin Luedtke + * + * This file is part of avcap. + * + * avcap is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * avcap is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with avcap. If not, see . + */ + +/* avcap is free for non-commercial use. + * To use it in commercial endeavors, please contact Nico Pranke + */ + + +#ifndef PROBEVALUES_H +#define PROBEVALUES_H + +namespace avcap { + +//! Standard resolutions to probe, if querying resolutions is not supported by the API/device +static int NumResolutions = 13; +static unsigned int Resolutions[][2] = +{ + {640, 480}, + {640, 360}, + {352, 288}, + {352, 240}, + {320, 240}, + {176, 144}, + {160, 120}, + {80, 60}, + {720, 480}, + {720, 576}, + {800, 600}, + {1280, 1024}, + {1600, 1200} +}; + +} + +#endif diff --git a/extra_lib/include/avcap/Tuner_avcap.h b/extra_lib/include/avcap/Tuner_avcap.h new file mode 100644 index 0000000..197f644 --- /dev/null +++ b/extra_lib/include/avcap/Tuner_avcap.h @@ -0,0 +1,188 @@ +/* + * (c) 2005, 2008 Nico Pranke , Robin Luedtke + * + * This file is part of avcap. + * + * avcap is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * avcap is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with avcap. If not, see . + */ + +/* avcap is free for non-commercial use. + * To use it in commercial endeavors, please contact Nico Pranke + */ + +#ifndef TUNER_H_ +#define TUNER_H_ + +#include + +#include "avcap-export.h" + +namespace avcap +{ + class DeviceDescriptor; + + //! Interface of a tuner. + + /*! This class provides access to the tuner functionality of TV or Radio-cards. + * Applications can adjust things like frequency, audio mode etc. + * Applications don't create Tuner objects themselfes but get them from the Connector + * the tuner is associated with. The connector in turn can be obtained from the + * ConnectorManager of the CaptureDevice. + **/ + + class AVCAP_Export Tuner + { + private: + public: + virtual inline ~Tuner() + {} + + //! Determine whether the tuner is able to receive radio frequencies. + /*! The default implementation returns false. + * \return true if radio tuner, false else */ + virtual inline bool isRadioTuner() const + { return false; } + + //! Determine whether the tuner is able to receive TV frequencies. + /*! The default implementation returns false. + * \return true if TV tuner, false else */ + virtual inline bool isTVTuner() const + { return false; }; + + //! Set the audio mode to stereo. + /*! The default implementation is noop and returns -1. + * \return 0, if successful, -1 else. */ + virtual inline int setStereo() + { return -1; } + + //! Set the audio mode to mono. + /*! Default implementation is noop and return -1. + * \return 0, if successful, -1 else. */ + virtual inline int setMono() + { return -1; } + + //! Set the audio mode to secondary audio program (SAP). + /*! The default implementation is noop and returns -1. + * \return 0, if successful, -1 else. */ + virtual inline int setSAP() + { return -1; } + + //! Set the audio mode to language 1. + /*! The default implementation is noop and returns -1. + * \return 0, if successful, -1 else. */ + virtual inline int setLang1() + { return -1; } + + //! Set the audio mode to language 2. + /*! The default implementation is noop and returns -1. + * \return 0, if successful, -1 else. */ + virtual inline int setLang2() + { return -1; } + + //! Returns the current frequency in MHz. + /*! The default implementation is noop and returns -1. + * \return tuner frequency */ + virtual inline double getFreq() const + { return -1.0f; } + + //! Returns the step with in which the frequency can be increased or decreased. + /*! The default implementation is noop and returns -1. + * \return frequency step width*/ + virtual inline double getFreqStep() const + { return -1.0f; } + + //! Returns the minimum possible frequency in MHz which can be applied to the tuner. + /*! The default implementation is noop and returns -1. + * \return minimal tuner frequency */ + virtual inline double getMinFreq() const + { return -1.0f; } + + //! Returns the maximum possible frequency in MHz which can be applied to the tuner. + /*! The default implementation is noop and returns -1. + * \return maximal tuner frequency */ + virtual inline double getMaxFreq() const + { return -1.0f; } + + //! Returns the tuner name. + /*! Default implementation returns an empty string. + * \return tuner name */ + virtual inline const std::string getName() const + { return ""; } + + //! This method tries to readjust and to fine-tune the frequency by means of the current AFC-value. + /*! The default implementation is noop and returns -1. + * \return 0, if successful, -1 else. */ + virtual inline int finetune(int maxsteps) + { return -1; } + + //! Get the current automatic frequency control (AFC) value. + /*! If the afc value is negative, the frequency is too low, if positive it is too high. + *! The default implementation is noop and returns -1. + * \return afc */ + virtual inline int getAFCValue() const + { return -1; } + + //! Return the strength of the signal. + /*! The default implementation is noop and returns -1. + * \return signal strength */ + virtual inline int getSignalStrength() const + { return -1; } + + //! Increase the frequency a step corresponding to getFreqStep(). + /*! The default implementation is noop and returns -1. + * \return 0, if successful, -1 else. */ + virtual inline int increaseFreq() + { return 0; } + + //! Decrease the frequency a step corresponding to getFreqStep(). + /*! The default implementation is noop and returns -1. + * \return 0, if successful, -1 else. */ + virtual inline int decreaseFreq() + { return 0; } + + //! Set the new frequency. The frequency is given in MHz. + /*! The default implementation is noop and returns -1. + * \param f The new frequency. + * \return 0, if successful, -1 else. */ + virtual inline int setFreq(double f) + { return -1; } + }; + + /*! Known european terrestric analog TV channels. They must be multiplied by 10^6 + * to obtain the frequency in Hz. */ + static const double TV_Channels[] = + { + 48.25, 55.25, 62.25, 175.25, 182.25, 189.25, 196.25, 203.25, 210.25, 217.25, 224.25, 471.25, 479.25, 487.25, + 495.25, 503.25, 511.25, 519.25, 527.25, 535.25, 543.25, 551.25, 559.25, 567.25, 575.25, 583.25, 591.25, + 599.25, 607.25, 615.25, 623.25, 631.25, 639.25, 647.25, 655.25, 663.25, 671.25, 679.25, 687.25, 695.25, + 703.25, 711.25, 719.25, 727.25, 735.25, 743.25, 751.25, 759.25, 767.25, 775.25, 783.25, 791.25, 799.25, + 807.25, 815.25, 823.25, 831.25, 839.25, 847.25, 855.25 + }; + + /*! The number of TV channels */ + static const int TV_Num_Channels = 60; + + /*! Some german analog radio channels. They must be multiplied by 10^6 + * to obtain the frequency in Hz. */ + static const double Radio_Channels[] = + { + 102.4, 102.0 + }; + + /*! The number of radio channels */ + static const int Radio_Num_Channels = 2; + +} + +#endif // TUNER_H_ diff --git a/extra_lib/include/avcap/avcap-export.h b/extra_lib/include/avcap/avcap-export.h new file mode 100644 index 0000000..58d0e22 --- /dev/null +++ b/extra_lib/include/avcap/avcap-export.h @@ -0,0 +1,43 @@ +/* + * (c) 2005, 2008 Nico Pranke , Robin Luedtke + * + * This file is part of avcap. + * + * avcap is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * avcap is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with avcap. If not, see . + */ + +/* avcap is free for non-commercial use. + * To use it in commercial endeavors, please contact Nico Pranke + */ + + +#ifndef AVCAP_EXPORT_H +#define AVCAP_EXPORT_H + +#if !defined(_MSC_VER) && !defined(USE_PREBUILD_LIBS) +#include "avcap-config.h" +#endif + +#ifdef AVCAP_WINDOWS +# if defined AVCAP_EXPORTS +# define AVCAP_Export __declspec(dllexport) +# else /* AVCAP_BUILD_DLL */ +# define AVCAP_Export /*__declspec(dllimport)*/ +# endif +#else +#define AVCAP_Export +#endif + +#endif /* AVCAP_EXPORT_H */ + diff --git a/extra_lib/include/avcap/avcap.h b/extra_lib/include/avcap/avcap.h new file mode 100644 index 0000000..0e21a61 --- /dev/null +++ b/extra_lib/include/avcap/avcap.h @@ -0,0 +1,45 @@ +/* + * (c) 2005, 2008 Nico Pranke , Robin Luedtke + * + * This file is part of avcap. + * + * avcap is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * avcap is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with avcap. If not, see . + */ + +/* avcap is free for non-commercial use. + * To use it in commercial endeavors, please contact Nico Pranke + */ + + +#ifndef AVCAP_H +#define AVCAP_H + +#if !defined(_MSC_VER) && !defined(USE_PREBUILD_LIBS) +# include "avcap-config.h" +#endif +#include "avcap/DeviceCollector.h" +#include "avcap/DeviceDescriptor.h" +#include "avcap/CaptureDevice.h" +#include "avcap/FormatManager.h" +#include "avcap/CaptureHandler.h" +#include "avcap/CaptureManager.h" +#include "avcap/Control_avcap.h" +#include "avcap/ControlManager.h" +#include "avcap/Connector.h" +#include "avcap/ConnectorManager.h" +#include "avcap/IOBuffer.h" +#include "avcap/Tuner_avcap.h" +#include "avcap/log.h" + +#endif diff --git a/extra_lib/include/avcap/linux/AVC_ConnectorManager.h b/extra_lib/include/avcap/linux/AVC_ConnectorManager.h new file mode 100644 index 0000000..14c0847 --- /dev/null +++ b/extra_lib/include/avcap/linux/AVC_ConnectorManager.h @@ -0,0 +1,61 @@ +/* + * (c) 2005, 2008 Nico Pranke , Robin Luedtke + * + * This file is part of avcap. + * + * avcap is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * avcap is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with avcap. If not, see . + */ + +/* avcap is free for non-commercial use. + * To use it in commercial endeavors, please contact Nico Pranke + */ + +#ifdef HAS_AVC_SUPPORT + +#ifndef AVC_CONNECTORMANAGER_H_ +#define AVC_CONNECTORMANAGER_H_ + +#include + +#include "ConnectorManager.h" + +namespace avcap +{ + class AVC_DeviceDescriptor; + + //! \brief This class implements the ConnectorManager for AV/C-devices. + + /*! Such devices don't have Connectors the user could chose. So the query() + * method is a noop. */ + + class AVC_ConnectorManager: public ConnectorManager + { + public: + + inline AVC_ConnectorManager(AVC_DeviceDescriptor *dd): + ConnectorManager((DeviceDescriptor*) dd) + {} + + virtual inline ~AVC_ConnectorManager() + {} + + inline void query() + {} + }; +} + +#endif // AVC_CONNECTORMANAGER_H_ + +#endif // HAS_AVC_SUPPORT + diff --git a/extra_lib/include/avcap/linux/AVC_ControlManager.h b/extra_lib/include/avcap/linux/AVC_ControlManager.h new file mode 100644 index 0000000..9c2f698 --- /dev/null +++ b/extra_lib/include/avcap/linux/AVC_ControlManager.h @@ -0,0 +1,59 @@ +/* + * (c) 2005, 2008 Nico Pranke , Robin Luedtke + * + * This file is part of avcap. + * + * avcap is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * avcap is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with avcap. If not, see . + */ + +/* avcap is free for non-commercial use. + * To use it in commercial endeavors, please contact Nico Pranke + */ + + +#ifdef HAS_AVC_SUPPORT + +#ifndef AVC_CONTROLMANAGER_H_ +#define AVC_CONTROLMANAGER_H_ + +#include +#include + +#include "ControlManager.h" + +namespace avcap +{ + //! Implementation of the ControlManager for AV/C-devices. + /*! These devices have usually no controls, so there the query method is a noop. */ + + class AVC_ControlManager:public ControlManager + { + public: + //! The constructor. + /*! \param dd The device descriptor to access the device. */ + inline AVC_ControlManager(AVC_DeviceDescriptor *dd): + ControlManager((DeviceDescriptor*) dd) + {} + + //! The destructor. + virtual inline ~AVC_ControlManager() + {} + + virtual inline void query() + {} + }; +}; + +#endif // AVC_CONTROLMANAGER_H_ +#endif diff --git a/extra_lib/include/avcap/linux/AVC_Device.h b/extra_lib/include/avcap/linux/AVC_Device.h new file mode 100644 index 0000000..4d8d8b2 --- /dev/null +++ b/extra_lib/include/avcap/linux/AVC_Device.h @@ -0,0 +1,103 @@ +/* + * (c) 2005, 2008 Nico Pranke , Robin Luedtke + * + * This file is part of avcap. + * + * avcap is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * avcap is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with avcap. If not, see . + */ + +/* avcap is free for non-commercial use. + * To use it in commercial endeavors, please contact Nico Pranke + */ + + +#ifdef HAS_AVC_SUPPORT + +#ifndef AVC_DEVICE_H_ +#define AVC_DEVICE_H_ + +#include +#include + +#include "DeviceDescriptor.h" +#include "CaptureDevice.h" +#include "AVC_FormatManager.h" + +namespace avcap +{ + // forward declarations + class AVC_DeviceDescriptor; + class AVC_VidCapManager; + class AVC_ConnectorManager; + class AVC_ControlManager; + + //! Implementation of the CaptureDevice for IEEE 1394 AV/C-devices under linux. + + /*! Such devices don't have controls, various resolutions, extensions, or connectors. + * So the implementation of the methods rely mostly on the default implementations or are + * implemented as noop. + * + * Note: If capturing from AV/C-devices is enabled, avcap depends on + * libiec61883, libavc1394, librom1394, libraw1394 + * Furthermore the user requires read/write access to /dev/raw1394. + * + * AV/C-support is enabled by the configure-script, if the neccessary libs and developement-headers + * are found on the system. + * + * The AV/C-support is based on dvgrab (http://www.kinodv.org), which is released under the GPLv2. + */ + + class AVC_Device : public CaptureDevice + { + public: + + private: + AVC_DeviceDescriptor* mDeviceDescriptor; + AVC_FormatManager* mFormatMgr; + AVC_VidCapManager* mVidCapMgr; + AVC_ConnectorManager* mConnectorMgr; + AVC_ControlManager* mControlMgr; + + public: + //! Constructor + AVC_Device(AVC_DeviceDescriptor* dd); + + //! Destructor + virtual ~AVC_Device(); + + inline const DeviceDescriptor* getDescriptor() + { return mDeviceDescriptor; } + + inline CaptureManager* getVidCapMgr() + { return (CaptureManager*) mVidCapMgr; } + + inline ConnectorManager* getConnectorMgr() + { return (ConnectorManager*) mConnectorMgr; } + + inline ControlManager* getControlMgr() + { return (ControlManager*) mControlMgr; } + + inline FormatManager* getFormatMgr() + { return (FormatManager*) mFormatMgr; } + + private: + int open(); + + int close(); + }; +} + +#endif // AVC_DEVICE_H_ +#endif // HAS_AVC_SUPPORT + diff --git a/extra_lib/include/avcap/linux/AVC_DeviceDescriptor.h b/extra_lib/include/avcap/linux/AVC_DeviceDescriptor.h new file mode 100644 index 0000000..e6a78fd --- /dev/null +++ b/extra_lib/include/avcap/linux/AVC_DeviceDescriptor.h @@ -0,0 +1,103 @@ +/* + * (c) 2005, 2008 Nico Pranke , Robin Luedtke + * + * This file is part of avcap. + * + * avcap is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * avcap is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with avcap. If not, see . + */ + +/* avcap is free for non-commercial use. + * To use it in commercial endeavors, please contact Nico Pranke + */ + + +#ifndef AVC_DEVICEDESCRIPTOR_H_ +#define AVC_DEVICEDESCRIPTOR_H_ + +#ifdef HAS_AVC_SUPPORT + +#include +#include + +#include "DeviceDescriptor.h" + +namespace avcap +{ +class CaptureDevice; +class AVC_Device; + + //! This class implements a descriptor for a IEEE 1394 AV/C capture device under linux (e.g. a DV-Camera). + + class AVC_DeviceDescriptor : public DeviceDescriptor + { + private: + std::string mName; + std::string mGUIDString; + std::string mInfo; + std::string mDriver; + + octlet_t mGUID; + static int mDevCount; + AVC_Device* mDevice; + + public: + //! This constructor uses a numerical global unique identifier to represent a IEEE 1394 capture device (e.g. a firewire DV-Cam). + /*! \param guid The unique identifier of device.*/ + AVC_DeviceDescriptor(const octlet_t guid); + + //! The destructor */ + virtual ~AVC_DeviceDescriptor(); + + int open(); + + int close(); + + //! Returns the unique identifier of the device. AV/C-devices get the name + /*! "AV/C_n", where n is the number of the device in the system starting with 1. + * So the 3rd device found has the name AV/C_3. + * \return unique identifier of device */ + inline const std::string& getName() const + { return mName; } + + virtual inline const std::string& getInfo() const + { return mInfo; } + + virtual inline const std::string& getDriver() const + { return mDriver; } + + virtual inline const std::string& getCard() const + { return mGUIDString; } + + //! There is no handle associated with a AV/C-device. So this method always returns -1. + inline const DEV_HANDLE_T getHandle() const + { return -1; } + + //! Device is an audio/video device. Always returns true. + inline bool isAVDev() const + { return true; } + + //! Device is capable to capture some data. Always returns true. + inline bool isVideoCaptureDev() const + { return true; } + + inline octlet_t& getGUID() + { return mGUID; } + + virtual CaptureDevice* getDevice(); + }; +} + +#endif // AVC_DEVICEDESCRIPTOR_H_ +#endif // HAS_DVLIBS + diff --git a/extra_lib/include/avcap/linux/AVC_FormatManager.h b/extra_lib/include/avcap/linux/AVC_FormatManager.h new file mode 100644 index 0000000..dfd420c --- /dev/null +++ b/extra_lib/include/avcap/linux/AVC_FormatManager.h @@ -0,0 +1,69 @@ +/* + * (c) 2005, 2008 Nico Pranke , Robin Luedtke + * + * This file is part of avcap. + * + * avcap is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * avcap is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with avcap. If not, see . + */ + +/* avcap is free for non-commercial use. + * To use it in commercial endeavors, please contact Nico Pranke + */ + + +#ifdef HAS_AVC_SUPPORT + +#ifndef AVC_FORMATMANAGER_H_ +#define AVC_FORMATMANAGER_H_ + +#include "FormatManager.h" + +namespace avcap +{ + class AVC_DeviceDescriptor; + + //! Implementation of the FormatManager for AV/C devices. + + /*! DV-Cams usually provide only a fixed resolution (PAL: 720x576, NTSC: 720x480) + * YUV-format and no choice of video standards. + * Additionally, this manager provides RGB.*/ + + class AVC_FormatManager: public FormatManager + { + private: + bool mIsPal; + + public: + AVC_FormatManager(AVC_DeviceDescriptor *dd); + + virtual ~AVC_FormatManager(); + + //! Set the image with. + /*! For AV/C -devices the reolution is fixed and thus can't be realy modified. + * \param w: width + * \param h: height + * \return 0, if the desired resolution matches the native resolution, -1 else*/ + int setResolution(int w, int h); + + //! Get the current framerate. Setting the framerate is not possible. + /*! \return 25 for PAL, 30 for NTSC(this is a little bit inaccurate, since the proper NTSC-frame rate is 29.97 fps)*/ + inline int getFramerate() { return mIsPal ? 25 : 30; } + + void query(); + }; +}; +#endif // AVC_FORMATMANAGER_H_ + +#endif // HAS_AVC_SUPPORT + diff --git a/extra_lib/include/avcap/linux/AVC_Reader.h b/extra_lib/include/avcap/linux/AVC_Reader.h new file mode 100644 index 0000000..2c214b0 --- /dev/null +++ b/extra_lib/include/avcap/linux/AVC_Reader.h @@ -0,0 +1,74 @@ +/* + * (c) 2005, 2008 Nico Pranke , Robin Luedtke + * + * This file is part of avcap. + * + * avcap is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * avcap is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with avcap. If not, see . + */ + +/* avcap is free for non-commercial use. + * To use it in commercial endeavors, please contact Nico Pranke + */ + +#ifdef HAS_AVC_SUPPORT + +#ifndef AVC_READER_H_ +#define AVC_READER_H_ + +#include + +#include "ieee1394io.h" + +namespace avcap +{ +class AVC_VidCapManager; +class AVC_FormatManager; +class IOBuffer; + + //! AVC_Reader, used by the AVC_VidCapManager. + + class AVC_Reader : public iec61883Reader + { + typedef std::list BufferList_t; + + + AVC_VidCapManager* mVidCapMgr; + AVC_FormatManager* mFormatMgr; + CaptureHandler* mCaptureHandler; + + BufferList_t mBuffers; + long mSequence; + int mAvailableBuffers; + + public: + AVC_Reader(AVC_VidCapManager* cap_mgr, AVC_FormatManager* fmt_mgr, int port, int channel, int num_bufs); + + virtual ~AVC_Reader(); + + void registerCaptureHandler(CaptureHandler *handler); + + void removeCaptureHandler(); + + virtual void TriggerAction(); + + void enqueue(IOBuffer* io_buf); + + inline int getNumIOBuffers() + { return mAvailableBuffers; } + + }; +} + +#endif // AVC_READER_H_ +#endif diff --git a/extra_lib/include/avcap/linux/AVC_VidCapManager.h b/extra_lib/include/avcap/linux/AVC_VidCapManager.h new file mode 100644 index 0000000..8f33572 --- /dev/null +++ b/extra_lib/include/avcap/linux/AVC_VidCapManager.h @@ -0,0 +1,102 @@ +/* + * (c) 2005, 2008 Nico Pranke , Robin Luedtke + * + * This file is part of avcap. + * + * avcap is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * avcap is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with avcap. If not, see . + */ + +/* avcap is free for non-commercial use. + * To use it in commercial endeavors, please contact Nico Pranke + */ + + +#ifdef HAS_AVC_SUPPORT + +#ifndef AVC_VIDCAPMANAGER_H_ +#define AVC_VIDCAPMANAGER_H_ + +#include "avcap-config.h" + +#include +#include +#include + +#include "CaptureManager.h" + +namespace avcap +{ + class AVC_DeviceDescriptor; + class AVC_FormatManager; + class IOBuffer; + class CaptureHandler; + class AVC_Reader; + class iec61883Connection; + + //! The IEEE 1394 AV/C video capture manager. + + /*! This class is used to capture video data from a AV/C-Device (e.g. DV-Cams) connected + * to the computer via IEEE 1394 (aka Firewire or iLink) under Linux. + */ + class AVC_VidCapManager: public CaptureManager + { + public: + enum + { + MAX_BUFFERS = 32, //!< The maximum number of IOBuffers. + DEFAULT_BUFFERS = 8 //!< The default number of used IOBuffers. + }; + + private: + typedef std::list IOBufList; + + AVC_DeviceDescriptor *mDeviceDescriptor; + AVC_FormatManager *mFormatMgr; + AVC_Reader *mReader; + iec61883Connection *mConnection; + + IOBufList mBuffers; + int mNumBufs; + int mSequence; + + public: + + AVC_VidCapManager(AVC_DeviceDescriptor* dd, AVC_FormatManager* fmt_mgr, int nbufs = DEFAULT_BUFFERS); + + virtual ~AVC_VidCapManager(); + + int init(); + + int destroy(); + + int startCapture(); + + int stopCapture(); + + void registerCaptureHandler(CaptureHandler *handler); + + void removeCaptureHandler(); + + virtual int getNumIOBuffers(); + + private: + virtual IOBuffer* dequeue(); + + virtual int enqueue(IOBuffer* buf); + }; +} +#endif // AVC_VIDCAPMANAGER_H_ + +#endif // HAS_AVC_SUPPORT + diff --git a/extra_lib/include/avcap/linux/V4L1_Connector.h b/extra_lib/include/avcap/linux/V4L1_Connector.h new file mode 100644 index 0000000..827b5eb --- /dev/null +++ b/extra_lib/include/avcap/linux/V4L1_Connector.h @@ -0,0 +1,57 @@ +/* + * (c) 2005, 2008 Nico Pranke , Robin Luedtke + * + * This file is part of avcap. + * + * avcap is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * avcap is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with avcap. If not, see . + */ + +/* avcap is free for non-commercial use. + * To use it in commercial endeavors, please contact Nico Pranke + */ + + +#ifndef V4L1_CONNECTOR_H_ +#define V4L1_CONNECTOR_H_ + +#include +#include + +#include +#include + +#include "Connector.h" + +namespace avcap +{ + // forward declaration + class V4L1_DeviceDescriptor; + class Tuner; + + //! Implementation of the Connector for a Video4Linux1 device. + + class V4L1_Connector : public Connector + { + public: + //! The Constructor. Objects should be instantiated only by the ConnectorManager. + inline V4L1_Connector(V4L1_DeviceDescriptor *dd, int index, const std::string& name, int type=0): + Connector(dd, index, name, type) + {} + + virtual inline ~V4L1_Connector() + {} + }; +} + +#endif // V4L1_CONNECTOR_H_ diff --git a/extra_lib/include/avcap/linux/V4L1_ConnectorManager.h b/extra_lib/include/avcap/linux/V4L1_ConnectorManager.h new file mode 100644 index 0000000..f771b67 --- /dev/null +++ b/extra_lib/include/avcap/linux/V4L1_ConnectorManager.h @@ -0,0 +1,60 @@ +/* + * (c) 2005, 2008 Nico Pranke , Robin Luedtke + * + * This file is part of avcap. + * + * avcap is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * avcap is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with avcap. If not, see . + */ + +/* avcap is free for non-commercial use. + * To use it in commercial endeavors, please contact Nico Pranke + */ + + +#ifndef V4L1_CONNECTORMANAGER_H_ +#define V4L1_CONNECTORMANAGER_H_ + +#include +#include + +#include "ConnectorManager.h" + +namespace avcap +{ + class V4L1_DeviceDescriptor; + + //! Implementation of the ConnectorManager for Video4Linux2-devices. */ + + class V4L1_ConnectorManager: public ConnectorManager + { + private: + Connector* mCurrentVideoInput; + + public: + V4L1_ConnectorManager(V4L1_DeviceDescriptor *dd); + + virtual ~V4L1_ConnectorManager(); + + Connector* getVideoInput(); + + int setVideoInput(Connector* c); + + void query(); + + private: + Connector* findByIndex(const ListType& l, int index); + }; +} + +#endif // V4L1_CONNECTORMANAGER_H_ diff --git a/extra_lib/include/avcap/linux/V4L1_Control.h b/extra_lib/include/avcap/linux/V4L1_Control.h new file mode 100644 index 0000000..f0cfd55 --- /dev/null +++ b/extra_lib/include/avcap/linux/V4L1_Control.h @@ -0,0 +1,88 @@ +/* + * (c) 2005, 2008 Nico Pranke , Robin Luedtke + * + * This file is part of avcap. + * + * avcap is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * avcap is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with avcap. If not, see . + */ + +/* avcap is free for non-commercial use. + * To use it in commercial endeavors, please contact Nico Pranke + */ + +#ifndef V4L1_CONTROL_H_ +#define V4L1_CONTROL_H_ + +#include +#include + +#include "Control_avcap.h" + +namespace avcap +{ + // forward declaration + class V4L1_DeviceDescriptor; + + //! Implementation of a V4L1-Control. + + /*! The V4L1 API supprots only controls for brightness, hue, colour, contrast, depth and whiteness. + * They are all integer values between 0 and 65535.*/ + + class V4L1_Control: public IntegerControl + { + public: + enum Ctrl { + BRIGHTNESS = 0, + HUE, + COLOUR, + CONTRAST, + WHITENESS, + DEPTH + }; + + private: + static std::string mNames[6]; + + V4L1_DeviceDescriptor* mDescriptor; + Ctrl mType; + int mDefaultValue; + Interval mInterval; + + public: + V4L1_Control(V4L1_DeviceDescriptor* dd, Ctrl type, __u16 def); + + virtual inline ~V4L1_Control() + {} + + virtual inline int getId() const + { return mType; } + + virtual inline int getDefaultValue() const + { return mDefaultValue; } + + virtual const std::string& getName() const; + + virtual int setValue(int val); + + virtual int getValue() const; + + virtual int reset(); + + virtual inline const Interval& getInterval() const + { return mInterval; } + }; +} + +#endif // V4L1_CONTROL_H_ + diff --git a/extra_lib/include/avcap/linux/V4L1_ControlManager.h b/extra_lib/include/avcap/linux/V4L1_ControlManager.h new file mode 100644 index 0000000..2f08164 --- /dev/null +++ b/extra_lib/include/avcap/linux/V4L1_ControlManager.h @@ -0,0 +1,49 @@ +/* + * (c) 2005, 2008 Nico Pranke , Robin Luedtke + * + * This file is part of avcap. + * + * avcap is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * avcap is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with avcap. If not, see . + */ + +/* avcap is free for non-commercial use. + * To use it in commercial endeavors, please contact Nico Pranke + */ + +#ifndef V4L1_CONTROLMANAGER_H_ +#define V4L1_CONTROLMANAGER_H_ + +#include +#include + +#include "ControlManager.h" + +namespace avcap +{ + class V4L1_DeviceDescriptor; + + //! Implementation of the ControlManager for Video4Linux2 devices. + + class V4L1_ControlManager:public ControlManager + { + public: + V4L1_ControlManager(V4L1_DeviceDescriptor *dd); + + virtual ~V4L1_ControlManager(); + + void query(); + }; +} + +#endif // V4L1_CONTROLMANAGER_H_ diff --git a/extra_lib/include/avcap/linux/V4L1_Device.h b/extra_lib/include/avcap/linux/V4L1_Device.h new file mode 100644 index 0000000..cd7db28 --- /dev/null +++ b/extra_lib/include/avcap/linux/V4L1_Device.h @@ -0,0 +1,83 @@ +/* + * (c) 2005, 2008 Nico Pranke , Robin Luedtke + * + * This file is part of avcap. + * + * avcap is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * avcap is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with avcap. If not, see . + */ + +/* avcap is free for non-commercial use. + * To use it in commercial endeavors, please contact Nico Pranke + */ + +#ifndef V4L1_DEVICE_H_ +#define V4L1_DEVICE_H_ + +#include +#include + +#include "V4L1_DeviceDescriptor.h" +#include "CaptureDevice.h" + +namespace avcap +{ + // forward declarations + class V4L1_ConnectorManager; + class V4L1_ControlManager; + class V4L1_VidCapManager; + class V4L1_DeviceDescriptor; + class V4L1_FormatManager; + + //! Implementation of the CaptureDevice interface for Video4Linux2 devices. */ + + class V4L1_Device : public CaptureDevice + { + public: + + private: + V4L1_VidCapManager *mVidCapMgr; + V4L1_ConnectorManager *mConnectorMgr; + V4L1_ControlManager *mControlMgr; + V4L1_FormatManager *mFormatMgr; + V4L1_DeviceDescriptor *mDeviceDescriptor; + + public: + V4L1_Device(V4L1_DeviceDescriptor* dd); + + virtual ~V4L1_Device(); + + inline const DeviceDescriptor* getDescriptor() + { return mDeviceDescriptor; } + + inline CaptureManager* getVidCapMgr() + { return (CaptureManager*) mVidCapMgr; } + + inline ConnectorManager* getConnectorMgr() + { return (ConnectorManager*) mConnectorMgr; } + + inline ControlManager* getControlMgr() + { return (ControlManager*) mControlMgr; } + + inline FormatManager* getFormatMgr() + { return (FormatManager*) mFormatMgr; } + + private: + int open(); + + int close(); + }; +} + +#endif //V4L1_DEVICE_H_ + diff --git a/extra_lib/include/avcap/linux/V4L1_DeviceDescriptor.h b/extra_lib/include/avcap/linux/V4L1_DeviceDescriptor.h new file mode 100644 index 0000000..f555312 --- /dev/null +++ b/extra_lib/include/avcap/linux/V4L1_DeviceDescriptor.h @@ -0,0 +1,132 @@ +/* + * (c) 2005, 2008 Nico Pranke , Robin Luedtke + * + * This file is part of avcap. + * + * avcap is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * avcap is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with avcap. If not, see . + */ + +/* avcap is free for non-commercial use. + * To use it in commercial endeavors, please contact Nico Pranke . + */ + + +#ifndef V4L1_DEVICEDESCRIPTOR_H_ +#define V4L1_DEVICEDESCRIPTOR_H_ + +#include + +#include "DeviceDescriptor.h" + +namespace avcap +{ +class CaptureDevice; +class V4L1_Device; + + //! This class uniquely identifies a Video4Linux1 capture device. + + class V4L1_DeviceDescriptor : public DeviceDescriptor + { + public: + struct bounds { + unsigned int minwidth, minheight, maxwidth, maxheight; + }; + + private: + std::string mName; + std::string mDriver; + std::string mCard; + std::string mInfo; + std::string mVersionString; + + int mVersion; + + DEV_HANDLE_T mHandle; + bool mValid; + + // Video4Linux1 specific data + int mType; + bounds mBounds; + int mAudios; + int mChannels; + bool mIsStreamingDev; + + V4L1_Device* mDevice; + + public: + V4L1_DeviceDescriptor(const std::string &name); + + virtual ~V4L1_DeviceDescriptor(); + + virtual CaptureDevice* getDevice(); + + virtual int open(); + + virtual int close(); + + virtual const std::string& getName() const; + + inline const std::string& getDriver() const + { return mDriver; } + + inline const std::string& getCard() const + { return mCard; } + + inline const std::string& getInfo() const + { return mInfo; } + + inline int getVersion() const + { return mVersion; } + + const std::string& getVersionString() const; + + inline const DEV_HANDLE_T getHandle() const + { return mHandle; } + + bool isAVDev() const; + + bool isVideoCaptureDev() const; + + bool isVBIDev() const; + + bool isTuner() const; + + bool isAudioDev() const; + + bool isRadioDev() const; + + bool isOverlayDev() const; + + bool isRWDev () const; + + bool isAsyncIODev() const; + + bool isStreamingDev() const; + + inline int getChannels() const + { return mChannels; } + + inline int getAudios() const + { return mAudios; } + + //! Returns a reference to the bounds of the video size. + inline bounds& getBounds() + { return mBounds; } + + private: + bool queryCapabilities(); + }; +}; + +#endif // V4L1_DEVICEDESCRIPTOR_H_ diff --git a/extra_lib/include/avcap/linux/V4L1_FormatManager.h b/extra_lib/include/avcap/linux/V4L1_FormatManager.h new file mode 100644 index 0000000..2fe7068 --- /dev/null +++ b/extra_lib/include/avcap/linux/V4L1_FormatManager.h @@ -0,0 +1,107 @@ +/* + * (c) 2005, 2008 Nico Pranke , Robin Luedtke + * + * This file is part of avcap. + * + * avcap is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * avcap is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with avcap. If not, see . + */ + +/* avcap is free for non-commercial use. + * To use it in commercial endeavors, please contact Nico Pranke . + */ + + +#ifndef V4L1_FORMATMANAGER_H_ +#define V4L1_FORMATMANAGER_H_ + +#include + +#include "FormatManager.h" + +namespace avcap +{ + class V4L1_DeviceDescriptor; + + //! This class implements the FormatManager for Video4Linux2 devices. */ + /*! This CaptureManager starts an own thread to capture data. + * The access to the internal buffer-list is synchronized, so \c release() can be called + * from any thread at any time. + */ + + class V4L1_FormatManager: public FormatManager + { + private: + struct fmtdesc { + char description[32]; + __u16 palette; + __u32 fourcc; + float sizefactor; + }; + + static const int mNumDescriptors = 18; + + static const fmtdesc mDescriptors[mNumDescriptors]; + + bool mIsOVFX2; + + public: + V4L1_FormatManager(V4L1_DeviceDescriptor *dd); + + virtual ~V4L1_FormatManager(); + + int setFormat(Format *fmt); + + int setFormat(unsigned int fourcc); + + Format* getFormat(); + + int setResolution(int w, int h); + + int setBytesPerLine(int bpl); + + int getWidth(); + + int getHeight(); + + int getBytesPerLine(); + + int flush(); + + size_t getImageSize(); + + const VideoStandard* getVideoStandard(); + + int setVideoStandard(const VideoStandard* std); + + //! Setting the frame-rate is currently only possible for some philips-cams + int setFramerate(int fps); + + int getFramerate(); + + __u16 getPalette(); + + void query(); + + private: + int getParams(); + + void queryResolutions(); + + __u16 getPalette(__u32 fourcc); + + int updateDimensions(); + }; +} + +#endif //V4L1_FORMATMANAGER_H_ diff --git a/extra_lib/include/avcap/linux/V4L1_VidCapManager.h b/extra_lib/include/avcap/linux/V4L1_VidCapManager.h new file mode 100644 index 0000000..f1e83ce --- /dev/null +++ b/extra_lib/include/avcap/linux/V4L1_VidCapManager.h @@ -0,0 +1,111 @@ +/* + * (c) 2005, 2008 Nico Pranke , Robin Luedtke + * + * This file is part of avcap. + * + * avcap is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * avcap is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with avcap. If not, see . + */ + +/* avcap is free for non-commercial use. + * To use it in commercial endeavors, please contact Nico Pranke . + */ + + +#ifndef V4L1_VIDCAPMANAGER_H_ +#define V4L1_VIDCAPMANAGER_H_ + +#include +#include +#include + +#include "CaptureManager.h" + +namespace avcap +{ + class V4L1_DeviceDescriptor; + class V4L1_FormatManager; + class IOBuffer; + class CaptureHandler; + + //! The Video4Linux2-API video capture manager. + + class V4L1_VidCapManager: public CaptureManager + { + private: + typedef std::list IOBufList_t; + typedef std::list IndexList_t; + + V4L1_DeviceDescriptor *mDeviceDescriptor; + V4L1_FormatManager *mFormatMgr; + + IOBufList_t mBuffers; + IndexList_t mCaptureIndices; + + int mNumBufs; + int mMethod; + int mState; + + pthread_t* mThread; + int mFinish; + pthread_mutex_t mLock; + timeval mStartTime; + + long mSequence; + + unsigned char* mVideobuf; + size_t mVideobufSize; + int mWidth; + int mHeight; + unsigned int mPalette; + int mAvailableBuffers; + + public: + + V4L1_VidCapManager(V4L1_DeviceDescriptor* dd, V4L1_FormatManager* fmt_mgr, int nbufs = DEFAULT_BUFFERS); + + virtual ~V4L1_VidCapManager(); + + int init(); + + int destroy(); + + int startCapture(); + + int stopCapture(); + + int getNumIOBuffers(); + + private: + int start_read(); + + int start_mmap(); + + int stop_read(); + + int stop_mmap(); + + IOBuffer* dequeue(); + + int enqueue(IOBuffer* buf); + + IOBuffer* findBuffer(int index); + + static void run(void* mgr); + + void clearBuffers(); + + int getUsedBufferCount(); + }; +}; +#endif // V4L1_VIDCAPMANAGER_H_ diff --git a/extra_lib/include/avcap/linux/V4L2_BoolControl.h b/extra_lib/include/avcap/linux/V4L2_BoolControl.h new file mode 100644 index 0000000..b713019 --- /dev/null +++ b/extra_lib/include/avcap/linux/V4L2_BoolControl.h @@ -0,0 +1,68 @@ +/* + * (c) 2005, 2008 Nico Pranke , Robin Luedtke + * + * This file is part of avcap. + * + * avcap is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * avcap is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with avcap. If not, see . + */ + +/* avcap is free for non-commercial use. + * To use it in commercial endeavors, please contact Nico Pranke . + */ + +#ifndef V4L2_BOOLCONTROL_H_ +#define V4L2_BOOLCONTROL_H_ + +#include "V4L2_ControlBase.h" + +namespace avcap +{ + class V4L2_DeviceDescriptor; + + //! A control for a Video4Linux2 device with one of the two values true (1) or false (0). + + class V4L2_BoolControl: public BoolControl + { + private: + V4L2_ControlBase mControlBase; + + public: + inline V4L2_BoolControl(V4L2_DeviceDescriptor *dd, struct v4l2_queryctrl* query): + mControlBase(dd, query) + {} + + virtual inline ~V4L2_BoolControl() + {} + + virtual inline int getId() const + { return mControlBase.getId(); } + + virtual inline int getDefaultValue() const + { return mControlBase.getDefaultValue(); } + + virtual inline const std::string& getName() const + { return mControlBase.getName(); } + + virtual inline int setValue(int val) + { return mControlBase.setValue(val); } + + virtual inline int getValue() const + { return mControlBase.getValue(); } + + virtual inline int reset() + { return mControlBase.reset(); } + }; +} + +#endif // V4L2_BOOLCONTROL_H_ diff --git a/extra_lib/include/avcap/linux/V4L2_ButtonControl.h b/extra_lib/include/avcap/linux/V4L2_ButtonControl.h new file mode 100644 index 0000000..ea589a5 --- /dev/null +++ b/extra_lib/include/avcap/linux/V4L2_ButtonControl.h @@ -0,0 +1,72 @@ +/* + * (c) 2005, 2008 Nico Pranke , Robin Luedtke + * + * This file is part of avcap. + * + * avcap is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * avcap is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with avcap. If not, see . + */ + +/* avcap is free for non-commercial use. + * To use it in commercial endeavors, please contact Nico Pranke . + */ + + +#ifndef V4L2_BUTTONCONTROL_H_ +#define V4L2_BUTTONCONTROL_H_ + +#include "V4L2_ControlBase.h" + +namespace avcap +{ + class V4L2_DeviceDescriptor; + + //! A control for a Video4Linux2 device that performs an action when set, independently from the value. + + class V4L2_ButtonControl: public ButtonControl + { + private: + V4L2_ControlBase mControlBase; + + public: + inline V4L2_ButtonControl(V4L2_DeviceDescriptor *dd, struct v4l2_queryctrl* query): + mControlBase(dd, query) + {} + + virtual inline ~V4L2_ButtonControl() + {} + + virtual inline int push() + { return setValue(0); } + + virtual inline int getId() const + { return mControlBase.getId(); } + + virtual inline int getDefaultValue() const + { return mControlBase.getDefaultValue(); } + + virtual inline const std::string& getName() const + { return mControlBase.getName(); } + + virtual inline int setValue(int val) + { return mControlBase.setValue(val); } + + virtual inline int getValue() const + { return mControlBase.getValue(); } + + virtual inline int reset() + { return mControlBase.reset(); } + }; +} + +#endif // V4L2_BUTTONCONTROL_H_ diff --git a/extra_lib/include/avcap/linux/V4L2_Connector.h b/extra_lib/include/avcap/linux/V4L2_Connector.h new file mode 100644 index 0000000..3927bf2 --- /dev/null +++ b/extra_lib/include/avcap/linux/V4L2_Connector.h @@ -0,0 +1,62 @@ +/* + * (c) 2005, 2008 Nico Pranke , Robin Luedtke + * + * This file is part of avcap. + * + * avcap is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * avcap is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with avcap. If not, see . + */ + +/* avcap is free for non-commercial use. + * To use it in commercial endeavors, please contact Nico Pranke . + */ + + +#ifndef V4L2_CONNECTOR_H_ +#define V4L2_CONNECTOR_H_ + +#include +#include + +#include +#include + +#include "Connector.h" + +namespace avcap +{ + // forward declaration + class V4L2_DeviceDescriptor; + class Tuner; + + //! This class implements Connector (a video/audio input or output) for a Video4Linux2 device. + + class V4L2_Connector : public Connector + { + private: + Tuner* mTuner; + + public: + V4L2_Connector(V4L2_DeviceDescriptor *dd, int index, const std::string& name, int type=0, int audioset=0, int tuner=0); + + virtual ~V4L2_Connector(); + + inline Tuner* getTuner() + { return mTuner; } + + inline bool hasTuner() const + { return mType & INPUT_TYPE_TUNER; } + }; +} + +#endif // V4L2_CONNECTOR_H_ diff --git a/extra_lib/include/avcap/linux/V4L2_ConnectorManager.h b/extra_lib/include/avcap/linux/V4L2_ConnectorManager.h new file mode 100644 index 0000000..7be974c --- /dev/null +++ b/extra_lib/include/avcap/linux/V4L2_ConnectorManager.h @@ -0,0 +1,69 @@ +/* + * (c) 2005, 2008 Nico Pranke , Robin Luedtke + * + * This file is part of avcap. + * + * avcap is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * avcap is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with avcap. If not, see . + */ + +/* avcap is free for non-commercial use. + * To use it in commercial endeavors, please contact Nico Pranke . + */ + + +#ifndef V4L2_CONNECTORMANAGER_H_ +#define V4L2_CONNECTORMANAGER_H_ + +#include + +#include "ConnectorManager.h" + +namespace avcap +{ + class V4L2_DeviceDescriptor; + + //! This class implements the ConnectorManager for Video4Linux2-devices. + + class V4L2_ConnectorManager: public ConnectorManager + { + public: + + V4L2_ConnectorManager(V4L2_DeviceDescriptor *dd); + + virtual ~V4L2_ConnectorManager(); + + Connector* getVideoInput(); + + int setVideoInput(Connector* c); + + Connector* getAudioInput(); + + int setAudioInput(Connector* c); + + Connector* getVideoOutput(); + + int setVideoOutput(Connector* c); + + Connector* getAudioOutput(); + + int setAudioOutput(Connector* c); + + void query(); + + private: + Connector* findByIndex(const ListType& l, int index); + }; +} + +#endif // V4L2_CONNECTORMANAGER_H_ diff --git a/extra_lib/include/avcap/linux/V4L2_ControlBase.h b/extra_lib/include/avcap/linux/V4L2_ControlBase.h new file mode 100644 index 0000000..806574c --- /dev/null +++ b/extra_lib/include/avcap/linux/V4L2_ControlBase.h @@ -0,0 +1,82 @@ +/* + * (c) 2005, 2008 Nico Pranke , Robin Luedtke + * + * This file is part of avcap. + * + * avcap is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * avcap is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with avcap. If not, see . + */ + +/* avcap is free for non-commercial use. + * To use it in commercial endeavors, please contact Nico Pranke . + */ + +#ifndef V4L2_CONTROLBASE_H_ +#define V4L2_CONTROLBASE_H_ + +#include +#include +#include + +namespace avcap +{ + // forward declaration + class V4L2_DeviceDescriptor; + + //! Basic implementation of a device control for a Video4Linux2 device. + + /*! Implements common methods of V4L2-controls. */ + + class V4L2_ControlBase + { + protected: + V4L2_DeviceDescriptor *mDeviceDescriptor; + int mId; + int mValue; + + private: + std::string mName; + int mDefaultValue; + int mFlags; + + public: + V4L2_ControlBase(V4L2_DeviceDescriptor *dd, struct v4l2_queryctrl* query); + + virtual ~V4L2_ControlBase(); + + inline int getId() const + { return mId; } + + inline int getDefaultValue() const + { return mDefaultValue; } + + inline const std::string& getName() const + { return mName; } + + virtual int setValue(int val); + + virtual int getValue() const; + + virtual int reset(); + + //! Return the flags of the v4l2_queryctrl structure associated with the control. + __u32 getFlags() const { return mFlags; } + + protected: + // updates the value of the control + int update(); + }; +} + +#endif // V4L2_CONTROLBASE_H_ + diff --git a/extra_lib/include/avcap/linux/V4L2_ControlManager.h b/extra_lib/include/avcap/linux/V4L2_ControlManager.h new file mode 100644 index 0000000..2b3e5ed --- /dev/null +++ b/extra_lib/include/avcap/linux/V4L2_ControlManager.h @@ -0,0 +1,55 @@ +/* + * (c) 2005, 2008 Nico Pranke , Robin Luedtke + * + * This file is part of avcap. + * + * avcap is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * avcap is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with avcap. If not, see . + */ + +/* avcap is free for non-commercial use. + * To use it in commercial endeavors, please contact Nico Pranke . + */ + + +#ifndef V4L2_CONTROLMANAGER_H_ +#define V4L2_CONTROLMANAGER_H_ + +#include +#include + +#include "ControlManager.h" + +namespace avcap +{ + class V4L2_DeviceDescriptor; + + //! Implementation of the ControlManager for Video4Linux2 devices. + + class V4L2_ControlManager:public ControlManager + { + public: + V4L2_ControlManager(V4L2_DeviceDescriptor *dd); + + virtual ~V4L2_ControlManager(); + + void query(); + + private: + void query(int start_id, int end_id); + + bool queryExtended(); + }; +} + +#endif // V4L2_CONTROLMANAGER_H_ diff --git a/extra_lib/include/avcap/linux/V4L2_CtrlClassControl.h b/extra_lib/include/avcap/linux/V4L2_CtrlClassControl.h new file mode 100644 index 0000000..c4a37f1 --- /dev/null +++ b/extra_lib/include/avcap/linux/V4L2_CtrlClassControl.h @@ -0,0 +1,68 @@ +/* + * (c) 2005, 2008 Nico Pranke , Robin Luedtke + * + * This file is part of avcap. + * + * avcap is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * avcap is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with avcap. If not, see . + */ + +/* avcap is free for non-commercial use. + * To use it in commercial endeavors, please contact Nico Pranke . + */ + +#ifndef V4L2_CTRLCLASS_CONTROL_H_ +#define V4L2_CTRLCLASS_CONTROL_H_ + +#include "V4L2_ControlBase.h" + +namespace avcap +{ + class V4L2_DeviceDescriptor; + + //! A control for a Video4Linux2 device naming the control-class of the subsequent enumerated controls. + + class V4L2_CtrlClassControl: public CtrlClassControl + { + private: + V4L2_ControlBase mControlBase; + + public: + inline V4L2_CtrlClassControl(V4L2_DeviceDescriptor *dd, struct v4l2_queryctrl* query): + mControlBase(dd, query) + {} + + virtual inline ~V4L2_CtrlClassControl() + {} + + virtual inline int getId() const + { return mControlBase.getId(); } + + virtual inline int getDefaultValue() const + { return 0; } + + virtual inline const std::string& getName() const + { return mControlBase.getName(); } + + virtual inline int setValue(int val) + { return 0; } + + virtual inline int getValue() const + { return 0; } + + virtual inline int reset() + { return 0; } + }; +} + +#endif // V4L2_CTRLCLASS_CONTROL_H_ diff --git a/extra_lib/include/avcap/linux/V4L2_Device.h b/extra_lib/include/avcap/linux/V4L2_Device.h new file mode 100644 index 0000000..ddd0b90 --- /dev/null +++ b/extra_lib/include/avcap/linux/V4L2_Device.h @@ -0,0 +1,84 @@ +/* + * (c) 2005, 2008 Nico Pranke , Robin Luedtke + * + * This file is part of avcap. + * + * avcap is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * avcap is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with avcap. If not, see . + */ + +/* avcap is free for non-commercial use. + * To use it in commercial endeavors, please contact Nico Pranke . + */ + + +#ifndef V4L2_DEVICE_H_ +#define V4L2_DEVICE_H_ + +#include +#include + +#include "V4L2_DeviceDescriptor.h" +#include "CaptureDevice.h" + +namespace avcap +{ + // forward declarations + class V4L2_ConnectorManager; + class V4L2_ControlManager; + class V4L2_VidCapManager; + class V4L2_DeviceDescriptor; + class V4L2_FormatManager; + + //! Implementation of the CaptureDevice for Video4Linux2 devices. */ + + class V4L2_Device : public CaptureDevice + { + public: + + private: + V4L2_VidCapManager *mVidCapMgr; + V4L2_ConnectorManager *mConnectorMgr; + V4L2_ControlManager *mControlMgr; + V4L2_FormatManager *mFormatMgr; + V4L2_DeviceDescriptor *mDeviceDescriptor; + + public: + V4L2_Device(V4L2_DeviceDescriptor* dd); + + virtual ~V4L2_Device(); + + inline const DeviceDescriptor* getDescriptor() + { return mDeviceDescriptor; } + + inline CaptureManager* getVidCapMgr() + { return (CaptureManager*) mVidCapMgr; } + + inline ConnectorManager* getConnectorMgr() + { return (ConnectorManager*) mConnectorMgr; } + + inline ControlManager* getControlMgr() + { return (ControlManager*) mControlMgr; } + + inline FormatManager* getFormatMgr() + { return (FormatManager*) mFormatMgr; } + + private: + int open(); + + int close(); + }; +} + +#endif // V4L2_DEVICE_H_ + diff --git a/extra_lib/include/avcap/linux/V4L2_DeviceDescriptor.h b/extra_lib/include/avcap/linux/V4L2_DeviceDescriptor.h new file mode 100644 index 0000000..99f66f8 --- /dev/null +++ b/extra_lib/include/avcap/linux/V4L2_DeviceDescriptor.h @@ -0,0 +1,112 @@ +/* + * (c) 2005, 2008 Nico Pranke , Robin Luedtke + * + * This file is part of avcap. + * + * avcap is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * avcap is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with avcap. If not, see . + */ + +/* avcap is free for non-commercial use. + * To use it in commercial endeavors, please contact Nico Pranke . + */ + +#ifndef V4L2_DEVICEDESCRIPTOR_H_ +#define V4L2_DEVICEDESCRIPTOR_H_ + +#include + +#include "DeviceDescriptor.h" + +// IVTV driver name +#define DRIVER_IVTV "ivtv" + +namespace avcap +{ +class CaptureDevice; +class V4L2_Device; + + //! This class uniquely identifies a Video4Linux2 capture device. + + class V4L2_DeviceDescriptor : public DeviceDescriptor + { + private: + std::string mName; + std::string mDriver; + std::string mCard; + std::string mInfo; + std::string mVersionString; + + int mVersion; + int mCapabilities; + + DEV_HANDLE_T mHandle; + bool mValid; + V4L2_Device* mDevice; + + public: + V4L2_DeviceDescriptor(const std::string &name); + + virtual ~V4L2_DeviceDescriptor(); + + virtual CaptureDevice* getDevice(); + + int open(); + + int close(); + + virtual const std::string& getName() const; + + inline const std::string& getDriver() const + { return mDriver; } + + inline const std::string& getCard() const + { return mCard; } + + inline const std::string& getInfo() const + { return mInfo; } + + inline int getVersion() const + { return mVersion; } + + const std::string& getVersionString() const; + + inline const DEV_HANDLE_T getHandle() const + { return mHandle; } + + bool isAVDev() const; + + bool isVideoCaptureDev() const; + + bool isVBIDev() const; + + bool isTuner() const; + + bool isAudioDev() const; + + bool isRadioDev() const; + + bool isOverlayDev() const; + + bool isRWDev () const; + + bool isAsyncIODev() const; + + bool isStreamingDev() const; + + private: + bool queryCapabilities(); + }; +} + +#endif // V4L2_DEVICEDESCRIPTOR_H_ diff --git a/extra_lib/include/avcap/linux/V4L2_FormatManager.h b/extra_lib/include/avcap/linux/V4L2_FormatManager.h new file mode 100644 index 0000000..d729c42 --- /dev/null +++ b/extra_lib/include/avcap/linux/V4L2_FormatManager.h @@ -0,0 +1,85 @@ +/* + * (c) 2005, 2008 Nico Pranke , Robin Luedtke + * + * This file is part of avcap. + * + * avcap is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * avcap is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with avcap. If not, see . + */ + +/* avcap is free for non-commercial use. + * To use it in commercial endeavors, please contact Nico Pranke . + */ + +#ifndef V4L2_FORMATMANAGER_H_ +#define V4L2_FORMATMANAGER_H_ + +#include "FormatManager.h" + +namespace avcap +{ + class V4L2_DeviceDescriptor; + + //! This class implements the FormatManager for Video4Linux2 devices. */ + + class V4L2_FormatManager: public FormatManager + { + + public: + V4L2_FormatManager(V4L2_DeviceDescriptor *dd); + + virtual ~V4L2_FormatManager(); + + int setFormat(Format *fmt); + + int setFormat(unsigned int fourcc); + + Format* getFormat(); + + int setResolution(int w, int h); + + int setBytesPerLine(int bpl); + + int getWidth(); + + int getHeight(); + + int getBytesPerLine(); + + int flush(); + + size_t getImageSize(); + + const VideoStandard* getVideoStandard(); + + int setVideoStandard(const VideoStandard* std); + + int setFramerate(int fps); + + int getFramerate(); + + void query(); + + private: + int tryFormat(); + + int getParams(); + + void queryVideoStandards(); + + void queryResolutions(Format* f); + + }; +} + +#endif // V4L2_FORMATMANAGER_H_ diff --git a/extra_lib/include/avcap/linux/V4L2_IntControl.h b/extra_lib/include/avcap/linux/V4L2_IntControl.h new file mode 100644 index 0000000..da07a18 --- /dev/null +++ b/extra_lib/include/avcap/linux/V4L2_IntControl.h @@ -0,0 +1,75 @@ +/* + * (c) 2005, 2008 Nico Pranke , Robin Luedtke + * + * This file is part of avcap. + * + * avcap is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * avcap is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with avcap. If not, see . + */ + +/* avcap is free for non-commercial use. + * To use it in commercial endeavors, please contact Nico Pranke . + */ + + +#ifndef V4L2_INTCONTROL_H_ +#define V4L2_INTCONTROL_H_ + +#include "V4L2_ControlBase.h" + +namespace avcap +{ + class V4L2_DeviceDescriptor; + + //! Implementation of an integer valued control for Video4Linux2 devices. + + class V4L2_IntControl: public IntegerControl + { + private: + V4L2_ControlBase mControlBase; + Interval mInterval; + + public: + inline V4L2_IntControl(V4L2_DeviceDescriptor *dd, struct v4l2_queryctrl* query): + mControlBase(dd, query), + mInterval(query->minimum, query->maximum, query->step) + {} + + virtual inline ~V4L2_IntControl() + {} + + inline const Interval& getInterval() const + { return mInterval; } + + virtual inline int getId() const + { return mControlBase.getId(); } + + virtual inline int getDefaultValue() const + { return mControlBase.getDefaultValue(); } + + virtual inline const std::string& getName() const + { return mControlBase.getName(); } + + virtual inline int setValue(int val) + { return mControlBase.setValue(val); } + + virtual inline int getValue() const + { return mControlBase.getValue(); } + + virtual inline int reset() + { return mControlBase.reset(); } + + }; +} + +#endif // V4L2_INTCONTROL_H_ diff --git a/extra_lib/include/avcap/linux/V4L2_MenuControl.h b/extra_lib/include/avcap/linux/V4L2_MenuControl.h new file mode 100644 index 0000000..c1182fb --- /dev/null +++ b/extra_lib/include/avcap/linux/V4L2_MenuControl.h @@ -0,0 +1,77 @@ +/* + * (c) 2005, 2008 Nico Pranke , Robin Luedtke + * + * This file is part of avcap. + * + * avcap is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * avcap is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with avcap. If not, see . + */ + +/* avcap is free for non-commercial use. + * To use it in commercial endeavors, please contact Nico Pranke . + */ + +#ifndef V4L2_MENUCONTROL_H_ +#define V4L2_MENUCONTROL_H_ + +#include "V4L2_ControlBase.h" +#include "Control_avcap.h" + +namespace avcap +{ + // forward declaration + class V4L2_DeviceDescriptor; + + //! A Control for a Video4Linux2 device which has various menu-like items to choose from. + + class V4L2_MenuControl: public MenuControl + { + private: + V4L2_ControlBase mControlBase; + ItemList mMenuItems; + V4L2_DeviceDescriptor* mDeviceDescriptor; + + public: + //! The constructor. + V4L2_MenuControl(V4L2_DeviceDescriptor *dd, struct v4l2_queryctrl* query); + + //! The destructor. + virtual ~V4L2_MenuControl(); + + virtual inline int getId() const + { return mControlBase.getId(); } + + virtual inline int getDefaultValue() const + { return mControlBase.getDefaultValue(); } + + virtual inline const std::string& getName() const + { return mControlBase.getName(); } + + virtual inline int setValue(int val) + { return mControlBase.setValue(val); } + + virtual inline int getValue() const + { return mControlBase.getValue(); } + + virtual inline int reset() + { return mControlBase.reset(); } + + virtual inline const ItemList& getItemList () + { return mMenuItems; } + + private: + void queryMenuItems(); + }; +} + +#endif //V4L2_MENUCONTROL_H_ diff --git a/extra_lib/include/avcap/linux/V4L2_Tuner.h b/extra_lib/include/avcap/linux/V4L2_Tuner.h new file mode 100644 index 0000000..043acb4 --- /dev/null +++ b/extra_lib/include/avcap/linux/V4L2_Tuner.h @@ -0,0 +1,120 @@ +/* + * (c) 2005, 2008 Nico Pranke , Robin Luedtke + * + * This file is part of avcap. + * + * avcap is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * avcap is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with avcap. If not, see . + */ + +/* avcap is free for non-commercial use. + * To use it in commercial endeavors, please contact Nico Pranke . + */ + + +#ifndef V4L2_TUNER_H_ +#define V4L2_TUNER_H_ + +#include +#include +#include +#include + +#include "Tuner_avcap.h" + +namespace avcap +{ + class V4L2_DeviceDescriptor; + + //! Implementation of the Tuner class for Video4Linux2 -devices. + + class V4L2_Tuner: public Tuner + { + private: + + enum { + TUNER_RADIO = V4L2_TUNER_RADIO, + TUNER_ANALOG_TV = V4L2_TUNER_ANALOG_TV, + }; + + enum { + TUNER_CAP_LOW = V4L2_TUNER_CAP_LOW + }; + + typedef __u32 uint; + + private: + V4L2_DeviceDescriptor* mDeviceDescriptor; + int mIndex; + std::string mName; + int mType; + int mCapabilities; + uint mRangeHigh; + uint mRangeLow; + double mStep; + + public: + V4L2_Tuner(V4L2_DeviceDescriptor *dd, int index, const std::string &name, int type, int caps, uint high, uint low); + virtual ~V4L2_Tuner(); + + inline bool isRadioTuner() const + { return mType & TUNER_RADIO; } + + inline bool isTVTuner() const + { return mType & TUNER_ANALOG_TV; }; + + int setStereo(); + + int setMono(); + + int setSAP(); + + int setLang1(); + + int setLang2(); + + double getFreq() const; + + inline double getFreqStep() const + { return mStep; } + + inline double getMinFreq() const + { return mRangeLow*mStep; } + + inline double getMaxFreq() const + { return mRangeHigh*mStep; } + + inline const std::string getName() const + { return mName; } + + int finetune(int maxsteps); + + int getAFCValue() const; + + int getSignalStrength() const; + + int increaseFreq(); + + int decreaseFreq(); + + int setFreq(double f); + + private: + int setAudioMode(int mode); + + int getAudioMode(); + }; +} + +#endif // V4L2_TUNER_H_ + diff --git a/extra_lib/include/avcap/linux/V4L2_VidCapManager.h b/extra_lib/include/avcap/linux/V4L2_VidCapManager.h new file mode 100644 index 0000000..63d004a --- /dev/null +++ b/extra_lib/include/avcap/linux/V4L2_VidCapManager.h @@ -0,0 +1,125 @@ +/* + * (c) 2005, 2008 Nico Pranke , Robin Luedtke + * + * This file is part of avcap. + * + * avcap is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * avcap is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with avcap. If not, see . + */ + +/* avcap is free for non-commercial use. + * To use it in commercial endeavors, please contact Nico Pranke . + */ + + +#ifndef V4L2_VIDCAPMANAGER_H_ +#define V4L2_VIDCAPMANAGER_H_ + +#include +#include +#include + +#include "CaptureManager.h" + +namespace avcap +{ + class V4L2_DeviceDescriptor; + class FormatManager; + class IOBuffer; + class CaptureHandler; + + //! The Video4Linux2-API video capture manager. + + /*! This class can be used to capture video data from a V4L2 video device. + * The manager creates a defined number + * of IOBuffers and permanently reuses them to store the captured data. + * Since the number of these buffers is finite, it is important that applications + * call IOBuffer::release() as soon as they don't need the data anymore. The + * access to the internal buffer-list is synchronized, so \c release() can be called + * from any thread at any time. + * Typical applications don't create objects of this class directly. They obtain + * an instance from CaptureDevice. */ + + class V4L2_VidCapManager: public CaptureManager + { + public: + enum + { + MAX_BUFFERS = 32, //!< The maximum number of IOBuffers. + DEFAULT_BUFFERS = 16 //!< The default number of used IOBuffers. + }; + + enum IOMethod + { + IO_METHOD_NOCAP = 0, + IO_METHOD_READ, + IO_METHOD_MMAP, + IO_METHOD_USERPTR, + }; + + private: + typedef std::list IOBufList; + + V4L2_DeviceDescriptor *mDeviceDescriptor; + FormatManager *mFormatMgr; + + IOBufList mBuffers; + int mNumBufs; + int mMethod; + int mState; + pthread_t* mThread; + int mFinish; + pthread_mutex_t mLock; + timeval mStartTime; + + int mSequence; + int mDTNumerator; + int mDTDenominator; + int mAvailableBuffers; + + public: + V4L2_VidCapManager(V4L2_DeviceDescriptor* dd, FormatManager* fmt_mgr, int nbufs = DEFAULT_BUFFERS); + + virtual ~V4L2_VidCapManager(); + + int init(); + + int destroy(); + + int startCapture(); + + int stopCapture(); + + int getNumIOBuffers(); + + private: + int start_read(); + int start_mmap(); + int start_userptr(); + + int stop_read(); + int stop_mmap(); + int stop_userptr(); + + IOBuffer* dequeue(); + int enqueue(IOBuffer* buf); + + IOBuffer* findBuffer(int index); + static void run(void* mgr); + + void clearBuffers(); + int getUsedBufferCount(); + }; +} + +#endif // V4L2_VIDCAPMANAGER_H_ diff --git a/extra_lib/include/avcap/linux/error.h b/extra_lib/include/avcap/linux/error.h new file mode 100644 index 0000000..217a25a --- /dev/null +++ b/extra_lib/include/avcap/linux/error.h @@ -0,0 +1,44 @@ +/* +* error.h Error handling +* Copyright (C) 2000 - 2002 Arne Schirmacher +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifdef HAS_AVC_SUPPORT + +#ifndef _ERROR_H +#define _ERROR_H 1 + +#ifdef __cplusplus +extern "C" +{ +#endif + +#define fail_neg(eval) real_fail_neg (eval, #eval, __ASSERT_FUNCTION, __FILE__, __LINE__) +#define fail_null(eval) real_fail_null (eval, #eval, __ASSERT_FUNCTION, __FILE__, __LINE__) +#define fail_if(eval) real_fail_if (eval, #eval, __ASSERT_FUNCTION, __FILE__, __LINE__) + + void real_fail_neg ( int eval, const char * eval_str, const char * func, const char * file, int line ); + void real_fail_null ( const void * eval, const char * eval_str, const char * func, const char * file, int line ); + void real_fail_if ( bool eval, const char * eval_str, const char * func, const char * file, int line ); + +#ifdef __cplusplus +} +#endif + +#endif +#endif + diff --git a/extra_lib/include/avcap/linux/frame.h b/extra_lib/include/avcap/linux/frame.h new file mode 100644 index 0000000..1a4cd0b --- /dev/null +++ b/extra_lib/include/avcap/linux/frame.h @@ -0,0 +1,161 @@ +/* +* frame.h -- utilities for process digital video frames +* Copyright (C) 2000 Arne Schirmacher +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifdef HAS_AVC_SUPPORT +#define HAVE_LIBDV + +#ifndef _FRAME_H +#define _FRAME_H 1 + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +using std::string; + +#include +#include +#define DV_AUDIO_MAX_SAMPLES 1944 + +#define FRAME_MAX_WIDTH 720 +#define FRAME_MAX_HEIGHT 576 + +namespace avcap +{ + +typedef struct Pack +{ + /// the five bytes of a packet + unsigned char data[ 5 ]; +} +Pack; + +typedef struct TimeCode +{ + int hour; + int min; + int sec; + int frame; +} +TimeCode; + + +typedef struct AudioInfo +{ + int frames; + int frequency; + int samples; + int channels; + int quantization; +} +AudioInfo; + + +class VideoInfo +{ +public: + int width; + int height; + bool isPAL; + TimeCode timeCode; + struct tm recDate; + + VideoInfo(); + + // string GetTimeCodeString(); + // string GetRecDateString(); +} +; + + +class Frame +{ +public: + /// enough space to hold a PAL frame + unsigned char data[ 144000 ]; + /// the number of bytes written to the frame + int bytesInFrame; +#ifdef HAVE_LIBDV + + dv_decoder_t *decoder; +#endif + + int16_t *audio_buffers[ 4 ]; + +public: + Frame(); + ~Frame(); + + bool GetSSYBPack( int packNum, Pack &pack ) const; + bool GetVAUXPack( int packNum, Pack &pack ) const; + bool GetAAUXPack( int packNum, Pack &pack ) const; + bool GetTimeCode( TimeCode &timeCode ) const; + bool GetRecordingDate( struct tm &recDate ) const; + string GetRecordingDate( void ) const; + bool GetAudioInfo( AudioInfo &info ) const; + bool GetVideoInfo( VideoInfo &info ) const; + int GetFrameSize( void ) const; + float GetFrameRate() const; + bool IsPAL( void ) const; + bool IsNewRecording( void ) const; + bool IsNormalSpeed() const; + bool IsComplete( void ) const; + int ExtractAudio( void *sound ) const; + void ExtractHeader( void ); + +#ifdef HAVE_LIBDV + + void SetPreferredQuality( ); + int ExtractAudio( int16_t **channels ) const; + int ExtractRGB( void *rgb ); + int ExtractPreviewRGB( void *rgb ); + int ExtractYUV( void *yuv ); + int ExtractPreviewYUV( void *yuv ); + bool IsWide( void ) const; + int GetWidth(); + int GetHeight(); + void SetRecordingDate( time_t *datetime, int frame ); + void SetTimeCode( int frame ); +#endif + + void Deinterlace( void *image, int bpp ); + +private: +#ifndef HAVE_LIBDV + /// flag for initializing the lookup maps once at startup + static bool maps_initialized; + /// lookup tables for collecting the shuffled audio data + static int palmap_ch1[ 2000 ]; + static int palmap_ch2[ 2000 ]; + static int palmap_2ch1[ 2000 ]; + static int palmap_2ch2[ 2000 ]; + static int ntscmap_ch1[ 2000 ]; + static int ntscmap_ch2[ 2000 ]; + static int ntscmap_2ch1[ 2000 ]; + static int ntscmap_2ch2[ 2000 ]; + static short compmap[ 4096 ]; +#endif +}; + +} + +#endif +#endif diff --git a/extra_lib/include/avcap/linux/ieee1394io.h b/extra_lib/include/avcap/linux/ieee1394io.h new file mode 100644 index 0000000..7b2792f --- /dev/null +++ b/extra_lib/include/avcap/linux/ieee1394io.h @@ -0,0 +1,188 @@ +/* +* ieee1394io.cc -- asynchronously grabbing DV data +* Copyright (C) 2000 Arne Schirmacher +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifdef HAS_AVC_SUPPORT + +#ifndef _IEEE1394IO_H +#define _IEEE1394IO_H 1 + +#include +#include +#include + + +#include +using std::string; +#include +using std::deque; + +namespace avcap +{ + +class Frame; + +class IEEE1394Reader +{ +protected: + /// the number of frames that had to be thrown away because + /// our inFrames queue did not contain available frames + int droppedFrames; + + /// the number of frames that are tainted because they are incomplete - + /// a packet was dropped + int incompleteFrames; + + /// a pointer to the frame which is currently been transmitted + Frame *currentFrame; + + /// a list of empty frames + deque < Frame* > inFrames; + + /// a list of already received frames + deque < Frame* > outFrames; + +public: + + IEEE1394Reader( int channel = 63, int frames = 5 ); + virtual ~IEEE1394Reader(); + + // Mutex protected public methods + virtual bool StartThread( void ) = 0; + virtual void StopThread( void ) = 0; + Frame* GetFrame( void ); + + void DoneWithFrame( Frame* ); + int GetDroppedFrames( void ); + int GetIncompleteFrames( void ); + int GetOutQueueSize( void ) + { + return outFrames.size(); + } + int GetInQueueSize( void ) + { + return inFrames.size(); + } + + // These two public methods are not mutex protected + virtual bool Open( void ) = 0; + virtual void Close( void ) = 0; + + bool WaitForAction( int seconds = 0 ); + virtual void TriggerAction( ); + + virtual bool StartReceive( void ) = 0; + virtual void StopReceive( void ) = 0; + +protected: + /// the iso channel we listen to (typically == 63) + int channel; + + /// contains information about our thread after calling StartThread + pthread_t thread; + + /// this mutex protects capture related variables that could possibly + /// accessed from two threads at the same time + pthread_mutex_t mutex; + + // This condition and mutex are used to indicate when new frames are + // received + pthread_mutex_t condition_mutex; + pthread_cond_t condition; + + /// A state variable for starting and stopping thread + bool isRunning; + + void Flush( void ); +}; + + +typedef void (*BusResetHandler)( void* ); +typedef void* BusResetHandlerData; + +class CaptureHandler; + +class iec61883Reader: public IEEE1394Reader +{ +private: + + /// the interface card to use (typically == 0) + int m_port; + + /// the handle to libraw1394 + raw1394handle_t m_handle; + + /// the handle to libiec61883 + iec61883_dv_fb_t m_iec61883dv; + + BusResetHandler m_resetHandler; + const void* m_resetHandlerData; + + CaptureHandler* m_captureHandler; + +public: + iec61883Reader( int port = 0, int channel = 63, int buffers = 5, + BusResetHandler = 0, BusResetHandlerData = 0 ); + ~iec61883Reader(); + + bool Open( void ); + void Close( void ); + bool StartReceive( void ); + void StopReceive( void ); + bool StartThread( void ); + void StopThread( void ); + int Handler( int length, int complete, unsigned char *data ); + void *Thread(); + void ResetHandler( void ); + + +private: + static int ResetHandlerProxy( raw1394handle_t handle, unsigned int generation ); + static int HandlerProxy( unsigned char *data, int length, int complete, + void *callback_data ); + static void* ThreadProxy( void *arg ); +}; + + +class iec61883Connection +{ +private: + raw1394handle_t m_handle; + nodeid_t m_node; + int m_channel; + int m_bandwidth; + int m_outputPort; + int m_inputPort; + +public: + + iec61883Connection( int port, int node ); + ~iec61883Connection(); + + static void CheckConsistency( int port, int node ); + int GetChannel( void ) const + { + return m_channel; + } + int Reconnect( void ); +}; + +} + +#endif +#endif diff --git a/extra_lib/include/avcap/linux/ivtv.h b/extra_lib/include/avcap/linux/ivtv.h new file mode 100644 index 0000000..55bb9de --- /dev/null +++ b/extra_lib/include/avcap/linux/ivtv.h @@ -0,0 +1,397 @@ +/* + Public ivtv API header + Copyright (C) 2003-2004 Kevin Thayer + + VBI portions: + Copyright (C) 2004 Hans Verkuil + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef LINUX_IVTV_H +#define LINUX_IVTV_H + +// 00 = Program stream +// 01 = Transport stream +// 02 = MPEG1 stream +// 10 or 0a = DVD stream +// 11 or 0b = VCD stream +// 12 or 0c = SVCD stream +// 13 or 0d = DVD-Special 1 +// 14 or 0e = DVD-Special 2 + +/* Stream types */ +#define IVTV_STREAM_PS 0 +#define IVTV_STREAM_TS 1 +#define IVTV_STREAM_MPEG1 2 +#define IVTV_STREAM_PES_AV 3 +#define IVTV_STREAM_PES_V 5 +#define IVTV_STREAM_PES_A 7 +#define IVTV_STREAM_DVD 10 +#define IVTV_STREAM_VCD 11 +#define IVTV_STREAM_SVCD 12 +#define IVTV_STREAM_DVD_S1 13 +#define IVTV_STREAM_DVD_S2 14 + +#define IVTV_SLICED_TELETEXT_B (1 << 0) +#define IVTV_SLICED_CAPTION_625 (1 << 1) +#define IVTV_SLICED_CAPTION_525 (1 << 2) +#define IVTV_SLICED_WSS_625 (1 << 3) +#define IVTV_SLICED_VPS (1 << 4) + +struct ivtv_sliced_vbi_format { + unsigned long service_set; /* one or more of the IVTV_SLICED_ defines */ + unsigned long packet_size; /* the size in bytes of the ivtv_sliced_data packet */ + unsigned long io_size; /* maximum number of bytes passed by one read() call */ + unsigned long reserved; +}; + +/* This structure is the same as the proposed v4l2_sliced_data structure */ +/* id is one of the VBI_SLICED_ flags. */ +struct ivtv_sliced_data { + unsigned long id; + unsigned long line; + unsigned char data[]; +}; + +/* The four bit VBI data type found in the embedded VBI data of an + MPEG stream has one of the following values: */ +#define VBI_TYPE_TELETEXT 0x1 // Teletext (uses lines 6-22 for PAL, 10-21 for NTSC) +#define VBI_TYPE_CC 0x4 // Closed Captions (line 21 NTSC, line 22 PAL) +#define VBI_TYPE_WSS 0x5 // Wide Screen Signal (line 20 NTSC, line 23 PAL) +#define VBI_TYPE_VPS 0x7 // Video Programming System (PAL) (line 16) + +/* These data types are not (yet?) used but are already reserved + for future use. */ +#ifdef IVTV_INTERNAL +#define VBI_TYPE_NABST 0x2 // NABST (NTSC) +#define VBI_TYPE_MOJI 0x3 // MOJI (NTSC) +#define VBI_TYPE_VITC 0x6 // Vertical Interval Time Code +#define VBI_TYPE_GEMSTAR2X 0x7 // Gemstar TV Guide (NTSC) +#define VBI_TYPE_GEMSTAR1X 0x8 // Gemstar TV Guide (NTSC) +#endif + +/* device ioctls should use the range 29-199 */ +#define IVTV_IOC_START_DECODE _IOW ('@', 29, struct ivtv_cfg_start_decode) +#define IVTV_IOC_STOP_DECODE _IOW ('@', 30, struct ivtv_cfg_stop_decode) +#define IVTV_IOC_G_SPEED _IOR ('@', 31, struct ivtv_speed) +#define IVTV_IOC_S_SPEED _IOW ('@', 32, struct ivtv_speed) +#define IVTV_IOC_DEC_STEP _IOW ('@', 33, int) +#define IVTV_IOC_DEC_FLUSH _IOW ('@', 34, int) +#define IVTV_IOC_S_VBI_MODE _IOWR('@', 35, struct ivtv_sliced_vbi_format) +#define IVTV_IOC_G_VBI_MODE _IOR ('@', 36, struct ivtv_sliced_vbi_format) +#define IVTV_IOC_PLAY _IO ('@', 37) +#define IVTV_IOC_PAUSE _IO ('@', 38) +#define IVTV_IOC_FRAMESYNC _IOR ('@', 39, struct ivtv_ioctl_framesync) +#define IVTV_IOC_GET_TIMING _IOR ('@', 40, struct ivtv_ioctl_framesync) +#define IVTV_IOC_S_SLOW_FAST _IOW ('@', 41, struct ivtv_slow_fast) +#define IVTV_IOC_S_START_DECODE _IOW ('@', 42, struct ivtv_cfg_start_decode) +#define IVTV_IOC_S_STOP_DECODE _IOW ('@', 43, struct ivtv_cfg_stop_decode) +#define IVTV_IOC_GET_FB _IOR ('@', 44, int) +#define IVTV_IOC_G_CODEC _IOR ('@', 48, struct ivtv_ioctl_codec) +#define IVTV_IOC_S_CODEC _IOW ('@', 49, struct ivtv_ioctl_codec) +#define IVTV_IOC_S_GOP_END _IOWR('@', 50, int) +#define IVTV_IOC_S_VBI_PASSTHROUGH _IOW ('@', 51, int) +#define IVTV_IOC_G_VBI_PASSTHROUGH _IOR ('@', 52, int) +#define IVTV_IOC_PASSTHROUGH _IOW ('@', 53, int) +#define IVTV_IOC_S_VBI_EMBED _IOW ('@', 54, int) +#define IVTV_IOC_G_VBI_EMBED _IOR ('@', 55, int) +#define IVTV_IOC_PAUSE_ENCODE _IO ('@', 56) +#define IVTV_IOC_RESUME_ENCODE _IO ('@', 57) + +#define PACK_ME __attribute__((packed)) +// Note: You only append to this structure, you never reorder the members, +// you never play tricks with its alignment, you never change the size of +// anything. +#define IVTV_DRIVER_INFO_MAX_COMMENT_LENGTH 100 +struct ivtv_driver_info { + uint32_t size; // size of this structure + uint32_t version; // version bits 31-16 = major, 15-8 = minor, + // 7-0 = patchlevel + char comment[IVTV_DRIVER_INFO_MAX_COMMENT_LENGTH]; +} PACK_ME; + +#define IVTV_DRIVER_INFO_V1_SIZE 108 + +#define IVTV_IOC_G_DRIVER_INFO _IOWR('@', 100, struct ivtv_driver_info *) + +// Version info +// Note: never use the _INTERNAL versions of these macros + +// Internal version macros, don't use these +#define IVTV_VERSION_NUMBER_INTERNAL(name) name##_version_int +#define IVTV_VERSION_STRING_INTERNAL(name) name##_version_string +#define IVTV_VERSION_COMMENT_INTERNAL(name) name##_comment_string + +#define IVTV_VERSION_EXTERN_NUMBER_INTERNAL(name) \ + extern uint32_t IVTV_VERSION_NUMBER_INTERNAL(name) +#define IVTV_VERSION_EXTERN_STRING_INTERNAL(name) \ + extern const char * const IVTV_VERSION_STRING_INTERNAL(name) +#define IVTV_VERSION_EXTERN_COMMENT_INTERNAL(name) \ + extern const char * const IVTV_VERSION_COMMENT_INTERNAL(name) + +#define IVTV_VERSION_MAJOR_INTERNAL(name) \ + (0xFF & (IVTV_VERSION_NUMBER_INTERNAL(name) >> 16)) +#define IVTV_VERSION_MINOR_INTERNAL(name) \ + (0xFF & (IVTV_VERSION_NUMBER_INTERNAL(name) >> 8)) +#define IVTV_VERSION_PATCHLEVEL_INTERNAL(name) \ + (0xFF & (IVTV_VERSION_NUMBER_INTERNAL(name))) + +// External version macros +#define IVTV_VERSION_NUMBER(name) IVTV_VERSION_NUMBER_INTERNAL(name) +#define IVTV_VERSION_STRING(name) IVTV_VERSION_STRING_INTERNAL(name) +#define IVTV_VERSION_COMMENT(name) IVTV_VERSION_COMMENT_INTERNAL(name) +#define IVTV_VERSION_EXTERN_NUMBER(name) \ + IVTV_VERSION_EXTERN_NUMBER_INTERNAL(name) +#define IVTV_VERSION_EXTERN_STRING(name) \ + IVTV_VERSION_EXTERN_STRING_INTERNAL(name) +#define IVTV_VERSION_EXTERN_COMMENT(name) \ + IVTV_VERSION_EXTERN_COMMENT_INTERNAL(name) + +#define IVTV_VERSION_INFO_NAME ivtv_rev + +IVTV_VERSION_EXTERN_NUMBER(IVTV_VERSION_INFO_NAME); +IVTV_VERSION_EXTERN_STRING(IVTV_VERSION_INFO_NAME); +IVTV_VERSION_EXTERN_COMMENT(IVTV_VERSION_INFO_NAME); + +/* Custom v4l controls */ +#ifndef V4L2_CID_PRIVATE_BASE +#define V4L2_CID_PRIVATE_BASE 0x08000000 +#endif + +#define V4L2_CID_IVTV_FREQ (V4L2_CID_PRIVATE_BASE) +#define V4L2_CID_IVTV_ENC (V4L2_CID_PRIVATE_BASE + 1) +#define V4L2_CID_IVTV_BITRATE (V4L2_CID_PRIVATE_BASE + 2) +#define V4L2_CID_IVTV_MONO (V4L2_CID_PRIVATE_BASE + 3) +#define V4L2_CID_IVTV_JOINT (V4L2_CID_PRIVATE_BASE + 4) +#define V4L2_CID_IVTV_EMPHASIS (V4L2_CID_PRIVATE_BASE + 5) +#define V4L2_CID_IVTV_CRC (V4L2_CID_PRIVATE_BASE + 6) +#define V4L2_CID_IVTV_COPYRIGHT (V4L2_CID_PRIVATE_BASE + 7) +#define V4L2_CID_IVTV_GEN (V4L2_CID_PRIVATE_BASE + 8) + +#define V4L2_CID_IVTV_DEC_SMOOTH_FF (V4L2_CID_PRIVATE_BASE + 9) +#define V4L2_CID_IVTV_DEC_FR_MASK (V4L2_CID_PRIVATE_BASE + 10) +#define V4L2_CID_IVTV_DEC_SP_MUTE (V4L2_CID_PRIVATE_BASE + 11) +#define V4L2_CID_IVTV_DEC_FR_FIELD (V4L2_CID_PRIVATE_BASE + 12) +#define V4L2_CID_IVTV_DEC_AUD_SKIP (V4L2_CID_PRIVATE_BASE + 13) +#define V4L2_CID_IVTV_DEC_NUM_BUFFERS (V4L2_CID_PRIVATE_BASE + 14) +#define V4L2_CID_IVTV_DEC_PREBUFFER (V4L2_CID_PRIVATE_BASE + 15) + +struct ivtv_ioctl_framesync { + uint32_t frame; + uint64_t pts; + uint64_t scr; +}; + +struct ivtv_speed { + int scale; /* 1-?? (50 for now) */ + int smooth; /* Smooth mode when in slow/fast mode */ + int speed; /* 0 = slow, 1 = fast */ + int direction; /* 0 = forward, 1 = reverse (not supportd */ + int fr_mask; /* 0 = I, 1 = I,P, 2 = I,P,B 2 = default!*/ + int b_per_gop; /* frames per GOP (reverse only) */ + int aud_mute; /* Mute audio while in slow/fast mode */ + int fr_field; /* 1 = show every field, 0 = show every frame */ + int mute; /* # of audio frames to mute on playback resume */ +}; + +struct ivtv_slow_fast { + int speed; /* 0 = slow, 1 = fast */ + int scale; /* 1-?? (50 for now) */ +}; + +struct ivtv_cfg_start_decode { + uint32_t gop_offset; /*Frames in GOP to skip before starting */ + uint32_t muted_audio_frames;/* #of audio frames to mute */ +}; + +struct ivtv_cfg_stop_decode { + int hide_last; /* 1 = show black after stop, + 0 = show last frame */ + uint64_t pts_stop; /* PTS to stop at */ +}; + + +/* For use with IVTV_IOC_G_CODEC and IVTV_IOC_S_CODEC */ +struct ivtv_ioctl_codec { + uint32_t aspect; + uint32_t audio_bitmask; + uint32_t bframes; + uint32_t bitrate_mode; + uint32_t bitrate; + uint32_t bitrate_peak; + uint32_t dnr_mode; + uint32_t dnr_spatial; + uint32_t dnr_temporal; + uint32_t dnr_type; + uint32_t framerate; /* read only, ignored on write */ + uint32_t framespergop; /* read only, ignored on write */ + uint32_t gop_closure; + uint32_t pulldown; + uint32_t stream_type; +}; + + +/* Framebuffer external API */ + +struct ivtvfb_ioctl_state_info { + unsigned long status; + unsigned long alpha; +}; + +struct ivtvfb_ioctl_blt_copy_args { + int x, y, width, height, source_offset, source_stride; +}; + +struct ivtvfb_ioctl_blt_fill_args { + int rasterop, alpha_mode, alpha_mask, width, height, x, y; + unsigned int destPixelMask, colour; + +}; + +struct ivtvfb_ioctl_dma_host_to_ivtv_args { + void* source; + unsigned long dest_offset; + int count; +}; + +struct ivtvfb_ioctl_get_frame_buffer { + void* mem; + int size; + int sizex; + int sizey; +}; + +struct ivtv_osd_coords { + unsigned long offset; + unsigned long max_offset; + int pixel_stride; + int lines; + int x; + int y; +}; + +struct rectangle { + int x0; + int y0; + int x1; + int y1; +}; + +/* Framebuffer ioctls should use the range 1 - 28 */ +#define IVTVFB_IOCTL_GET_STATE _IOR('@', 1, struct ivtvfb_ioctl_state_info) +#define IVTVFB_IOCTL_SET_STATE _IOW('@', 2, struct ivtvfb_ioctl_state_info) +#define IVTVFB_IOCTL_PREP_FRAME _IOW('@', 3, struct ivtvfb_ioctl_dma_host_to_ivtv_args) +#define IVTVFB_IOCTL_BLT_COPY _IOW('@', 4, struct ivtvfb_ioctl_blt_copy_args) +#define IVTVFB_IOCTL_GET_ACTIVE_BUFFER _IOR('@', 5, struct ivtv_osd_coords) +#define IVTVFB_IOCTL_SET_ACTIVE_BUFFER _IOW('@', 6, struct ivtv_osd_coords) +#define IVTVFB_IOCTL_GET_FRAME_BUFFER _IOR('@', 7, struct ivtvfb_ioctl_get_frame_buffer) +#define IVTVFB_IOCTL_BLT_FILL _IOW('@', 8, struct ivtvfb_ioctl_blt_fill_args) +#define IVTVFB_IOCTL_PREP_FRAME_BUF _IOW('@', 9, struct ivtvfb_ioctl_dma_host_to_ivtv_args) +#define IVTVFB_IOCTL_PREP_FRAME_YUV _IOW('@', 10, struct ivtvfb_ioctl_dma_host_to_ivtv_args) + +#define IVTVFB_STATUS_ENABLED (1 << 0) +#define IVTVFB_STATUS_GLOBAL_ALPHA (1 << 1) +#define IVTVFB_STATUS_LOCAL_ALPHA (1 << 2) +#define IVTVFB_STATUS_FLICKER_REDUCTION (1 << 3) + +#ifdef IVTV_INTERNAL +/* Do not use these structures and ioctls in code that you want to release. + Only to be used for testing and by the utilities ivtvctl, ivtvfbctl and fwapi. */ + +#define IVTV_MBOX_MAX_DATA 16 + +struct ivtv_ioctl_fwapi { + uint32_t cmd; + uint32_t result; + int32_t args; + uint32_t data[IVTV_MBOX_MAX_DATA]; +}; + +struct ivtv_ioctl_event { + uint32_t type; + uint32_t mbox; + struct ivtv_ioctl_fwapi api; +}; + +struct ivtv_saa71xx_reg { + unsigned char reg; + unsigned char val; +}; + +struct ivtv_itvc_reg { + uint32_t reg; + uint32_t val; +}; + +struct ivtv_msp_matrix { + int input; + int output; +}; + +/* Debug flags */ +#define IVTV_DEBUG_ERR (1 << 0) +#define IVTV_DEBUG_INFO (1 << 1) +#define IVTV_DEBUG_API (1 << 2) +#define IVTV_DEBUG_DMA (1 << 3) +#define IVTV_DEBUG_IOCTL (1 << 4) +#define IVTV_DEBUG_I2C (1 << 5) +#define IVTV_DEBUG_IRQ (1 << 6) +#define IVTV_DEBUG_DEC (1 << 7) + +/* BLT RasterOps */ +#define IVTV_BLT_RASTER_ZERO 0 +#define IVTV_BLT_RASTER_NOTDEST_AND_NOTSRC 1 +#define IVTV_BLT_RASTER_NOTDEST_AND_SRC 2 +#define IVTV_BLT_RASTER_NOTDEST 3 +#define IVTV_BLT_RASTER_DEST_AND_NOTSRC 4 +#define IVTV_BLT_RASTER_NOTSRC 5 +#define IVTV_BLT_RASTER_DEST_XOR_SRC 6 +#define IVTV_BLT_RASTER_NOTDEST_OR_NOTSRC 7 +/* #define IVTV_BLT_RASTER_NOTDEST_AND_NOTSRC 8 */ /* Same as 1 */ +#define IVTV_BLT_RASTER_DEST_XNOR_SRC 9 +#define IVTV_BLT_RASTER_SRC 10 +#define IVTV_BLT_RASTER_NOTDEST_OR_SRC 11 +#define IVTV_BLT_RASTER_DEST 12 +#define IVTV_BLT_RASTER_DEST_OR_NOTSRC 13 +#define IVTV_BLT_RASTER_DEST_OR_SRC 14 +#define IVTV_BLT_RASTER_ONE 15 + +/* BLT Alpha blending */ + +#define IVTV_BLT_ALPHABLEND_SRC 0x01 +#define IVTV_BLT_ALPHABLEND_DEST 0x10 +#define IVTV_BLT_ALPHABLEND_DEST_X_SRC 0x11 /* dest x src +1 , = zero if both zero */ + + +/* Internal ioctls should use the range 200-255 */ +#define IVTV_IOC_S_DEBUG_LEVEL _IOWR('@', 200, int) +#define IVTV_IOC_G_DEBUG_LEVEL _IOR ('@', 201, int) +#define IVTV_IOC_RELOAD_FW _IO ('@', 202) +#define IVTV_IOC_ZCOUNT _IO ('@', 203) +#define IVTV_IOC_FWAPI _IOWR('@', 204, struct ivtv_ioctl_fwapi) +#define IVTV_IOC_EVENT_SETUP _IOWR('@', 205, struct ivtv_ioctl_event) +#define IVTV_IOC_G_SAA7115_REG _IOWR('@', 206, struct ivtv_saa71xx_reg) +#define IVTV_IOC_S_SAA7115_REG _IOW ('@', 207, struct ivtv_saa71xx_reg) +#define IVTV_IOC_G_SAA7127_REG _IOWR('@', 208, struct ivtv_saa71xx_reg) +#define IVTV_IOC_S_SAA7127_REG _IOW ('@', 209, struct ivtv_saa71xx_reg) +#define IVTV_IOC_S_MSP_MATRIX _IOW ('@', 210, struct ivtv_msp_matrix) +#define IVTV_IOC_G_ITVC_REG _IOWR('@', 211, struct ivtv_itvc_reg) +#define IVTV_IOC_S_ITVC_REG _IOW ('@', 212, struct ivtv_itvc_reg) + +#endif + +#endif + diff --git a/extra_lib/include/avcap/linux/pwc-ioctl.h b/extra_lib/include/avcap/linux/pwc-ioctl.h new file mode 100644 index 0000000..63754ab --- /dev/null +++ b/extra_lib/include/avcap/linux/pwc-ioctl.h @@ -0,0 +1,329 @@ +#ifndef PWC_IOCTL_H +#define PWC_IOCTL_H + +/* (C) 2001-2004 Nemosoft Unv. + (C) 2004-2006 Luc Saillard (luc@saillard.org) + + NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx + driver and thus may have bugs that are not present in the original version. + Please send bug reports and support requests to . + The decompression routines have been implemented by reverse-engineering the + Nemosoft binary pwcx module. Caveat emptor. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* This is pwc-ioctl.h belonging to PWC 10.0.10 + It contains structures and defines to communicate from user space + directly to the driver. + */ + +/* + Changes + 2001/08/03 Alvarado Added ioctl constants to access methods for + changing white balance and red/blue gains + 2002/12/15 G. H. Fernandez-Toribio VIDIOCGREALSIZE + 2003/12/13 Nemosft Unv. Some modifications to make interfacing to + PWCX easier + 2006/01/01 Luc Saillard Add raw format definition + */ + +/* These are private ioctl() commands, specific for the Philips webcams. + They contain functions not found in other webcams, and settings not + specified in the Video4Linux API. + + The #define names are built up like follows: + VIDIOC VIDeo IOCtl prefix + PWC Philps WebCam + G optional: Get + S optional: Set + ... the function + */ + +#include +#include + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 10) +/* Compatibility for older kernel */ +typedef __u16 __le16; +#endif + + /* Enumeration of image sizes */ +#define PSZ_SQCIF 0x00 +#define PSZ_QSIF 0x01 +#define PSZ_QCIF 0x02 +#define PSZ_SIF 0x03 +#define PSZ_CIF 0x04 +#define PSZ_VGA 0x05 +#define PSZ_MAX 6 + + +/* The frame rate is encoded in the video_window.flags parameter using + the upper 16 bits, since some flags are defined nowadays. The following + defines provide a mask and shift to filter out this value. + This value can also be passing using the private flag when using v4l2 and + VIDIOC_S_FMT ioctl. + + In 'Snapshot' mode the camera freezes its automatic exposure and colour + balance controls. + */ +#define PWC_FPS_SHIFT 16 +#define PWC_FPS_MASK 0x00FF0000 +#define PWC_FPS_FRMASK 0x003F0000 +#define PWC_FPS_SNAPSHOT 0x00400000 +#define PWC_QLT_MASK 0x03000000 +#define PWC_QLT_SHIFT 24 + + +/* structure for transferring x & y coordinates */ +struct pwc_coord +{ + int x, y; /* guess what */ + int size; /* size, or offset */ +}; + + +/* Used with VIDIOCPWCPROBE */ +struct pwc_probe +{ + char name[32]; + int type; +}; + +struct pwc_serial +{ + char serial[30]; /* String with serial number. Contains terminating 0 */ +}; + +/* pwc_whitebalance.mode values */ +#define PWC_WB_INDOOR 0 +#define PWC_WB_OUTDOOR 1 +#define PWC_WB_FL 2 +#define PWC_WB_MANUAL 3 +#define PWC_WB_AUTO 4 + +/* Used with VIDIOCPWC[SG]AWB (Auto White Balance). + Set mode to one of the PWC_WB_* values above. + *red and *blue are the respective gains of these colour components inside + the camera; range 0..65535 + When 'mode' == PWC_WB_MANUAL, 'manual_red' and 'manual_blue' are set or read; + otherwise undefined. + 'read_red' and 'read_blue' are read-only. +*/ +struct pwc_whitebalance +{ + int mode; + int manual_red, manual_blue; /* R/W */ + int read_red, read_blue; /* R/O */ +}; + +/* + 'control_speed' and 'control_delay' are used in automatic whitebalance mode, + and tell the camera how fast it should react to changes in lighting, and + with how much delay. Valid values are 0..65535. +*/ +struct pwc_wb_speed +{ + int control_speed; + int control_delay; + +}; + +/* Used with VIDIOCPWC[SG]LED */ +struct pwc_leds +{ + int led_on; /* Led on-time; range = 0..25000 */ + int led_off; /* Led off-time; range = 0..25000 */ +}; + +/* Image size (used with GREALSIZE) */ +struct pwc_imagesize +{ + int width; + int height; +}; + +/* Defines and structures for Motorized Pan & Tilt */ +#define PWC_MPT_PAN 0x01 +#define PWC_MPT_TILT 0x02 +#define PWC_MPT_TIMEOUT 0x04 /* for status */ + +/* Set angles; when absolute != 0, the angle is absolute and the + driver calculates the relative offset for you. This can only + be used with VIDIOCPWCSANGLE; VIDIOCPWCGANGLE always returns + absolute angles. + */ +struct pwc_mpt_angles +{ + int absolute; /* write-only */ + int pan; /* degrees * 100 */ + int tilt; /* degress * 100 */ +}; + +/* Range of angles of the camera, both horizontally and vertically. + */ +struct pwc_mpt_range +{ + int pan_min, pan_max; /* degrees * 100 */ + int tilt_min, tilt_max; +}; + +struct pwc_mpt_status +{ + int status; + int time_pan; + int time_tilt; +}; + + +/* This is used for out-of-kernel decompression. With it, you can get + all the necessary information to initialize and use the decompressor + routines in standalone applications. + */ +struct pwc_video_command +{ + int type; /* camera type (645, 675, 730, etc.) */ + int release; /* release number */ + + int size; /* one of PSZ_* */ + int alternate; + int command_len; /* length of USB video command */ + unsigned char command_buf[13]; /* Actual USB video command */ + int bandlength; /* >0 = compressed */ + int frame_size; /* Size of one (un)compressed frame */ +}; + +/* Flags for PWCX subroutines. Not all modules honour all flags. */ +#define PWCX_FLAG_PLANAR 0x0001 +#define PWCX_FLAG_BAYER 0x0008 + + +/* IOCTL definitions */ + + /* Restore user settings */ +#define VIDIOCPWCRUSER _IO('v', 192) + /* Save user settings */ +#define VIDIOCPWCSUSER _IO('v', 193) + /* Restore factory settings */ +#define VIDIOCPWCFACTORY _IO('v', 194) + + /* You can manipulate the compression factor. A compression preference of 0 + means use uncompressed modes when available; 1 is low compression, 2 is + medium and 3 is high compression preferred. Of course, the higher the + compression, the lower the bandwidth used but more chance of artefacts + in the image. The driver automatically chooses a higher compression when + the preferred mode is not available. + */ + /* Set preferred compression quality (0 = uncompressed, 3 = highest compression) */ +#define VIDIOCPWCSCQUAL _IOW('v', 195, int) + /* Get preferred compression quality */ +#define VIDIOCPWCGCQUAL _IOR('v', 195, int) + + +/* Retrieve serial number of camera */ +#define VIDIOCPWCGSERIAL _IOR('v', 198, struct pwc_serial) + + /* This is a probe function; since so many devices are supported, it + becomes difficult to include all the names in programs that want to + check for the enhanced Philips stuff. So in stead, try this PROBE; + it returns a structure with the original name, and the corresponding + Philips type. + To use, fill the structure with zeroes, call PROBE and if that succeeds, + compare the name with that returned from VIDIOCGCAP; they should be the + same. If so, you can be assured it is a Philips (OEM) cam and the type + is valid. + */ +#define VIDIOCPWCPROBE _IOR('v', 199, struct pwc_probe) + + /* Set AGC (Automatic Gain Control); int < 0 = auto, 0..65535 = fixed */ +#define VIDIOCPWCSAGC _IOW('v', 200, int) + /* Get AGC; int < 0 = auto; >= 0 = fixed, range 0..65535 */ +#define VIDIOCPWCGAGC _IOR('v', 200, int) + /* Set shutter speed; int < 0 = auto; >= 0 = fixed, range 0..65535 */ +#define VIDIOCPWCSSHUTTER _IOW('v', 201, int) + + /* Color compensation (Auto White Balance) */ +#define VIDIOCPWCSAWB _IOW('v', 202, struct pwc_whitebalance) +#define VIDIOCPWCGAWB _IOR('v', 202, struct pwc_whitebalance) + + /* Auto WB speed */ +#define VIDIOCPWCSAWBSPEED _IOW('v', 203, struct pwc_wb_speed) +#define VIDIOCPWCGAWBSPEED _IOR('v', 203, struct pwc_wb_speed) + + /* LEDs on/off/blink; int range 0..65535 */ +#define VIDIOCPWCSLED _IOW('v', 205, struct pwc_leds) +#define VIDIOCPWCGLED _IOR('v', 205, struct pwc_leds) + + /* Contour (sharpness); int < 0 = auto, 0..65536 = fixed */ +#define VIDIOCPWCSCONTOUR _IOW('v', 206, int) +#define VIDIOCPWCGCONTOUR _IOR('v', 206, int) + + /* Backlight compensation; 0 = off, otherwise on */ +#define VIDIOCPWCSBACKLIGHT _IOW('v', 207, int) +#define VIDIOCPWCGBACKLIGHT _IOR('v', 207, int) + + /* Flickerless mode; = 0 off, otherwise on */ +#define VIDIOCPWCSFLICKER _IOW('v', 208, int) +#define VIDIOCPWCGFLICKER _IOR('v', 208, int) + + /* Dynamic noise reduction; 0 off, 3 = high noise reduction */ +#define VIDIOCPWCSDYNNOISE _IOW('v', 209, int) +#define VIDIOCPWCGDYNNOISE _IOR('v', 209, int) + + /* Real image size as used by the camera; tells you whether or not there's a gray border around the image */ +#define VIDIOCPWCGREALSIZE _IOR('v', 210, struct pwc_imagesize) + + /* Motorized pan & tilt functions */ +#define VIDIOCPWCMPTRESET _IOW('v', 211, int) +#define VIDIOCPWCMPTGRANGE _IOR('v', 211, struct pwc_mpt_range) +#define VIDIOCPWCMPTSANGLE _IOW('v', 212, struct pwc_mpt_angles) +#define VIDIOCPWCMPTGANGLE _IOR('v', 212, struct pwc_mpt_angles) +#define VIDIOCPWCMPTSTATUS _IOR('v', 213, struct pwc_mpt_status) + + /* Get the USB set-video command; needed for initializing libpwcx */ +#define VIDIOCPWCGVIDCMD _IOR('v', 215, struct pwc_video_command) +struct pwc_table_init_buffer { + int len; + char *buffer; + +}; +#define VIDIOCPWCGVIDTABLE _IOR('v', 216, struct pwc_table_init_buffer) + +/* + * This is private command used when communicating with v4l2. + * In the future all private ioctl will be remove/replace to + * use interface offer by v4l2. + */ + +#define V4L2_CID_PRIVATE_SAVE_USER (V4L2_CID_PRIVATE_BASE + 0) +#define V4L2_CID_PRIVATE_RESTORE_USER (V4L2_CID_PRIVATE_BASE + 1) +#define V4L2_CID_PRIVATE_RESTORE_FACTORY (V4L2_CID_PRIVATE_BASE + 2) +#define V4L2_CID_PRIVATE_COLOUR_MODE (V4L2_CID_PRIVATE_BASE + 3) +#define V4L2_CID_PRIVATE_AUTOCONTOUR (V4L2_CID_PRIVATE_BASE + 4) +#define V4L2_CID_PRIVATE_CONTOUR (V4L2_CID_PRIVATE_BASE + 5) +#define V4L2_CID_PRIVATE_BACKLIGHT (V4L2_CID_PRIVATE_BASE + 6) +#define V4L2_CID_PRIVATE_FLICKERLESS (V4L2_CID_PRIVATE_BASE + 7) +#define V4L2_CID_PRIVATE_NOISE_REDUCTION (V4L2_CID_PRIVATE_BASE + 8) + +struct pwc_raw_frame { + __le16 type; /* type of the webcam */ + __le16 vbandlength; /* Size of 4lines compressed (used by the decompressor) */ + __u8 cmd[4]; /* the four byte of the command (in case of nala, + only the first 3 bytes is filled) */ + __u8 rawframe[0]; /* frame_size = H/4*vbandlength */ +} __attribute__ ((packed)); + + +#endif diff --git a/extra_lib/include/avcap/linux/raw1394util.h b/extra_lib/include/avcap/linux/raw1394util.h new file mode 100644 index 0000000..5a5c433 --- /dev/null +++ b/extra_lib/include/avcap/linux/raw1394util.h @@ -0,0 +1,25 @@ +#ifdef HAS_AVC_SUPPORT + +#ifndef RAW1394UTIL_H +#define RAW1394UTIL_H 1 + +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + + int raw1394_get_num_ports( void ); + raw1394handle_t raw1394_open( int port ); + void raw1394_close( raw1394handle_t handle ); + int discoverAVC( int * port, octlet_t* guid ); + void reset_bus( int port ); + +#ifdef __cplusplus +} + +#endif +#endif + +#endif // HAS_AVC_SUPPORT diff --git a/extra_lib/include/avcap/linux/uvc_compat.h b/extra_lib/include/avcap/linux/uvc_compat.h new file mode 100644 index 0000000..e400499 --- /dev/null +++ b/extra_lib/include/avcap/linux/uvc_compat.h @@ -0,0 +1,129 @@ +#ifndef _UVC_COMPAT_H +#define _UVC_COMPAT_H + +#include + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18) +/* + * Extended control API + */ +struct v4l2_ext_control +{ + __u32 id; + __u32 reserved2[2]; + union { + __s32 value; + __s64 value64; + void *reserved; + }; +} __attribute__ ((packed)); + +struct v4l2_ext_controls +{ + __u32 ctrl_class; + __u32 count; + __u32 error_idx; + __u32 reserved[2]; + struct v4l2_ext_control *controls; +}; + +/* Values for ctrl_class field */ +#define V4L2_CTRL_CLASS_USER 0x00980000 /* Old-style 'user' controls */ +#define V4L2_CTRL_CLASS_MPEG 0x00990000 /* MPEG-compression controls */ + +#define V4L2_CTRL_ID_MASK (0x0fffffff) +#define V4L2_CTRL_ID2CLASS(id) ((id) & 0x0fff0000UL) +#define V4L2_CTRL_DRIVER_PRIV(id) (((id) & 0xffff) >= 0x1000) + +/* User-class control IDs defined by V4L2 */ +#undef V4L2_CID_BASE +#define V4L2_CID_BASE (V4L2_CTRL_CLASS_USER | 0x900) +#define V4L2_CID_USER_BASE V4L2_CID_BASE +#define V4L2_CID_USER_CLASS (V4L2_CTRL_CLASS_USER | 1) + +#define VIDIOC_G_EXT_CTRLS _IOWR ('V', 71, struct v4l2_ext_controls) +#define VIDIOC_S_EXT_CTRLS _IOWR ('V', 72, struct v4l2_ext_controls) +#define VIDIOC_TRY_EXT_CTRLS _IOWR ('V', 73, struct v4l2_ext_controls) + +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) +/* + * Frame size and frame rate enumeration + * + * Included in Linux 2.6.19 + */ +enum v4l2_frmsizetypes +{ + V4L2_FRMSIZE_TYPE_DISCRETE = 1, + V4L2_FRMSIZE_TYPE_CONTINUOUS = 2, + V4L2_FRMSIZE_TYPE_STEPWISE = 3, +}; + +struct v4l2_frmsize_discrete +{ + __u32 width; /* Frame width [pixel] */ + __u32 height; /* Frame height [pixel] */ +}; + +struct v4l2_frmsize_stepwise +{ + __u32 min_width; /* Minimum frame width [pixel] */ + __u32 max_width; /* Maximum frame width [pixel] */ + __u32 step_width; /* Frame width step size [pixel] */ + __u32 min_height; /* Minimum frame height [pixel] */ + __u32 max_height; /* Maximum frame height [pixel] */ + __u32 step_height; /* Frame height step size [pixel] */ +}; + +struct v4l2_frmsizeenum +{ + __u32 index; /* Frame size number */ + __u32 pixel_format; /* Pixel format */ + __u32 type; /* Frame size type the device supports. */ + + union { /* Frame size */ + struct v4l2_frmsize_discrete discrete; + struct v4l2_frmsize_stepwise stepwise; + }; + + __u32 reserved[2]; /* Reserved space for future use */ +}; + +enum v4l2_frmivaltypes +{ + V4L2_FRMIVAL_TYPE_DISCRETE = 1, + V4L2_FRMIVAL_TYPE_CONTINUOUS = 2, + V4L2_FRMIVAL_TYPE_STEPWISE = 3, +}; + +struct v4l2_frmival_stepwise +{ + struct v4l2_fract min; /* Minimum frame interval [s] */ + struct v4l2_fract max; /* Maximum frame interval [s] */ + struct v4l2_fract step; /* Frame interval step size [s] */ +}; + +struct v4l2_frmivalenum +{ + __u32 index; /* Frame format index */ + __u32 pixel_format; /* Pixel format */ + __u32 width; /* Frame width */ + __u32 height; /* Frame height */ + __u32 type; /* Frame interval type the device supports. */ + + union { /* Frame interval */ + struct v4l2_fract discrete; + struct v4l2_frmival_stepwise stepwise; + }; + + __u32 reserved[2]; /* Reserved space for future use */ +}; + +#define VIDIOC_ENUM_FRAMESIZES _IOWR ('V', 74, struct v4l2_frmsizeenum) +#define VIDIOC_ENUM_FRAMEINTERVALS _IOWR ('V', 75, struct v4l2_frmivalenum) +#endif + + +#endif /* _UVC_COMPAT_H */ + diff --git a/extra_lib/include/avcap/log.h b/extra_lib/include/avcap/log.h new file mode 100644 index 0000000..aff6824 --- /dev/null +++ b/extra_lib/include/avcap/log.h @@ -0,0 +1,44 @@ +/* + * (c) 2005, 2008 Nico Pranke , Robin Luedtke + * + * This file is part of avcap. + * + * avcap is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * avcap is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with avcap. If not, see . + */ + +/* avcap is free for non-commercial use. + * To use it in commercial endeavors, please contact Nico Pranke + */ + +#ifndef AVCAP_LOG_H +#define AVCAP_LOG_H + +#include +#include + +static inline void logDebug(const std::string& msg) +{ +#ifdef DEBUG + std::cerr<, Robin Luedtke + * + * This file is part of avcap. + * + * avcap is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * avcap is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with avcap. If not, see . + */ + +/* avcap is free for non-commercial use. + * To use it in commercial endeavors, please contact Nico Pranke . + */ + + +#ifndef QT_CONNECTORMANAGER_H +#define QT_CONNECTORMANAGER_H + +#include + +#include "ConnectorManager.h" + +namespace avcap +{ + class QT_DeviceDescriptor; + + //! This class implements the ConnectorManager for QuickTime-devices. + + /*! Such devices don't have Connectors the user could chose. So the abstract + * methods are implemented as noops. */ + + class QT_ConnectorManager: public ConnectorManager + { + public: + + QT_ConnectorManager(QT_DeviceDescriptor *dd); + + virtual ~QT_ConnectorManager(); + void query(); + }; +} + +#endif // QT_CONNECTORMANAGER_H + diff --git a/extra_lib/include/avcap/osx/QT_Control.h b/extra_lib/include/avcap/osx/QT_Control.h new file mode 100644 index 0000000..40837ce --- /dev/null +++ b/extra_lib/include/avcap/osx/QT_Control.h @@ -0,0 +1,89 @@ +/* + * (c) 2005, 2008 Nico Pranke , Robin Luedtke + * + * This file is part of avcap. + * + * avcap is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * avcap is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with avcap. If not, see . + */ + +/* avcap is free for non-commercial use. + * To use it in commercial endeavors, please contact Nico Pranke . + */ + +#ifndef QT_CONTROL_H_ +#define QT_CONTROL_H_ + +#include +#include + +#include "Control_avcap.h" + +namespace avcap +{ + // forward declaration + class QT_DeviceDescriptor; + + //! Implementation of a Control for the QuickTime-implementation of CaptureDevice. + + /*! The QT API supports only controls for brightness, hue, colour, contrast, depth and whiteness. + * They are all integer values between 0 and 65535.*/ + + class QT_Control: public IntegerControl + { + public: + enum Ctrl { + BRIGHTNESS = 0, + BLACKLEVEL, + WHITELEVEL, + HUE, + CONTRAST, + SATURATION, + SHARPNESS + }; + + private: + static std::string mNames[7]; + + QT_DeviceDescriptor* mDescriptor; + Ctrl mType; + unsigned short mDefaultValue; + Interval mInterval; + + public: + QT_Control(QT_DeviceDescriptor* dd, Ctrl type); + + virtual inline ~QT_Control() + {} + + virtual inline int getId() const + { return mType; } + + virtual inline int getDefaultValue() const + { return mDefaultValue; } + + virtual const std::string& getName() const; + + virtual int setValue(int val); + + virtual int getValue() const; + + virtual int reset(); + + virtual inline const Interval& getInterval() const + { return mInterval; } + }; +} + +#endif // QT_CONTROL_H_ + diff --git a/extra_lib/include/avcap/osx/QT_ControlManager.h b/extra_lib/include/avcap/osx/QT_ControlManager.h new file mode 100644 index 0000000..5c33d25 --- /dev/null +++ b/extra_lib/include/avcap/osx/QT_ControlManager.h @@ -0,0 +1,53 @@ +/* + * (c) 2005, 2008 Nico Pranke , Robin Luedtke + * + * This file is part of avcap. + * + * avcap is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * avcap is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with avcap. If not, see . + */ + +/* avcap is free for non-commercial use. + * To use it in commercial endeavors, please contact Nico Pranke . + */ + + +#ifndef QT_CONTROLMANAGER_H +#define QT_CONTROLMANAGER_H + +#include +#include + +#include "ControlManager.h" + +namespace avcap +{ + +class QT_DeviceDescriptor; + + //! Implementation of the ControlManager for the QuickTime-implementation of CaptureDevice. + + class QT_ControlManager: public ControlManager + { + public: + + QT_ControlManager(QT_DeviceDescriptor *dd); + + virtual ~QT_ControlManager(); + + virtual void query(); + }; +} + +#endif // QT_CONTROLMANAGER_H + diff --git a/extra_lib/include/avcap/osx/QT_Device.h b/extra_lib/include/avcap/osx/QT_Device.h new file mode 100644 index 0000000..4e10a0c --- /dev/null +++ b/extra_lib/include/avcap/osx/QT_Device.h @@ -0,0 +1,83 @@ +/* + * (c) 2005, 2008 Nico Pranke , Robin Luedtke + * + * This file is part of avcap. + * + * avcap is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * avcap is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with avcap. If not, see . + */ + +/* avcap is free for non-commercial use. + * To use it in commercial endeavors, please contact Nico Pranke . + */ + +#ifndef QT_DEVICE_H +#define QT_DEVICE_H + +#include +#include + +#include "DeviceDescriptor.h" +#include "CaptureDevice.h" +#include "QT_FormatManager.h" + +namespace avcap +{ + // forward declarations + class QT_DeviceDescriptor; + class QT_VidCapManager; + class QT_ConnectorManager; + class QT_ControlManager; + + //! Implementation of the CaptureDevice for QuickTime. + + class QT_Device : public CaptureDevice + { + public: + + private: + QT_DeviceDescriptor* mDeviceDescriptor; + QT_FormatManager* mFormatMgr; + QT_VidCapManager* mVidCapMgr; + QT_ConnectorManager* mConnectorMgr; + QT_ControlManager* mControlMgr; + + public: + QT_Device(QT_DeviceDescriptor* dd); + + virtual ~QT_Device(); + + inline const DeviceDescriptor* getDescriptor() + { return (const DeviceDescriptor*) mDeviceDescriptor; } + + inline CaptureManager* getVidCapMgr() + { return (CaptureManager*) mVidCapMgr; } + + inline ConnectorManager* getConnectorMgr() + { return (ConnectorManager*) mConnectorMgr; } + + inline ControlManager* getControlMgr() + { return (ControlManager*) mControlMgr; } + + inline FormatManager* getFormatMgr() + { return (FormatManager*) mFormatMgr; } + + private: + int open(); + + int close(); + }; +} + +#endif // QT_DEVICE_H + diff --git a/extra_lib/include/avcap/osx/QT_DeviceDescriptor.h b/extra_lib/include/avcap/osx/QT_DeviceDescriptor.h new file mode 100644 index 0000000..bc5c156 --- /dev/null +++ b/extra_lib/include/avcap/osx/QT_DeviceDescriptor.h @@ -0,0 +1,103 @@ +/* + * (c) 2005, 2008 Nico Pranke , Robin Luedtke + * + * This file is part of avcap. + * + * avcap is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * avcap is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with avcap. If not, see . + */ + +/* avcap is free for non-commercial use. + * To use it in commercial endeavors, please contact Nico Pranke . + */ + + +#ifndef QT_DEVICEDESCRIPTOR_H_ +#define QT_DEVICEDESCRIPTOR_H_ + +#include +#include + +#include "DeviceDescriptor.h" +#include + +namespace avcap +{ +class CaptureDevice; +class QT_Device; + + //! Implementation of the DeviceDescriptor for QuickTime. + + class QT_DeviceDescriptor : public DeviceDescriptor + { + public: + + private: + SeqGrabComponent mGrabber; + SGChannel mChannel; + VideoDigitizerComponent mDigitizer; + DigitizerInfo mDigiInfo; + + int mDeviceID; + int mInputID; + bool mValid; + + std::string mName; + std::string mDriver; + QT_Device* mDevice; + + public: + QT_DeviceDescriptor(int device, int input, const std::string& dev_name, const std::string& driver_name, + SeqGrabComponent current_grabber, SGChannel current_channel); + + QT_DeviceDescriptor(); + + virtual ~QT_DeviceDescriptor(); + + virtual CaptureDevice* getDevice(); + + virtual int open(); + + virtual int close(); + + virtual const std::string& getName() const; + + virtual const std::string& getDriver() const; + + bool isVideoCaptureDev() const; + + virtual inline const DEV_HANDLE_T getHandle() const + { return 0; } + + //! Get the SequenceGrabber-Component. + /*! \return the SequenceGrabber-Component. */ + inline SeqGrabComponent getGrabber(void) + { return mGrabber; } + + //! Get the SequenceGrabber-Channel associated with the device. + /*! \return the SequenceGrabber-Channel. */ + inline SGChannel getChannel(void) + { return mChannel; } + + //! Get the VideoDigitizer associated with the device. + /*! \return the VideoDigitizer. */ + inline VideoDigitizerComponent getDigitizer(void) + { return mDigitizer; } + + private: + bool queryCapabilities(SeqGrabComponent current_grabber, SGChannel current_channel); + + }; +} + +#endif // QT_DEVICEDESCRIPTOR_H_ diff --git a/extra_lib/include/avcap/osx/QT_DeviceEnumerator.h b/extra_lib/include/avcap/osx/QT_DeviceEnumerator.h new file mode 100644 index 0000000..9e1e375 --- /dev/null +++ b/extra_lib/include/avcap/osx/QT_DeviceEnumerator.h @@ -0,0 +1,58 @@ +/* + * (c) 2005, 2008 Nico Pranke , Robin Luedtke + * + * This file is part of avcap. + * + * avcap is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * avcap is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with avcap. If not, see . + */ + +/* avcap is free for non-commercial use. + * To use it in commercial endeavors, please contact Nico Pranke . + */ + + +#ifndef QT_DEVICEENUMERATOR_H_ +#define QT_DEVICEENUMERATOR_H_ + +#include + +#include "DeviceCollector.h" +#include "QT_DeviceDescriptor.h" + +namespace avcap +{ + //! This special DeviceDescriptor is used by the DeviceCollector to enumerate QuickTime capture-devices. + + class QT_DeviceEnumerator: public QT_DeviceDescriptor + { + private: + SGDeviceList mDeviceList; + + public: + QT_DeviceEnumerator(); + virtual ~QT_DeviceEnumerator(); + + //! Use this method to populate a DeviceList with the descriptors of the Devices found on the system. + /*! \param dev_list : the list that is filled with DeviceDescriptor-objects. + * \return 0 on success, -1 else */ + int findDevices(DeviceCollector::DeviceList& dev_list); + + virtual int open(); + + virtual int close(); + }; +} + +#endif // QT_DEVICEENUMERATOR_H_ + diff --git a/extra_lib/include/avcap/osx/QT_FormatManager.h b/extra_lib/include/avcap/osx/QT_FormatManager.h new file mode 100644 index 0000000..a819d14 --- /dev/null +++ b/extra_lib/include/avcap/osx/QT_FormatManager.h @@ -0,0 +1,92 @@ +/* + * (c) 2005, 2008 Nico Pranke , Robin Luedtke + * + * This file is part of avcap. + * + * avcap is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * avcap is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with avcap. If not, see . + */ + +/* avcap is free for non-commercial use. + * To use it in commercial endeavors, please contact Nico Pranke . + */ + +#ifndef QT_FORMATMANAGER_H +#define QT_FORMATMANAGER_H + +#include +#include + +#include "FormatManager.h" + +namespace avcap +{ + class QT_DeviceDescriptor; + + //! Implementation of the FormatManager for the QuickTime-implementation of CaptureDevice. + + /*! This FormatManager supports at most one format, so setting the format is not realy neccessary. */ + + class QT_FormatManager: public FormatManager + { + private: + QT_DeviceDescriptor* mQTDeviceDescriptor; + GWorldPtr mGWorld; + Rect mCurrentBounds; + bool mNeedsDecompression; + + public: + QT_FormatManager(QT_DeviceDescriptor *dd); + + virtual ~QT_FormatManager(); + + int setFormat(Format *fmt); + + int setFormat(uint32_t fourcc); + + Format* getFormat(); + + int setResolution(int w, int h); + + int getWidth(); + + int getHeight(); + + size_t getImageSize(); + + int setFramerate(int fps); + + int getFramerate(); + + void query(); + + private: + + int probeResolutions(Format* fmt); + + int checkResolution(int w, int h); + + int updateGWorld(const Rect* bounds); + + static uint32_t OSType2Fourcc(OSType pixel_format); + + static OSType Fourcc2OSType(uint32_t fourcc); + + inline bool needsDecompression() const + { return mNeedsDecompression; } + + friend class QT_VidCapManager; + }; +} + +#endif // QT_FORMATMANAGER_H diff --git a/extra_lib/include/avcap/osx/QT_VidCapManager.h b/extra_lib/include/avcap/osx/QT_VidCapManager.h new file mode 100644 index 0000000..ff34c3c --- /dev/null +++ b/extra_lib/include/avcap/osx/QT_VidCapManager.h @@ -0,0 +1,122 @@ +/* + * (c) 2005, 2008 Nico Pranke , Robin Luedtke + * + * This file is part of avcap. + * + * avcap is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * avcap is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with avcap. If not, see . + */ + +/* avcap is free for non-commercial use. + * To use it in commercial endeavors, please contact Nico Pranke . + */ + +#ifndef QT_VIDCAPMANAGER_H_ +#define QT_VIDCAPMANAGER_H_ + +#include +#include +#include +#include + +#include + +#include "CaptureManager.h" + +namespace avcap +{ + class QT_DeviceDescriptor; + class QT_FormatManager; + class IOBuffer; + class CaptureHandler; + + //! Implementation of the CaptureManager for the QuickTime-implementation of a CaptureDevice. + + /*! This CaptureManager captures the data in an ow thread. + * Decompression is performed, if neccessary (e.g. for DV-Cams). */ + + class QT_VidCapManager: public CaptureManager + { + public: + + private: + typedef std::list IOBufList_t; + + QT_DeviceDescriptor *mQTDeviceDescriptor; + QT_FormatManager *mFormatMgr; + IOBufList_t mBuffers; + TimeScale mTimeScale; + TimeScale mLastTime; + pthread_t* mThread; + pthread_mutex_t mLock; + int mFinish; + long mSequence; + int mNumBufs; + int mAvailableBuffers; + ICMDecompressionSessionRef mDecompSession; + + public: + QT_VidCapManager(QT_DeviceDescriptor* dd, QT_FormatManager* fmt_mgr, int nbufs); + + virtual ~QT_VidCapManager(); + + int init(); + + int destroy(); + + int startCapture(); + + int stopCapture(); + + virtual IOBuffer* dequeue(); + + virtual int enqueue(IOBuffer* buf); + + virtual int getNumIOBuffers(); + + private: + int startCaptureImpl(); + + int stopCaptureImpl(); + + void notifyCaptureHandler(void* data, size_t length, size_t bytes_per_row, TimeValue time); + + int poll(); + + struct timespec getPollTimespec(double fps); + + int decompressData(void* data, long length, TimeValue timeValue); + + static OSErr captureCallback(SGChannel ch, Ptr data, long data_len, long * offset, + long ch_ref_con, TimeValue time_value, short write_type, long priv); + + OSErr captureCallbackImpl(SGChannel ch, Ptr data, long data_len, long * offset, + long ch_ref_con, TimeValue time_value, short write_type); + + int createDecompSession(); + + static void* threadFunc(void* arg); + + static void decompTrackingCallback(void *decompressionTrackingRefCon, OSStatus result, + ICMDecompressionTrackingFlags decompressionTrackingFlags, CVPixelBufferRef pixelBuffer, + TimeValue64 displayTime, TimeValue64 displayDuration, ICMValidTimeFlags validTimeFlags, + void *reserved, void *sourceFrameRefCon ); + + void threadFuncImpl(); + + void clearBuffers(); + }; +} + +#endif // QT_VIDCAPMANAGER_H + diff --git a/extra_lib/include/avcap/osx/avcap-config.h b/extra_lib/include/avcap/osx/avcap-config.h new file mode 100644 index 0000000..9639389 --- /dev/null +++ b/extra_lib/include/avcap/osx/avcap-config.h @@ -0,0 +1,264 @@ +#ifndef _AVCAP_CONFIG_H +#define _AVCAP_CONFIG_H 1 + +/* avcap-config.h. Generated automatically at end of configure. */ +/* config.h. Generated from config.h.in by configure. */ +/* config.h.in. Generated from configure.in by autoheader. */ + +/* Define to 1 if you have the `alarm' function. */ +#ifndef AVCAP_HAVE_ALARM +#define AVCAP_HAVE_ALARM 1 +#endif + +/* Define to 1 if you have the header file. */ +#ifndef AVCAP_HAVE_DLFCN_H +#define AVCAP_HAVE_DLFCN_H 1 +#endif + +/* Define to 1 if you have the header file. */ +#ifndef AVCAP_HAVE_FCNTL_H +#define AVCAP_HAVE_FCNTL_H 1 +#endif + +/* Define to 1 if you have the header file. */ +#ifndef AVCAP_HAVE_FLOAT_H +#define AVCAP_HAVE_FLOAT_H 1 +#endif + +/* Define to 1 if you have the `getpagesize' function. */ +#ifndef AVCAP_HAVE_GETPAGESIZE +#define AVCAP_HAVE_GETPAGESIZE 1 +#endif + +/* Define to 1 if you have the `gettimeofday' function. */ +#ifndef AVCAP_HAVE_GETTIMEOFDAY +#define AVCAP_HAVE_GETTIMEOFDAY 1 +#endif + +/* Define to 1 if you have the header file. */ +#ifndef AVCAP_HAVE_INTTYPES_H +#define AVCAP_HAVE_INTTYPES_H 1 +#endif + +/* Define to 1 if you have the header file. */ +#ifndef AVCAP_HAVE_LIMITS_H +#define AVCAP_HAVE_LIMITS_H 1 +#endif + +/* Define to 1 if your system has a GNU libc compatible `malloc' function, and + to 0 otherwise. */ +#ifndef AVCAP_HAVE_MALLOC +#define AVCAP_HAVE_MALLOC 1 +#endif + +/* Define to 1 if you have the header file. */ +#ifndef AVCAP_HAVE_MEMORY_H +#define AVCAP_HAVE_MEMORY_H 1 +#endif + +/* Define to 1 if you have the `memset' function. */ +#ifndef AVCAP_HAVE_MEMSET +#define AVCAP_HAVE_MEMSET 1 +#endif + +/* Define to 1 if you have a working `mmap' system call. */ +#ifndef AVCAP_HAVE_MMAP +#define AVCAP_HAVE_MMAP 1 +#endif + +/* Define to 1 if you have the `munmap' function. */ +#ifndef AVCAP_HAVE_MUNMAP +#define AVCAP_HAVE_MUNMAP 1 +#endif + +/* Define to 1 if you have the `pow' function. */ +#ifndef AVCAP_HAVE_POW +#define AVCAP_HAVE_POW 1 +#endif + +/* Define to 1 if you have the `select' function. */ +#ifndef AVCAP_HAVE_SELECT +#define AVCAP_HAVE_SELECT 1 +#endif + +/* Define to 1 if you have the `sqrt' function. */ +#ifndef AVCAP_HAVE_SQRT +#define AVCAP_HAVE_SQRT 1 +#endif + +/* Define to 1 if `stat' has the bug that it succeeds when given the + zero-length file name argument. */ +/* #undef HAVE_STAT_EMPTY_STRING_BUG */ + +/* Define to 1 if stdbool.h conforms to C99. */ +#ifndef AVCAP_HAVE_STDBOOL_H +#define AVCAP_HAVE_STDBOOL_H 1 +#endif + +/* Define to 1 if you have the header file. */ +#ifndef AVCAP_HAVE_STDDEF_H +#define AVCAP_HAVE_STDDEF_H 1 +#endif + +/* Define to 1 if you have the header file. */ +#ifndef AVCAP_HAVE_STDINT_H +#define AVCAP_HAVE_STDINT_H 1 +#endif + +/* Define to 1 if you have the header file. */ +#ifndef AVCAP_HAVE_STDLIB_H +#define AVCAP_HAVE_STDLIB_H 1 +#endif + +/* Define to 1 if you have the `strerror' function. */ +#ifndef AVCAP_HAVE_STRERROR +#define AVCAP_HAVE_STRERROR 1 +#endif + +/* Define to 1 if you have the header file. */ +#ifndef AVCAP_HAVE_STRINGS_H +#define AVCAP_HAVE_STRINGS_H 1 +#endif + +/* Define to 1 if you have the header file. */ +#ifndef AVCAP_HAVE_STRING_H +#define AVCAP_HAVE_STRING_H 1 +#endif + +/* Define to 1 if you have the header file. */ +#ifndef AVCAP_HAVE_SYS_IOCTL_H +#define AVCAP_HAVE_SYS_IOCTL_H 1 +#endif + +/* Define to 1 if you have the header file. */ +#ifndef AVCAP_HAVE_SYS_SELECT_H +#define AVCAP_HAVE_SYS_SELECT_H 1 +#endif + +/* Define to 1 if you have the header file. */ +#ifndef AVCAP_HAVE_SYS_SOCKET_H +#define AVCAP_HAVE_SYS_SOCKET_H 1 +#endif + +/* Define to 1 if you have the header file. */ +#ifndef AVCAP_HAVE_SYS_STAT_H +#define AVCAP_HAVE_SYS_STAT_H 1 +#endif + +/* Define to 1 if you have the header file. */ +#ifndef AVCAP_HAVE_SYS_TIME_H +#define AVCAP_HAVE_SYS_TIME_H 1 +#endif + +/* Define to 1 if you have the header file. */ +#ifndef AVCAP_HAVE_SYS_TYPES_H +#define AVCAP_HAVE_SYS_TYPES_H 1 +#endif + +/* Define to 1 if you have the header file. */ +#ifndef AVCAP_HAVE_UNISTD_H +#define AVCAP_HAVE_UNISTD_H 1 +#endif + +/* Define to 1 if the system has the type `_Bool'. */ +/* #undef HAVE__BOOL */ + +/* Compile avcap for Linux */ +/* #undef LINUX */ + +/* Define to 1 if `lstat' dereferences a symlink specified with a trailing + slash. */ +/* #undef LSTAT_FOLLOWS_SLASHED_SYMLINK */ + +/* Compile avcap for Mac OS X */ +#ifndef AVCAP_OSX +#define AVCAP_OSX 1 +#endif + +/* Name of package */ +#ifndef AVCAP_PACKAGE +#define AVCAP_PACKAGE "avcap" +#endif + +/* Define to the address where bug reports for this package should be sent. */ +#ifndef AVCAP_PACKAGE_BUGREPORT +#define AVCAP_PACKAGE_BUGREPORT "Nico.Pranke@googlemail.com" +#endif + +/* Define to the full name of this package. */ +#ifndef AVCAP_PACKAGE_NAME +#define AVCAP_PACKAGE_NAME "A video capture library" +#endif + +/* Define to the full name and version of this package. */ +#ifndef AVCAP_PACKAGE_STRING +#define AVCAP_PACKAGE_STRING "A video capture library 0.1.9" +#endif + +/* Define to the one symbol short name of this package. */ +#ifndef AVCAP_PACKAGE_TARNAME +#define AVCAP_PACKAGE_TARNAME "avcap" +#endif + +/* Define to the version of this package. */ +#ifndef AVCAP_PACKAGE_VERSION +#define AVCAP_PACKAGE_VERSION "0.1.9" +#endif + +/* Define to the type of arg 1 for `select'. */ +#ifndef AVCAP_SELECT_TYPE_ARG1 +#define AVCAP_SELECT_TYPE_ARG1 int +#endif + +/* Define to the type of args 2, 3 and 4 for `select'. */ +#ifndef AVCAP_SELECT_TYPE_ARG234 +#define AVCAP_SELECT_TYPE_ARG234 (fd_set *) +#endif + +/* Define to the type of arg 5 for `select'. */ +#ifndef AVCAP_SELECT_TYPE_ARG5 +#define AVCAP_SELECT_TYPE_ARG5 (struct timeval *) +#endif + +/* Define to 1 if you have the ANSI C header files. */ +#ifndef AVCAP_STDC_HEADERS +#define AVCAP_STDC_HEADERS 1 +#endif + +/* Define to 1 if you can safely include both and . */ +#ifndef AVCAP_TIME_WITH_SYS_TIME +#define AVCAP_TIME_WITH_SYS_TIME 1 +#endif + +/* Define to 1 if your declares `struct tm'. */ +/* #undef TM_IN_SYS_TIME */ + +/* Version number of package */ +#ifndef AVCAP_VERSION +#define AVCAP_VERSION "0.1.9" +#endif + +/* Compile avcap for Windows */ +/* #undef WINDOWS */ + +/* Define to empty if `const' does not conform to ANSI C. */ +/* #undef const */ + +/* Define to `__inline__' or `__inline' if that's what the C compiler + calls it, or to nothing if 'inline' is not supported under any name. */ +#ifndef __cplusplus +/* #undef inline */ +#endif + +/* Define to rpl_malloc if the replacement function should be used. */ +/* #undef malloc */ + +/* Define to `unsigned int' if does not define. */ +/* #undef size_t */ + +/* Define to empty if the keyword `volatile' does not work. Warning: valid + code using `volatile' can become incorrect without. Disable with care. */ +/* #undef volatile */ + +/* once: _AVCAP_CONFIG_H */ +#endif diff --git a/extra_lib/include/avcap/singleton.h b/extra_lib/include/avcap/singleton.h new file mode 100644 index 0000000..97b4375 --- /dev/null +++ b/extra_lib/include/avcap/singleton.h @@ -0,0 +1,57 @@ +/* + * (c) 2005, 2008 Nico Pranke , Robin Luedtke + * + * This file is part of avcap. + * + * avcap is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * avcap is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with avcap. If not, see . + */ + +/* avcap is free for non-commercial use. + * To use it in commercial endeavors, please contact Nico Pranke + */ + +#ifndef SINGLETON_H_ +#define SINGLETON_H_ + + + +namespace avcap +{ + //! Base for classes that implement the singleton pattern. + + /*! There are lots of more spohisticated approaches to implement + * a singleton but this simple approach is sufficient here. */ + template + class Singleton + { + public: + + //! Get the global instance of the singleton. + static T& instance() + { + static T _instance; + return _instance; + } + + private: + + Singleton(); // ctor hidden + ~Singleton(); // dtor hidden + Singleton(Singleton const&); // copy ctor hidden + Singleton& operator=(Singleton const&); // assign op hidden + }; +} + +#endif // SINGLETON_H_ + diff --git a/extra_lib/include/avcap/windows/Crossbar.h b/extra_lib/include/avcap/windows/Crossbar.h new file mode 100644 index 0000000..698f3ff --- /dev/null +++ b/extra_lib/include/avcap/windows/Crossbar.h @@ -0,0 +1,123 @@ +/* + * (c) 2005, 2008 Nico Pranke , Robin Luedtke + * + * This file is part of avcap. + * + * avcap is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * avcap is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with avcap. If not, see . + */ + +/* avcap is free for non-commercial use. + * To use it in commercial endeavors, please contact Nico Pranke . + */ + + +#ifndef CROSSBAR_H_ +#define CROSSBAR_H_ + +#include + +#include "avcap-export.h" + +namespace avcap +{ + struct AVCAP_Export STConnector + { + std::string NameOfConnector; + LONG PhysicalType; + bool IsAudioConnector; + bool IsVideoConnector; + bool IsRouted; + + IAMCrossbar *Crossbar; + long PinIndexOfCrossbar; + int AudioSet; + + /* Video inputs correspond to zero or max. one audio input. + The pins are numbered from 0 to N-1, N <= 32. + Each bit set is the index of a audio pin on the + same crossbar corresponding to the selected video pin. + AudioSet only used for video input pins. */ + + int PinIndex; // Independent PinIndex + }; + + struct AVCAP_Export STRouting + { + long OutputPinIndex; + IPin *OutputPin; + long RelatedInPinIndex; + IPin *RelatedInputPin; + IAMCrossbar *Crossbar; + int CrossbarIndex; // Zero based index + bool HasConnector; + }; + + //! A class for controlling DirectShow video crossbars. + + /*! This class creates a single object which encapsulates all connected + * crossbars, enumerates all unique inputs which can be reached from + * a given starting downstream filter. + * + * The class supports an arbitrarily complex graph of crossbars, + * which can be cascaded and disjoint, that is not all inputs need + * to traverse the same set of crossbars. + * + * Given a starting filter (typically the capture filter), the class + * recursively traces upstream searching for all viable inputs. + * An input is considered viable if it is either: + * + * - unconnected + * - connects to a DirectShow filter which does not support IAMCrossbar + * COM-Interface. + * */ + + class AVCAP_Export CCrossbar + { + public: + CCrossbar(ICaptureGraphBuilder2 *CaptureGraphBuilder); + ~CCrossbar(void); + + /*! Searches upstrean for all available crossbars in the filter graph, + * starting from a given filter. + * \param StartFilter Filter to start search from. + * \return The number of crossbars found in the filter graph, -1 else. */ + int FindAllCrossbarsAndConnectors(IBaseFilter *StartFilter); + std::list& GetInputConnectorList(); + + /*! Gets the currently selected video input connector. + * \param Connector Connector data */ + bool GetCurrentVideoInput(STConnector *Connector); + + /*! Gets the currently selected audio input connector. + * \param Connector Connector data */ + bool GetCurrentAudioInput(STConnector *Connector); + + /*! Sets the input connector. + * \param PinIndex Pin index */ + bool SetInput(int PinIndex); + + private: + QzCComPtr m_CaptureBuilder; + std::list m_CrossbarList; + std::list m_RoutingList; + std::list m_InputConnectorList; + + HRESULT GetCrossbarIPinAtIndex(IAMCrossbar *pXbar, LONG PinIndex, + BOOL IsInputPin, IPin ** ppPin); + void StringFromPinType(std::string &PinName, long lType); + void DeleteAllLists(); + }; +} + +#endif // CROSSBAR_H_ diff --git a/extra_lib/include/avcap/windows/DS_Connector.h b/extra_lib/include/avcap/windows/DS_Connector.h new file mode 100644 index 0000000..ada6fc7 --- /dev/null +++ b/extra_lib/include/avcap/windows/DS_Connector.h @@ -0,0 +1,74 @@ +/* + * (c) 2005, 2008 Nico Pranke , Robin Luedtke + * + * This file is part of avcap. + * + * avcap is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * avcap is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with avcap. If not, see . + */ + +/* avcap is free for non-commercial use. + * To use it in commercial endeavors, please contact Nico Pranke . + */ + + +#ifndef DSCONNECTOR_H_ +#define DSCONNECTOR_H_ + +#include +#include + +#include "Connector.h" +#include "DeviceDescriptor.h" +#include "avcap-export.h" + +namespace avcap +{ +// forward declaration +class DS_DeviceDescriptor; +class DS_Tuner; + + //! Implementation of the Connector for DirectShow. + + + class AVCAP_Export DS_Connector : public Connector + { + public: + +#pragma warning( disable : 4800 ) + enum { + INPUT_TYPE_TUNER = 0x00010000 // CAP_TUNERDEVICE --> see "HelpFunc.h" file + }; + + protected: + DS_DeviceDescriptor* mDSDeviceDescriptor; + int mIndex; + int mAudioset; + int mType; + std::string mName; + DS_Tuner* mTuner; + + public: + /*! The Constructor. */ + DS_Connector(DS_DeviceDescriptor *dd, int index, const std::string& name, + int type=0, int audioset=0, int tuner=0); + + /*! The Destructor. */ + virtual ~DS_Connector(); + + bool hasTuner() const + { return mType & INPUT_TYPE_TUNER; } + }; +} + +#endif // DSConnector_H_ diff --git a/extra_lib/include/avcap/windows/DS_ConnectorManager.h b/extra_lib/include/avcap/windows/DS_ConnectorManager.h new file mode 100644 index 0000000..5dce4fa --- /dev/null +++ b/extra_lib/include/avcap/windows/DS_ConnectorManager.h @@ -0,0 +1,91 @@ +/* + * (c) 2005, 2008 Nico Pranke , Robin Luedtke + * + * This file is part of avcap. + * + * avcap is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * avcap is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with avcap. If not, see . + */ + +/* avcap is free for non-commercial use. + * To use it in commercial endeavors, please contact Nico Pranke . + */ + + +#ifndef DSCONNECTORMANAGER_H_ +#define DSCONNECTORMANAGER_H_ + +#include + +#include "Connector.h" +#include "ConnectorManager.h" +#include "avcap-export.h" + +class IPin; +class IBaseFilter; + +namespace avcap +{ +class DS_DeviceDescriptor; +class DS_Connector; + + //! DirectShow implementation of the ConnectorManager. + + class AVCAP_Export DS_ConnectorManager : public ConnectorManager + { + private: + void* mCrossbar; + DS_DeviceDescriptor* mDSDeviceDescriptor; + + public: + + /*! Construct the manager and query for available inputs and outputs for audio and video. + * The manager is usualy created by an CaptureDevice object. + * \param dd The DeviceDescriptor to acces the device. */ + DS_ConnectorManager(DS_DeviceDescriptor *dd); + + /*! The destructor. */ + virtual ~DS_ConnectorManager(); + + Connector* getVideoInput(); + int setVideoInput(Connector* c); + + Connector* getAudioInput(); + int setAudioInput(Connector* c); + + Connector* getVideoOutput(); + int setVideoOutput(Connector* c); + + Connector* getAudioOutput(); + int setAudioOutput(Connector* c); + + void query(); + + private: + + int setConnector(bool IsVideoConnector, Connector* c); + + Connector* getConnector(bool IsVideoConnector); + + Connector* findByIndex(const ListType& l, int index); + + void getIPinFromConnector(Connector *con, IPin **ConnectorPin, + IBaseFilter *CaptureFilter); + + bool removeAllFilters(IPin *OutputPinToStart); + + bool getPinCategory(IPin *Pin, GUID *PinCategory); + }; +} + +#endif // DSCONNECTORMANAGER_H_ diff --git a/extra_lib/include/avcap/windows/DS_Control.h b/extra_lib/include/avcap/windows/DS_Control.h new file mode 100644 index 0000000..d968bbe --- /dev/null +++ b/extra_lib/include/avcap/windows/DS_Control.h @@ -0,0 +1,185 @@ +/* + * (c) 2005, 2008 Nico Pranke , Robin Luedtke + * + * This file is part of avcap. + * + * avcap is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * avcap is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with avcap. If not, see . + */ + +/* avcap is free for non-commercial use. + * To use it in commercial endeavors, please contact Nico Pranke . + */ + + +#ifndef DSCONTROL_H_ +#define DSCONTROL_H_ + +#include + +#include "Control_avcap.h" +#include "Interval.h" +#include "avcap-export.h" + +namespace avcap +{ +class DS_DeviceDescriptor; + + //! Implementation of the Control-class for DirectShow. + + class AVCAP_Export DS_Control + { + public: + enum Ctrl_Type + { + CTRL_TYPE_INTEGER = 1, + CTRL_TYPE_BOOLEAN = 2, + }; + + enum DShowInterface + { + IAM_VIDEOPROCAMP = 1, + IAM_CAMERACONTROL = 2, + }; + + //! Helper-class to query control-properties + + struct Query_Ctrl + { + unsigned int id; + Ctrl_Type type; + std::string name; + + long minimum; + long maximum; + long step; + long default_value; + long flags; + long property; + + DS_Control::DShowInterface DShowInterfaceType; + }; + + private: + DShowInterface mDShowInterfaceType; + long mProperty; + int mId; + int mValue; + std::string mName; + int mDefaultValue; + int mFlags; + bool mInitialized; + + protected: + DS_DeviceDescriptor* mDSDeviceDescriptor; + + public: + DS_Control(DS_DeviceDescriptor *dd, Query_Ctrl* query); + virtual ~DS_Control(); + + inline int getId() const + { return mId; } + + inline int getDefaultValue() const + { return mDefaultValue; } + + inline const std::string& getName() const + { return mName; } + + int setValue(int val); + + int getValue() const; + + int reset(); + + private: + int update(); + + int retrieveValue(); + }; + + //! DirectShow integer valued control. + + class DS_IntControl : public IntegerControl + { + private: + Interval mInterval; + DS_Control mControlBase; + + public: + inline DS_IntControl(DS_DeviceDescriptor *dd, struct DS_Control::Query_Ctrl* query) : + mInterval(query->minimum, query->maximum, query->step), + mControlBase(dd, query) + {} + + virtual inline ~DS_IntControl() + {} + + inline const Interval& getInterval() const + { return mInterval; } + + virtual inline int getId() const + { return mControlBase.getId(); } + + virtual inline int getDefaultValue() const + { return mControlBase.getDefaultValue(); } + + virtual inline const std::string& getName() const + { return mControlBase.getName(); } + + virtual inline int setValue(int val) + { return mControlBase.setValue(val); } + + virtual inline int getValue() const + { return mControlBase.getValue(); } + + virtual inline int reset() + { return mControlBase.reset(); } + }; + + //! DirectShow boolean control. + + class DS_BoolControl : public BoolControl + { + private: + DS_Control mControlBase; + + public: + inline DS_BoolControl(DS_DeviceDescriptor *dd, struct DS_Control::Query_Ctrl* query) : + mControlBase(dd, query) + {} + + virtual inline ~DS_BoolControl() + {} + + virtual inline int getId() const + { return mControlBase.getId(); } + + virtual inline int getDefaultValue() const + { return mControlBase.getDefaultValue(); } + + virtual inline const std::string& getName() const + { return mControlBase.getName(); } + + virtual inline int setValue(int val) + { return mControlBase.setValue(val); } + + virtual inline int getValue() const + { return mControlBase.getValue(); } + + virtual inline int reset() + { return mControlBase.reset(); } + }; +} + +#endif // DSCONTROL_H_ diff --git a/extra_lib/include/avcap/windows/DS_ControlManager.h b/extra_lib/include/avcap/windows/DS_ControlManager.h new file mode 100644 index 0000000..278ea84 --- /dev/null +++ b/extra_lib/include/avcap/windows/DS_ControlManager.h @@ -0,0 +1,80 @@ +/* + * (c) 2005, 2008 Nico Pranke , Robin Luedtke + * + * This file is part of avcap. + * + * avcap is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * avcap is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with avcap. If not, see . + */ + +/* avcap is free for non-commercial use. + * To use it in commercial endeavors, please contact Nico Pranke . + */ + + +#ifndef DSCONTROLMANAGER_H_ +#define DSCONTROLMANAGER_H_ + +#include +#include + +#include "ControlManager.h" +#include "DS_Control.h" +#include "avcap-export.h" + +namespace avcap +{ +class DS_DeviceDescriptor; + + //! DirectShow ControlManager implementation. + + /*! DirectShow doesn't provide any methods to enumerate driver- + * specific controls like V4L2 does. + * There are only few DirectShow COM-Interfaces (e.g. IAMExtTransport (for + * Firewire or other external devices), IAMVideoControl, IAMVideoCompression, IAMVideoProcAmp, + * IAMCameraControl --> see DirectShow documentation), the capture device DirectShow filter + * (only WDM capture devices) can expose to support the setting of global properties. The most + * important settings are covered by these DirectShow COM-Interfaces. + * In the future, DirectShow will provide more COM-Interfaces. + * + * The DirectShow COM-Interfaces IAMVideoProcAmp and IAMCameraControl have been already + * implemented in the avcap-library. + * + * Most WDM capture devices and \b all VFW capture devices have their own driver-supplied dialog + * boxes with user-setable controls on it, that can't be set programmatically. */ + + class AVCAP_Export DS_ControlManager : public ControlManager + { + private: + DS_DeviceDescriptor* mDSDeviceDescriptor; + + public: + DS_ControlManager(DS_DeviceDescriptor *dd); + + virtual ~DS_ControlManager(); + + Control* getControl(const std::string& name); + + Control* getControl(int id); + + const ListType& getControlList() + { return (const ListType&) mControls; } + + void query(); + + private: + void query(int start_id, int end_id); + }; +} + +#endif diff --git a/extra_lib/include/avcap/windows/DS_Device.h b/extra_lib/include/avcap/windows/DS_Device.h new file mode 100644 index 0000000..1575a6f --- /dev/null +++ b/extra_lib/include/avcap/windows/DS_Device.h @@ -0,0 +1,82 @@ +/* + * (c) 2005, 2008 Nico Pranke , Robin Luedtke + * + * This file is part of avcap. + * + * avcap is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * avcap is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with avcap. If not, see . + */ + +/* avcap is free for non-commercial use. + * To use it in commercial endeavors, please contact Nico Pranke . + */ + + +#ifndef DSDEVICE_H_ +#define DSDEVICE_H_ + +#include +#include + +#include "DS_DeviceDescriptor.h" +#include "CaptureDevice.h" +#include "avcap-export.h" + +namespace avcap +{ +// forward declarations +class DS_ConnectorManager; +class DS_ControlManager; +class DS_VidCapManager; +class DS_DeviceDescriptor; +class DS_FormatManager; + + //! Implementation of the CaptureDevice for DirectShow. + + class AVCAP_Export DS_Device : public CaptureDevice + { + private: + DS_VidCapManager* mVidCapMgr; + DS_ConnectorManager* mConnectorMgr; + DS_ControlManager* mControlMgr; + DS_FormatManager* mFormatMgr; + DS_DeviceDescriptor* mDSDeviceDescriptor; + + public: + DS_Device(DS_DeviceDescriptor* dd); + + virtual ~DS_Device(); + + inline const DeviceDescriptor* getDescriptor() + { return (const DeviceDescriptor*) mDSDeviceDescriptor; } + + inline CaptureManager* getVidCapMgr() + { return (CaptureManager*) mVidCapMgr; } + + inline ConnectorManager* getConnectorMgr() + { return (ConnectorManager*) mConnectorMgr; } + + inline ControlManager* getControlMgr() + { return (ControlManager*) mControlMgr; } + + inline FormatManager* getFormatMgr() + { return (FormatManager*)mFormatMgr; } + + private: + int open(); + + int close(); + }; +} + +#endif // DSDEVICE_H_ diff --git a/extra_lib/include/avcap/windows/DS_DeviceDescriptor.h b/extra_lib/include/avcap/windows/DS_DeviceDescriptor.h new file mode 100644 index 0000000..6f96b3c --- /dev/null +++ b/extra_lib/include/avcap/windows/DS_DeviceDescriptor.h @@ -0,0 +1,131 @@ +/* + * (c) 2005, 2008 Nico Pranke , Robin Luedtke + * + * This file is part of avcap. + * + * avcap is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * avcap is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with avcap. If not, see . + */ + +/* avcap is free for non-commercial use. + * To use it in commercial endeavors, please contact Nico Pranke . + */ + + +#ifndef DSDEVICEDESCRIPTOR_H_ +#define DSDEVICEDESCRIPTOR_H_ + +#include +#include +#include + +#include "DeviceDescriptor.h" +#include "avcap-export.h" + +class IBaseFilter; + +namespace avcap +{ +class CaptureDevice; +class DS_Device; + + //! Implementation of the DeviceDescriptor for DirectShow. + + class AVCAP_Export DS_DeviceDescriptor : public DeviceDescriptor + { + public: + // Used by AddToRot() and RemoveFromRot() for spying on + // filter graph with GraphEdit software + unsigned long mRegister; // For debugging purpose + + private: + std::string mName; + std::string mCard; + std::string mInfo; + + int mCapabilities; + DEV_HANDLE_T mHandle; + bool mValid; + bool mIsOpen; + DS_Device* mDevice; + + public: + DS_DeviceDescriptor(const std::string &card); + + virtual ~DS_DeviceDescriptor(); + + virtual CaptureDevice* getDevice(); + + int open(); + + int close(); + + virtual inline const std::string& getName() const + { return mName; } + + // implementation of pure virtual method + inline const DEV_HANDLE_T getHandle() const + { return mHandle; } + + // but non-const access is needed by the device-class + inline DEV_HANDLE_T getHandle() + { return mHandle; } + + bool isAVDev() const; + + bool isVideoCaptureDev() const; + + bool isVBIDev() const; + + bool isTuner() const; + + bool isAudioDev() const; + + bool isRadioDev() const; + + bool isOverlayDev() const; + + //! Device is a VFW (Video for Windows) device. + /*! \return true VFW device, else false */ + bool isVfWDevice() const; + + private: + bool queryCapabilities(); + + void findDevice(std::string &UniqueDeviceID, IBaseFilter **CaptureFilter, + std::string &DeviceName, std::string &DeviceInfo, + bool *IsVideoDevice=NULL, bool *IsAudioDevice=NULL); + + bool createCaptureFilterGraph(std::string &UniqueDeviceID, + IBaseFilter *CaptureFilter); + + bool getInfosFromDevice(IBaseFilter *CaptureFilter, int *Capabilities, + std::string &Card, std::string &Info); + + bool findOverlaySupport(IBaseFilter *CaptureFilter, int *Capabilities); + + bool findVBISupport(IBaseFilter *CaptureFilter, int *Capabilities); + + bool findTunerRadioSupport(IBaseFilter *CaptureFilter, int *Capabilities); + + bool isAudioOrVideoDevice(IBaseFilter *CaptureFilter, int *Capabilities); + + HRESULT addToRot(IUnknown *pUnkGraph, DWORD *pdwRegister); + + void removeFromRot(DWORD pdwRegister); + + friend class DS_Device; + }; +} + +#endif // DSEVICEDESCRIPTOR_H_ diff --git a/extra_lib/include/avcap/windows/DS_FormatManager.h b/extra_lib/include/avcap/windows/DS_FormatManager.h new file mode 100644 index 0000000..2d773b1 --- /dev/null +++ b/extra_lib/include/avcap/windows/DS_FormatManager.h @@ -0,0 +1,101 @@ +/* + * (c) 2005, 2008 Nico Pranke , Robin Luedtke + * + * This file is part of avcap. + * + * avcap is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * avcap is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with avcap. If not, see . + */ + +/* avcap is free for non-commercial use. + * To use it in commercial endeavors, please contact Nico Pranke . + */ + +#ifndef DSFORMATMANAGER_H_ +#define DSFORMATMANAGER_H_ + +#include +#include +#include + +#ifndef _MSC_VER +# include +#endif + +#include "FormatManager.h" +#include "avcap-export.h" + +class IBaseFilter; +class IPin; + +namespace avcap +{ +class DS_DeviceDescriptor; + + //! Implementation of the FormatManager for DirectShow. + + /*! A capture device that still uses a VFW (Video for Windows) + * driver can select the available formats only by using the VFW driver-supplied dialog box. + * But GUI-related stuff is out of the scope of this library. */ + + class AVCAP_Export DS_FormatManager : public FormatManager + { + private: + DS_DeviceDescriptor* mDSDeviceDescriptor; + int mFrameRate; + + public: + DS_FormatManager(DS_DeviceDescriptor *dd); + + virtual ~DS_FormatManager(); + + virtual int setFormat(Format *fmt); + + virtual Format* getFormat(); + + virtual int setResolution(int w, int h); + + virtual int getWidth(); + + virtual int getHeight(); + + virtual int getBytesPerLine(); + + virtual int setFramerate(int fps); + + virtual int getFramerate(); + + virtual int flush(); + + virtual size_t getImageSize(); + + virtual const VideoStandard* getVideoStandard(); + + virtual int setVideoStandard(const VideoStandard* std); + + virtual void query(); + + private: + int getParams(); + + void queryVideoStandards(); + + bool getVideoInfoHeader(AM_MEDIA_TYPE *MediaType, + VIDEOINFOHEADER *VideoInfoHeader); + + bool getCurrentConnectedVideoPin(IBaseFilter *CaptureFilter, + IPin **VideoPin); + }; +} + +#endif // DSFORMATMANAGER_H_ diff --git a/extra_lib/include/avcap/windows/DS_Tuner.h b/extra_lib/include/avcap/windows/DS_Tuner.h new file mode 100644 index 0000000..187d2a8 --- /dev/null +++ b/extra_lib/include/avcap/windows/DS_Tuner.h @@ -0,0 +1,134 @@ +/* + * (c) 2005, 2008 Nico Pranke , Robin Luedtke + * + * This file is part of avcap. + * + * avcap is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * avcap is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with avcap. If not, see . + */ + +/* avcap is free for non-commercial use. + * To use it in commercial endeavors, please contact Nico Pranke . + */ + + +#ifndef DSTUNER_H_ +#define DSTUNER_H_ + +#include + +#include "Tuner_avcap.h" +#include "avcap-export.h" + +class IAMTVAudio; +class IAMTVTuner; +class IBaseFilter; + +namespace avcap +{ +class DS_DeviceDescriptor; + + //! Implementation of the Tuner-class for DirectShow. + + /*! This implementation uses the PROPSETID_TUNER property-set which is defined in + * the library kstvtuner.ax which can not be linked with gcc. + * It thus can not be used under mingw, but only if build with Visual-C++. + * So the mingw-build library misses this tuner-implentation. + * + * There are only three states of signal strength: + * signal present, signal not present, signal not determined. Because of this limitation + * the method getAFCValue() is not implemented. + */ + + class AVCAP_Export DS_Tuner : public Tuner + { + private: + +#pragma warning( disable : 4800 ) + + enum + { + TUNER_RADIO = 0x00080000, // CAP_RADIO --> see "HelpFunc.h" file + TUNER_ANALOG_TV = 0x00020000 // CAP_TUNER --> see "HelpFunc.h" file + }; + + int mIndex; + std::string mName; + int mType; + int mCapabilities; + unsigned int mRangeHigh; + unsigned int mRangeLow; + double mStep; + double mFreq; + + private: + DS_DeviceDescriptor* mDSDeviceDescriptor; + + public: + DS_Tuner(DS_DeviceDescriptor *dd, int index, const std::string &name, + int type, int caps, unsigned int high, unsigned int low); + + virtual ~DS_Tuner(); + + inline bool isRadioTuner() const + { return mType & TUNER_RADIO; } + + inline bool isTVTuner() const + { return mType & TUNER_ANALOG_TV; } + + int setStereo(); + + int setMono(); + + int setSAP(); + + int setLang1(); + + int setLang2(); + + double getFreq() const; + + inline double getFreqStep() const + { return mStep; } + + inline double getMinFreq() const + { return mRangeLow*mStep; } + + inline double getMaxFreq() const + { return mRangeHigh*mStep; } + + const std::string getName() const + { return mName; } + + int getSignalStrength() const; + + int increaseFreq(); + + int decreaseFreq(); + + int setFreq(double f); + + private: + int setAudioMode(int mode); + + int getAudioMode(); + + bool getIAMTVTunerInterfaceFromFilter(IBaseFilter *Filter, + IAMTVTuner **Tuner) const; + + bool getIAMTVAudioInterfaceFromFilter(IBaseFilter *Filter, + IAMTVAudio **Audio); + }; +} + +#endif // DSTUNER_H_ diff --git a/extra_lib/include/avcap/windows/DS_VidCapManager.h b/extra_lib/include/avcap/windows/DS_VidCapManager.h new file mode 100644 index 0000000..d606e88 --- /dev/null +++ b/extra_lib/include/avcap/windows/DS_VidCapManager.h @@ -0,0 +1,98 @@ +/* + * (c) 2005, 2008 Nico Pranke , Robin Luedtke + * + * This file is part of avcap. + * + * avcap is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * avcap is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with avcap. If not, see . + */ + +/* avcap is free for non-commercial use. + * To use it in commercial endeavors, please contact Nico Pranke . + */ + + +#ifndef DSVIDCAPMANAGER_H_ +#define DSVIDCAPMANAGER_H_ + +#include + +#include "CaptureManager.h" +#include "SampleGrabberCallback.h" +#include "avcap-export.h" + +namespace avcap +{ +class DS_DeviceDescriptor; +class DS_FormatManager; +class IOBuffer; +class CaptureHandler; +class SampleGrabberCallback; + + //! Implementation of the CaptureManager for DirectShow. + + class AVCAP_Export DS_VidCapManager : public CaptureManager + { + private: + typedef std::list IOBufList; + + DS_DeviceDescriptor* mDSDeviceDescriptor; + SampleGrabberCallback* mGrabberCallback; + HANDLE mLock; + int mSequence; + int mNumBuffers; + + public: + DS_VidCapManager(DS_DeviceDescriptor* dd, DS_FormatManager* fmt_mgr, + int nbufs = DEFAULT_BUFFERS); + + virtual ~DS_VidCapManager(); + + int init(); + + int destroy(); + + int startCapture(); + + int stopCapture(); + + int getNumIOBuffers(); + + private: + IOBuffer* dequeue(); + + int enqueue(IOBuffer* buf); + + friend class IOBuffer; + friend class SampleGrabberCallback; + }; + + class MutexGuard + { + private: + HANDLE mLock; + + public: + inline MutexGuard(HANDLE lock) : + mLock(lock) + { WaitForSingleObject(mLock, INFINITE); } + + inline virtual ~MutexGuard() + { ReleaseMutex(mLock); } + private: + inline MutexGuard() + {} + }; +} + +#endif // DSVIDCAPMANAGER_H_ diff --git a/extra_lib/include/avcap/windows/FormatNames.h b/extra_lib/include/avcap/windows/FormatNames.h new file mode 100644 index 0000000..e85b449 --- /dev/null +++ b/extra_lib/include/avcap/windows/FormatNames.h @@ -0,0 +1,356 @@ +/* + * (c) 2005, 2008 Nico Pranke , Robin Luedtke + * + * This file is part of avcap. + * + * avcap is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * avcap is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with avcap. If not, see . + */ + +/* avcap is free for non-commercial use. + * To use it in commercial endeavors, please contact Nico Pranke . + */ + + +#ifndef FORMATNAMES_H_ +#define FORMATNAMES_H_ + +#include +#include + +void GetVideoFormatName(const GUID* guid, std::string& FormatName) +{ + // list creation date:05/27/2005 - taken from DirectShow documentation + + // Uncompressed RGB video subtypes + if (*guid==MEDIASUBTYPE_ARGB1555) { + FormatName="RGB 555 with alpha channel"; + return; + } + if (*guid==MEDIASUBTYPE_ARGB4444) { + FormatName="16-bit RGB with alpha channel; 4 bits per channel"; + return; + } + if (*guid==MEDIASUBTYPE_ARGB32) { + FormatName="RGB 32 with alpha channel"; + return; + } + if (*guid==MEDIASUBTYPE_A2R10G10B10) { + FormatName + ="32-bit RGB with alpha channel; 10 bits per RGB channel plus 2 bits for alpha"; + return; + } + if (*guid==MEDIASUBTYPE_A2B10G10R10) { + FormatName + ="32-bit RGB with alpha channel; 10 bits per RGB channel plus 2 bits for alpha"; + return; + } + if (*guid==MEDIASUBTYPE_RGB1) { + FormatName="RGB, 1 bit per pixel (bpp), palettized"; + return; + } + if (*guid==MEDIASUBTYPE_RGB4) { + FormatName="RGB, 4 bpp, palettized"; + return; + } + if (*guid==MEDIASUBTYPE_RGB8) { + FormatName="RGB, 8 bpp"; + return; + } + if (*guid==MEDIASUBTYPE_RGB555) { + FormatName="RGB 555, 16 bpp"; + return; + } + if (*guid==MEDIASUBTYPE_RGB565) { + FormatName="RGB 565, 16 bpp"; + return; + } + if (*guid==MEDIASUBTYPE_RGB24) { + FormatName="RGB, 24 bpp"; + return; + } + if (*guid==MEDIASUBTYPE_RGB32) { + FormatName="RGB, 32 bpp"; + return; + } + + // DV video subtypes + if (*guid==MEDIASUBTYPE_dvsl) { + FormatName="DV, 12.5 Mbps SD-DVCR 525-60 or SD-DVCR 625-50"; + return; + } + if (*guid==MEDIASUBTYPE_dvsd) { + FormatName="DV, 25 Mbps SDL-DVCR 525-60 or SDL-DVCR 625-50"; + return; + } + if (*guid==MEDIASUBTYPE_dvhd) { + FormatName="DV, 50 Mbps HD-DVCR 1125-60 or HD-DVCR 1250-50"; + return; + } + if (*guid==MEDIASUBTYPE_dv25) { + FormatName="DV, 25 Mbps DVCPRO 25 (525-60 or 625-50)"; + return; + } + if (*guid==MEDIASUBTYPE_dv50) { + FormatName="DV, 50 Mbps DVCPRO 50 (525-60 or 625-50)"; + return; + } + if (*guid==MEDIASUBTYPE_dvh1) { + FormatName="DV, 100 Mbps DVCPRO 100 (1080/60i, 1080/50i, or 720/60P)"; + return; + } + + // YUV video subtypes + if (*guid==MEDIASUBTYPE_AYUV) { + FormatName="4:4:4 YUV formats"; + return; + } + if (*guid==MEDIASUBTYPE_UYVY) { + FormatName="UYVY (packed 4:2:2)"; + return; + } + if (*guid==MEDIASUBTYPE_Y411) { + FormatName="Y411 (packed 4:1:1)"; + return; + } + if (*guid==MEDIASUBTYPE_Y41P) { + FormatName="Y41P (packed 4:1:1)"; + return; + } + if (*guid==MEDIASUBTYPE_Y211) { + FormatName="Y211"; + return; + } + if (*guid==MEDIASUBTYPE_YUY2) { + FormatName="YUYV (packed 4:2:2)"; + return; + } + if (*guid==MEDIASUBTYPE_YVYU) { + FormatName="YVYU (packed 4:2:2)"; + return; + } + if (*guid==MEDIASUBTYPE_YUYV) { + FormatName="YUYV (packed 4:2:2)(Used by Canopus; FOURCC 'YUYV')"; + return; + } + if (*guid==MEDIASUBTYPE_IF09) { + FormatName="Indeo YVU9"; + return; + } + if (*guid==MEDIASUBTYPE_IYUV) { + FormatName="IYUV"; + return; + } + /* + if (*guid==MEDIASUBTYPE_I420) { + FormatName="I420"; + return; + } + */ + if (*guid==MEDIASUBTYPE_YV12) { + FormatName="YV12"; + return; + } + if (*guid==MEDIASUBTYPE_YVU9) { + FormatName="YVU9"; + return; + } + + // Miscellaneous video subtypes + if (*guid==MEDIASUBTYPE_CFCC) { + FormatName="MJPG format produced by some cards. (FOURCC 'CFCC')"; + return; + } + if (*guid==MEDIASUBTYPE_CLJR) { + FormatName="Cirrus Logic CLJR format. (FOURCC 'CLJR')"; + return; + } + if (*guid==MEDIASUBTYPE_CPLA) { + FormatName="Cinepak UYVY format. (FOURCC 'CPLA')"; + return; + } + if (*guid==MEDIASUBTYPE_CLPL) { + FormatName + ="A YUV format supported by some Cirrus Logic drivers. (FOURCC 'CLPL')"; + return; + } + if (*guid==MEDIASUBTYPE_IJPG) { + FormatName="Intergraph JPEG format. (FOURCC 'IJPG')"; + return; + } + if (*guid==MEDIASUBTYPE_MDVF) { + FormatName="A DV encoding format. (FOURCC 'MDVF')"; + return; + } + if (*guid==MEDIASUBTYPE_MJPG) { + FormatName="Motion JPEG (MJPG) compressed video. (FOURCC 'MJPG')"; + return; + } + if (*guid==MEDIASUBTYPE_MPEG1Packet) { + FormatName="MPEG1 Video Packet"; + return; + } + if (*guid==MEDIASUBTYPE_MPEG1Payload) { + FormatName="MPEG1 Video Payload"; + return; + } + if (*guid==MEDIASUBTYPE_Overlay) { + FormatName="Video delivered using hardware overlay"; + return; + } + if (*guid==MEDIASUBTYPE_Plum) { + FormatName="Plum MJPG format. (FOURCC 'Plum')"; + return; + } + if (*guid==MEDIASUBTYPE_QTJpeg) { + FormatName="QuickTime JPEG compressed data"; + return; + } + if (*guid==MEDIASUBTYPE_QTMovie) { + FormatName="Apple® QuickTime® compression"; + return; + } + if (*guid==MEDIASUBTYPE_QTRle) { + FormatName="QuickTime RLE compressed data"; + return; + } + if (*guid==MEDIASUBTYPE_QTRpza) { + FormatName="QuickTime RPZA compressed data"; + return; + } + if (*guid==MEDIASUBTYPE_QTSmc) { + FormatName="QuickTime SMC compressed data"; + return; + } + if (*guid==MEDIASUBTYPE_TVMJ) { + FormatName="TrueVision MJPG format. (FOURCC 'TVMJ')"; + return; + } + if (*guid==MEDIASUBTYPE_VPVBI) { + FormatName="Video port vertical blanking interval (VBI) data"; + return; + } + if (*guid==MEDIASUBTYPE_VPVideo) { + FormatName="Video port video data"; + return; + } + if (*guid==MEDIASUBTYPE_WAKE) { + FormatName="MJPG format produced by some cards. (FOURCC 'WAKE')"; + return; + } + + if (*guid==MEDIASUBTYPE_MPEG1Video) { + FormatName="MPEG1 Video"; + return; + } + if (*guid==MEDIASUBTYPE_MPEG2_VIDEO) { + FormatName="MPEG2 Video"; + return; + } + + FormatName="Unknown format type!"; + return; +} + +void GetVideoStandardName(ULONG VideoStandard, + std::list& VidStandard) +{ + // list creation date:05/27/2005 - taken from DirectShow documentation + + if (VideoStandard & AnalogVideo_None) { + avcap::VideoStandard *vidSt=new avcap::VideoStandard("Digital sensor", 0); + VidStandard.push_back(vidSt); + } + if (VideoStandard & AnalogVideo_NTSC_M) { + avcap::VideoStandard *vidSt=new avcap::VideoStandard("NTSC (M) standard, 7.5 IRE black", 0x1); + VidStandard.push_back(vidSt); + } + if (VideoStandard & AnalogVideo_NTSC_M_J) { + avcap::VideoStandard *vidSt=new avcap::VideoStandard("NTSC (M) standard, 0 IRE black (Japan)", 0x2); + VidStandard.push_back(vidSt); + } + if (VideoStandard & AnalogVideo_NTSC_433) { + avcap::VideoStandard *vidSt=new avcap::VideoStandard("NTSC-433", 0x4); + VidStandard.push_back(vidSt); + } + if (VideoStandard & AnalogVideo_PAL_B) { + avcap::VideoStandard *vidSt=new avcap::VideoStandard("PAL-B standard", 0x10); + VidStandard.push_back(vidSt); + } + if (VideoStandard & AnalogVideo_PAL_D) { + avcap::VideoStandard *vidSt=new avcap::VideoStandard("PAL (D) standard", 0x20); + VidStandard.push_back(vidSt); + } + if (VideoStandard & AnalogVideo_PAL_G) { + avcap::VideoStandard *vidSt=new avcap::VideoStandard("PAL (G) standard", 0x40); + VidStandard.push_back(vidSt); + } + if (VideoStandard & AnalogVideo_PAL_H) { + avcap::VideoStandard *vidSt=new avcap::VideoStandard("PAL (H) standard", 0x80); + VidStandard.push_back(vidSt); + } + if (VideoStandard & AnalogVideo_PAL_I) { + avcap::VideoStandard *vidSt=new avcap::VideoStandard("PAL (I) standard", 0x100); + VidStandard.push_back(vidSt); + } + if (VideoStandard & AnalogVideo_PAL_M) { + avcap::VideoStandard *vidSt=new avcap::VideoStandard("PAL (M) standard", 0x200); + VidStandard.push_back(vidSt); + } + if (VideoStandard & AnalogVideo_PAL_N) { + avcap::VideoStandard *vidSt=new avcap::VideoStandard("PAL (N) standard", 0x400); + VidStandard.push_back(vidSt); + } + if (VideoStandard & AnalogVideo_PAL_60) { + avcap::VideoStandard *vidSt=new avcap::VideoStandard("PAL-60 standard", 0x800); + VidStandard.push_back(vidSt); + } + if (VideoStandard & AnalogVideo_SECAM_B) { + avcap::VideoStandard *vidSt=new avcap::VideoStandard("SECAM (B) standard", 0x1000); + VidStandard.push_back(vidSt); + } + if (VideoStandard & AnalogVideo_SECAM_D) { + avcap::VideoStandard *vidSt=new avcap::VideoStandard("SECAM (D) standard", 0x2000); + VidStandard.push_back(vidSt); + } + if (VideoStandard & AnalogVideo_SECAM_G) { + avcap::VideoStandard *vidSt=new avcap::VideoStandard("SECAM (G) standard", 0x4000); + VidStandard.push_back(vidSt); + } + if (VideoStandard & AnalogVideo_SECAM_H) { + avcap::VideoStandard *vidSt=new avcap::VideoStandard("SECAM (H) standard", 0x8000); + VidStandard.push_back(vidSt); + } + if (VideoStandard & AnalogVideo_SECAM_K) { + avcap::VideoStandard *vidSt=new avcap::VideoStandard("SECAM (K) standard", 0x10000); + VidStandard.push_back(vidSt); + } + if (VideoStandard & AnalogVideo_SECAM_K1) { + avcap::VideoStandard *vidSt=new avcap::VideoStandard("SECAM (K1) standard", 0x20000); + VidStandard.push_back(vidSt); + } + if (VideoStandard & AnalogVideo_SECAM_L) { + avcap::VideoStandard *vidSt=new avcap::VideoStandard("SECAM (L) standard", 0x40000); + VidStandard.push_back(vidSt); + } + if (VideoStandard & AnalogVideo_SECAM_L1) { + avcap::VideoStandard *vidSt=new avcap::VideoStandard("SECAM (L1) standard", 0x80000); + VidStandard.push_back(vidSt); + } + if (VideoStandard & AnalogVideo_PAL_N_COMBO) { + avcap::VideoStandard *vidSt=new avcap::VideoStandard("Combination (N) PAL standard (Argentina)", 0x100000); + VidStandard.push_back(vidSt); + } +} + +#endif // FORMATNAMES_H_ diff --git a/extra_lib/include/avcap/windows/HelpFunc.h b/extra_lib/include/avcap/windows/HelpFunc.h new file mode 100644 index 0000000..cf56b11 --- /dev/null +++ b/extra_lib/include/avcap/windows/HelpFunc.h @@ -0,0 +1,410 @@ +/* + * (c) 2005, 2008 Nico Pranke , Robin Luedtke + * + * This file is part of avcap. + * + * avcap is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * avcap is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with avcap. If not, see . + */ + +/* avcap is free for non-commercial use. + * To use it in commercial endeavors, please contact Nico Pranke . + */ + + +#ifndef HELPFUNC_H_ +#define HELPFUNC_H_ + +#include +#include +#include + +#include // DirectShow +#include // DeleteMediaType() function +#include // CLSID_NullRenderer/CLSID_ISampleGrabber definitions +#include + +#ifndef __STREAMS__ +# include +#endif + +// Some definitions +#define VIDEO_SAMPLEGRABBER_FILTER_NAME L"SampleGrabber Video" +#define AUDIO_SAMPLEGRABBER_FILTER_NAME L"SampleGrabber Audio" + +#define CAP_VIDEO_CAPTURE 0x00000001 /* Is a video capture device */ +#define CAP_VIDEO_OUTPUT 0x00000002 /* Is a video output device */ +#define CAP_VIDEO_OVERLAY 0x00000004 /* Can do video overlay */ +#define CAP_VBI_CAPTURE 0x00000010 /* Is a VBI capture device */ + +#define CAP_TUNERDEVICE 0x00010000 /* Has radio tuner or tv tuner */ +#define CAP_TUNER 0x00020000 /* Has a tuner */ +#define CAP_AUDIO_CAPTURE 0x00040000 /* Has audio support */ +#define CAP_RADIO 0x00080000 /* Is a radio device */ + +// Helper Functions -> see end of file +static inline void DeleteList(std::list &PinList); + +static inline void DeleteList(std::list &AudioCaptureFilterList); + +static inline void DeleteList(std::list &MediaTypeList); + +static inline void EnumMediaTypesOnPin(IPin *Pin, + std::list &MediaTypeList); + +static inline void EnumPinsOnFilter(IBaseFilter *Filter, + std::list &PinList); + +static inline bool GetFilterGraphFromFilter(IBaseFilter *Filter, + IGraphBuilder **FilterGraph, + ICaptureGraphBuilder2 **CaptureGraphBuilder=NULL); + +static inline char* WChar2Char(const wchar_t* szWChar); + +static inline bool FindTunerRadioSupport(IBaseFilter *CaptureFilter, + int *Capabilities); + +static inline bool RenderStream(IUnknown *FilterOrPinToRender, + bool RenderVideo, bool RenderAudio); + +static inline bool GetInstalledDeviceIDs( + std::list &UniqueDeviceIDList); + +static inline std::string bstr2string(BSTR bstr); + +static inline std::string bstr2string(BSTR bstr) +{ + + int length = ((DWORD*) bstr)[0]; + wchar_t* wchar_data = (wchar_t*) (((DWORD*) bstr) ); + + int converted_length = WideCharToMultiByte(CP_ACP, 0, wchar_data, -1, 0, 0, + 0, 0); + char* char_data = new char[converted_length]; + WideCharToMultiByte(CP_ACP, 0, wchar_data, -1, char_data, converted_length, + 0, 0); + std::string res = char_data; + delete[] char_data; + return res; +} + +//######################### DEBUG FUNCTIONS - MAKE THE FILTERGRAPH VISIBLE IN GRPAHEDIT ################### + +static inline void EnumMediaTypesOnPin(IPin *Pin, + std::list &MediaTypeList) +{ + QzCComPtr EnumMediaTypes; + if (Pin->EnumMediaTypes(&EnumMediaTypes)==S_OK) { + AM_MEDIA_TYPE *MediaType=NULL; + while (EnumMediaTypes->Next(1, &MediaType, NULL) == S_OK) { + MediaTypeList.push_back(MediaType); + } + } +} + +static inline void EnumPinsOnFilter(IBaseFilter *Filter, + std::list &PinList) +{ + QzCComPtr EnumPins; + IPin *Pin=NULL; + if (Filter->EnumPins(&EnumPins)==S_OK) { + while (EnumPins->Next(1, &Pin, 0) == S_OK) { + PinList.push_back(Pin); + } + } +} + +static inline bool GetFilterGraphFromFilter(IBaseFilter *Filter, + IGraphBuilder **FilterGraph, + ICaptureGraphBuilder2 **CaptureGraphBuilder) +{ + if (Filter==NULL) { + return false; + } + // Get the FilterGraph from the Filter + FILTER_INFO FilterInfo; + if (FAILED(Filter->QueryFilterInfo(&FilterInfo))) { + return false; + } + if (FilterInfo.pGraph==NULL) { + return false; + } + if (FAILED(FilterInfo.pGraph->QueryInterface(IID_IGraphBuilder, + (void**) &*FilterGraph))) { + return false; + } + // Get the CaptureGraphBuilder2 + // CaptureGraphBuilder is useful for building many kinds of custom filter graphs, not only capture graphs + if (CaptureGraphBuilder!=NULL) { + if (FAILED(CoCreateInstance(CLSID_CaptureGraphBuilder2, NULL, + CLSCTX_INPROC_SERVER, IID_ICaptureGraphBuilder2, + (void**)&*CaptureGraphBuilder))) { + return false; + } + if (FAILED((*CaptureGraphBuilder)->SetFiltergraph(*FilterGraph))) { + return false; + } + } + + return true; +} + +static inline char* WChar2Char(const wchar_t* szWChar) +{ + if (szWChar == NULL) { + return NULL; + } + char* szChar = NULL; + size_t size = 0; + if ((size = wcstombs(0, szWChar, 0)) == -1) { + return NULL; + } + szChar = new char[size + 1]; + szChar[size] = 0; + wcstombs(szChar, szWChar, size); + return szChar; +} + +static inline bool FindTunerRadioSupport(IBaseFilter *CaptureFilter, + int *Capabilities) +{ + if (CaptureFilter==NULL) { + return false; + } + + // Get the CaptureBuilderGraph COM-Interface from CaptureFilter + QzCComPtr CaptureGraphBuilder; + QzCComPtr FilterGraph; + if (!GetFilterGraphFromFilter(CaptureFilter, &FilterGraph, + &CaptureGraphBuilder)) { + return false; + } + + // Look for the tuner, radio + QzCComPtr Tuner; + if (SUCCEEDED(CaptureGraphBuilder->FindInterface(&LOOK_UPSTREAM_ONLY, NULL, + CaptureFilter, IID_IAMTVTuner, (void**)&Tuner))) { + long lModes = 0; + HRESULT hr = Tuner->GetAvailableModes(&lModes); + if (SUCCEEDED(hr) && (lModes & AMTUNER_MODE_FM_RADIO)) { + *Capabilities |= CAP_RADIO; + } + if (SUCCEEDED(hr) && (lModes & AMTUNER_MODE_TV)) { + *Capabilities |= CAP_TUNER; + } + } + + return true; +} + +static inline void GetInstalledAudioDevices( + std::list &AudioCaptureFilterList) +{ + // Enumerate the audio category + std::list EnumCategoriesList; + EnumCategoriesList.push_back(CLSID_AudioInputDeviceCategory); + + for (std::list::iterator EnumCategoriesListIter= + EnumCategoriesList.begin(); EnumCategoriesListIter + !=EnumCategoriesList.end(); EnumCategoriesListIter++) { + // Create the System Device Enumerator. + HRESULT hr; + ICreateDevEnum *SysDevEnum = NULL; + if (FAILED(CoCreateInstance(CLSID_SystemDeviceEnum, NULL, + CLSCTX_INPROC_SERVER, IID_ICreateDevEnum, (void **)&SysDevEnum))) { + return; + } + + // Obtain a class enumerator for the device capture category. + IEnumMoniker *EnumCat = NULL; + hr = SysDevEnum->CreateClassEnumerator(*EnumCategoriesListIter, + &EnumCat, 0); + + if (hr == S_OK) { + // Enumerate the monikers. + IMoniker *Moniker = NULL; + ULONG Fetched; + while (EnumCat->Next(1, &Moniker, &Fetched) == S_OK) { + IBaseFilter *AudioCaptureFilter=NULL; + if (SUCCEEDED(Moniker->BindToObject(0, 0, IID_IBaseFilter, + (void**)&AudioCaptureFilter))) { + AudioCaptureFilterList.push_back(AudioCaptureFilter); + } + else { + if (AudioCaptureFilter!=NULL) { + AudioCaptureFilter->Release(); + } + } + Moniker->Release(); + } + EnumCat->Release(); + } + SysDevEnum->Release(); + } +} + +static inline bool RenderStream(IUnknown *FilterOrPinToRender, + bool RenderVideo, bool RenderAudio) +{ + QzCComPtr FilterGraph; + QzCComPtr CaptureGraphBuilder; + + // Get the IGraphBuilder and CaptureGraphBuilder2 interfaces from pin or filter + QzCComPtr Pin; + bool IsPin=false; + if (SUCCEEDED(FilterOrPinToRender->QueryInterface(IID_IPin, (void**)&Pin))) // It's a pin + { + PIN_INFO PinInfo; + Pin->QueryPinInfo(&PinInfo); + IsPin=true; + + if (!GetFilterGraphFromFilter(PinInfo.pFilter, &FilterGraph, + &CaptureGraphBuilder)) { + return false; + } + } + else // It's a filter + { + if (!GetFilterGraphFromFilter((IBaseFilter*)FilterOrPinToRender, + &FilterGraph, &CaptureGraphBuilder)) { + return false; + } + } + + //if(SUCCEEDED(CaptureGraphBuilder->RenderStream(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Interleaved, CaptureFilter, NULL, NullRendererVideo))){return true;} + + // Build complete filter graph + if (RenderVideo) { + // Add NullRenderer filters because we want to set formats with IAMStreamConfig COM-Interface while the complete + // filtergraph is connected. NullRenderer filters and the samplegrabber filter accept all formats. + QzCComPtr RendererVideo; + if (FAILED(CoCreateInstance(CLSID_NullRenderer, 0, + CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void**)&RendererVideo))) { + return false; + } + if (FAILED(FilterGraph->AddFilter(RendererVideo,L"Video Null Renderer" ))) {return false;} + QzCComPtr SampleGrabberVideo; + if(FAILED(CoCreateInstance(CLSID_SampleGrabber, 0, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void**)&SampleGrabberVideo))) {return false;} + if(FAILED(FilterGraph->AddFilter(SampleGrabberVideo, VIDEO_SAMPLEGRABBER_FILTER_NAME))) {return false;} + // Set the video samplegrabber + QzCComPtr SampleGrabber; + if(FAILED(SampleGrabberVideo->QueryInterface(IID_ISampleGrabber, (void**)&SampleGrabber))) {return false;} + AM_MEDIA_TYPE mt; + mt.majortype=MEDIATYPE_Video; + mt.subtype=GUID_NULL; + mt.pUnk=NULL; + mt.cbFormat=0; + if(FAILED(SampleGrabber->SetMediaType(&mt))) {return false;} + //render the pin or filter + if(FAILED(CaptureGraphBuilder->RenderStream(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video, FilterOrPinToRender, SampleGrabberVideo, RendererVideo))) {return false;} + } + + if(RenderAudio) + { + // Add NullRenderer filters because we want to set formats with IAMStreamConfig COM-Interface while the complete + // filtergraph is connected. NullRenderer filters and the SampleGrabber filter accept all formats. + QzCComPtr RendererAudio; + CoCreateInstance(CLSID_NullRenderer, 0, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void**)&RendererAudio); //CLSID_DSoundRender for DirectSound Filter + FilterGraph->AddFilter(RendererAudio, L"Audio Null Renderer"); + QzCComPtr SampleGrabberAudio; + CoCreateInstance(CLSID_SampleGrabber, 0, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void**)&SampleGrabberAudio); + FilterGraph->AddFilter(SampleGrabberAudio, AUDIO_SAMPLEGRABBER_FILTER_NAME); + // Set the audio samplegrabber filter + QzCComPtr SampleGrabber; + SampleGrabberAudio->QueryInterface(IID_ISampleGrabber, (void**)&SampleGrabber); + AM_MEDIA_TYPE mt; + mt.majortype=MEDIATYPE_Audio; + mt.subtype=GUID_NULL; + mt.pUnk=NULL; + mt.cbFormat=0; + SampleGrabber->SetMediaType(&mt); + + //Render Audio + if(IsPin) //if we should render a pin + { + HRESULT hr=CaptureGraphBuilder->RenderStream(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Audio, FilterOrPinToRender, SampleGrabberAudio, RendererAudio); + if(hr!=S_OK) {return false;} + } + // If we should render a filter + + else if(FAILED(CaptureGraphBuilder->RenderStream(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Audio, FilterOrPinToRender, SampleGrabberAudio, RendererAudio))) + { + /* The avcap library doesn't provide full audio support at this time. There are only some basic audio + functions implemented (not implemented: audio capture functions, ...)*/ + + /* Some notes about DirectShow and audio capture + - Old VFW driver based capture devices don't support audio. + - New WDM driver based capture devices can support audio: + 1. The capture filter has audio output pins which have to be connected to a sound rendering DirectShow filter + 2. The capture filter has crossbar support and the audio output pins are on the crossbar and have to + be connected to a sound rendering DirectShow filter. + 3. Many capture devices (e.g. tv tuner cards) don't provide direct audio support. They have audio + connectors which have to be connected to a soundcard by cable. The soundcard takes up the + capture process of the audio data. In this case, a soundcard capture DirectShow filter is needed. + What we do if the function above fails?? We insert a audio capture DirectShow filter in the filtergraph + and connect it to the sound rendering DirectShow filter. What can we do if multiple soundcards + are installed - which audio capture DirectShow filter should we use?? Normally the user has to choose the + audio capture DirectShow filter. But because of the uncomplete audio implementation of the avcap library + we use the first audio capture DirectShow Filter we'll find in the system. */ + + /* TODO: Complete the audio functionality */ + + std::list AudioCaptureFilterList; + GetInstalledAudioDevices(AudioCaptureFilterList); + for(std::list::iterator Iter=AudioCaptureFilterList.begin(); Iter!=AudioCaptureFilterList.end(); Iter++) + { + FilterGraph->AddFilter((*Iter), L"AudioCaptureFilter"); + if(SUCCEEDED(CaptureGraphBuilder->RenderStream(NULL, &MEDIATYPE_Audio, (*Iter), SampleGrabberAudio, RendererAudio))) + { + break; + } + FilterGraph->RemoveFilter((*Iter)); + } + DeleteList(AudioCaptureFilterList); + } + } + + return true; +} + +static inline void DeleteList(std::list &PinList) +{ + for (std::list::iterator Iter=PinList.begin(); Iter!=PinList.end(); Iter++) { + if ((*Iter)!=NULL) { + (*Iter)->Release(); + } + } + PinList.clear(); +} + +static inline void DeleteList(std::list &AudioCaptureFilterList) +{ + for (std::list::iterator Iter=AudioCaptureFilterList.begin(); Iter + !=AudioCaptureFilterList.end(); Iter++) { + if ((*Iter)!=NULL) { + (*Iter)->Release(); + } + } + AudioCaptureFilterList.clear(); +} + +static inline void DeleteList(std::list &MediaTypeList) +{ + for (std::list::iterator Iter=MediaTypeList.begin(); Iter + !=MediaTypeList.end(); Iter++) { + DeleteMediaType(*Iter); + } + MediaTypeList.clear(); +} + +#endif diff --git a/extra_lib/include/avcap/windows/SampleGrabberCallback.h b/extra_lib/include/avcap/windows/SampleGrabberCallback.h new file mode 100644 index 0000000..195615d --- /dev/null +++ b/extra_lib/include/avcap/windows/SampleGrabberCallback.h @@ -0,0 +1,86 @@ +/* + * (c) 2005, 2008 Nico Pranke , Robin Luedtke + * + * This file is part of avcap. + * + * avcap is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * avcap is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with avcap. If not, see . + */ + +/* avcap is free for non-commercial use. + * To use it in commercial endeavors, please contact Nico Pranke . + */ + +#ifndef SAMPLEGRABBER_H_ +#define SAMPLEGRABBER_H_ + +#include "QEdit.h" + +#include "IOBuffer.h" +#include "avcap-export.h" + +namespace avcap +{ +class DS_VidCapManager; + + //! Data capture handler for DirectShow devices. + + /*! Each time new data arrives, the method + * SampleCB() is called and delivers the data to the video capture + * manager set by the SetVideoCaptureManager() method.*/ + + class AVCAP_Export SampleGrabberCallback : public ISampleGrabberCB + { + private: + DS_VidCapManager* mVidCapMngr; + ISampleGrabber* mSampleGrabberFilter; + HANDLE& mLock; + + public: + SampleGrabberCallback(HANDLE& lock); + + ~SampleGrabberCallback(); + + /*! Sets the video capture manager. + * New data will be delivered to the video capture manager. + * \param vidCapManager The video capture manager. */ + void SetVideoCaptureManager(DS_VidCapManager *vidCapManager); + + /*! Sets the samplegrabber filter. + * Needed to get some information (e.g. currently used format). + * \param SampleGrabberFilter The samplegrabber filter. */ + void SetSampleGrabberFilter(ISampleGrabber *SampleGrabberFilter); + + /* Fake referance counting - This is safe because the application + * creates the object on the stack, and the object remains in scope + * throughout the lifetime of the filter graph.*/ + STDMETHODIMP_(ULONG) AddRef() + { return 1; } + + STDMETHODIMP_(ULONG) Release() + { return 2; } + + STDMETHODIMP QueryInterface(REFIID riid, void **ppvObject); + + /*! New data (captured data) arrives here and will be + * delivered to the video capture manager set + * by the SetVideoCaptureManager() method. */ + STDMETHODIMP SampleCB(double Time, IMediaSample *pSample); + + STDMETHODIMP BufferCB(double Time, BYTE *pBuffer, long BufferLen); + + friend class IOBuffer; + }; +} + +#endif // SAMPLEGRABBER_H_ diff --git a/gui/extensions/widget_manager/init.js b/gui/extensions/widget_manager/init.js index e8e8b79..f839212 100644 --- a/gui/extensions/widget_manager/init.js +++ b/gui/extensions/widget_manager/init.js @@ -80,12 +80,12 @@ function open_widget_manager(extension) WidgetManager.last_widget_dir = directory; widget_insert_icon(new_wid); dock.layout(dock.width, dock.height); - open_dock(true); + show_dock(true); } filebrowse.on_directory = function(directory) { WidgetManager.last_widget_dir = directory; scan_directory(directory); - open_dock(true); + show_dock(true); } filebrowse.set_size(320 , 240); gpacui_show_window(filebrowse); @@ -100,7 +100,7 @@ function open_widget_manager(extension) widman_cfg_dlg.close(); widman_cfg_dlg = null; dock.layout(dock.width, dock.height); - open_dock(true); + show_dock(true); } widman_cfg_dlg.on_close = function() { widman_cfg_dlg = null; @@ -332,6 +332,49 @@ function on_widget_load() } +mpegu_targets = null; + +function select_mpegu_target(callback) +{ + if (mpegu_targets) return; + mpegu_targets = gw_new_file_open(); + mpegu_targets.set_label('Select Remote Display'); + mpegu_targets.callback = callback; + + mpegu_targets.on_close = function() { + mpegu_targets = null; + } + /*override browse function*/ + mpegu_targets._on_mpegu_click = function() { + var target = (this.render_idx<0) ? null : WidgetManager.get_mpegu_service_providers(this.render_idx); + + mpegu_targets.callback(target); + mpegu_targets.callback = null; + mpegu_targets.close(); + mpegu_targets = null; + } + mpegu_targets._browse = function(dir, up) { + var w, h, i, y; + this.area.reset_children(); + + for (i=0; ; i++) { + var target = WidgetManager.get_mpegu_service_providers(i); + if (!target) break; + var icon = 'icons/applications-internet.svg'; + var item = gw_new_icon_button(this.area, icon, target.Name, 'button'); + item.set_size(item.width, item.height); + item.render_idx = i; + item.on_click = this._on_mpegu_click; + } + this.layout(this.width, this.height); + } + mpegu_targets.browse(''); + mpegu_targets.go_up.hide(); + mpegu_targets.set_size(display_width, display_height); + gpacui_show_window(mpegu_targets); +} + + function new_widget_control(widget) { var ctrl; @@ -447,7 +490,7 @@ function new_widget_control(widget) if (UPnP_Enabled) { ctrl.remote = ctrl.add_tool(gwskin.images.remote_display, gwskin.labels.remote_display); ctrl.remote.on_click = function() { - var dlg = select_remote_display('callback', new_migrate_callback(this.dlg.widget) ); + var dlg = select_mpegu_target(new_migrate_callback(this.dlg.widget) ); }; ctrl.show_remote = function(show) { @@ -575,7 +618,7 @@ function on_widget_launch() { } else { widget_launch(this.widget); } - open_dock(false); + show_dock(false); } @@ -721,9 +764,10 @@ function widget_request_install(wid, args) break; } } + alert('opening widget '+wid_url); /*not found, install new widget*/ if (j == count) { - var new_wid = WidgetManager.open(wid_url, null); + var new_wid = WidgetManager.open(wid_url, null, wid); if (new_wid==null) return; widget_insert_icon(new_wid); } @@ -751,7 +795,7 @@ function widget_migrate_component(wid, args) WidgetManager.migrate_widget(UPnP.GetMediaRenderer(parseInt(args[1])), comp); widget_close(comp); } else { - var dlg = select_remote_display('callback', new_migrate_callback(comp) ); + var dlg = select_mpegu_target(new_migrate_callback(comp) ); } if (ifce != null) { wmjs_core_out_invoke_reply(coreOut.migrateComponentMessage, ifce.get_message("migrateComponent"), wid, 1); // send return code 1 = success diff --git a/gui/gui.js b/gui/gui.js index 6767db7..d09946b 100644 --- a/gui/gui.js +++ b/gui/gui.js @@ -28,6 +28,7 @@ UPnP_Enabled = false; browser_mode = false; upnp_renderers = null; current_url = ''; +current_duration = 0.0; current_time = 0.0; player_control = null; @@ -45,6 +46,7 @@ function new_extension() function on_movie_duration(value) { if (value<0) value=0; + current_duration = value; player_control.set_duration(value); if (UPnP_Enabled) UPnP.MovieDuration = value; } @@ -102,11 +104,11 @@ function filter_event(evt) if ((start_drag_x == evt.mouse_x) && (start_drag_y == evt.mouse_y)) { /* if (dock.visible) { - open_dock(false); + show_dock(false); } else */ { - open_dock(false); + show_dock(false); if (player_control.visible) { //player_control.hide(); top_wnd = null; @@ -123,7 +125,7 @@ function filter_event(evt) //commented out as HOME is used for viewpoint reset /* if (evt.keycode=='Home') { - open_dock(!dock.visible); + show_dock(!dock.visible); return true; } */ @@ -162,16 +164,27 @@ function filter_event(evt) } -function open_dock(show) +function show_dock(show) { if (show) { dock.show(); + if (movie_connected) { + dock.set_size(display_width, display_height/2); + dock.move(0, -display_height/4); + movie.scale.x = movie.scale.y = 0.5; + movie.translation.y = display_height/4; + } else { + dock.set_size(display_width, display_height); + dock.move(0, 0); + } dock.layout(); player_control.hide(); - set_movie_url(''); +// set_movie_url(''); top_wnd = dock; // uidisplay.hide(); } else { + movie.scale.x = movie.scale.y = 1; + movie.translation.y = 0; dock.hide(); player_control.hide(); top_wnd = null; @@ -212,6 +225,21 @@ function gpacui_show_window(obj) layout(); } +function compute_movie_size(width, height) +{ + var w, h, r_w, r_h; + if (!width || !height) return; + + w = width; + h = height; + r_w = r_h = 1; + if (w < min_width) r_w = Math.ceil(min_width/w); + if (h < min_height) r_h = Math.ceil(min_height/h); + if (r_w < r_h) r_w = r_h; + w = r_w * w; + h = r_w * h; + gpac.set_size(w, h); +} //Initialize the main UI script function initialize() { @@ -222,8 +250,8 @@ function initialize() { gpac.caption = 'Osmo4'; current_time = 0; - min_width = 320; - min_height = 240; + min_width = 160; + min_height = 80; /*load the UI lib*/ Browser.loadScript('gwlib.js', false); @@ -239,10 +267,42 @@ function initialize() { // gwskin.tooltip_callback = function(over, label) { alert('' + over ? label : ''); }; root.children[0].backColor = gwskin.back_color; + movie.children[0].on_size = function(evt) { + if (!gpac.fullscreen) { + compute_movie_size(evt.width, evt.height); + } + } + movie.children[0].addEventListener('gpac_scene_attached', movie.children[0].on_size, 0); + + movie.children[0].on_media_progress = function(evt) { + if (!current_duration) return; + /*this is not conform to HTML5, we're using the old MediaAccessEvent syntax ...*/ + var percent_dload = 100.0 * evt.loaded / evt.total; + var percent_playback = 100.0 * current_time / current_duration; + //alert('URL data ' + percent_dload + ' - ' + percent_playback + ' playback'); + } + movie.children[0].addEventListener('progress', movie.children[0].on_media_progress, 0); + + movie.children[0].on_media_playing = function(evt) { + alert('URL is now paying'); + } + movie.children[0].addEventListener('playing', movie.children[0].on_media_playing, 0); + movie.children[0].addEventListener('canplay', movie.children[0].on_media_playing, 0); + + movie.children[0].on_media_waiting = function(evt) { + alert('URL is now buffering'); + } + movie.children[0].addEventListener('waiting', movie.children[0].on_media_waiting, 0); + display_width = parseInt( gpac.getOption('General', 'LastWidth') ); display_height = parseInt( gpac.getOption('General', 'LastHeight') ); + if (!gpac.fullscreen && (!display_width || !display_height)) { + display_width = 320; + display_height = 240; + } + if (!gpac.fullscreen && display_width && display_height) { gpac.set_size(display_width, display_height); } else { @@ -309,14 +369,14 @@ function initialize() { /*init our internal extensions*/ var icon = gpacui_insert_dock_icon('Player', 'icons/applications-multimedia.svg'); - icon.on_click = function () { dock.hide(); player_control.show(); }; + icon.on_click = function () { show_dock(false); player_control.show(); }; if (UPnP_Enabled) { icon = gpacui_insert_dock_icon('Control', 'icons/video-display.svg'); - icon.on_click = function () { select_remote_display('select'); }; + icon.on_click = function () { show_dock(false); select_remote_display('select'); }; icon = gpacui_insert_dock_icon('Remote', 'icons/applications-internet.svg'); - icon.on_click = function () { browse_remote_servers('select'); }; + icon.on_click = function () { set_movie_url(''); show_dock(false); browse_remote_servers('select'); }; } /*init all extensions*/ @@ -339,7 +399,7 @@ function initialize() { if (extension.icon && extension.launch) { var icon = gpacui_insert_dock_icon(extension.label, extension.path+extension.icon); icon.extension = extension; - icon.on_click = function () { dock.hide(); this.extension.launch(this.extension); } + icon.on_click = function () { show_dock(false); this.extension.launch(this.extension); } } if (extension.initialize) extension.initialize(extension); } @@ -354,7 +414,7 @@ function initialize() { if (url.indexOf('://')<0) set_movie_url('gpac://'+url); else set_movie_url(url); } else { -// open_dock(true); +// show_dock(true); player_control.show(); } } @@ -377,12 +437,13 @@ function set_movie_url(url) test_resource.on_attached = function(evt) { this.callback_done = true; + var current_url = this.url[0]; + + /*process the error or connect service*/ if (evt.error) { var notif = gw_new_message(null, 'Error!', 'Failed to open\n'+this.url[0]+ '\nReason: '+gpac.error_string(evt.error) ); gpacui_show_window(notif); } else { - current_url = this.url[0]; - movie.children[0].url[0] = current_url; movie_ctrl.url[0] = current_url; movie_sensor.url[0] = current_url; @@ -390,22 +451,18 @@ function set_movie_url(url) if (!movie_connected) { if (!gpac.fullscreen) { - var w, h; - w = evt.width; - if (w < min_width) w = min_width; - h = evt.height; - if (h < min_height) h = min_height; - gpac.set_size(w, h); + compute_movie_size(evt.width, evt.height); } movie_connected = true; gpac.set_3d(evt.type3d ? 1 : 0); } } - /*and destroy the resource node*/ + /*destroy the resource node*/ this.url.length = 0; gw_detach_child(this); test_resource.removeEventListener('gpac_scene_attached', test_resource.on_attached, 0); this.on_attached = null; + } /*get notified when service loads or fails*/ test_resource.addEventListener('gpac_scene_attached', test_resource.on_attached, 0); @@ -441,7 +498,9 @@ function layout() var i, list, start_x; player_control.set_size(display_width, player_control.height); + dock.set_size(display_width, display_height); +// dock.set_size(display_width, display_height); // if (uidisplay.children.length) uidisplay.children[0].set_size(display_width, display_height); } @@ -667,7 +726,7 @@ function new_player_control(container) if (!browser_mode) { wnd.home = gw_new_icon_button(wnd.infobar, 'icons/go-home.svg', 'Home', 'icon'); - wnd.home.on_click = function() { open_dock(true); } + wnd.home.on_click = function() { show_dock(true); } wnd.home.set_size(small_control_icon_size, small_control_icon_size); } else { wnd.home = null; @@ -866,7 +925,7 @@ function open_local_file() filebrowse.on_browse = function(value, directory) { if (directory) gpac.last_working_directory = directory; set_movie_url(value); - open_dock(false); + show_dock(false); } filebrowse.set_size(display_width, display_height); diff --git a/gui/gwlib.js b/gui/gwlib.js index 705dbc0..9c7ab05 100644 --- a/gui/gwlib.js +++ b/gui/gwlib.js @@ -259,24 +259,26 @@ function gw_window_show_hide() this.timer = gw_new_timer(1); this.timer.set_timeout(0.25, false); this.timer.on_fraction = function(val) { + if (!this.wnd) return; if (this.wnd.visible) { this.wnd.scale.x = 1-val; this.wnd.scale.y = 1-val; - this.wnd.set_alpha(1-val); + this.wnd.set_alpha( (1-val) * this.wnd.alpha); } else { this.wnd.scale.x = val; this.wnd.scale.y = val; - this.wnd.set_alpha(val); + this.wnd.set_alpha(val * this.wnd.alpha); } } this.timer.on_active = function(val) { var fun; - if (val) return; + if (val || !this.wnd) return; var wnd = this.wnd; this.wnd = null; wnd.visible = !wnd.visible; wnd.scale.x = wnd.visible ? 1 : 0; wnd.scale.y = wnd.visible ? 1 : 0; + wnd.set_alpha(wnd.alpha); fun = this.call_on_end; this.call_on_end = null; if (fun) { @@ -284,6 +286,10 @@ function gw_window_show_hide() } } } + /*not done yet! This can happen when the function is called faster than the animation duration*/ + if (this.timer.wnd) return; + + this.alpha = this.get_alpha(); this.set_alpha(1.0); this.timer.wnd = this; this.timer.start(0); @@ -616,6 +622,9 @@ function gw_new_rectangle(class_name, style) obj.set_alpha = function(alpha) { this.children[0].appearance.material.transparency = 1-alpha; } + obj.get_alpha = function() { + return 1 - this.children[0].appearance.material.transparency; + } obj.set_fill_color = function(r, g, b) { this.children[0].appearance.material.emissiveColor.r = r; this.children[0].appearance.material.emissiveColor.g = g; @@ -636,6 +645,7 @@ function gw_new_rectangle(class_name, style) obj.set_fill_color = function(r, g, b) {} obj.set_strike_color = function(r, g, b) {} obj.set_line_width = function(width) {} + obj.get_alpha = function() { return 1; } } obj.set_style = function(classname, style) { @@ -839,6 +849,10 @@ function gw_new_window(parent, offscreen, class_name) obj.set_alpha = function(alpha) { this.children[0].appearance.material.transparency = 1 - alpha; } + obj.get_alpha = function() { + return 1 - this.children[0].appearance.material.transparency; + } + obj.add_child = function(child) { this.children[0].appearance.texture.children[this.children[0].appearance.texture.children.length] = child; } @@ -1618,6 +1632,8 @@ function gw_new_slider(parent, vertical, class_name) } obj.min = 0; obj.max = 100; + obj.frac = 0; + obj.set_trackpoint(0); obj.move = function(x,y) { this.translation.x = x; diff --git a/gui/iphone_wm_gui.js b/gui/iphone_wm_gui.js index e10827e..d9cb1a9 100644 --- a/gui/iphone_wm_gui.js +++ b/gui/iphone_wm_gui.js @@ -1,302 +1,733 @@ -// to make sure the initialization is done only once -init = true; - -// constant -xlinkns = 'http://www.w3.org/1999/xlink'; -evns = 'http://www.w3.org/2001/xml-events'; - -// state of the widget manager: displays homepage (value=home) or executes widget (value="exec") -state = 'home'; - -// convenience variables for SVG elements -homepage = null, arrows = null, icons = null, widgetContainer = null, homebar = null, execbar = null; - -// where is the index into pages of icons on the home page -where = 0, maxwhere = 0, whereW = 0; - -// array of activated widgets -activatedWidgets = new Array(); -numActivatedWidgets = 0; - -// variables for flexible layout -// variables for flexible layout -totalWidth = 0, totalHeight = 0, iconNbHoriz = 0, iconNbVert = 0, iconsPerPage = 0; - -//previous size -previousWidth = 0, previousHeight = 0; - -// to differentiate between install icon and scan dir for icons -isThisAScan = null; - -// preferred icon type -preferredIconType = '.svg'; - -// adapt layout to the size of the screen -function adaptLayoutToSize() { - if (l_deb < log_level) alert("[UI] adaptLayoutToSize"); - var tmpObject, tmpObj2; - // get size to adapt to - totalWidth = document.documentElement.viewport.width; - totalHeight = document.documentElement.viewport.height; - if (totalWidth == 0) totalWidth = 160; - if (totalHeight == 0) totalHeight = 280; - while (totalWidth < 160 || totalHeight < 280) { - totalWidth *= 4; - totalHeight *= 4; - } - // min size is 160 by 280 - if (totalWidth < 160) totalWidth = 160; - if (totalHeight < 280) totalHeight = 280; - // round to lower multiple of 80 - totalWidth -= totalWidth % 80; - var iconHeight = totalHeight - 120; - iconHeight -= iconHeight % 80; - // how many lines and columns of icons - iconNbHoriz = totalWidth / 80; - iconNbVert = iconHeight / 80; - totalHeight = iconHeight + 120; - // 120 is upper bar (60) + lower bar (60) - iconsPerPage = iconNbHoriz * iconNbVert; - // fix svg viewbox - document.getElementById("svg").setAttribute("viewBox", "0 0 " + totalWidth + " " + totalHeight); - // fix odd line - tmpObj2 = document.getElementById("odd"); - var i, already = tmpObj2.getElementsByTagName("use").length; - //alert("odd line "+already+" "+iconNbHoriz); - for (i = already; i < iconNbHoriz; i++) { - tmpObject = document.createElement("use"); - tmpObject.setAttribute("x", i * 80); - tmpObject.setAttributeNS(xlinkns, "href", (((i % 2) == 0) ? "#lightRect" : "#darkRect" )); - tmpObj2.appendChild(tmpObject); - } - // fix even line - tmpObj2 = document.getElementById("even"); - already = tmpObj2.getElementsByTagName("use").length; - //alert("even line "+already+" "+iconNbHoriz); - for (i = already; i < iconNbHoriz; i++) { - tmpObject = document.createElement("use"); - tmpObject.setAttribute("x", i * 80); - tmpObject.setAttributeNS(xlinkns, "href", (((i % 2) == 0) ? "#darkRect" : "#lightRect" )); - tmpObj2.appendChild(tmpObject); - } - // fix frame (black with rounded corners) - tmpObject = document.getElementById("frame"); - tmpObject.setAttribute("width", totalWidth); - tmpObject.setAttribute("height", totalHeight); - tmpObject = document.getElementById("frame2"); - tmpObject.setAttribute("width", totalWidth); - tmpObject.setAttribute("height", totalHeight); - // fix screen (white) - tmpObject = document.getElementById("screen"); - tmpObject.setAttribute("width", totalWidth); - tmpObject.setAttribute("height", totalHeight - 120); - // fix grid back (gray) - tmpObject = document.getElementById("gridback"); - tmpObject.setAttribute("width", totalWidth); - tmpObject.setAttribute("height", totalHeight - 120); - // fix icon background grid - tmpObject = document.getElementById("grid"); - already = tmpObject.getElementsByTagName("use").length; - for (i = already; i < iconNbVert; i++) { - tmpObj2 = document.createElement("use"); - tmpObj2.setAttribute("y", i * 80); - tmpObj2.setAttributeNS(xlinkns, "href", (((i % 2) == 0) ? "#odd" : "#even" )); - tmpObject.appendChild(tmpObj2); - } - // if there are already too many lines, remove the extra ones otherwise they are rendered on top of the lower - // part of the decoration - if (already > iconNbVert) { - while (already-- > iconNbVert) tmpObject.removeChild(tmpObject.lastChild); - } - // fix commands (lower bar) - document.getElementById("commands").setAttribute("transform", "translate(0, " + (totalHeight - 60) + ")"); - document.getElementById("homeButton").setAttribute("transform", "translate(" + (totalWidth / 2) + ", 30)"); - document.getElementById("right").setAttribute("transform", "translate(" + (totalWidth - 50) + ", 10)"); - document.getElementById("rightW").setAttribute("transform", "translate(" + (totalWidth - 50) + ", 10)"); - // fix the cuts (white rects left and right because we have no clipping) - tmpObject = document.getElementById("leftCut"); - tmpObject.setAttribute("width", totalWidth); - tmpObject.setAttribute("height", totalHeight); - tmpObject.setAttribute("x", -1 - totalWidth); - tmpObject = document.getElementById("rightCut"); - tmpObject.setAttribute("width", totalWidth); - tmpObject.setAttribute("height", totalHeight); - tmpObject.setAttribute("x", 1 + totalWidth); - // respace executing widgets if any - tmpObject = widgetContainer.getElementsByTagName("g"); - for (i = 0; i < tmpObject.length; i++) { - var gg = tmpObject.item(i); - gg.setAttribute("transform", "translate(" + (totalWidth * (i - 1)) + ", 0)"); - if (gg.firstElementChild != null) { - gg.firstElementChild.setAttribute("width", totalWidth); - gg.firstElementChild.setAttribute("height", totalHeight - 120); +// 01122011 AMD1 startWidget listWidgets getWidget implemented + +var iphone_wm_gui = (function () { + + // to make sure the initialization is done only once + var init = true; + + // constant + var xlinkns = 'http://www.w3.org/1999/xlink'; + //var evns = 'http://www.w3.org/2001/xml-events'; + + // state of the widget manager: displays homepage (value=home) or executes widget (value="exec") + var state = 'home'; + + // convenience variables for SVG elements + var homepage = null, arrows = null, icons = null, widgetContainer = null, homebar = null, execbar = null; + + // where is the index into pages of icons on the home page + var where = 0,maxwhere = 0,whereW = 0; + + // array of activated widgets + var activatedWidgets = new Array(); + var numActivatedWidgets = 0; + + // variables for flexible layout + // variables for flexible layout + var totalWidth = 0,totalHeight = 0,iconNbHoriz = 0,iconNbVert = 0,iconsPerPage = 0; + + //previous size + var previousWidth = 0,previousHeight = 0; + + // to differentiate between install icon and scan dir for icons + var isThisAScan = null; + + // preferred icon type + var preferredIconType = '.svg'; + + // adapt layout to the size of the screen + function adaptLayoutToSize() { + if (l_deb < log_level) { + alert("[UI] adaptLayoutToSize"); + } + var tmpObject, tmpObj2; + // get size to adapt to + totalWidth = document.documentElement.viewport.width; + totalHeight = document.documentElement.viewport.height; + if (totalWidth == 0) { + totalWidth = 160; + } + if (totalHeight == 0) { + totalHeight = 280; + } + while (totalWidth < 160 || totalHeight < 280) { + totalWidth *= 4; + totalHeight *= 4; + } + // min size is 160 by 280 + if (totalWidth < 160) { + totalWidth = 160; + } + if (totalHeight < 280) { + totalHeight = 280; + } + // round to lower multiple of 80 + totalWidth -= totalWidth % 80; + var iconHeight = totalHeight - 120; + iconHeight -= iconHeight % 80; + // how many lines and columns of icons + iconNbHoriz = totalWidth / 80; + iconNbVert = iconHeight / 80; + totalHeight = iconHeight + 120; + // 120 is upper bar (60) + lower bar (60) + iconsPerPage = iconNbHoriz * iconNbVert; + // fix svg viewbox + document.getElementById("svg").setAttribute("viewBox", "0 0 " + totalWidth + " " + totalHeight); + // fix odd line + tmpObj2 = document.getElementById("odd"); + var i, already = tmpObj2.getElementsByTagName("use").length; + //alert("odd line "+already+" "+iconNbHoriz); + for (i = already; i < iconNbHoriz; i++) { + tmpObject = document.createElement("use"); + tmpObject.setAttribute("x", i * 80); + tmpObject.setAttributeNS(xlinkns, "href", (((i % 2) == 0) ? "#lightRect" : "#darkRect" )); + tmpObj2.appendChild(tmpObject); + } + // fix even line + tmpObj2 = document.getElementById("even"); + already = tmpObj2.getElementsByTagName("use").length; + //alert("even line "+already+" "+iconNbHoriz); + for (i = already; i < iconNbHoriz; i++) { + tmpObject = document.createElement("use"); + tmpObject.setAttribute("x", i * 80); + tmpObject.setAttributeNS(xlinkns, "href", (((i % 2) == 0) ? "#darkRect" : "#lightRect" )); + tmpObj2.appendChild(tmpObject); + } + // fix frame (black with rounded corners) + tmpObject = document.getElementById("frame"); + tmpObject.setAttribute("width", totalWidth); + tmpObject.setAttribute("height", totalHeight); + tmpObject = document.getElementById("frame2"); + tmpObject.setAttribute("width", totalWidth); + tmpObject.setAttribute("height", totalHeight); + // fix screen (white) + tmpObject = document.getElementById("screen"); + tmpObject.setAttribute("width", totalWidth); + tmpObject.setAttribute("height", totalHeight - 120); + // fix grid back (gray) + tmpObject = document.getElementById("gridback"); + tmpObject.setAttribute("width", totalWidth); + tmpObject.setAttribute("height", totalHeight - 120); + // fix icon background grid + tmpObject = document.getElementById("grid"); + already = tmpObject.getElementsByTagName("use").length; + for (i = already; i < iconNbVert; i++) { + tmpObj2 = document.createElement("use"); + tmpObj2.setAttribute("y", i * 80); + tmpObj2.setAttributeNS(xlinkns, "href", (((i % 2) == 0) ? "#odd" : "#even" )); + tmpObject.appendChild(tmpObj2); + } + // if there are already too many lines, remove the extra ones otherwise they are rendered on top of the lower + // part of the decoration + if (already > iconNbVert) { + while (already-- > iconNbVert) { + tmpObject.removeChild(tmpObject.lastChild); + } + } + // fix commands (lower bar) + document.getElementById("commands").setAttribute("transform", "translate(0, " + (totalHeight - 60) + ")"); + document.getElementById("homeButton").setAttribute("transform", "translate(" + (totalWidth / 2) + ", 30)"); + document.getElementById("right").setAttribute("transform", "translate(" + (totalWidth - 50) + ", 10)"); + document.getElementById("rightW").setAttribute("transform", "translate(" + (totalWidth - 50) + ", 10)"); + // fix the cuts (white rects left and right because we have no clipping) + tmpObject = document.getElementById("leftCut"); + tmpObject.setAttribute("width", totalWidth); + tmpObject.setAttribute("height", totalHeight); + tmpObject.setAttribute("x", -1 - totalWidth); + tmpObject = document.getElementById("rightCut"); + tmpObject.setAttribute("width", totalWidth); + tmpObject.setAttribute("height", totalHeight); + tmpObject.setAttribute("x", 1 + totalWidth); + // respace executing widgets if any + tmpObject = widgetContainer.getElementsByTagName("g"); + for (i = 0; i < tmpObject.length; i++) { + var gg = tmpObject.item(i); + gg.setAttribute("transform", "translate(" + (totalWidth * (i - 1)) + ", 0)"); + if (gg.firstElementChild != null) { + gg.firstElementChild.setAttribute("width", totalWidth); + gg.firstElementChild.setAttribute("height", totalHeight - 120); + } } } -} -// -// widget close on click at "Kill" button -// -function on_kill_widget() { - if (state == 'exec') { - widget_close(activatedWidgets[whereW]); + // + // widget close on click at "Kill" button + // + function on_kill_widget() { + if (state == 'exec') { + widget_close(activatedWidgets[whereW]); + } } -} -// -// when deleting an executing widgets, all executing widgets to its right are moved to the left -// -function recurseMoveAfterDelete(target) { - if (target != null && target.nextElementSibling != null) { - var v = target.nextElementSibling; - recurseMoveAfterDelete(v); - v.setAttribute("transform", target.getAttribute("transform")); + // + // widget get on click at "GetW" button + // + function on_get_widget() { + alert("on_get_widget "+WidgetManager.MPEGUStandardServiceProviders.length); + if (WidgetManager.MPEGUStandardServiceProviders.length != 0) { + upnp_renders = selector_window1(); + upnp_renders.on_select = function(item) { + upnp_renders.unregister(root); + upnp_renders = null; + if (item == -1) { + return; + } + alert("upnp_renders.on_select(" + item + ")"); + var device = WidgetManager.MPEGUStandardServiceProviders[item]; + device.standardService.SetActionListener("listWidgets", get_widget_callback(device), true); + device.standardService.CallAction("listWidgets", new Array()); + } + upnp_renders.register(root); + } } -} -// -// click on home button to switch from icons to executing widgets -// -function home_button(evt) { - if (l_deb < log_level) alert("[UI] home_button"); - if (state != 'home') { - state = 'home'; - widgetContainer.setAttribute('display', 'none'); - homepage.setAttribute('display', 'inline'); - homebar.setAttribute('display', 'inline'); - execbar.setAttribute('display', 'none'); - arrows.setAttribute('display', 'inline'); - arrowsW.setAttribute('display', 'none'); - widgetAddList.setAttribute('display', 'none'); - } else { - state = 'exec'; - widgetContainer.setAttribute('display', 'inline'); - homepage.setAttribute('display', 'none'); - homebar.setAttribute('display', 'none'); - execbar.setAttribute('display', 'inline'); - arrows.setAttribute('display', 'none'); - arrowsW.setAttribute('display', 'inline'); - widgetAddList.setAttribute('display', 'none'); + function get_widget_callback(device) { + return function() { + //alert("get_widget_callback"); + // msgHandler is the first argument, the next arguments are from the reply to listWidgets + var act = arguments[0]; + var act1 = act.GetArgumentValue("widgetCodes"); + var act2 = act.GetArgumentValue("widgetNames"); + //alert(act1+" "+act2); + target_widgets = selector_window2(act1.split(" "), act2.split(" ")); + target_widgets.on_select = function(item) { + alert("target_widgets.on_select"); + target_widgets.unregister(root); + target_widgets = null; + if (item == -1) { + return; + } + alert("target_widgets.on_select(" + item + ")"); + var args = new Array(); + args[0] = "widgetCode"; + args[1] = item; + device.standardService.CallAction("getWidget", args); + } + target_widgets.register(root); + } } -} -// constants -adjustFrom = "0,0"; -animDue = true; - -// -// after each change of icon page, this function adjusts the visibility of arrows in the lower bar -// -function adjustwhere(animDue) { - if (l_deb < log_level) alert("[UI] adjust " + where + " 0 " + maxwhere); - document.getElementById("left").setAttribute("display", ((where > 0) ? "inline" : "none")); - document.getElementById("right").setAttribute("display", ((where < maxwhere) ? "inline" : "none")); - if (!animDue) { - icons.setAttribute("transform", 'translate(' + (-80 * iconNbHoriz * where) + ',0)'); - } else { - var aw = document.createElement("animateTransform"); - aw.setAttribute("attributeName", "transform"); - aw.setAttribute("type", "translate"); - //alert('time '+document.documentElement.getCurrentTime()); - aw.setAttribute("begin", document.documentElement.getCurrentTime()); - aw.setAttribute("dur", "1s"); - aw.setAttribute("fill", "freeze"); - aw.setAttributeNS(xlinkns, "href", "#icons"); - aw.setAttribute("from", adjustFrom); - aw.setAttribute("to", (-80 * iconNbHoriz * where) + ",0"); - document.documentElement.appendChild(aw); - } - adjustFrom = (-80 * iconNbHoriz * where) + ",0"; -} + // + // creates the menu of available targets for pushing a widget elsewhere + // + function selector_window1() { + var i, count, render; + var selector = document.createElement('g'), obj, child; + selector.setAttribute('transform', 'translate(10,10)'); + count = WidgetManager.MPEGUStandardServiceProviders.length; + selector.appendChild(rect(0, 0, 300, 20 * (count + 1), 'white', 'black')); + for (i = 0; i < count; i++) { + render = WidgetManager.MPEGUStandardServiceProviders[i]; + obj = createtext(render.Name, 'black', 5, 17 + (20 * i), 15, 'sans-serif'); + obj.setAttribute('id', "select" + i); + selector.appendChild(obj); + obj.addEventListener('mouseover', sw1("select" + i), false); + obj.addEventListener('mouseout', sw2("select" + i), false); + obj.addEventListener('click', sw4(i), false); + } + obj = createtext('Cancel', 'rgb(0,0,120)', 55, 17 + (20 * i), 15, 'sans-serif'); + obj.setAttribute('id', "canc"); + selector.appendChild(obj); + obj.addEventListener('mouseover', function(evt) { document.getElementById("canc").setAttribute("fill", "red"); }, false); + obj.addEventListener('mouseout', function(evt) { document.getElementById("canc").setAttribute("fill", "black"); }, false); + obj.addEventListener('click', function(evt) { upnp_renders.on_select(-1); }, false); + selector.register = function(disp) { + disp.appendChild(this); + }; + selector.unregister = function(disp) { + disp.removeChild(this); + }; + return selector; + } -// -// action of the left button on the lower bar -// -function left_button() { - if (l_deb < log_level) alert("[UI] left button " + where + " 0"); - if (where > 0) { - where--; - adjustwhere(true); + // + // creates the menu of available targets for pushing a widget elsewhere + // + function selector_window2(codes, names) { + alert("selector_window2"); + var i, count, render; + var selector = document.createElement('g'), obj, child; + selector.setAttribute('transform', 'translate(10,10)'); + count = codes.length; + selector.appendChild(rect(0, 0, 300, 20 * (count + 1), 'white', 'black')); + for (i = 0; i < count; i++) { + render = names[i]; + obj = createtext(render, 'black', 5, 17 + (20 * i), 15, 'sans-serif'); + obj.setAttribute('id', "selecto" + i); + selector.appendChild(obj); + obj.addEventListener('mouseover', sw1("selecto" + i), false); + obj.addEventListener('mouseout', sw2("selecto" + i), false); + obj.addEventListener('click', sw5(i), false); + } + obj = createtext('Cancel', 'rgb(0,0,120)', 55, 17 + (20 * i), 15, 'sans-serif'); + obj.setAttribute('id', "cance"); + selector.appendChild(obj); + obj.addEventListener('mouseover', function(evt) { document.getElementById("cance").setAttribute("fill", "red"); }, false); + obj.addEventListener('mouseout', function(evt) { document.getElementById("cance").setAttribute("fill", "black"); }, false); + obj.addEventListener('click', function(evt) { target_widgets.on_select(-1); }, false); + selector.register = function(disp) { + disp.appendChild(this); + }; + selector.unregister = function(disp) { + disp.removeChild(this); + }; + return selector; } -} -// -// action of the right button of the lower bar -// -function right_button() { - if (l_deb < log_level) alert("[UI] right button " + where + " " + maxwhere); - if (where < maxwhere) { - where++; - adjustwhere(true); + function sw4(si) { + return function(evt) { upnp_renders.on_select(si); }; } -} -adjustFromW = "0,0"; -oldwhereW = -1; - -// -// after each change of icon page, this function adjusts the visibility of arrows in the lower bar -// -function adjustWhereWidgets(animDue) { - if (oldwhereW != whereW) { - // hide oldwhereW - if (oldwhereW >= 0 && activatedWidgets[oldwhereW] != null) WidgetManager.corein_message(activatedWidgets[oldwhereW], "hide"); - } - if (l_deb < log_level) alert("[UI] adjustW " + whereW + " 0 " + (numActivatedWidgets - 1)); - document.getElementById("leftW").setAttribute("display", ((whereW > 0) ? "inline" : "none")); - document.getElementById("rightW").setAttribute("display", ((whereW < (numActivatedWidgets - 1)) ? "inline" : "none")); - if (!animDue) { - widgetContainer.setAttribute("transform", "translate(" + (-totalWidth * whereW) + ",0)"); - } else { - var aw = document.createElement("animateTransform"); - aw.setAttribute("attributeName", "transform"); - aw.setAttribute("type", "translate"); - aw.setAttribute("begin", document.documentElement.getCurrentTime()); - aw.setAttribute("dur", "1s"); - aw.setAttribute("fill", "freeze"); - aw.setAttributeNS(xlinkns, "href", "#widget"); - aw.setAttribute("from", adjustFromW); - aw.setAttribute("to", (-totalWidth * whereW) + ",0"); - document.documentElement.appendChild(aw); - } - adjustFromW = (-totalWidth * whereW) + ",0"; - document.getElementById("widgetName").textContent = - (whereW >= 0 && activatedWidgets[whereW] != null ? activatedWidgets[whereW].shortName : '-'); - if (oldwhereW != whereW) { - // show whereW - if (whereW >= 0 && activatedWidgets[whereW] != null) WidgetManager.corein_message(activatedWidgets[whereW], "show"); - oldwhereW = whereW; + function sw5(si) { + return function(evt) { target_widgets.on_select(si); }; } -} -// -// action of the left button on the lower bar -// -function left_buttonW() { - if (l_deb < log_level) alert("[UI] left button " + whereW); - if (whereW > 0) { - whereW--; - adjustWhereWidgets(animDue); + // + // when deleting an executing widgets, all executing widgets to its right are moved to the left + // + function recurseMoveAfterDelete(target) { + if (target != null && target.nextElementSibling != null) { + var v = target.nextElementSibling; + recurseMoveAfterDelete(v); + v.setAttribute("transform", target.getAttribute("transform")); + } } -} -// -// action of the right button of the lower bar -// -function right_buttonW() { - if (l_deb < log_level) alert("[UI] right button " + whereW + " " + (numActivatedWidgets - 1)); - if (whereW < (numActivatedWidgets - 1)) { - whereW++; - adjustWhereWidgets(animDue); + // + // click on home button to switch from icons to executing widgets + // + function home_button(evt) { + if (l_deb < log_level) { + alert("[UI] home_button"); + } + if (state != 'home') { + state = 'home'; + widgetContainer.setAttribute('display', 'none'); + homepage.setAttribute('display', 'inline'); + homebar.setAttribute('display', 'inline'); + execbar.setAttribute('display', 'none'); + arrows.setAttribute('display', 'inline'); + arrowsW.setAttribute('display', 'none'); + widgetAddList.setAttribute('display', 'none'); + } else { + state = 'exec'; + widgetContainer.setAttribute('display', 'inline'); + homepage.setAttribute('display', 'none'); + homebar.setAttribute('display', 'none'); + execbar.setAttribute('display', 'inline'); + arrows.setAttribute('display', 'none'); + arrowsW.setAttribute('display', 'inline'); + widgetAddList.setAttribute('display', 'none'); + } + } + + // constants + var adjustFrom = "0,0"; + var animDue = true; + + // + // after each change of icon page, this function adjusts the visibility of arrows in the lower bar + // + function adjustwhere(animDue) { + if (l_deb < log_level) { + alert("[UI] adjust " + where + " 0 " + maxwhere); + } + document.getElementById("left").setAttribute("display", ((where > 0) ? "inline" : "none")); + document.getElementById("right").setAttribute("display", ((where < maxwhere) ? "inline" : "none")); + if (!animDue) { + icons.setAttribute("transform", 'translate(' + (-80 * iconNbHoriz * where) + ',0)'); + } else { + var aw = document.createElement("animateTransform"); + aw.setAttribute("attributeName", "transform"); + aw.setAttribute("type", "translate"); + //alert('time '+document.documentElement.getCurrentTime()); + aw.setAttribute("begin", document.documentElement.getCurrentTime()); + aw.setAttribute("dur", "1s"); + aw.setAttribute("fill", "freeze"); + aw.setAttributeNS(xlinkns, "href", "#icons"); + aw.setAttribute("from", adjustFrom); + aw.setAttribute("to", (-80 * iconNbHoriz * where) + ",0"); + document.documentElement.appendChild(aw); + } + adjustFrom = (-80 * iconNbHoriz * where) + ",0"; + } + + // + // action of the left button on the lower bar + // + function left_button() { + if (l_deb < log_level) { + alert("[UI] left button " + where + " 0"); + } + if (where > 0) { + where--; + adjustwhere(true); + } + } + + // + // action of the right button of the lower bar + // + function right_button() { + if (l_deb < log_level) { + alert("[UI] right button " + where + " " + maxwhere); + } + if (where < maxwhere) { + where++; + adjustwhere(true); + } + } + + var adjustFromW = "0,0"; + var oldwhereW = -1; + + // + // after each change of icon page, this function adjusts the visibility of arrows in the lower bar + // + function adjustWhereWidgets(animDue) { + if (oldwhereW != whereW) { + // hide oldwhereW + if (oldwhereW >= 0 && activatedWidgets[oldwhereW] != null) { + WidgetManager.corein_message(activatedWidgets[oldwhereW], "hide"); + } + } + if (l_deb < log_level) { + alert("[UI] adjustW " + whereW + " 0 " + (numActivatedWidgets - 1)); + } + document.getElementById("leftW").setAttribute("display", ((whereW > 0) ? "inline" : "none")); + document.getElementById("rightW").setAttribute("display", ((whereW < (numActivatedWidgets - 1)) ? "inline" : "none")); + if (!animDue) { + widgetContainer.setAttribute("transform", "translate(" + (-totalWidth * whereW) + ",0)"); + } else { + var aw = document.createElement("animateTransform"); + aw.setAttribute("attributeName", "transform"); + aw.setAttribute("type", "translate"); + aw.setAttribute("begin", document.documentElement.getCurrentTime()); + aw.setAttribute("dur", "1s"); + aw.setAttribute("fill", "freeze"); + aw.setAttributeNS(xlinkns, "href", "#widget"); + aw.setAttribute("from", adjustFromW); + aw.setAttribute("to", (-totalWidth * whereW) + ",0"); + document.documentElement.appendChild(aw); + } + adjustFromW = (-totalWidth * whereW) + ",0"; + document.getElementById("widgetName").textContent = + (whereW >= 0 && activatedWidgets[whereW] != null ? activatedWidgets[whereW].shortName : '-'); + if (oldwhereW != whereW) { + // show whereW + if (whereW >= 0 && activatedWidgets[whereW] != null) { + WidgetManager.corein_message(activatedWidgets[whereW], "show"); + } + oldwhereW = whereW; + } + } + + // + // action of the left button on the lower bar + // + function left_buttonW() { + if (l_deb < log_level) { + alert("[UI] left button " + whereW); + } + if (whereW > 0) { + whereW--; + adjustWhereWidgets(animDue); + } + } + + // + // action of the right button of the lower bar + // + function right_buttonW() { + if (l_deb < log_level) { + alert("[UI] right button " + whereW + " " + (numActivatedWidgets - 1)); + } + if (whereW < (numActivatedWidgets - 1)) { + whereW++; + adjustWhereWidgets(animDue); + } + } + + // + // action of each icon, starting the matching widget + // + function activating_widget(w) { + if (l_deb < log_level) { + alert("[UI] activating widget: " + w.name); + } + widget_add(w); + } + + // + // main initialization function + // init variables, then init the widget manager C code, then inits UPnP + // + function initialize() { + if (l_deb < log_level) { + alert("[UI] initialize"); + } + init = false; + var display_width = parseInt(gpac.getOption('Widgets', 'LastWMWidth')); + var display_height = parseInt(gpac.getOption('Widgets', 'LastWMHeight')); + if (display_width && display_height) { + gpac.set_size(display_width, display_height); + } + root = document.documentElement; + homepage = document.getElementById('homepage'); + homebar = document.getElementById('homebar'); + execbar = document.getElementById('execbar'); + arrows = document.getElementById('arrows'); + arrowsW = document.getElementById('arrowsW'); + icons = document.getElementById('icons'); + widgetContainer = document.getElementById('widget'); + widgetAddList = document.getElementById('widgetAddList'); + /* Setup the GPAC Widget Manager - this will also scan the available widgets */ + log_level = l_inf; + widget_manager_init(); + WidgetManager.on_widget_remove = widget_remove; + WidgetManager.on_widget_add = widget_add; + /* register the callback to be notified of incoming widgets */ + has_upnp = (typeof UPnP != 'undefined'); + if (has_upnp) { + /* setting the callback to allow other devices to push their widgets */ + UPnP.onMediaConnect = onMediaConnect; + /* Tell GPAC that the calls to the main Renderer (like open, ...) must be forwared to this scene */ + UPnP.BindRenderer(); + } + WidgetManager.coreOutShow = coreOutShowImplementation; + WidgetManager.coreOutGetAttention = coreOutGetAttentionImplementation; + WidgetManager.coreOutHide = coreOutHideImplementation; + WidgetManager.coreOutRequestDeactivate = coreOutRequestDeactivateImplementation; + WidgetManager.coreOutInstallWidget = coreOutInstallWidgetImplementation; + WidgetManager.coreOutActivateTemporaryWidget = coreOutActivateTemporaryWidgetImplementation; + WidgetManager.coreOutMigrateComponent = coreOutMigrateComponentImplementation; + WidgetManager.coreOutRequestMigrationTargets = coreOutRequestMigrationTargetsImplementation; + } + + // + // implementation of core:out install widget + // + function coreOutInstallWidgetImplementation(wid, args) { + var w = widgetInstall(args[0], true, false, wid); + var ifce = getInterfaceByType(wid, "urn:mpeg:mpegu:schema:widgets:core:out:2010"); + if (ifce != null) { + wmjs_core_out_invoke_reply(coreOut.installWidgetMessage, ifce.get_message("installWidget"), + wid, (w != null ? 1 : 0)); // send return code 1 = success + } + } + + // + // implementation of core:out activate temporary widget + // + function coreOutActivateTemporaryWidgetImplementation(wid, args) { + var w = widgetInstall(args[0], true, true, null); + if (w != null) { + activating_widget(w); + } + var ifce = getInterfaceByType(wid, "urn:mpeg:mpegu:schema:widgets:core:out:2010"); + if (ifce != null) { + wmjs_core_out_invoke_reply(coreOut.activateTemporaryWidgetMessage, ifce.get_message("activateTemporaryWidget"), + wid, (w != null ? 1 : 0)); // send return code 1 = success + } + } + + // + // implementation of core:out migrate component + // + function coreOutMigrateComponentImplementation(wid, args) { + //alert("coreOutMigrateComponent "+wid.name+" "+args.length); + var comp = wid.get_component(args[0], true); + var ifce = getInterfaceByType(wid, "urn:mpeg:mpegu:schema:widgets:core:out:2010"); + if (comp == null) { + log(l_err, 'Component ' + args[0] + ' cannot be found in widget ' + wid.name); + if (ifce != null) { + wmjs_core_out_invoke_reply(coreOut.migrateComponentMessage, ifce.get_message("migrateComponent"), wid, 0); + } + return; + } + if (args.length > 1 && args[1] != null) { + //WidgetManager.migrate_widget(UPnP.GetMediaRenderer(parseInt(args[1])), comp); + WidgetManager.migrate_widget(WidgetManager.get_mpegu_service_providers(parseInt(args[1])), comp); + widget_close(comp); + } else { + upnp_renders = selector_window(comp); + upnp_renders.on_select = function(item, wid) { + upnp_renders.unregister(root); + upnp_renders = null; + if (item == -1) { + return; + } + if (comp != null) { + alert("upnp_renders.on_select(" + item + "," + comp.name + ")"); + //WidgetManager.migrate_widget(UPnP.GetMediaRenderer(item), comp); + WidgetManager.migrate_widget(WidgetManager.get_mpegu_service_providers(item), comp); + widget_close(comp); + } + }; + upnp_renders.register(root); + } + if (ifce != null) { + wmjs_core_out_invoke_reply(coreOut.migrateComponentMessage, ifce.get_message("migrateComponent"), wid, 1); // send return code 1 = success + } + } + + // + // implementation of core:out request migration targets + // + function coreOutRequestMigrationTargetsImplementation(wid, args) { + var count = UPnP.MediaRenderersCount, codes = new Array(), names = new Array(), descriptions = new Array(), i; + for (i = 0; i < count; i++) { + var render = UPnP.GetMediaRenderer(i); + codes.push("" + i); + names.push(render.Name); + descriptions.push(render.HostName + " " + render.UUID); + } + i = null; + var ifce_count = wid.num_interfaces, j; + for (j = 0; j < ifce_count; j++) { + var ifce = wid.get_interface(j); + if (ifce.type == "urn:mpeg:mpegu:schema:widgets:core:out:2010") { + i = ifce; + break; + } + } + if (i != null) { + wmjs_core_out_invoke_reply(coreOut.requestMigrationTargetsMessage, i.get_message("requestMigrationTargets"), + wid, codes, names, descriptions); + } + } + + // + // implementation of core:out Show message + // this is a request by the widget to be shown + // + function coreOutShowImplementation(wid, args) { + //alert("core:out show "+wid.name); + var target = widgetContainer.firstElementChild; + var i; + for (i = 0; i < numActivatedWidgets; i++) { + //alert("is it "+activatedWidgets[i].name); + if (activatedWidgets[i] == wid) { + break; + } + target = target.nextElementSibling; + } + // here, i is the index of the current widget + //alert(" "+i+" "+numActivatedWidgets); + if (i < numActivatedWidgets) { + whereW = i; + adjustWhereWidgets(false); + } + var ifce = getInterfaceByType(wid, "urn:mpeg:mpegu:schema:widgets:core:out:2010"); + if (ifce != null) { + wmjs_core_out_invoke_reply(coreOut.showMessage, ifce.get_message("show"), wid, 1); // send return code 1 = success + } + } + + // + // implementation of core:out GetAttention message + // this is a request by the widget to be shown and some special signal is given + // + function coreOutGetAttentionImplementation(wid, args) { + //alert("core:out getAttention "+wid.name); + var target = widgetContainer.firstElementChild; + var i; + for (i = 0; i < numActivatedWidgets; i++) { + //alert("is it "+activatedWidgets[i].name); + if (activatedWidgets[i] == wid) { + break; + } + target = target.nextElementSibling; + } + // here, i is the index of the current widget + //alert(" "+i+" "+numActivatedWidgets); + if (i < numActivatedWidgets) { + whereW = i; + adjustWhereWidgets(false); + } + document.getElementById("getAttention").beginElement(); + var ifce = getInterfaceByType(wid, "urn:mpeg:mpegu:schema:widgets:core:out:2010"); + if (ifce != null) { + wmjs_core_out_invoke_reply(coreOut.getAttentionMessage, ifce.get_message("getAttention"), wid, 1); // send return code 1 = success + } } -} -// -// action of each icon, starting the matching widget -// -function activating_widget(w) { - if (l_deb < log_level) alert("[UI] activating widget: " + w.name); - if (widget_add(w)) { + // + // implementation of core:out hide message + // this is a request by the widget to be hidden (some other widget (if any) is shown) + // + function coreOutHideImplementation(wid, args) { + //alert("core:out hide "+wid.name); + var target = widgetContainer.firstElementChild; + var i; + for (i = 0; i < numActivatedWidgets; i++) { + //alert("is it "+activatedWidgets[i].name); + if (activatedWidgets[i] == wid) { + break; + } + target = target.nextElementSibling; + } + // here, i is the index of the current widget + //alert("hide "+i+" "+numActivatedWidgets); + if (i < numActivatedWidgets) { + if (whereW > 0) { + whereW--; + } + else if (whereW < numActivatedWidgets - 1) { + whereW++; + } + else { + return; + } + adjustWhereWidgets(false); + } + var ifce = getInterfaceByType(wid, "urn:mpeg:mpegu:schema:widgets:core:out:2010"); + if (ifce != null) { + wmjs_core_out_invoke_reply(coreOut.hideMessage, ifce.get_message("hide"), wid, 1); // send return code 1 = success + } + } + + // + // implementation of core:out requestDeactivate message + // this is a request by the widget to be stopped + // + function coreOutRequestDeactivateImplementation(wid, args) { + //alert("core:out hide "+wid.name); + var target = widgetContainer.firstElementChild; + var i; + for (i = 0; i < numActivatedWidgets; i++) { + //alert("is it "+activatedWidgets[i].name); + if (activatedWidgets[i] == wid) { + break; + } + target = target.nextElementSibling; + } + // here, i is the index of the current widget + //alert("hide "+i+" "+numActivatedWidgets); + if (i < numActivatedWidgets) { + widget_close(activatedWidgets[i]); + } + var ifce = getInterfaceByType(wid, "urn:mpeg:mpegu:schema:widgets:core:out:2010"); + if (ifce != null) { + wmjs_core_out_invoke_reply(coreOut.requestDeactivateMessage, ifce.get_message("requestDeactivate"), wid, 1); // send return code 1 = success + } + } + + // + // WM callback for when a component is activated by its parent + // + function widget_add(w) { + log(l_inf, "widget add " + w.name); + if (!w.activated) { + if (sameFileIgnoringSVGView(w.icon, w.main)) { + // same file in icon and main + } else { + widget_launch(w, document.createElement("animation")); + } + } else if (w.multipleInstances) { + var newwid = WidgetManager.open(w.manifest, null); + widget_launch(newwid, document.createElement("animation")); + } else return false; state = 'exec'; widgetContainer.setAttribute('display', 'inline'); homepage.setAttribute('display', 'none'); @@ -305,485 +736,549 @@ function activating_widget(w) { arrows.setAttribute('display', 'none'); arrowsW.setAttribute('display', 'inline'); widgetAddList.setAttribute('display', 'none'); + return true; } -} -function widget_activated_and_bound(wid) { - WidgetManager.corein_message(wid, "activate"); -} + function sameFileIgnoringSVGView(name1, name2) { + if (name1 == name2) { + return true; + } + if (name1 == null) { + return false; + } + var i1 = name1.indexOf("#"); + var i2 = name2.indexOf("#"); + if (i1 < 0) { + i1 = name1.length; + } + else { + i1--; + } + if (i2 < 0) { + i2 = name2.length; + } + else { + i2--; + } + return name1.substring(0, i1) == name2.substring(0, i2); + } -// -// main initialization function -// init variables, then init the widget manager C code, then inits UPnP -// -function initialize() { - if (l_deb < log_level) alert("[UI] initialize"); - init = false; - var display_width = parseInt(gpac.getOption('Widgets', 'LastWMWidth')); - var display_height = parseInt(gpac.getOption('Widgets', 'LastWMHeight')); - if (display_width && display_height) { - gpac.set_size(display_width, display_height); - } - root = document.documentElement; - homepage = document.getElementById('homepage'); - homebar = document.getElementById('homebar'); - execbar = document.getElementById('execbar'); - arrows = document.getElementById('arrows'); - arrowsW = document.getElementById('arrowsW'); - icons = document.getElementById('icons'); - widgetContainer = document.getElementById('widget'); - widgetAddList = document.getElementById('widgetAddList'); - /* Setup the GPAC Widget Manager - this will also scan the available widgets */ - log_level = l_inf; - widget_manager_init(); - WidgetManager.on_widget_remove = widget_remove; - WidgetManager.on_widget_add = widget_add; - /* register the callback to be notified of incoming widgets */ - has_upnp = (typeof UPnP != 'undefined'); - if (has_upnp) { - /* setting the callback to allow other devices to push their widgets */ - UPnP.onMediaConnect = onMediaConnect; - /* Tell GPAC that the calls to the main Renderer (like open, ...) must be forwared to this scene */ - UPnP.BindRenderer(); - } - WidgetManager.coreOutShow = coreOutShowImplementation; - WidgetManager.coreOutGetAttention = coreOutGetAttentionImplementation; - WidgetManager.coreOutHide = coreOutHideImplementation; - WidgetManager.coreOutRequestDeactivate = coreOutRequestDeactivateImplementation; - WidgetManager.coreOutInstallWidget = coreOutInstallWidgetImplementation; - WidgetManager.coreOutActivateTemporaryWidget = coreOutActivateTemporaryWidgetImplementation; - WidgetManager.coreOutMigrateComponent = coreOutMigrateComponentImplementation; - WidgetManager.coreOutRequestMigrationTargets = coreOutRequestMigrationTargetsImplementation; -} + // + // recompute the number of widgets loaded currently + // + function getNbWidgets() { + var i, nbWidgets = 0; + for (i = 0; i < WidgetManager.num_widgets; i++) { + var w = WidgetManager.get(i); + if (w != null && w.loaded) { + nbWidgets++; + } + } + return nbWidgets; + } -// -// implementation of core:out install widget -// -function coreOutInstallWidgetImplementation(wid, args) { - var w = widgetInstall(args[0], true, false); - var ifce = getInterfaceByType(wid, "urn:mpeg:mpegu:schema:widgets:core:out:2010"); - if (ifce != null) { - wmjs_core_out_invoke_reply(coreOut.installWidgetMessage, ifce.get_message("installWidget"), - wid, (w != null ? 1 : 0)); // send return code 1 = success + // + // just resize the window and viewport, and initialize the first time this is called + // + function resize() { + if (init) { + initialize(); + } + if (document.documentElement.viewport.width == previousWidth && document.documentElement.viewport.height == previousHeight) { + return; + } + if (l_deb < log_level) { + alert("[UI] start initialize() w:" + document.documentElement.viewport.width + " h:" + document.documentElement.viewport.height); + } + adaptLayoutToSize(); + // start by filling the "home page" with known icons + where = 0; + var nbWidgets = getNbWidgets(); + maxwhere = ((nbWidgets - 1) - ((nbWidgets - 1) % iconsPerPage)) / iconsPerPage; + var wid; + var iconIterator = document.getElementById("icons").firstElementChild; + var position = 0; + for (i = 1; i <= WidgetManager.num_widgets; i++) { + wid = WidgetManager.get(i - 1); + // alert("build:"+wid.main+" "+wid.loaded); + if (wid.loaded) { + insert_icon(wid, position, i - 1, iconIterator); + WidgetManager.corein_message(wid, 'setSize', 'width', totalWidth, 'height', totalHeight - 120, 'dpi', 96); + position++; + if (iconIterator != null) { + iconIterator = iconIterator.nextElementSibling; + } + } + } + adjustwhere(false); + adjustWhereWidgets(false); + previousWidth = document.documentElement.viewport.width; + previousHeight = document.documentElement.viewport.height; + gpac.setOption("Widgets", "LastWMWidth", '' + previousWidth); + gpac.setOption("Widgets", "LastWMHeight", '' + previousHeight); } -} -// -// implementation of core:out activate temporary widget -// -function coreOutActivateTemporaryWidgetImplementation(wid, args) { - var w = widgetInstall(args[0], true, true); - if (w != null) activating_widget(w); - var ifce = getInterfaceByType(wid, "urn:mpeg:mpegu:schema:widgets:core:out:2010"); - if (ifce != null) { - wmjs_core_out_invoke_reply(coreOut.activateTemporaryWidgetMessage, ifce.get_message("activateTemporaryWidget"), - wid, (w != null ? 1 : 0)); // send return code 1 = success + // + // function inserting an icon on the home page + // + function insert_icon(widget, position, widgetIndex, previousIcon) { + if (l_deb < log_level) { + alert("[UI] insert_icon: " + widget.shortName + " " + position + " " + widgetIndex + " WMnw:" + WidgetManager.num_widgets); + } + widget.loaded = true; + if (l_deb < log_level) { + alert("[UI] widget name: " + widget.name); + } + var icon = null, original = null; + for (var i = 0; i < widget.icons.length; i++) { + // default to the first icon even if not of the preferred type + if (i == 0) { + icon = widget.icons[0].relocated_src; + //original = widget.icons[0].original; + // alert("choosing default icon " + icon); + } + // check for preferred type + if (widget.icons[i].relocated_src.indexOf(preferredIconType) > 0) { + icon = widget.icons[i].relocated_src; + //original = widget.icons[i].original; + break; + } + } + var shortName = widget.shortName; + if (typeof shortName == 'undefined') { + shortName = widget.name.substring(0, 9); + } + createIconSVGdecoration(previousIcon, widget, (((position % iconNbHoriz) * 80) + ((80 * (position - (position % iconsPerPage))) / iconNbVert)), + ((((position % iconsPerPage) - (position % iconNbHoriz)) / iconNbHoriz) * 80), "icons", icon, + shortName, widgetIndex); } -} -// -// implementation of core:out migrate component -// -function coreOutMigrateComponentImplementation(wid, args) { - //alert("coreOutMigrateComponent "+wid.name+" "+args.length); - var comp = wid.get_component(args[0], true); - var ifce = getInterfaceByType(wid, "urn:mpeg:mpegu:schema:widgets:core:out:2010"); - if (comp==null) { - log(l_err, 'Component '+args[0]+' cannot be found in widget '+wid.name); - if (ifce != null) { - wmjs_core_out_invoke_reply(coreOut.migrateComponentMessage, ifce.get_message("migrateComponent"), wid, 0); - } - return; - } - if (args.length > 1 && args[1] != null) { - WidgetManager.migrate_widget(UPnP.GetMediaRenderer(parseInt(args[1])), comp); - widget_close(comp); - } else { - upnp_renders = selector_window(comp); - upnp_renders.on_select = function(item, wid) { - upnp_renders.unregister(root); - upnp_renders = null; - if (item == -1) return; - if (comp != null) { - alert("upnp_renders.on_select("+item+","+comp.name+")"); - WidgetManager.migrate_widget(UPnP.GetMediaRenderer(item), comp); - widget_close(comp); + /*function restoreFragmentOnURL(iconUrl, original) { + var l = original.indexOf('#'); + if (l >= 0) { + return iconUrl + original.substring(l); + } + return iconUrl; + }*/ + + // constant + // const corein = "urn:mpeg:mpegu:schema:widgets:core:in:2010"; + + // + // commodity method to empty a list of children + // + function removeAllChildren(o) { + if (o != null && o.hasChildNodes()) { + while (o.childNodes.length >= 1) { + o.removeChild(o.firstChild); } - }; - upnp_renders.register(root); + } + } + + // + // create the home page icon with all its behaviours + // + function createIconSVGdecoration(previousIcon, widget, x, y, fatherId, iconUrl, name, widIndex) { + var g, g2; + if (l_inf < log_level) { + alert("[UI] createIconSVGdecoration " + iconUrl + " " + x + " " + y); + } + if (previousIcon != null) { + g = previousIcon; + } else { + g = document.createElement("g"); + document.getElementById(fatherId).appendChild(g); + } + g.setAttribute("transform", 'translate(' + x + ',' + y + ')'); + if (previousIcon != null) { + g2 = g.firstElementChild; + } else { + g2 = document.createElement("g"); + g2.setAttribute("transform", 'translate(15,10)'); + g.appendChild(g2); + } + if (iconUrl == null || iconUrl == "") { + iconUrl = "icons/face-surprise.svg"; + } + // + // process differently cases where widget.icon == widget.main + // + var container; + if (sameFileIgnoringSVGView(iconUrl, widget.main) && widget.main.indexOf('.svg') >= 0) { + // see if the animation already exists + container = document.getElementById(name); + if (container == null) { + // if the animation does not exist yet, create it + container = media('animation', iconUrl, 50, 50); + // store the animation in the main defs + document.getElementById("mainDefs").appendChild(container); + container.setAttribute('id', name); + } + if (previousIcon == null) { + // put the container in a use + var use = document.createElement("use"); + use.setAttribute('id', 'iconContainer' + name); + use.setAttributeNS(xlinkns, 'href', '#' + name); + g2.appendChild(use); + } + } else { + if (previousIcon == null) { + container = appropriateElementForMedia(iconUrl, 50, 50); + container.setAttribute('id', name); + g2.appendChild(container); + } + } + if (previousIcon == null) { + g2 = document.createElement("g"); + g2.setAttribute("transform", 'translate(40,70)'); + g.appendChild(g2); + var anim = createtext(name, 'white', 0, 0, 14, 'Arial Unicode MS'); + anim.setAttribute("text-anchor", "middle"); + anim.setAttribute("display-align", "center"); + g2.appendChild(anim); + var rect = invisible_rect(80, 80); + g.appendChild(rect); + rect.addEventListener("click", csi(widget), false); + } } - if (ifce != null) { - wmjs_core_out_invoke_reply(coreOut.migrateComponentMessage, ifce.get_message("migrateComponent"), wid, 1); // send return code 1 = success + + function csi(widget) { + return function(evt) { activating_widget(widget);}; } -} -// -// implementation of core:out request migration targets -// -function coreOutRequestMigrationTargetsImplementation(wid, args) { - var count = UPnP.MediaRenderersCount, codes = new Array(), names = new Array(), descriptions = new Array(), i; - for (i = 0; i < count; i++) { - var render = UPnP.GetMediaRenderer(i); - codes.push(""+i); - names.push(render.Name); - descriptions.push(render.HostName +" "+ render.UUID); - } - i = null; - var ifce_count = wid.num_interfaces, j; - for (j = 0; j < ifce_count; j++) { - var ifce = wid.get_interface(j); - if (ifce.type == "urn:mpeg:mpegu:schema:widgets:core:out:2010") { - i = ifce; - break; - } - } - if (i != null) { - wmjs_core_out_invoke_reply(coreOut.requestMigrationTargetsMessage, i.get_message("requestMigrationTargets"), - wid, codes, names, descriptions); + // + // widget closing action (WM callback) + // + function widget_close(wid) { + if (wid == null) { + return; + } + if (l_inf <= log_level) { + alert('[UI] widget_close:' + wid.name); + } + // maybe inform the widget that it is going to be deactivated + WidgetManager.corein_message(wid, "deactivate"); + var target = widgetContainer.firstElementChild; + var i; + for (i = 0; i < numActivatedWidgets; i++) { + if (activatedWidgets[i] == wid) { + break; + } + target = target.nextElementSibling; + } + if (target != null) { + // move next widgets back one slot + recurseMoveAfterDelete(target); + // stop the subscene + if (target.firstElementChild != null) { + target.firstElementChild.setAttributeNS(xlinkns, "href", ""); + } + // end trying + widgetContainer.removeChild(target); + } + wid.deactivate(); + wid.activated = false; + activatedWidgets.splice(i, 1); + numActivatedWidgets--; + whereW = (whereW >= i ? (whereW > 0 ? whereW - 1 : 0) : whereW); + adjustWhereWidgets(false); + // if no more widgets, go back to the icons + if (numActivatedWidgets == 0) { + state = 'home'; + widgetContainer.setAttribute('display', 'none'); + homepage.setAttribute('display', 'inline'); + homebar.setAttribute('display', 'inline'); + execbar.setAttribute('display', 'none'); + arrows.setAttribute('display', 'inline'); + arrowsW.setAttribute('display', 'none'); + widgetAddList.setAttribute('display', 'none'); + } + if (!wid.permanent) { + WidgetManager.unload(wid, false); + } + } + + // + // widget unloading action (WM callback) + // + function widget_remove(wid) { + if (l_deb <= log_level) { + alert('[UI] widget_remove:' + wid.name); + } + widget_close(wid); + WidgetManager.unload(wid, false); + } + + // + // widget launcher action + // + function widget_launch(wid, scene_container) { + if (l_inf <= log_level) { + alert('[UI] widget_launch:' + wid.name); + } + var tmp = document.createElement("g"); + tmp.setAttribute("transform", "translate(" + (totalWidth * numActivatedWidgets) + ", 0)"); + widgetContainer.appendChild(tmp); + var icon = null; + alert("wid: " + wid.name + "|" + wid.shortName); + if (typeof wid.shortName != 'undefined') { + var container = document.getElementById(wid.shortName); + if (container != null) { + icon = container.getAttributeNS(xlinkns, 'href'); + } + } + if (icon != null && + sameFileIgnoringSVGView(icon, wid.main) && + endsWith(wid.main, '.svg')) { + // get the animation with id=shortName stored in mainDefs + scene_container = document.getElementById(wid.shortName); + // get the original use on it, used in the icon + var iconContainer = document.getElementById('iconContainer' + wid.shortName); + // create a new use + var use = document.createElement('use'); + // point to the animation + use.setAttributeNS(xlinkns, 'href', '#' + wid.shortName); + // resize the animation + scene_container.setAttribute("width", totalWidth); + scene_container.setAttribute("height", totalHeight - 120); + // resize the original use //TODO fix the aspect ratio conservation + var m, t = Math.abs(totalHeight - 120 - totalWidth) / 2; + if (totalWidth > totalHeight - 120) { + m = 50 / (totalHeight - 120); + iconContainer.setAttribute('transform', 'scale(' + m + ',' + m + ') translate(' + (-t) + ',0)'); + } else { + m = 50 / totalWidth; + iconContainer.setAttribute('transform', 'scale(' + m + ',' + m + ') translate(0,' + (-t) + ') '); + } + // add the new use as widget execution container + tmp.appendChild(use); + wid.activate(scene_container); + } else { + scene_container.setAttribute("width", totalWidth); + scene_container.setAttribute("height", totalHeight - 120); + tmp.appendChild(scene_container); + scene_container.setAttributeNS(xlinkns, 'href', wid.main); + wid.activate(scene_container); + } + wid.activated = true; + activatedWidgets.splice(numActivatedWidgets, 0, wid); + whereW = numActivatedWidgets; + numActivatedWidgets++; + adjustWhereWidgets(false); + wid.load_component = widget_load_component; + wid.permanent = true; + wid.on_load = function () { + WidgetManager.corein_message(this, 'setSize', 'width', totalWidth, 'height', totalHeight - 120, 'dpi', 96); + }; + // + if (log_level > l_inf) { + var i = 0; + alert(">>>>>>>>>>>>> " + wid.name + " interfaces:"); + for (; i < wid.num_interfaces; i++) { + alert("" + wid.get_interface(i).type); + } + } + // } -} -// -// implementation of core:out Show message -// this is a request by the widget to be shown -// -function coreOutShowImplementation(wid, args) { - //alert("core:out show "+wid.name); - var target = widgetContainer.firstElementChild; - var i; - for (i = 0; i < numActivatedWidgets; i++) { - //alert("is it "+activatedWidgets[i].name); - if (activatedWidgets[i] == wid) break; - target = target.nextElementSibling; - } - // here, i is the index of the current widget - //alert(" "+i+" "+numActivatedWidgets); - if (i < numActivatedWidgets) { - whereW = i; - adjustWhereWidgets(false); - } - var ifce = getInterfaceByType(wid, "urn:mpeg:mpegu:schema:widgets:core:out:2010"); - if (ifce != null) { - wmjs_core_out_invoke_reply(coreOut.showMessage, ifce.get_message("show"), wid, 1); // send return code 1 = success + // + // widget load component (WM callback) + // + function widget_load_component(comp, is_unload) { + if (l_deb <= log_level) { + alert('[UI] widget_load_component:' + comp.name); + } + if (is_unload) { + widget_close(comp); + comp.parent = null; + } else { + widget_add(comp); + comp.permanent = false; + comp.parent = this; + } } -} -// -// implementation of core:out GetAttention message -// this is a request by the widget to be shown and some special signal is given -// -function coreOutGetAttentionImplementation(wid, args) { - //alert("core:out getAttention "+wid.name); - var target = widgetContainer.firstElementChild; - var i; - for (i = 0; i < numActivatedWidgets; i++) { - //alert("is it "+activatedWidgets[i].name); - if (activatedWidgets[i] == wid) break; - target = target.nextElementSibling; - } - // here, i is the index of the current widget - //alert(" "+i+" "+numActivatedWidgets); - if (i < numActivatedWidgets) { - whereW = i; - adjustWhereWidgets(false); + var upnp_renders = null, target_widgets = null; + + // + // widget remoting function + // + function on_widget_remote() { + if (WidgetManager.MPEGUStandardServiceProviders.length != 0 && numActivatedWidgets > 0) { + upnp_renders = selector_window(activatedWidgets[whereW]); + upnp_renders.on_select = function(item, wid) { + upnp_renders.unregister(root); + upnp_renders = null; + if (item == -1) { + return; + } + if (wid != null) { + alert("upnp_renders.on_select(" + item + "," + wid.name + ")"); + //WidgetManager.migrate_widget(UPnP.GetMediaRenderer(item), wid); + WidgetManager.migrate_widget(WidgetManager.get_mpegu_service_providers(item), wid); + widget_close(wid); + } + }; + upnp_renders.register(root); + } } - document.getElementById("getAttention").beginElement(); - var ifce = getInterfaceByType(wid, "urn:mpeg:mpegu:schema:widgets:core:out:2010"); - if (ifce != null) { - wmjs_core_out_invoke_reply(coreOut.getAttentionMessage, ifce.get_message("getAttention"), wid, 1); // send return code 1 = success + + // + // creates the menu of available targets for pushing a widget elsewhere + // + function selector_window(widget) { + var i, count, render; + var selector = document.createElement('g'), obj, child; + selector.setAttribute('transform', 'translate(10,10)'); + count = WidgetManager.MPEGUStandardServiceProviders.length; + selector.appendChild(rect(0, 0, 300, 20 * (count + 1), 'white', 'black')); + for (i = 0; i < count; i++) { + render = WidgetManager.MPEGUStandardServiceProviders[i]; + obj = createtext(render.Name, 'black', 5, 17 + (20 * i), 15, 'sans-serif'); + obj.setAttribute('id', "selector" + i); + selector.appendChild(obj); + obj.addEventListener('mouseover', sw1("selector" + i), false); + obj.addEventListener('mouseout', sw2("selector" + i), false); + obj.addEventListener('click', sw3(i, widget), false); + } + obj = createtext('Cancel', 'rgb(0,0,120)', 55, 17 + (20 * i), 15, 'sans-serif'); + obj.setAttribute('id', "cancel"); + selector.appendChild(obj); + obj.addEventListener('mouseover', function(evt) { document.getElementById("cancel").setAttribute("fill", "red"); }, false); + obj.addEventListener('mouseout', function(evt) { document.getElementById("cancel").setAttribute("fill", "black"); }, false); + obj.addEventListener('click', function(evt) { upnp_renders.on_select(-1, null); }, false); + selector.register = function(disp) { + disp.appendChild(this); + }; + selector.unregister = function(disp) { + disp.removeChild(this); + }; + return selector; } -} -// -// implementation of core:out hide message -// this is a request by the widget to be hidden (some other widget (if any) is shown) -// -function coreOutHideImplementation(wid, args) { - //alert("core:out hide "+wid.name); - var target = widgetContainer.firstElementChild; - var i; - for (i = 0; i < numActivatedWidgets; i++) { - //alert("is it "+activatedWidgets[i].name); - if (activatedWidgets[i] == wid) break; - target = target.nextElementSibling; - } - // here, i is the index of the current widget - //alert("hide "+i+" "+numActivatedWidgets); - if (i < numActivatedWidgets) { - if (whereW > 0) whereW--; - else if (whereW < numActivatedWidgets - 1) whereW++; - else return; - adjustWhereWidgets(false); + function sw1(s) { + return function(evt) { document.getElementById(s).setAttribute("fill", "blue"); }; } - var ifce = getInterfaceByType(wid, "urn:mpeg:mpegu:schema:widgets:core:out:2010"); - if (ifce != null) { - wmjs_core_out_invoke_reply(coreOut.hideMessage, ifce.get_message("hide"), wid, 1); // send return code 1 = success + + function sw2(s) { + return function(evt) { document.getElementById(s).setAttribute("fill", "black"); }; } -} -// -// implementation of core:out requestDeactivate message -// this is a request by the widget to be stopped -// -function coreOutRequestDeactivateImplementation(wid, args) { - //alert("core:out hide "+wid.name); - var target = widgetContainer.firstElementChild; - var i; - for (i = 0; i < numActivatedWidgets; i++) { - //alert("is it "+activatedWidgets[i].name); - if (activatedWidgets[i] == wid) break; - target = target.nextElementSibling; - } - // here, i is the index of the current widget - //alert("hide "+i+" "+numActivatedWidgets); - if (i < numActivatedWidgets) { - widget_close(activatedWidgets[i]); - } - var ifce = getInterfaceByType(wid, "urn:mpeg:mpegu:schema:widgets:core:out:2010"); - if (ifce != null) { - wmjs_core_out_invoke_reply(coreOut.requestDeactivateMessage, ifce.get_message("requestDeactivate"), wid, 1); // send return code 1 = success + function sw3(si, widget) { + return function(evt) { upnp_renders.on_select(si, widget); }; } -} -// -// WM callback for when a component is activated by its parent -// -function widget_add(w) { - if (!w.activated) { -/* - if (testCyrilsExtension) { - var anima = document.createElement("animation"); - anima.setAttributeNS('http://gpac.sourceforge.net/svg-extensions', 'use-as-primary', 'false'); - widget_launch(w, anima); - } else { -*/ - if (sameFileIgnoringSVGView(w.icon, w.main)) { - // same file in icon and main - } else { - widget_launch(w, document.createElement("animation")); + // + // when a widget is pushed to here, install the widget and execute it + // + function onMediaConnect(url, src_ip) { + if (l_inf <= log_level) { + alert('[UI] onMediaConnect :\"' + url + '\"'); + } + if (WidgetManager.probe(url)) { + var w = WidgetManager.open(url, src_ip); + if (w == null) { + return; } -// } - return true; - } else if (w.multipleInstances) { - var newwid = WidgetManager.open(w.manifest, null); - widget_launch(newwid, document.createElement("animation")); - return true; + widget_add(w); + adjustWhereWidgets(false); + w.permanent = false; + } } - return false; -} -function sameFileIgnoringSVGView(name1, name2) { - if (name1 == name2) return true; - if (name1 == null) return false; - var i1 = name1.indexOf("#"); - var i2 = name2.indexOf("#"); - if (i1 < 0) i1 = name1.length; - else i1--; - if (i2 < 0) i2 = name2.length; - else i2--; - return name1.substring(0, i1) == name2.substring(0, i2); -} + // + // file list vars + // + var flstart = 0,fllist = null,maxFileNames = 14; -// -// recompute the number of widgets loaded currently -// -function getNbWidgets() { - var i, nbWidgets = 0; - for (i = 0; i < WidgetManager.num_widgets; i++) { - var w = WidgetManager.get(i); - if (w != null && w.loaded) nbWidgets++; + // + // create a file menu in the main screen, allowing to navigate directories and choose widget config files + // + function on_widget_add_menu() { + state = 'list'; + widgetContainer.setAttribute('display', 'none'); + homepage.setAttribute('display', 'none'); + homebar.setAttribute('display', 'none'); + execbar.setAttribute('display', 'inline'); + arrows.setAttribute('display', 'none'); + arrowsW.setAttribute('display', 'none'); + widgetAddList.setAttribute('display', 'inline'); + maxFileNames = Math.round(((iconNbVert * 80) / 25) - 1.4); + isThisAScan = false; + refillWidgetAddList(false); } - return nbWidgets; -} - -// -// just resize the window and viewport, and initialize the first time this is called -// -function resize() { - if (init) initialize(); - if (document.documentElement.viewport.width == previousWidth && document.documentElement.viewport.height == previousHeight) return; - if (l_deb < log_level) alert("[UI] start initialize() w:" + document.documentElement.viewport.width + " h:" + document.documentElement.viewport.height); - adaptLayoutToSize(); - // start by filling the "home page" with known icons - where = 0; - var nbWidgets = getNbWidgets(); - maxwhere = ((nbWidgets - 1) - ((nbWidgets - 1) % iconsPerPage)) / iconsPerPage; - var wid; - var iconIterator = document.getElementById("icons").firstElementChild; - var position = 0; - for (i = 1; i <= WidgetManager.num_widgets; i++) { - wid = WidgetManager.get(i - 1); - // alert("build:"+wid.main+" "+wid.loaded); - if (wid.loaded) { - insert_icon(wid, position, i - 1, iconIterator); - WidgetManager.corein_message(wid, 'setSize', 'width', totalWidth, 'height', totalHeight-120, 'dpi', 96); - position++; - if (iconIterator != null) iconIterator = iconIterator.nextElementSibling; - } - } - adjustwhere(false); - adjustWhereWidgets(false); - previousWidth = document.documentElement.viewport.width; - previousHeight = document.documentElement.viewport.height; - gpac.setOption("Widgets", "LastWMWidth", '' + previousWidth); - gpac.setOption("Widgets", "LastWMHeight", '' + previousHeight); -} - -// -// function inserting an icon on the home page -// -function insert_icon(widget, position, widgetIndex, previousIcon) { - if (l_deb < log_level) alert("[UI] insert_icon: " + widget.shortName + " " + position + " " + widgetIndex + " WMnw:" + WidgetManager.num_widgets); - widget.loaded = true; - if (l_deb < log_level) alert("[UI] widget name: " + widget.name); - var icon = null, original = null; - for (var i = 0; i < widget.icons.length; i++) { - // default to the first icon even if not of the preferred type - if (i == 0) { - icon = widget.icons[0].relocated_src; - //original = widget.icons[0].original; - // alert("choosing default icon " + icon); - } - // check for preferred type - if (widget.icons[i].relocated_src.indexOf(preferredIconType) > 0) { - icon = widget.icons[i].relocated_src; - //original = widget.icons[i].original; - break; - } - } - //alert(""); - //alert("content of widget:"); - //alert("main " + widget.main); - //alert("icon " + icon); - //alert("original " + original); - //alert(""); - //icon = restoreFragmentOnURL(icon, original); - var shortName = widget.shortName; - if (typeof shortName == 'undefined') shortName = widget.name.substring(0, 9); - createIconSVGdecoration(previousIcon, widget, (((position % iconNbHoriz) * 80) + ((80 * (position - (position % iconsPerPage))) / iconNbVert)), - ((((position % iconsPerPage) - (position % iconNbHoriz)) / iconNbHoriz) * 80), "icons", icon, - shortName, widgetIndex); -} -function restoreFragmentOnURL(iconUrl, original) { - var l = original.indexOf('#'); - if (l >= 0) { - return iconUrl + original.substring(l); + // + // create a file menu in the main screen, allowing to navigate directories and choose a directory to scan for widgets + // and load all their icons + // + function on_dir_scan() { + state = 'list'; + widgetContainer.setAttribute('display', 'none'); + homepage.setAttribute('display', 'none'); + homebar.setAttribute('display', 'none'); + execbar.setAttribute('display', 'inline'); + arrows.setAttribute('display', 'none'); + arrowsW.setAttribute('display', 'none'); + widgetAddList.setAttribute('display', 'inline'); + maxFileNames = Math.round(((iconNbVert * 80) / 25) - 1.4); + isThisAScan = true; + refillWidgetAddList(true); } - return iconUrl; -} - -// constant -const corein = "urn:mpeg:mpegu:schema:widgets:core:in:2010"; -// -// commodity method to empty a list of children -// -function removeAllChildren(o) { - if (o != null && o.hasChildNodes()) { - while (o.childNodes.length >= 1) { - o.removeChild(o.firstChild); + // + // remove all installed icons + // + function on_clean_up() { + var i; + //if (l_inf <= log_level) alert('[UI] unloading ' + WidgetManager.num_widgets + ' widgets'); + for (i = WidgetManager.num_widgets - 1; i >= 0; i--) { + var w = WidgetManager.get(i); + if (w.loaded) { + alert("unloading " + w.name); + w.loaded = false; + WidgetManager.unload(w, false); + } } + where = 0; + maxwhere = 0; + removeAllChildren(document.getElementById("icons")); + adjustwhere(false); } -} -// -// create the home page icon with all its behaviours -// -function createIconSVGdecoration(previousIcon, widget, x, y, fatherId, iconUrl, name, widIndex) { - var g, g2; - if (l_inf < log_level) alert("[UI] createIconSVGdecoration " + iconUrl + " " + x + " " + y); - if (previousIcon != null) { - g = previousIcon; - } else { - g = document.createElement("g"); - document.getElementById(fatherId).appendChild(g); - } - g.setAttribute("transform", 'translate(' + x + ',' + y + ')'); - if (previousIcon != null) { - g2 = g.firstElementChild; - } else { - g2 = document.createElement("g"); - g2.setAttribute("transform", 'translate(15,10)'); - g.appendChild(g2); - } - if (iconUrl == null || iconUrl == "") iconUrl = "icons/face-surprise.svg"; - // - // process differently cases where widget.icon == widget.main - // - var container; - if (sameFileIgnoringSVGView(iconUrl, widget.main) && widget.main.indexOf('.svg') >= 0) { - // see if the animation already exists - container = document.getElementById(name); - if (container == null) { - // if the animation does not exist yet, create it - container = media('animation', iconUrl, 50, 50); - // store the animation in the main defs - document.getElementById("mainDefs").appendChild(container); - container.setAttribute('id', name); + // + // install, but do not launch, the widget whose config.xml has been chosen, and return to the home page + // + function widgetInstall(uri, manual, temporary, parent_wid) { + var wid, j, count = WidgetManager.num_widgets, nbWidgets = getNbWidgets(); + for (j = 0; j < count; j++) { + wid = WidgetManager.get(j); + if (wid.url == uri) { + if (wid.loaded) { + break; + } + if (temporary) { + wid.permanent = false; + } + else { + insert_icon(wid, nbWidgets, nbWidgets); + } + } } - if (previousIcon == null) { - // put the container in a use - var use = document.createElement("use"); - use.setAttribute('id', 'iconContainer' + name); - use.setAttributeNS(xlinkns, 'href', '#' + name); - g2.appendChild(use); + if (j == count) { + wid = WidgetManager.open(uri, null, parent_wid); + if (wid != null) { + if (temporary) { + wid.permanent = false; + } + else { + insert_icon(wid, nbWidgets, nbWidgets); + } + } + } + if (manual) { + return wid; } - } else { - if (previousIcon == null) { - container = appropriateElementForMedia(iconUrl, 50, 50); - container.setAttribute('id', name); - g2.appendChild(container); - } - } - if (previousIcon == null) { - g2 = document.createElement("g"); - g2.setAttribute("transform", 'translate(40,70)'); - g.appendChild(g2); - var anim = createtext(name, 'white', 0, 0, 14, 'Arial Unicode MS'); - anim.setAttribute("text-anchor", "middle"); - anim.setAttribute("display-align", "center"); - g2.appendChild(anim); - var rect = invisible_rect(80, 80); - g.appendChild(rect); - rect.addEventListener("click", csi(widget), false); - } -} - -function csi(widget) { - return function(evt) { activating_widget(widget);}; -} - -// -// widget closing action (WM callback) -// -function widget_close(wid) { - if (wid == null) return; - if (l_inf <= log_level) alert('[UI] widget_close:' + wid.name); - // maybe inform the widget that it is going to be deactivated - WidgetManager.corein_message(wid, "deactivate"); - var target = widgetContainer.firstElementChild; - var i; - for (i = 0; i < numActivatedWidgets; i++) { - if (activatedWidgets[i] == wid) break; - target = target.nextElementSibling; - } - if (target != null) { - // move next widgets back one slot - recurseMoveAfterDelete(target); - // stop the subscene - if (target.firstElementChild != null) target.firstElementChild.setAttributeNS(xlinkns, "href", ""); - // end trying - widgetContainer.removeChild(target); - } - wid.deactivate(); - wid.activated = false; - activatedWidgets.splice(i, 1); - numActivatedWidgets--; - whereW = (whereW >= i ? (whereW > 0 ? whereW - 1 : 0) : whereW); - adjustWhereWidgets(false); - // if no more widgets, go back to the icons - if (numActivatedWidgets == 0) { state = 'home'; widgetContainer.setAttribute('display', 'none'); homepage.setAttribute('display', 'inline'); @@ -791,569 +1286,351 @@ function widget_close(wid) { execbar.setAttribute('display', 'none'); arrows.setAttribute('display', 'inline'); arrowsW.setAttribute('display', 'none'); - widgetAddList.setAttribute('display', 'none'); + removeAllChildren(widgetAddList); + maxwhere = ((nbWidgets - 1) - ((nbWidgets - 1) % iconsPerPage)) / iconsPerPage; + where = maxwhere; + adjustwhere(false); + return wid; } - if (!wid.permanent) WidgetManager.unload(wid, false); -} -// -// widget unloading action (WM callback) -// -function widget_remove(wid) { - if (l_deb <= log_level) alert('[UI] widget_remove:' + wid.name); - widget_close(wid); - WidgetManager.unload(wid, false); -} - -// -// widget launcher action -// -function widget_launch(wid, scene_container) { - if (l_inf <= log_level) alert('[UI] widget_launch:' + wid.name); - var tmp = document.createElement("g"); - tmp.setAttribute("transform", "translate(" + (totalWidth * numActivatedWidgets) + ", 0)"); - widgetContainer.appendChild(tmp); - var icon = null; - alert("wid: "+wid.name+"|"+wid.shortName); - if (typeof wid.shortName != 'undefined') { - var container = document.getElementById(wid.shortName); - if (container != null) icon = container.getAttributeNS(xlinkns, 'href'); - } - if (icon != null && - sameFileIgnoringSVGView(icon, wid.main) && - endsWith(wid.main, '.svg')) { - // get the animation with id=shortName stored in mainDefs - scene_container = document.getElementById(wid.shortName); - // get the original use on it, used in the icon - var iconContainer = document.getElementById('iconContainer' + wid.shortName); - // create a new use - var use = document.createElement('use'); - // point to the animation - use.setAttributeNS(xlinkns, 'href', '#' + wid.shortName); - // resize the animation - scene_container.setAttribute("width", totalWidth); - scene_container.setAttribute("height", totalHeight - 120); - // resize the original use //TODO fix the aspect ratio conservation - var m, t = Math.abs(totalHeight - 120 - totalWidth) / 2; - if (totalWidth > totalHeight - 120) { - m = 50 / (totalHeight - 120); - iconContainer.setAttribute('transform', 'scale(' + m + ',' + m + ') translate(' + (-t) + ',0)'); - } else { - m = 50 / totalWidth; - iconContainer.setAttribute('transform', 'scale(' + m + ',' + m + ') translate(0,' + (-t) + ') '); - } - // add the new use as widget execution container - tmp.appendChild(use); - wid.activate(scene_container); - } else { - scene_container.setAttribute("width", totalWidth); - scene_container.setAttribute("height", totalHeight - 120); - tmp.appendChild(scene_container); - scene_container.setAttributeNS(xlinkns, 'href', wid.main); - wid.activate(scene_container); - } - wid.activated = true; - activatedWidgets.splice(numActivatedWidgets, 0, wid); - whereW = numActivatedWidgets; - numActivatedWidgets++; - adjustWhereWidgets(false); - wid.load_component = widget_load_component; - wid.permanent = true; - wid.on_load = function () { - WidgetManager.corein_message(this, 'setSize', 'width', totalWidth, 'height', totalHeight-120, 'dpi', 96); - }; - // - if (log_level > l_inf) { - var i = 0; - alert(">>>>>>>>>>>>> "+wid.name+" interfaces:"); - for (;i < wid.num_interfaces; i++) { - alert(""+wid.get_interface(i).type); - } - } // -} - -// -// widget load component (WM callback) -// -function widget_load_component(comp, is_unload) { - if (l_deb <= log_level) alert('[UI] widget_load_component:' + comp.name); - if (is_unload) { - widget_close(comp); - comp.parent = null; - } else { - widget_add(comp); - comp.permanent = false; - comp.parent = this; - } -} - -upnp_renders = null; - -// -// widget remoting function -// -function on_widget_remote() { - if (UPnP.MediaRenderersCount && numActivatedWidgets > 0) { - upnp_renders = selector_window(activatedWidgets[whereW]); - upnp_renders.on_select = function(item, wid) { - upnp_renders.unregister(root); - upnp_renders = null; - if (item == -1) return; - if (wid != null) { - alert("upnp_renders.on_select("+item+","+wid.name+")"); - WidgetManager.migrate_widget(UPnP.GetMediaRenderer(item), wid); - widget_close(wid); - } - }; - upnp_renders.register(root); - } -} - -// -// creates the menu of available targets for pushing a widget elsewhere -// -function selector_window(widget) { - var i, count, render; - var selector = document.createElement('g'), obj, child; - selector.setAttribute('transform', 'translate(10,10)'); - count = UPnP.MediaRenderersCount; - selector.appendChild(rect(0, 0, 300, 20 * (count + 1), 'white', 'black')); - for (i = 0; i < count; i++) { - render = UPnP.GetMediaRenderer(i); - obj = createtext(render.Name, 'black', 5, 17 + (20 * i), 15, 'sans-serif'); - obj.setAttribute('id', "selector" + i); - selector.appendChild(obj); - obj.addEventListener('mouseover', sw1("selector"+i), false); - obj.addEventListener('mouseout', sw2("selector"+i), false); - obj.addEventListener('click', sw3(i, widget), false); - } - obj = createtext('Cancel', 'rgb(0,0,120)', 55, 17 + (20 * i), 15, 'sans-serif'); - obj.setAttribute('id', "cancel"); - selector.appendChild(obj); - obj.addEventListener('mouseover', function(evt) { document.getElementById("cancel").setAttribute("fill", "red"); }, false); - obj.addEventListener('mouseout', function(evt) { document.getElementById("cancel").setAttribute("fill", "black"); }, false); - obj.addEventListener('click', function(evt) { upnp_renders.on_select(-1, null); }, false); - selector.register = function(disp) { - disp.appendChild(this); - }; - selector.unregister = function(disp) { - disp.removeChild(this); - }; - return selector; -} - -function sw1(s) { - return function(evt) { document.getElementById(s).setAttribute("fill", "blue"); }; -} - -function sw2(s) { - return function(evt) { document.getElementById(s).setAttribute("fill", "black"); }; -} - -function sw3(si, widget) { - return function(evt) { upnp_renders.on_select(si, widget); }; -} - -// -// when a widget is pushed to here, install the widget and execute it -// -function onMediaConnect(url, src_ip) { - if (l_inf <= log_level) alert('[UI] onMediaConnect :\"' + url + '\"'); - if (WidgetManager.probe(url)) { - var w = WidgetManager.open(url, src_ip); - if (w == null) return; - state = 'exec'; - widgetContainer.setAttribute('display', 'inline'); - homepage.setAttribute('display', 'none'); - homebar.setAttribute('display', 'none'); - execbar.setAttribute('display', 'inline'); - arrows.setAttribute('display', 'none'); - arrowsW.setAttribute('display', 'inline'); - widgetAddList.setAttribute('display', 'none'); - var nbWidgets = getNbWidgets(); - widget_add(w); - adjustWhereWidgets(false); - w.permanent = false; + // clean up file list space and refill it + // + function refillWidgetAddList(flag) { + removeAllChildren(widgetAddList); + fllist = null; + flstart = 0; + fllist = gpac.enum_directory(gpac.last_working_directory, "", false); + fillWidgetAddList(flag); } -} - -// -// file list vars -// -flstart = 0, fllist = null, maxFileNames = 14; - -// -// create a file menu in the main screen, allowing to navigate directories and choose widget config files -// -function on_widget_add_menu() { - state = 'list'; - widgetContainer.setAttribute('display', 'none'); - homepage.setAttribute('display', 'none'); - homebar.setAttribute('display', 'none'); - execbar.setAttribute('display', 'inline'); - arrows.setAttribute('display', 'none'); - arrowsW.setAttribute('display', 'none'); - widgetAddList.setAttribute('display', 'inline'); - maxFileNames = Math.round(((iconNbVert * 80) / 25) - 1.4); - isThisAScan = false; - refillWidgetAddList(false); -} - -// -// create a file menu in the main screen, allowing to navigate directories and choose a directory to scan for widgets -// and load all their icons -// -function on_dir_scan() { - state = 'list'; - widgetContainer.setAttribute('display', 'none'); - homepage.setAttribute('display', 'none'); - homebar.setAttribute('display', 'none'); - execbar.setAttribute('display', 'inline'); - arrows.setAttribute('display', 'none'); - arrowsW.setAttribute('display', 'none'); - widgetAddList.setAttribute('display', 'inline'); - maxFileNames = Math.round(((iconNbVert * 80) / 25) - 1.4); - isThisAScan = true; - refillWidgetAddList(true); -} - -// -// remove all installed icons -// -function on_clean_up() { - var i; - //if (l_inf <= log_level) alert('[UI] unloading ' + WidgetManager.num_widgets + ' widgets'); - for (i = WidgetManager.num_widgets - 1; i >= 0; i--) { - var w = WidgetManager.get(i); - if (w.loaded) { - alert("unloading " + w.name); - w.loaded = false; - WidgetManager.unload(w, false); - } - } - where = 0; - maxwhere = 0; - removeAllChildren(document.getElementById("icons")); - adjustwhere(false); -} - -// -// install, but do not launch, the widget whose config.xml has been chosen, and return to the home page -// -function widgetInstall(uri, manual, temporary) { - var wid, j, count = WidgetManager.num_widgets, nbWidgets = getNbWidgets(); - for (j = 0; j < count; j++) { - wid = WidgetManager.get(j); - if (wid.url == uri) { - if (wid.loaded) break; - if (temporary) wid.permanent = false; - else insert_icon(wid, nbWidgets, nbWidgets); - } - } - if (j == count) { - wid = WidgetManager.open(uri, null); - if (wid != null) { - if (temporary) wid.permanent = false; - else insert_icon(wid, nbWidgets, nbWidgets); - } - } - if (manual) return wid; - state = 'home'; - widgetContainer.setAttribute('display', 'none'); - homepage.setAttribute('display', 'inline'); - homebar.setAttribute('display', 'inline'); - execbar.setAttribute('display', 'none'); - arrows.setAttribute('display', 'inline'); - arrowsW.setAttribute('display', 'none'); - removeAllChildren(widgetAddList); - maxwhere = ((nbWidgets - 1) - ((nbWidgets - 1) % iconsPerPage)) / iconsPerPage; - where = maxwhere; - adjustwhere(false); - return wid; -} - -// -// clean up file list space and refill it -// -function refillWidgetAddList(flag) { - removeAllChildren(widgetAddList); - fllist = null; - flstart = 0; - fllist = gpac.enum_directory(gpac.last_working_directory, "", false); - fillWidgetAddList(flag); -} -// -// go to parent directory -// -function flUpDir(evt) { - var s = gpac.last_working_directory; - if (l_inf <= log_level) alert("[UI] lwd:" + gpac.last_working_directory); - var index = s.lastIndexOf("\\"); - if (index != -1) { - gpac.last_working_directory = s.substring(0, index); - refillWidgetAddList(isThisAScan); - } else { - index = s.lastIndexOf("/"); + // + // go to parent directory + // + function flUpDir(evt) { + var s = gpac.last_working_directory; + if (l_inf <= log_level) { + alert("[UI] lwd:" + gpac.last_working_directory); + } + var index = s.lastIndexOf("\\"); if (index != -1) { gpac.last_working_directory = s.substring(0, index); refillWidgetAddList(isThisAScan); } else { - gpac.last_working_directory = "/"; - refillWidgetAddList(isThisAScan); + index = s.lastIndexOf("/"); + if (index != -1) { + gpac.last_working_directory = s.substring(0, index); + refillWidgetAddList(isThisAScan); + } else { + gpac.last_working_directory = "/"; + refillWidgetAddList(isThisAScan); + } } } -} - -// -// go to a named directory -// -function flGoTo(newDir) { - //alert("goto "+newDir); - var s = gpac.last_working_directory; - if (s == "/") { - gpac.last_working_directory = newDir; - } else { - var c = s.charAt(s.length - 1); - if (c != '\\' && c != '/') s += "/"; - gpac.last_working_directory = s + newDir; - } - //alert(gpac.last_working_directory); - refillWidgetAddList(isThisAScan); -} -// -// if the directory contains more files that can be shown, show previous page of file names -// -function flPrevFiles(evt) { - if (flstart == 0) return; - flstart -= maxFileNames; - if (flstart < 0) flstart = 0; - removeAllChildren(widgetAddList); - fillWidgetAddList(isThisAScan); -} + // + // go to a named directory + // + function flGoTo(newDir) { + //alert("goto "+newDir); + var s = gpac.last_working_directory; + if (s == "/") { + gpac.last_working_directory = newDir; + } else { + var c = s.charAt(s.length - 1); + if (c != '\\' && c != '/') { + s += "/"; + } + gpac.last_working_directory = s + newDir; + } + //alert(gpac.last_working_directory); + refillWidgetAddList(isThisAScan); + } -// -// if the directory contains more files that can be shown, show next page of file names -// -function flNextFiles(evt) { - if (flstart + maxFileNames < fllist.length) { - flstart += maxFileNames; + // + // if the directory contains more files that can be shown, show previous page of file names + // + function flPrevFiles(evt) { + if (flstart == 0) { + return; + } + flstart -= maxFileNames; + if (flstart < 0) { + flstart = 0; + } removeAllChildren(widgetAddList); fillWidgetAddList(isThisAScan); } -} -// -// scan the current directory recursively for widgets, clean up and return to home page -// -function flScanDir(evt) { - scan_directory(gpac.last_working_directory); - state = 'home'; - var nbWidgets = getNbWidgets(); - widgetContainer.setAttribute('display', 'none'); - homepage.setAttribute('display', 'inline'); - homebar.setAttribute('display', 'inline'); - execbar.setAttribute('display', 'none'); - arrows.setAttribute('display', 'inline'); - arrowsW.setAttribute('display', 'none'); - removeAllChildren(widgetAddList); - maxwhere = ((nbWidgets - 1) - ((nbWidgets - 1) % iconsPerPage)) / iconsPerPage; - where = maxwhere; - adjustwhere(false); -} + // + // if the directory contains more files that can be shown, show next page of file names + // + function flNextFiles(evt) { + if (flstart + maxFileNames < fllist.length) { + flstart += maxFileNames; + removeAllChildren(widgetAddList); + fillWidgetAddList(isThisAScan); + } + } -// -// scanning -// -function scan_directory(dir) { - var ii, j, count, list, w, uri, loadedWidgets = 0; - list = gpac.enum_directory(dir, '.xml;.wgt', 0); - for (ii = 0; ii < list.length; ii++) { - uri = list[ii].path + list[ii].name; - if (list[ii].directory) { - scan_directory(uri); - } else { - count = WidgetManager.num_widgets; - for (j = 0; j < count; j++) { - var wid = WidgetManager.get(j); - if (wid.loaded) loadedWidgets++; - if (wid.url == uri) { - if (wid.loaded) break; - insert_icon(wid, getNbWidgets(), j); - break; + // + // scan the current directory recursively for widgets, clean up and return to home page + // + function flScanDir(evt) { + scan_directory(gpac.last_working_directory); + state = 'home'; + var nbWidgets = getNbWidgets(); + widgetContainer.setAttribute('display', 'none'); + homepage.setAttribute('display', 'inline'); + homebar.setAttribute('display', 'inline'); + execbar.setAttribute('display', 'none'); + arrows.setAttribute('display', 'inline'); + arrowsW.setAttribute('display', 'none'); + removeAllChildren(widgetAddList); + maxwhere = ((nbWidgets - 1) - ((nbWidgets - 1) % iconsPerPage)) / iconsPerPage; + where = maxwhere; + adjustwhere(false); + } + + // + // scanning + // + function scan_directory(dir) { + var ii, j, count, list, w, uri, loadedWidgets = 0; + list = gpac.enum_directory(dir, '.xml;.wgt', 0); + for (ii = 0; ii < list.length; ii++) { + uri = list[ii].path + list[ii].name; + if (list[ii].directory) { + scan_directory(uri); + } else { + count = WidgetManager.num_widgets; + for (j = 0; j < count; j++) { + var wid = WidgetManager.get(j); + if (wid.loaded) { + loadedWidgets++; + } + if (wid.url == uri) { + if (wid.loaded) { + break; + } + insert_icon(wid, getNbWidgets(), j); + break; + } } - } - if (j == count) { - w = WidgetManager.open(uri, null); - if (w != null) { - insert_icon(w, loadedWidgets, WidgetManager.num_widgets - 1); + if (j == count) { + w = WidgetManager.open(uri, null); + if (w != null) { + insert_icon(w, loadedWidgets, WidgetManager.num_widgets - 1); + } } } } } -} -// -// create the up, prev, next button, show current directory and as many file names as possible -// the file names are active: clicking on a directory name goes to that directory -// clicking on a file tries to load that file as a widget -// -function fillWidgetAddList(flag) { - if (flag) { - widgetAddList.appendChild(use("cartoucheflag")); - document.getElementById("dirflag").textContent = gpac.last_working_directory; - } else { - widgetAddList.appendChild(use("cartouche")); - document.getElementById("dir").textContent = gpac.last_working_directory; - } - // next lines are file names - var obj; - for (i = 0; i < (fllist.length - flstart) && i < maxFileNames; i++) { - obj = use("fileMenuElement" + i); - obj.setAttribute('transform', 'translate(0,' + (25 * (i + 1)) + ')'); - widgetAddList.appendChild(obj); - document.getElementById("fileMenuElement" + i + "u").setAttributeNS(xlinkns, 'href', "#" + (fllist[i + flstart].directory ? 'folder' : 'new')); - document.getElementById("fileMenuElement" + i + "t").textContent = fllist[i + flstart].name; - if (obj.listener != null) obj.removeEventListener("click", obj.listener); - if (fllist[i + flstart].directory) { - obj.listener = createGoto(escaping(fllist[i + flstart].name)); - obj.addEventListener("click", obj.listener, false); - } else if (isWidgetFileName(fllist[i + flstart].name)) { - obj.listener = createWidgetInstall(escaping(gpac.last_working_directory + '/' + fllist[i + flstart].name)); - obj.addEventListener("click", obj.listener, false); + // + // create the up, prev, next button, show current directory and as many file names as possible + // the file names are active: clicking on a directory name goes to that directory + // clicking on a file tries to load that file as a widget + // + function fillWidgetAddList(flag) { + if (flag) { + widgetAddList.appendChild(use("cartoucheflag")); + document.getElementById("dirflag").textContent = gpac.last_working_directory; + } else { + widgetAddList.appendChild(use("cartouche")); + document.getElementById("dir").textContent = gpac.last_working_directory; + } + // next lines are file names + var obj; + for (i = 0; i < (fllist.length - flstart) && i < maxFileNames; i++) { + obj = use("fileMenuElement" + i); + obj.setAttribute('transform', 'translate(0,' + (25 * (i + 1)) + ')'); + widgetAddList.appendChild(obj); + document.getElementById("fileMenuElement" + i + "u").setAttributeNS(xlinkns, 'href', "#" + (fllist[i + flstart].directory ? 'folder' : 'new')); + document.getElementById("fileMenuElement" + i + "t").textContent = fllist[i + flstart].name; + if (obj.listener != null) { + obj.removeEventListener("click", obj.listener); + } + if (fllist[i + flstart].directory) { + obj.listener = createGoto(escaping(fllist[i + flstart].name)); + obj.addEventListener("click", obj.listener, false); + } else if (isWidgetFileName(fllist[i + flstart].name)) { + obj.listener = createWidgetInstall(escaping(gpac.last_working_directory + '/' + fllist[i + flstart].name)); + obj.addEventListener("click", obj.listener, false); + } } } -} - -function createGoto(s) { - return function () { - flGoTo(s); - }; -} - -function createWidgetInstall(s) { - return function () { - widgetInstall(s, false, false); - }; -} -// // // // // // // // // // // // // // // -// function library -// // // // // // // // // // // // // // // - -function isWidgetFileName(s) { - if (endsWith(s, 'config.xml')) return true; - if (endsWith(s, '.wgt')) return true; - return false; -} - -// -// replace globally \ with / in a string -// -function escaping(s) { - s = s.replace(/\\/g, '/'); - return s; -} - -// -// create rect -// -function rect(x, y, w, h, fill, stroke, id) { - var child = document.createElement('rect'); - if (id != null) { - child.setAttribute('id', id); - } - child.setAttribute('x', x); - child.setAttribute('y', y); - child.setAttribute('width', w); - child.setAttribute('height', h); - child.setAttribute('fill', fill); - child.setAttribute('stroke', stroke); - return child; -} + function createGoto(s) { + return function () { + flGoTo(s); + }; + } -// -// create text -// -function createtext(content, fill, x, y, size, family) { - var child = document.createElement('text'); - child.setAttribute('fill', fill); - child.textContent = content; - child.setAttribute('x', x); - child.setAttribute('y', y); - child.setAttribute('font-size', size); - child.setAttribute('font-family', family); - return child; -} + function createWidgetInstall(s) { + return function () { + widgetInstall(s, false, false, null); + }; + } -// -// create invisible rect getting all events -// -function invisible_rect(w, h) { - var child = document.createElement('rect'); - child.setAttribute('width', w); - child.setAttribute('height', h); - child.setAttribute('fill', 'none'); - child.setAttribute('stroke', 'none'); - child.setAttribute('pointer-events', 'all'); - return child; -} + // // // // // // // // // // // // // // // + // function library + // // // // // // // // // // // // // // // -// -// create animation -// -function media(etype, uri, w, h) { - var child = document.createElement(etype); - child.setAttributeNS(xlinkns, 'href', uri); - child.setAttribute('width', w); - child.setAttribute('height', h); - if (etype == 'animation') { - child.setAttributeNS('http://gpac.sourceforge.net/svg-extensions', 'use-as-primary', 'false'); - } - return child; -} + function isWidgetFileName(s) { + if (endsWith(s, 'config.xml')) { + return true; + } + if (endsWith(s, '.wgt')) { + return true; + } + return false; + } -// -// create use -// -function use(uri) { - var child = document.createElement('use'); - child.setAttributeNS(xlinkns, 'href', '#' + uri); - return child; -} + // + // replace globally \ with / in a string + // + function escaping(s) { + s = s.replace(/\\/g, '/'); + return s; + } -// -// create appropriate element for media reference by the given uri -// -function appropriateElementForMedia(uri, w, h) { - if (uri.indexOf('#') != -1) { - return media('animation', uri, w, h); + // + // create rect + // + function rect(x, y, w, h, fill, stroke, id) { + var child = document.createElement('rect'); + if (id != null) { + child.setAttribute('id', id); + } + child.setAttribute('x', x); + child.setAttribute('y', y); + child.setAttribute('width', w); + child.setAttribute('height', h); + child.setAttribute('fill', fill); + child.setAttribute('stroke', stroke); + return child; } - if (endsWith(uri, '.svg')) { - return media('animation', uri, w, h); + + // + // create text + // + function createtext(content, fill, x, y, size, family) { + var child = document.createElement('text'); + child.setAttribute('fill', fill); + child.textContent = content; + child.setAttribute('x', x); + child.setAttribute('y', y); + child.setAttribute('font-size', size); + child.setAttribute('font-family', family); + return child; } - if (endsWith(uri, '.bt')) { - return media('animation', uri, w, h); + + // + // create invisible rect getting all events + // + function invisible_rect(w, h) { + var child = document.createElement('rect'); + child.setAttribute('width', w); + child.setAttribute('height', h); + child.setAttribute('fill', 'none'); + child.setAttribute('stroke', 'none'); + child.setAttribute('pointer-events', 'all'); + return child; } - if (endsWith(uri, '.png')) { - return media('image', uri, w, h); + + // + // create animation + // + function media(etype, uri, w, h) { + var child = document.createElement(etype); + child.setAttributeNS(xlinkns, 'href', uri); + child.setAttribute('width', w); + child.setAttribute('height', h); + if (etype == 'animation') { + child.setAttributeNS('http://gpac.sourceforge.net/svg-extensions', 'use-as-primary', 'false'); + } + return child; } - if (endsWith(uri, '.jpg')) { - return media('image', uri, w, h); + + // + // create use + // + function use(uri) { + var child = document.createElement('use'); + child.setAttributeNS(xlinkns, 'href', '#' + uri); + return child; } - if (endsWith(uri, '.gif')) { + + // + // create appropriate element for media reference by the given uri + // + function appropriateElementForMedia(uri, w, h) { + if (uri.indexOf('#') != -1) { + return media('animation', uri, w, h); + } + if (endsWith(uri, '.svg')) { + return media('animation', uri, w, h); + } + if (endsWith(uri, '.bt')) { + return media('animation', uri, w, h); + } + if (endsWith(uri, '.png')) { + return media('image', uri, w, h); + } + if (endsWith(uri, '.jpg')) { + return media('image', uri, w, h); + } + if (endsWith(uri, '.gif')) { + return media('image', uri, w, h); + } + if (l_war <= log_level) { + alert("[UI] WARNING: bad suffix for an icon URI: " + uri); + } return media('image', uri, w, h); } - if (l_war <= log_level) alert("[UI] WARNING: bad suffix for an icon URI: " + uri); - return media('image', uri, w, h); -} -// -// substitute for the useful predefined function endsWith -// -function endsWith(s1, s2) { - return s1.toLowerCase().substring(s1.length - s2.length) == s2; + // + // substitute for the useful predefined function endsWith + // + function endsWith(s1, s2) { + return s1.toLowerCase().substring(s1.length - s2.length) == s2; + } + + // export the resize function as the resize member of iphone_wm_gui, as well as other functions + return { + resize: resize, + left_button: left_button, + right_button: right_button, + left_buttonW: left_buttonW, + right_buttonW: right_buttonW, + home_button: home_button, + on_dir_scan: on_dir_scan, + on_clean_up: on_clean_up, + on_widget_add_menu: on_widget_add_menu, + on_kill_widget: on_kill_widget, + on_get_widget: on_get_widget, + on_widget_remote: on_widget_remote, + flUpDir: flUpDir, + flPrevFiles: flPrevFiles, + flNextFiles: flNextFiles, + flScanDir: flScanDir + } + +}()); + +function widget_activated_and_bound(wid) { + WidgetManager.corein_message(wid, "activate"); } +function printAllFieldsOf(obj, printableName) { + var details = "fields of "+printableName+":\n"; + for (var field in obj) { + fieldContents = obj[field]; + if (typeof(fieldContents) == "function") { + fieldContents = "(function)"; + } + details += " " + field + ": " + fieldContents + "\n"; + } + alert(details); +} diff --git a/include/gpac/carousel.h b/include/gpac/ait.h similarity index 56% rename from include/gpac/carousel.h rename to include/gpac/ait.h index 2cbadc9..f393fa3 100644 --- a/include/gpac/carousel.h +++ b/include/gpac/ait.h @@ -1,178 +1,240 @@ -/* - * GPAC - Multimedia Framework C SDK - * - * Authors: Telecom Paristech - * Copyright (c)2006-200X ENST - All rights reserved - * - * This file is part of GPAC / MPEG2-TS sub-project - * - * GPAC is gf_free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the gf_free Software Foundation; either version 2, or (at your option) - * any later version. - * - * GPAC is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; see the file COPYING. If not, write to - * the gf_free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - * - */ - - -#ifndef _GF_CAROUSSEL_H_ -#define _GF_CAROUSSEL_H_ - -#include -#include -#include - -#define AIT_SECTION_LENGTH_MAX 1021 -#define APPLICATION_TYPE_HTTP_APPLICATION 16 - -typedef enum { - APPLICATION_DESCRIPTOR = 0x00, - APPLICATION_NAME_DESCRIPTOR = 0x01, - TRANSPORT_PROTOCOL_DESCRIPTOR = 0x02, - SIMPLE_APPLICATION_LOCATION_DESCRIPTOR = 0x15, - APPLICATION_USAGE_DESCRIPTOR = 0x16, -} DESCRIPTOR_TAG; - - -typedef struct -{ - - ABSTRACT_ES - GF_M2TS_SectionFilter *sec; - - u32 service_id; - u8 table_id; - Bool section_syntax_indicator; - u16 section_length; - Bool test_application_flag; - u16 application_type; - u8 version_number; - Bool current_next_indicator; - u8 section_number; - u8 last_section_number; - u16 common_descriptors_length; - GF_List * common_descriptors; - u16 application_loop_length; - GF_List * application; - u32 CRC_32; - -} GF_M2TS_AIT; - - -typedef struct -{ - u32 organisation_id; - u16 application_id; - u8 application_control_code; - u16 application_descriptors_loop_length; - GF_List * application_descriptors; - u8 application_descriptors_id[50]; - u8 index_app_desc_id; - -} -GF_M2TS_AIT_APPLICATION; - - -typedef enum { - FUTURE_USE = 0x00, - CAROUSEL = 0x01, - RESERVED = 0x02, - TRANSPORT_HTTP = 0x03, - DVB_USE = 0x04, - TO_REGISTER = 0x100, -} PROTOCOL_ID; - -typedef struct -{ - u8 descriptor_tag; - u8 descriptor_length; - u8 application_profiles_length; - u16 application_profile; - u8 version_major; - u8 version_minor; - u8 version_micro; - Bool service_bound_flag; - u8 visibility; - u8 application_priority; - u8 transport_protocol_label; - -} GF_M2TS_APPLICATION_DESCRIPTOR; - -typedef struct -{ - u8 descriptor_tag; - u8 descriptor_length; - u8 usage_type; - -} GF_M2TS_APPLICATION_USAGE; - -typedef struct -{ - u8 descriptor_tag; - u8 descriptor_length; - char* initial_path_bytes; - -}GF_M2TS_SIMPLE_APPLICATION_LOCATION; - -typedef struct -{ - Bool remote_connection; - u16 original_network_id; - u16 transport_stream_id; - u16 service_id; - u8 component_tag; - -} GF_M2TS_OBJECT_CAROUSEL_SELECTOR_BYTE; - -typedef struct{ - - u8 URL_extension_length; - char* URL_extension_byte; - -}GF_M2TS_TRANSPORT_HTTP_URL_EXTENTION; - -typedef struct -{ - u8 URL_base_length; - char* URL_base_byte; - u8 URL_extension_count; - GF_M2TS_TRANSPORT_HTTP_URL_EXTENTION* URL_extentions; - -} GF_M2TS_TRANSPORT_HTTP_SELECTOR_BYTE; - -typedef struct -{ - u8 descriptor_tag; - u8 descriptor_length; - u16 protocol_id; - u8 transport_protocol_label; - void* selector_byte; - -} GF_M2TS_TRANSPORT_PROTOCOL_DESCRIPTOR; - -typedef struct -{ - u8 descriptor_tag; - u8 descriptor_length; - u32 ISO_639_language_code; - u8 application_name_length; - char* application_name_char; - -} GF_M2TS_APPLICATION_NAME_DESCRIPTOR; - -GF_Err gf_m2ts_process_ait(GF_M2TS_AIT *es, char *data, u32 data_size, u32 table_id); -void on_ait_section(GF_M2TS_Demuxer *ts, u32 evt_type, void *par); -GF_M2TS_ES *gf_ait_section_new(u32 service_id); -void gf_ait_destroy(GF_M2TS_AIT* ait); - - -#endif //_GF_CAROUSSEL_H_ - +/* + * Copyright (c) TELECOM ParisTech 2011 + */ + +#ifndef _GF_AIT_H_ +#define _GF_AIT_H_ + +#ifndef GPAC_DISABLE_MPEG2TS + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include + + +#define AIT_SECTION_LENGTH_MAX 1021 +#define APPLICATION_TYPE_HTTP_APPLICATION 16 +#define DSMCC_SECTION_LENGTH_MAX 4093 + +typedef enum { + APPLICATION_DESCRIPTOR = 0x00, + APPLICATION_NAME_DESCRIPTOR = 0x01, + TRANSPORT_PROTOCOL_DESCRIPTOR = 0x02, + SIMPLE_APPLICATION_LOCATION_DESCRIPTOR = 0x15, + APPLICATION_USAGE_DESCRIPTOR = 0x16, + APPLICATION_BOUNDARY_DESCRIPTOR = 0x17, +} DESCRIPTOR_TAG; + +enum ApplicationControlCode { + AUTOSTART = 0x01, + PRESENT = 0x02, + DESTROY = 0x03, + KILL = 0x04, + PREFETCH = 0x05, + REMOTE = 0x06, + DISABLED = 0x07, + PLAYBACK_AUTOSTART = 0x08 +}; + +enum TransportType { + BROADCAST = 0x01, + BROADBAND = 0x03 +}; + +typedef struct +{ + u32 pid; + u32 service_id; + u8 table_id; + Bool section_syntax_indicator; + u16 section_length; + Bool test_application_flag; + u16 application_type; + u8 version_number; + Bool current_next_indicator; + u8 section_number; + u8 last_section_number; + u16 common_descriptors_length; + GF_List * common_descriptors; + u16 application_loop_length; + GF_List * application_decoded; + u32 CRC_32; + +} GF_M2TS_AIT; + + +typedef struct +{ + ABSTRACT_ES + GF_M2TS_SectionFilter *sec; + +} GF_M2TS_AIT_CARRY; + + +typedef struct +{ + u32 organisation_id; + u16 application_id; + u8 application_control_code; + u16 application_descriptors_loop_length; + GF_List * application_descriptors; + u8 application_descriptors_id[50]; + u8 index_app_desc_id; + +}GF_M2TS_AIT_APPLICATION_DECODE; + + +typedef enum { + FUTURE_USE = 0x00, + CAROUSEL = 0x01, + RESERVED = 0x02, + TRANSPORT_HTTP = 0x03, + DVB_USE = 0x04, + TO_REGISTER = 0x100, +} PROTOCOL_ID; + +typedef struct +{ + u8 descriptor_tag; + u8 descriptor_length; + u8 application_profiles_length; + u16 application_profile; + u8 version_major; + u8 version_minor; + u8 version_micro; + Bool service_bound_flag; + u8 visibility; + u8 application_priority; + u8 transport_protocol_label[5]; + +} GF_M2TS_APPLICATION_DESCRIPTOR; + +typedef struct +{ + u8 descriptor_tag; + u8 descriptor_length; + u8 usage_type; + +} GF_M2TS_APPLICATION_USAGE; + +typedef struct +{ + u8 descriptor_tag; + u8 descriptor_length; + char* initial_path_bytes; + +}GF_M2TS_SIMPLE_APPLICATION_LOCATION; + +typedef struct +{ + Bool remote_connection; + u16 original_network_id; + u16 transport_stream_id; + u16 service_id; + u8 component_tag; + +} GF_M2TS_OBJECT_CAROUSEL_SELECTOR_BYTE; + +typedef struct{ + + u8 URL_extension_length; + char* URL_extension_byte; + +}GF_M2TS_TRANSPORT_HTTP_URL_EXTENTION; + +typedef struct +{ + u8 URL_base_length; + char* URL_base_byte; + u8 URL_extension_count; + GF_M2TS_TRANSPORT_HTTP_URL_EXTENTION* URL_extentions; + +} GF_M2TS_TRANSPORT_HTTP_SELECTOR_BYTE; + +typedef struct +{ + u8 descriptor_tag; + u8 descriptor_length; + u16 protocol_id; + u8 transport_protocol_label; + void* selector_byte; + +} GF_M2TS_TRANSPORT_PROTOCOL_DESCRIPTOR; + +typedef struct +{ + u8 descriptor_tag; + u8 descriptor_length; + u32 ISO_639_language_code; + u8 application_name_length; + char* application_name_char; + +} GF_M2TS_APPLICATION_NAME_DESCRIPTOR; + +typedef struct +{ + u8 boundary_extension_length; + char* boundary_extension_byte; + +} GF_M2TS_APPLICATION_BOUNDARY_EXTENSION_INFO; + +typedef struct +{ + u8 descriptor_tag; + u8 descriptor_length; + u8 boundary_extension_count; + GF_M2TS_APPLICATION_BOUNDARY_EXTENSION_INFO* boundary_extension_info; + +} GF_M2TS_APPLICATION_BOUNDARY_DESCRIPTOR; + +typedef struct +{ + u32 application_id; + u8 application_control_code; + + u8 priority; + u16 application_profile; + + /* Transport mode - 1 Broadcast - 3 Broadband */ + Bool broadcast; + Bool broadband; + char* http_url; + char* carousel_url; + Bool url_received; + + /* Carousel */ + u32 carousel_pid; + u32 component_tag; + + + char* appli_name; + +} GF_M2TS_AIT_APPLICATION; + +typedef struct +{ + u32 service_id; + u32 version_number; + u32 ait_pid; + u32 nb_application; + GF_List *Application; + +} GF_M2TS_CHANNEL_APPLICATION_INFO; + +void on_ait_section(GF_M2TS_Demuxer *ts, u32 evt_type, void *par); +GF_M2TS_ES *gf_ait_section_new(u32 service_id); +GF_M2TS_CHANNEL_APPLICATION_INFO* gf_m2ts_get_channel_application_info(GF_List* ChannelAppList, u32 ait_service_id); +void gf_m2ts_delete_channel_application_info(GF_M2TS_CHANNEL_APPLICATION_INFO* ChannelApp); + +#ifdef __cplusplus +} +#endif +#endif + +#endif //_GF_CAROUSSEL_H_ + diff --git a/include/gpac/avparse.h b/include/gpac/avparse.h index b4e7486..eeb1afa 100644 --- a/include/gpac/avparse.h +++ b/include/gpac/avparse.h @@ -91,7 +91,7 @@ s32 gf_mv12_next_slice_start(unsigned char *pbuffer, u32 startoffset, u32 buflen u8 gf_mp3_num_channels(u32 hdr); u16 gf_mp3_sampling_rate(u32 hdr); u16 gf_mp3_window_size(u32 hdr); -u16 gf_mp3_bit_rate(u32 hdr); +u32 gf_mp3_bit_rate(u32 hdr); u8 gf_mp3_object_type_indication(u32 hdr); u8 gf_mp3_layer(u32 hdr); u16 gf_mp3_frame_size(u32 hdr); @@ -233,7 +233,7 @@ void gf_img_parse(GF_BitStream *bs, u8 *OTI, u32 *mtype, u32 *width, u32 *height GF_Err gf_img_jpeg_dec(char *jpg, u32 jpg_size, u32 *width, u32 *height, u32 *pixel_format, char *dst, u32 *dst_size, u32 dst_nb_comp); GF_Err gf_img_png_dec(char *png, u32 png_size, u32 *width, u32 *height, u32 *pixel_format, char *dst, u32 *dst_size); -GF_Err gf_img_png_file_dec(char *png_file, u32 *width, u32 *height, u32 *pixel_format, char **dst, u32 *dst_size); +GF_Err gf_img_file_dec(char *png_file, u32 *oti, u32 *width, u32 *height, u32 *pixel_format, char **dst, u32 *dst_size); GF_Err gf_img_png_enc(char *data, u32 width, u32 height, s32 stride, u32 pixel_format, char *dst, u32 *dst_size); #ifdef __cplusplus diff --git a/include/gpac/config_file.h b/include/gpac/config_file.h index d08a4f6..e1384a2 100644 --- a/include/gpac/config_file.h +++ b/include/gpac/config_file.h @@ -111,6 +111,16 @@ GF_Err gf_cfg_save(GF_Config *iniFile); *\return the desired key value if found, NULL otherwise. */ const char *gf_cfg_get_key(GF_Config *cfgFile, const char *secName, const char *keyName); +/*! + * \brief key value query ignoring case + * + *Gets a key value from its section and name. Comparison is performed while ignoring case. + *\param cfgFile the target configuration file + *\param secName the desired key parent section name (case ignored) + *\param keyName the desired key name (case ignored) + *\return the desired key value if found, NULL otherwise. + */ +const char *gf_cfg_get_ikey(GF_Config *cfgFile, const char *secName, const char *keyName); /*! * \brief key value update * diff --git a/include/gpac/constants.h b/include/gpac/constants.h index 8220aa4..601fe93 100644 --- a/include/gpac/constants.h +++ b/include/gpac/constants.h @@ -29,7 +29,6 @@ extern "C" { #endif -#include /*! \addtogroup cst_grp constants * \brief Constants used within GPAC @@ -154,6 +153,9 @@ enum * * Supported pixel formats for everything using video */ +#ifndef GF_4CC +#define GF_4CC(a,b,c,d) (((a)<<24)|((b)<<16)|((c)<<8)|(d)) +#endif typedef enum { /*!8 bit GREY */ @@ -214,6 +216,8 @@ typedef enum GF_PIXEL_IYUV = GF_4CC('I','Y','U','V'), /*!YUV planar format*/ GF_PIXEL_I420 = GF_4CC('I','4','2','0'), + /*!YUV planar format*/ + GF_PIXEL_NV21 = GF_4CC('N','V','2','1'), /*!YV12 + Alpha plane*/ GF_PIXEL_YUVA = GF_4CC('Y', 'U', 'V', 'A'), @@ -558,12 +562,12 @@ enum /*rate sizes - note that these sizes INCLUDE the rate_type header byte*/ -static const u32 GF_QCELP_RATE_TO_SIZE [] = {0, 1, 1, 4, 2, 8, 3, 17, 4, 35, 5, 8, 14, 1}; -static const u32 GF_QCELP_RATE_TO_SIZE_NB = 7; -static const u32 GF_SMV_EVRC_RATE_TO_SIZE [] = {0, 1, 1, 3, 2, 6, 3, 11, 4, 23, 5, 1}; -static const u32 GF_SMV_EVRC_RATE_TO_SIZE_NB = 6; -static const u32 GF_AMR_FRAME_SIZE[16] = { 12, 13, 15, 17, 19, 20, 26, 31, 5, 0, 0, 0, 0, 0, 0, 0 }; -static const u32 GF_AMR_WB_FRAME_SIZE[16] = { 17, 23, 32, 36, 40, 46, 50, 58, 60, 5, 5, 0, 0, 0, 0, 0 }; +static const unsigned int GF_QCELP_RATE_TO_SIZE [] = {0, 1, 1, 4, 2, 8, 3, 17, 4, 35, 5, 8, 14, 1}; +static const unsigned int GF_QCELP_RATE_TO_SIZE_NB = 7; +static const unsigned int GF_SMV_EVRC_RATE_TO_SIZE [] = {0, 1, 1, 3, 2, 6, 3, 11, 4, 23, 5, 1}; +static const unsigned int GF_SMV_EVRC_RATE_TO_SIZE_NB = 6; +static const unsigned int GF_AMR_FRAME_SIZE[16] = { 12, 13, 15, 17, 19, 20, 26, 31, 5, 0, 0, 0, 0, 0, 0, 0 }; +static const unsigned int GF_AMR_WB_FRAME_SIZE[16] = { 17, 23, 32, 36, 40, 46, 50, 58, 60, 5, 5, 0, 0, 0, 0, 0 }; /*out-of-band sample desc (128 and 255 reserved in RFC)*/ diff --git a/include/gpac/download.h b/include/gpac/download.h index a96194f..5d5380f 100644 --- a/include/gpac/download.h +++ b/include/gpac/download.h @@ -160,7 +160,8 @@ extern "C" { /*!session download flags*/ enum { - /*!session is not threaded, the user must explicitely fetch the data */ + /*!session is not threaded, the user must explicitely fetch the data , either with the function gf_dm_sess_fetch_data + or the function gf_dm_sess_process- if the session is threaded, the user must call gf_dm_sess_process to start the session*/ GF_NETIO_SESSION_NOT_THREADED = 1, /*! session data is live, e.g. data will be sent to the user if threaded mode (live streams like radios & co) Whether the data is cached or not to disk cannot be controlled by the user at the current time. @@ -242,14 +243,16 @@ extern "C" { *\param sess the download session */ void gf_dm_sess_del(GF_DownloadSession * sess); - /*! + + /*! *\brief aborts downloading * *Aborts all operations in the session, regardless of its state. The session cannot be reused once this is called. *\param sess the download session */ void gf_dm_sess_abort(GF_DownloadSession * sess); - /*! + + /*! *\brief sets private data * *associate private data with the session. @@ -299,9 +302,9 @@ extern "C" { GF_Err gf_dm_sess_fetch_data(GF_DownloadSession * sess, char *buffer, u32 buffer_size, u32 *read_size); /*! - *\brief get mime type + *\brief get mime type as lower case * - *Fetches the mime type of the URL this session is fetching + *Fetches the mime type of the URL this session is fetching, value will be returned lower case, so application/x-mpegURL will be returned as application/x-mpegurl *\param sess the download session *\return the mime type of the URL, or NULL if error. You should get the error with \ref gf_dm_sess_last_error */ diff --git a/include/gpac/dsmcc.h b/include/gpac/dsmcc.h new file mode 100644 index 0000000..4fd2add --- /dev/null +++ b/include/gpac/dsmcc.h @@ -0,0 +1,643 @@ +/* + * Copyright (c) TELECOM ParisTech 2011 + */ + + + +#ifndef _GF_DSMCC_H_ +#define _GF_DSMCC_H_ + +#ifndef GPAC_DISABLE_MPEG2TS + + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +#define DSMCC_SECTION_LENGTH_MAX 4093 + +typedef enum{ + DOWNLOAD_INFO_REQUEST = 0x1001, + DOWNLOAD_INFO_REPONSE_INDICATION = 0x1002, + DOWNLOAD_DATA_BLOCK = 0x1003, + DOWNLOAD_DATA_REQUEST = 0x1004, + DOWNLOAD_DATA_CANCEL = 0x1005, + DOWNLOAD_SERVER_INITIATE = 0x1006 +}DSMCC_DOWNLOAD_MESSAGE_ID; + +typedef enum{ + TAG_BIOP = 0x49534F06, + TAG_LITE_OPTIONS = 0x49534F05 +}DSMCC_DOWNLOAD_PROFILE_ID_TAG; + +typedef enum{ + CACHING_PRIORITY_DESCRIPTOR = 0x71, + CONTENT_TYPE_DESCRIPTOR = 0x72, + COMPRESSED_MODULE_DESCRIPTOR = 0x09 +}DSMCC_BIOP_DESCRIPTOR; + +typedef struct{ + u8 descriptor_tag; + u8 descriptor_length; + u32 carousel_id; + u8 FormatID; + char *private_data_byte; + u8 ModuleVersion; + u8 ModuleId; + u16 BlockSize; + u32 ModuleSize; + u8 CompressionMethod; + u32 OriginalSize; + u8 TimeOut; + u8 ObjectKeyLength; + char* ObjectKeyData; +}GF_M2TS_CAROUSEL_INDENTIFIER_DESCRIPTOR; + +typedef struct +{ + u32 moduleId; + u32 downloadId; + u32 version_number; + Bool done; +}GF_M2TS_DSMCC_PROCESSED; + +typedef struct +{ + /* Module identifier */ + u32 moduleId; + /* Version number of the module */ + u32 version_number; + /* size in byte of the module */ + u32 size; + /* Download identifier */ + u32 downloadId; + /* buffer of data */ + char* buffer; + /* byte shifting in the buffer of data */ + u32 byte_sift; + /* last section number processed */ + u16 section_number; + /* the last section number of the module */ + u16 last_section_number; + /* size in byte of each block in the module */ + u32 block_size; + /* Checks if the module has been processed */ + /* 1 if yes */ + /* 0 otherwise */ + Bool processed; + /* Checks if the module's data are zipped */ + /* 1 if yes */ + /* 0 otherwise */ + Bool Gzip; + /* Size of the module's data after uncompression */ + u32 original_size; +}GF_M2TS_DSMCC_MODULE; + +typedef struct +{ + /* table id : identifier for dsmcc message type */ + u8 table_id; + /* indicates the presence of CRC 32 */ + u8 section_syntax_indicator; + u8 private_indicator; + /* length in byte of the dsmcc section */ + u16 dsmcc_section_length; + /* linked with the moduleId if carried by the section */ + u16 table_id_extension; + /* version number linked with the Data block if carried by the section */ + u8 version_number; + u8 current_next_indicator; + /* section number of the data block if carried by the section */ + u8 section_number; + /* last section number of the data block if carried by the section */ + u8 last_section_number; + void* DSMCC_Extension; + u32 checksum; + u32 CRC_32; +}GF_M2TS_DSMCC_SECTION; + +typedef struct +{ + u8 adaptationType; + char* adaptationDataByte; +} +GF_M2TS_DSMCC_ADAPTATION_HEADER; + +typedef struct +{ + u8 protocolDiscriminator; + u8 dsmccType; + u16 messageId; + /* dsmccMessageHeader mode */ + u32 transactionId; + /* dsmccDownloadDataHeader */ + u32 downloadId; + u8 reserved; + u8 adaptationLength; + u16 messageLength; + /* added not in the spec */ + u8 header_length; + GF_M2TS_DSMCC_ADAPTATION_HEADER* DsmccAdaptationHeader; + +}GF_M2TS_DSMCC_MESSAGE_DATA_HEADER; + +/* DOWNLOAD_DATA_MESSAGE */ +typedef struct +{ + u8 protocolDiscriminator; + u8 dsmccType; + u16 messageId; + u32 downloadId; + u8 reserved; + u8 adaptationLength; + u16 messageLength; + GF_M2TS_DSMCC_ADAPTATION_HEADER* DsmccAdaptationHeader; + +}GF_M2TS_DSMCC_DOWNLOAD_DATA_HEADER; + +typedef struct +{ + u8 subDescriptorType; + u8 subDescriptorLength; + char *additionalInformation; + +}GF_M2TS_DSMCC_SUBDESCRIPTOR; + +typedef struct +{ + u8 descriptorType; + u8 descriptorLength; + u8 specifierType; + u32 specifierData; + u16 model; + u16 version; + u8 subDescriptorCount; + GF_M2TS_DSMCC_SUBDESCRIPTOR* SubDescriptor; + +}GF_M2TS_DSMCC_DESCRIPTOR; + +typedef struct +{ + u16 compatibilityDescriptorLength; + u16 descriptorCount; + GF_M2TS_DSMCC_DESCRIPTOR* Descriptor; +}GF_M2TS_DSMCC_COMPATIBILITY_DESCRIPTOR; + +typedef struct +{ + u32 bufferSize; + u16 maximumBlockSize; + GF_M2TS_DSMCC_COMPATIBILITY_DESCRIPTOR CompatibilityDescr; + u16 privateDataLength; + char* privateDataByte; +}GF_M2TS_DSMCC_DOWNLOAD_INFO_REQUEST; + +typedef struct +{ + u16 moduleId; + u32 moduleSize; + u8 moduleVersion; + u8 moduleInfoLength; + char* moduleInfoByte; +}GF_M2TS_DSMCC_INFO_MODULES; + +typedef struct +{ + u32 downloadId; + u16 blockSize; + u8 windowSize; + u8 ackPeriod; + u32 tCDownloadWindow; + u32 tCDownloadScenario; + GF_M2TS_DSMCC_COMPATIBILITY_DESCRIPTOR CompatibilityDescr; + u16 numberOfModules; + GF_M2TS_DSMCC_INFO_MODULES Modules; + u16 privateDataLength; + char* privateDataByte; +}GF_M2TS_DSMCC_DOWNLOAD_INFO_RESP_INDIC; + +typedef struct +{ + u8 moduleId; + u8 moduleVersion; + u8 reserved; + u8 blockNumber; + char* blockDataByte; + /*added not in the spec */ + u32 dataBlocksize; +}GF_M2TS_DSMCC_DOWNLOAD_DATA_BLOCK; + +typedef struct +{ + u16 moduleId; + u16 blockNumber; + u8 downloadReason; +}GF_M2TS_DSMCC_DOWNLOAD_DATA_REQUEST_MESSAGE; + +typedef struct +{ + u32 downloadId; + u16 moduleId; + u16 blockNumber; + u8 downloadCancelReason; + u8 reserved; + u16 privateDataLength; + char* privateDataByte; +}GF_M2TS_DSMCC_DOWNLOAD_CANCEL; + +typedef struct +{ + u32 GroupId; + u32 GroupSize; + GF_M2TS_DSMCC_COMPATIBILITY_DESCRIPTOR CompatibilityDescr; + u16 GroupInfoLength; + char* groupInfoByte; +}GF_M2TS_DSMCC_INFO_GROUP; + +typedef struct +{ + u16 NumberOfGroups; + GF_M2TS_DSMCC_INFO_GROUP* InfoGroup; + u16 PrivateDataLength; + char* privateDataByte; + +}GF_M2TS_DSMCC_GROUP_INFO_INDICATION; + +typedef struct +{ + u8 serverId[20]; + GF_M2TS_DSMCC_COMPATIBILITY_DESCRIPTOR CompatibilityDescr; + u16 privateDataLength; + char* privateDataByte; + GF_M2TS_DSMCC_GROUP_INFO_INDICATION* GroupInfoIndic; +}GF_M2TS_DSMCC_DOWNLOAD_SERVER_INIT; + +typedef struct +{ + GF_M2TS_DSMCC_MESSAGE_DATA_HEADER DownloadDataHeader; + void* dataMessagePayload; +}GF_M2TS_DSMCC_DOWNLOAD_DATA_MESSAGE; + +/* DESCRIPTOR LIST */ + +typedef struct +{ + u8 descriptorTag; + u8 descriptorLength; + u8 postDiscontinuityIndicator; + u8 contentId; + u8 STC_Reference[5]; + u8 NPT_Reference[5]; + u16 scaleNumerator; + u16 scaleDenominator; +}GF_M2TS_DSMCC_NPT_REFERENCE_DESCRIPTOR; + + +typedef struct +{ + u8 descriptorTag; + u8 descriptorLength; + void* descriptor; +}GF_M2TS_DSMCC_STREAM_DESCRIPTOR; + +/* OBJECT CAROUSEL */ +typedef struct{ + u16 id; + u16 use; + u16 assocTag; + u8 selector_length; + char* selector_data; + u16 selector_type; + u32 transactionId; + u32 timeout; +}GF_M2TS_DSMCC_BIOP_TAPS; + +typedef struct{ + u8 AFI; + u8 type; + u32 carouselId; + u8 specifierType; + u32 specifierData; + u16 transport_stream_id; + u16 original_network_id; + u16 service_id; + u32 reserved; +}GF_M2TS_DSMCC_SERVICE_DOMAIN; + +typedef struct{ + u32 componentId_tag; + u8 component_data_length; + u32 carouselId; + u16 moduleId; + u8 version_major; + u8 version_minor; + u8 objectKey_length; + u32 objectKey_data; +}GF_M2TS_DSMCC_BIOP_OBJECT_LOCATION; + +typedef struct{ + u32 componentId_tag; + u8 component_data_length; + u8 taps_count; + GF_M2TS_DSMCC_BIOP_TAPS* Taps; + char* additional_tap_byte; +}GF_M2TS_DSMCC_BIOP_CONN_BINDER; + +typedef struct{ + GF_M2TS_DSMCC_BIOP_OBJECT_LOCATION ObjectLocation; + GF_M2TS_DSMCC_BIOP_CONN_BINDER ConnBinder; +}GF_M2TS_DSMCC_BIOP_PROFILE_BODY; + +typedef struct{ + u32 id_length; + char* id_data; + u32 kind_length; + char* kind_data; +}GF_M2TS_DSMCC_BIOP_NAME_COMPONENT; + +typedef struct{ + u32 componentId_tag; + u8 component_data_length; + u8 serviceDomain_length; + GF_M2TS_DSMCC_SERVICE_DOMAIN serviceDomain_data; + u32 nameComponents_count; + GF_M2TS_DSMCC_BIOP_NAME_COMPONENT* NameComponent; + u32 initialContext_length; + char* InitialContext_data_byte; +}GF_M2TS_DSMCC_BIOP_SERVICE_LOCATION; + +typedef struct{ + u32 componentId_tag; + u8 component_data_length; + char* component_data_byte; +}GF_M2TS_DSMCC_BIOP_LITE_COMPONENT; + +typedef struct{ + u32 profileId_tag; + u32 profile_data_length; + u8 profile_data_byte_order; + u8 lite_component_count; + + GF_M2TS_DSMCC_BIOP_PROFILE_BODY* BIOPProfileBody; + GF_M2TS_DSMCC_BIOP_SERVICE_LOCATION* ServiceLocation; + GF_M2TS_DSMCC_BIOP_LITE_COMPONENT* LiteComponent; + +}GF_M2TS_DSMCC_BIOP_TAGGED_PROFILE; + +typedef struct{ + u32 type_id_length; + char* type_id_byte; + u32 taggedProfiles_count; + GF_List* taggedProfile; +}GF_M2TS_DSMCC_IOR; + +typedef struct{ + u32 moduleTimeOut; + u32 blockTimeOut; + u32 minBlockTime; + u8 taps_count; + GF_M2TS_DSMCC_BIOP_TAPS* Taps; + u8 userInfoLength; + u8* userInfo_data; + GF_List* descriptor; + + u8 compression_method; + u8 transparency_level; +}GF_M2TS_DSMCC_BIOP_MODULE_INFO; + +typedef struct{ + u32 context_id; + u16 context_data_length; + char* context_data_byte; +}GF_M2TS_DSMCC_SERVICE_CONTEXT; + +typedef struct{ + GF_M2TS_DSMCC_IOR IOR; + u8 downloadTaps_count; + GF_M2TS_DSMCC_BIOP_TAPS* Taps; + u8 serviceContextList_count; + GF_M2TS_DSMCC_SERVICE_CONTEXT* ServiceContext; + u16 userInfoLength; + char* userInfo_data; +}GF_M2TS_DSMCC_SERVICE_GATEWAY_INFO; + + +/* DESCRIPTORS */ +typedef struct{ + u8 descriptor_tag; + u8 descriptor_length; + u8 priority_value; + u8 transparency_level; +}GF_M2TS_DSMCC_BIOP_CACHING_PRIORITY_DESCRIPTOR; + +typedef struct{ + u8 descriptor_tag; + u8 descriptor_length; + u8 compression_method; + u32 original_size; +}GF_M2TS_DSMCC_BIOP_COMPRESSED_MODULE_DESCRIPTOR; + +typedef struct{ + u8 descriptor_tag; + u8 descriptor_length; + char* content_type_data_byte; +}GF_M2TS_DSMCC_BIOP_CONTENT_TYPE_DESRIPTOR; + + +typedef struct{ + /* "BIOP" */ + u32 magic; + u8 biop_version_major; + u8 biop_version_minor; + u8 byte_order; + u8 message_type; + /* size in byte of the whole object carousel */ + u32 message_size; + u8 objectKey_length; + /* witness the kind of object carousel the item is */ + /* fil for a file */ + /* dir for a directory */ + /* srg for the ServiceGateway */ + /* str for Stream Message */ + /* ste for Stream Event Message */ + u32 objectKey_data; + u32 objectKind_length; + /* The number that identifies the object in the module */ + char* objectKind_data; + u16 objectInfo_length; +}GF_M2TS_DSMCC_BIOP_HEADER; + +typedef struct{ + GF_M2TS_DSMCC_BIOP_HEADER* Header; + u64 ContentSize; + GF_List* descriptor; + u8 serviceContextList_count; + GF_M2TS_DSMCC_SERVICE_CONTEXT* ServiceContext; + u32 messageBody_length; + /* size in byte of the data of the file */ + u32 content_length; + /* data a the file */ + char* content_byte; +}GF_M2TS_DSMCC_BIOP_FILE; + +typedef struct{ + /* Name */ + u8 nameComponents_count; + /* There is must be only one nameComponent */ + u8 id_length; + /* the name of the item */ + char * id_data; + u8 kind_length; + /* the kind of the item */ + /* fil for a file */ + /* dir for a directory */ + /* srg for the ServiceGateway */ + /* str for Stream Message */ + /* ste for Stream Event Message */ + char* kind_data; + /* 1 if the item a file or a stream */ + /* 0 if the item si a directory */ + u8 BindingType; + GF_M2TS_DSMCC_IOR IOR; + u16 objectInfo_length; + u64 ContentSize; + GF_List* descriptor; +}GF_M2TS_DSMCC_BIOP_NAME; + +typedef struct{ + GF_M2TS_DSMCC_BIOP_HEADER* Header; + char* objectInfo_data; + u8 serviceContextList_count; + GF_M2TS_DSMCC_SERVICE_CONTEXT* ServiceContext; + /* Length is byte of the message */ + u32 messageBody_length; + /* Number of the item */ + u16 bindings_count; + /* List of the item in the directory */ + GF_M2TS_DSMCC_BIOP_NAME* Name; +}GF_M2TS_DSMCC_BIOP_DIRECTORY; + +typedef struct{ + u8 aDescription_length; + char* aDescription_bytes; + u32 duration_aSeconds; + u32 duration_aMicroseconds; + u8 audio; + u8 video; + u8 data; +}GF_M2TS_DSMCC_STREAM_INFO; + +typedef struct{ + GF_M2TS_DSMCC_BIOP_HEADER* Header; + GF_M2TS_DSMCC_STREAM_INFO Info; + char* objectInfo_byte; + u8 serviceContextList_count; + GF_M2TS_DSMCC_SERVICE_CONTEXT* ServiceContext; + u32 messageBody_length; + u8 taps_count; + GF_M2TS_DSMCC_BIOP_TAPS* Taps; +}GF_M2TS_DSMCC_BIOP_STREAM_MESSAGE; + +typedef struct{ + u8 eventName_length; + char* eventName_data_byte; +}GF_M2TS_DSMCC_BIOP_EVENT_LIST; + +typedef struct{ + GF_M2TS_DSMCC_BIOP_HEADER* Header; + GF_M2TS_DSMCC_STREAM_INFO Info; + u16 eventNames_count; + GF_M2TS_DSMCC_BIOP_EVENT_LIST* EventList; + char* objectInfo_byte; + u8 serviceContextList_count; + GF_M2TS_DSMCC_SERVICE_CONTEXT* ServiceContext; + u32 messageBody_length; + u8 taps_count; + GF_M2TS_DSMCC_BIOP_TAPS* Taps; + u8 eventIds_count; + u16* eventId; +}GF_M2TS_DSMCC_BIOP_STREAM_EVENT; + +/*Define the base element for extracted dsmcc element*/ +#define GF_M2TS_DSMCC_ELEMENT \ + u32 moduleId; \ + u32 downloadId; \ + u32 version_number; \ + u32 objectKey_data; \ + char* name; \ + void* parent; + +typedef struct +{ + GF_M2TS_DSMCC_ELEMENT + /*Path to the file */ + char* Path; +}GF_M2TS_DSMCC_FILE; + +typedef struct +{ GF_M2TS_DSMCC_ELEMENT + /* List of files in the directory*/ + GF_List* File; + /* List of directories of the directory*/ + GF_List* Dir; + /*Path to the directory */ + char* Path; +}GF_M2TS_DSMCC_DIR; + +typedef struct +{ GF_M2TS_DSMCC_ELEMENT + /* Number of process directories */ + u8 nb_processed_dir; + /* Service Id of the data carousel*/ + u32 service_id; + /* List of files of the root of the file system*/ + GF_List* File; + /* List of directories of the root of the file system*/ + GF_List* Dir; +}GF_M2TS_DSMCC_SERVICE_GATEWAY; + +typedef struct +{ + /* List that carries the modules to process */ + GF_List* dsmcc_modules; + /* List of processed module */ + GF_M2TS_DSMCC_PROCESSED processed[512]; + /*Check if the ServiceGateway has been recovered*/ + /* 1 ServiceGateway received */ + /* 0 otherwise */ + Bool Got_ServiceGateway; + /* ServiceGateway Structure */ + GF_M2TS_DSMCC_SERVICE_GATEWAY* ServiceGateway; + /* u32 transactionId for DownloadInfoIndicator versioning */ + u32 transactionId; + /* List that carries modules that have been received before inti - TO DO */ + GF_List* Unprocessed_module; + /* Service ID that carries this carousel */ + u32 service_id; + /* Path of the root directory of the file system */ + char* root_dir; + /*Check if the index.html (root of the application) has been recovered*/ + /* 1 index.html received received */ + /* 0 otherwise */ + Bool get_index; + /* Number of the application that uses the carousel*/ + u32 application_id; +}GF_M2TS_DSMCC_OVERLORD; + +void on_dsmcc_section(GF_M2TS_Demuxer *ts, u32 evt_type, void *par); +GF_Err gf_m2ts_process_dsmcc(GF_M2TS_DSMCC_OVERLORD* dsmcc_overlord,GF_M2TS_DSMCC_SECTION *dsmcc, char *data, u32 data_size, u32 table_id); +GF_M2TS_DSMCC_OVERLORD* gf_m2ts_init_dsmcc_overlord(u32 service_id); +GF_M2TS_DSMCC_OVERLORD* gf_m2ts_get_dmscc_overlord(GF_List* Dsmcc_controller,u32 service_id); +void gf_m2ts_delete_dsmcc_overlord(GF_M2TS_DSMCC_OVERLORD* dsmcc_overlord); + +#ifdef __cplusplus +} +#endif +#endif + +#endif //_GF_CAROUSSEL_H_ + diff --git a/include/gpac/dvb_mpe.h b/include/gpac/dvb_mpe.h index 6a6305c..f96664e 100644 --- a/include/gpac/dvb_mpe.h +++ b/include/gpac/dvb_mpe.h @@ -29,6 +29,8 @@ #include #include +#ifndef GPAC_DISABLE_MPEG2TS + typedef struct tag_m2ts_section_mpe GF_M2TS_SECTION_MPE; typedef struct _sock_entry GF_SOCK_ENTRY; @@ -38,4 +40,6 @@ GF_M2TS_ES *gf_dvb_mpe_section_new(); void gf_dvb_mpe_section_del(GF_M2TS_ES *es); void gf_m2ts_print_mpe_info(GF_M2TS_Demuxer *ts); +#endif //GPAC_DISABLE_MPEG2TS + #endif //_GF_DVB_MPE_H_ diff --git a/include/gpac/events.h b/include/gpac/events.h index f5f06d8..2fbe8ed 100644 --- a/include/gpac/events.h +++ b/include/gpac/events.h @@ -31,417 +31,11 @@ extern "C" { #endif + #include #include +#include -/* - minimal event system - - DO NOT CHANGE THEIR POSITION IN THE LIST, USED TO SPEED UP FILTERING OF USER INPUT EVENTS -*/ - -/*events*/ -enum { - - /****************************************************** - - Events used for both GPAC internals and DOM Events - - *******************************************************/ - /*MouseEvents*/ - GF_EVENT_CLICK, - GF_EVENT_MOUSEUP, - GF_EVENT_MOUSEDOWN, - GF_EVENT_MOUSEOVER, - GF_EVENT_MOUSEOUT, - /*!! ALL MOUSE EVENTS SHALL BE DECLARED BEFORE MOUSEMOVE !! */ - GF_EVENT_MOUSEMOVE, - /*mouse wheel event*/ - GF_EVENT_MOUSEWHEEL, - - /*Key Events*/ - GF_EVENT_KEYUP, - GF_EVENT_KEYDOWN, /* covers KeyDown, KeyPress and AccessKey */ - GF_EVENT_LONGKEYPRESS, - /*character input*/ - GF_EVENT_TEXTINPUT, - - - /****************************************************** - - Events used for DOM Events only - - *******************************************************/ - GF_EVENT_TEXTSELECT, - - /*DOM UIEvents*/ - GF_EVENT_FOCUSIN, - GF_EVENT_FOCUSOUT, - GF_EVENT_ACTIVATE, - GF_EVENT_CHANGE, - GF_EVENT_FOCUS, - GF_EVENT_BLUR, - /*SVG (HTML) Events*/ - GF_EVENT_LOAD, - GF_EVENT_UNLOAD, - GF_EVENT_ABORT, - GF_EVENT_ERROR, - GF_EVENT_RESIZE, - GF_EVENT_SCROLL, - GF_EVENT_ZOOM, - GF_EVENT_BEGIN, /*this is a fake event, it is NEVER fired, only used in SMIL begin*/ - GF_EVENT_BEGIN_EVENT, - GF_EVENT_END, /*this is a fake event, it is NEVER fired, only used in SMIL end*/ - GF_EVENT_END_EVENT, - GF_EVENT_REPEAT, /*this is a fake event, it is NEVER fired, only used in SMIL repeat*/ - GF_EVENT_REPEAT_EVENT, - - /*DOM MutationEvents - NOT SUPPORTED YET*/ - GF_EVENT_TREE_MODIFIED, - GF_EVENT_NODE_INSERTED, - GF_EVENT_NODE_REMOVED, - GF_EVENT_NODE_INSERTED_DOC, - GF_EVENT_NODE_REMOVED_DOC, - GF_EVENT_ATTR_MODIFIED, - GF_EVENT_CHAR_DATA_MODIFIED, - GF_EVENT_NODE_NAME_CHANGED, - GF_EVENT_ATTR_NAME_CHANGED, - - GF_EVENT_DCCI_PROP_CHANGE, - - /*LASeR events*/ - GF_EVENT_ACTIVATED, - GF_EVENT_DEACTIVATED, - GF_EVENT_PAUSE, - GF_EVENT_PAUSED_EVENT, - GF_EVENT_PLAY, - GF_EVENT_REPEAT_KEY, - GF_EVENT_RESUME_EVENT, - GF_EVENT_SHORT_ACCESSKEY, - /*pseudo-event, only used in LASeR coding*/ - GF_EVENT_EXECUTION_TIME, - - /*MediaAccess events - cf http://www.w3.org/TR/MediaAccessEvents*/ - GF_EVENT_MEDIA_BEGIN_SESSION_SETUP, - GF_EVENT_MEDIA_END_SESSION_SETUP, - GF_EVENT_MEDIA_DATA_REQUEST, - GF_EVENT_MEDIA_PLAYABLE, - GF_EVENT_MEDIA_NOT_PLAYABLE, - GF_EVENT_MEDIA_DATA_PROGRESS, - GF_EVENT_MEDIA_END_OF_DATA, - GF_EVENT_MEDIA_STOP, - GF_EVENT_MEDIA_ERROR, - - GF_EVENT_BATTERY, - GF_EVENT_CPU, - GF_EVENT_UNKNOWN, - - - /****************************************************** - - Events used for GPAC internals only - - *******************************************************/ - - /*same as mousedown, generated internally by GPAC*/ - GF_EVENT_DBLCLICK, - - /*scene attached event, dispatched when the root node of a scene is loaded and - attached to the window or parent object (animation, inline, ...)*/ - GF_EVENT_SCENE_ATTACHED, - - /*VP resize attached event, dispatched when viewport of a scene is being modified - attached to the window or parent object (animation, inline, ...)*/ - GF_EVENT_VP_RESIZE, - - /*window events*/ - /*size has changed - indicate new w & h in .x end .y fields of event. - When sent from gpac to a video plugin, indicates the output size should be changed. This is only sent when the plugin - manages the output video himself - When sent from a video plugin to gpac, indicates the output size has been changed. This is only sent when the plugin - manages the output video himself - */ - GF_EVENT_SIZE, - /*signals the scene size (if indicated in scene) upon connection (sent to the user event proc only) - if scene size hasn't changed (seeking or other) this event is not sent - */ - GF_EVENT_SCENE_SIZE, - GF_EVENT_SHOWHIDE, /*window show/hide (minimized or other). This is also sent to the user to signal focus switch in fullscreen*/ - GF_EVENT_SET_CURSOR, /*set mouse cursor*/ - GF_EVENT_SET_CAPTION, /*set window caption*/ - GF_EVENT_MOVE, /*move window*/ - GF_EVENT_REFRESH, /*window needs repaint (whenever needed, eg restore, hide->show, background refresh, paint)*/ - GF_EVENT_QUIT, /*window is being closed*/ - /*video hw setup message: - - when sent from gpac to plugin, indicates that the plugin should re-setup hardware context due to a window resize: - * for 2D output, this means resizing the backbuffer if needed (depending on HW constraints) - * for 3D output, this means re-setup of OpenGL context (depending on HW constraints). Depending on windowing systems - and implementations, it could be possible to resize a window without destroying the GL context. - - - when sent from plugin to gpac, indicates that hardware resources must be resetup before next render step (this is mainly - due to discard all openGL textures and cached objects) - */ - GF_EVENT_VIDEO_SETUP, - /*queries the list of system colors - only exchanged between compositor and video output*/ - GF_EVENT_SYS_COLORS, - - /*indicates some text has been pasted - from video output to compositor only*/ - GF_EVENT_PASTE_TEXT, - /*queries for text to be copied - from video output to compositor only*/ - GF_EVENT_COPY_TEXT, - - /*terminal events*/ - GF_EVENT_CONNECT, /*signal URL is connected*/ - GF_EVENT_DURATION, /*signal duration of presentation*/ - GF_EVENT_EOS, /*signal End of scene playback*/ - GF_EVENT_AUTHORIZATION, /*indicates a user and pass is queried*/ - GF_EVENT_NAVIGATE, /*indicates the user app should load or jump to the given URL.*/ - GF_EVENT_NAVIGATE_INFO, /*indicates the link or its description under the mouse pointer*/ - GF_EVENT_MESSAGE, /*message from the MPEG-4 terminal*/ - GF_EVENT_PROGRESS, /*progress message from the MPEG-4 terminal*/ - GF_EVENT_FORWARDED, /*event forwarded by service (MPEG-2, RTP, ...)*/ - GF_EVENT_VIEWPOINTS, /*indicates viewpoint list has changed - no struct associated*/ - GF_EVENT_STREAMLIST, /*indicates stream list has changed - no struct associated - only used when no scene info is present*/ - GF_EVENT_METADATA, /*indicates a change in associated metadata*/ - GF_EVENT_MIGRATE, /*indicates a session migration request*/ - GF_EVENT_DISCONNECT, /*indicates the current url should be disconnected*/ - GF_EVENT_RESOLUTION, /*indicates the screen resolution has changed*/ - GF_EVENT_OPENFILE, - /* Events for Keyboad */ - GF_EVENT_TEXT_EDITING_START, - GF_EVENT_TEXT_EDITING_END -}; - -/*GPAC/DOM3 key codes*/ -enum { - GF_KEY_UNIDENTIFIED = 0, - GF_KEY_ACCEPT = 1, /* "Accept" The Accept (Commit) key.*/ - GF_KEY_AGAIN, /* "Again" The Again key.*/ - GF_KEY_ALLCANDIDATES, /* "AllCandidates" The All Candidates key.*/ - GF_KEY_ALPHANUM, /*"Alphanumeric" The Alphanumeric key.*/ - GF_KEY_ALT, /*"Alt" The Alt (Menu) key.*/ - GF_KEY_ALTGRAPH, /*"AltGraph" The Alt-Graph key.*/ - GF_KEY_APPS, /*"Apps" The Application key.*/ - GF_KEY_ATTN, /*"Attn" The ATTN key.*/ - GF_KEY_BROWSERBACK, /*"BrowserBack" The Browser Back key.*/ - GF_KEY_BROWSERFAVORITES, /*"BrowserFavorites" The Browser Favorites key.*/ - GF_KEY_BROWSERFORWARD, /*"BrowserForward" The Browser Forward key.*/ - GF_KEY_BROWSERHOME, /*"BrowserHome" The Browser Home key.*/ - GF_KEY_BROWSERREFRESH, /*"BrowserRefresh" The Browser Refresh key.*/ - GF_KEY_BROWSERSEARCH, /*"BrowserSearch" The Browser Search key.*/ - GF_KEY_BROWSERSTOP, /*"BrowserStop" The Browser Stop key.*/ - GF_KEY_CAPSLOCK, /*"CapsLock" The Caps Lock (Capital) key.*/ - GF_KEY_CLEAR, /*"Clear" The Clear key.*/ - GF_KEY_CODEINPUT, /*"CodeInput" The Code Input key.*/ - GF_KEY_COMPOSE, /*"Compose" The Compose key.*/ - GF_KEY_CONTROL, /*"Control" The Control (Ctrl) key.*/ - GF_KEY_CRSEL, /*"Crsel" The Crsel key.*/ - GF_KEY_CONVERT, /*"Convert" The Convert key.*/ - GF_KEY_COPY, /*"Copy" The Copy key.*/ - GF_KEY_CUT, /*"Cut" The Cut key.*/ - GF_KEY_DOWN, /*"Down" The Down Arrow key.*/ - GF_KEY_END, /*"End" The End key.*/ - GF_KEY_ENTER, /*"Enter" The Enter key. - Note: This key identifier is also used for the Return (Macintosh numpad) key.*/ - GF_KEY_ERASEEOF, /*"EraseEof" The Erase EOF key.*/ - GF_KEY_EXECUTE, /*"Execute" The Execute key.*/ - GF_KEY_EXSEL, /*"Exsel" The Exsel key.*/ - GF_KEY_F1, /*"F1" The F1 key.*/ - GF_KEY_F2, /*"F2" The F2 key.*/ - GF_KEY_F3, /*"F3" The F3 key.*/ - GF_KEY_F4, /*"F4" The F4 key.*/ - GF_KEY_F5, /*"F5" The F5 key.*/ - GF_KEY_F6, /*"F6" The F6 key.*/ - GF_KEY_F7, /*"F7" The F7 key.*/ - GF_KEY_F8, /*"F8" The F8 key.*/ - GF_KEY_F9, /*"F9" The F9 key.*/ - GF_KEY_F10, /*"F10" The F10 key.*/ - GF_KEY_F11, /*"F11" The F11 key.*/ - GF_KEY_F12, /*"F12" The F12 key.*/ - GF_KEY_F13, /*"F13" The F13 key.*/ - GF_KEY_F14, /*"F14" The F14 key.*/ - GF_KEY_F15, /*"F15" The F15 key.*/ - GF_KEY_F16, /*"F16" The F16 key.*/ - GF_KEY_F17, /*"F17" The F17 key.*/ - GF_KEY_F18, /*"F18" The F18 key.*/ - GF_KEY_F19, /*"F19" The F19 key.*/ - GF_KEY_F20, /*"F20" The F20 key.*/ - GF_KEY_F21, /*"F21" The F21 key.*/ - GF_KEY_F22, /*"F22" The F22 key.*/ - GF_KEY_F23, /*"F23" The F23 key.*/ - GF_KEY_F24, /*"F24" The F24 key.*/ - GF_KEY_FINALMODE, /*"FinalMode" The Final Mode (Final) key used on some asian keyboards.*/ - GF_KEY_FIND, /*"Find" The Find key.*/ - GF_KEY_FULLWIDTH, /*"FullWidth" The Full-Width Characters key.*/ - GF_KEY_HALFWIDTH, /*"HalfWidth" The Half-Width Characters key.*/ - GF_KEY_HANGULMODE, /*"HangulMode" The Hangul (Korean characters) Mode key.*/ - GF_KEY_HANJAMODE, /*"HanjaMode" The Hanja (Korean characters) Mode key.*/ - GF_KEY_HELP, /*"Help" The Help key.*/ - GF_KEY_HIRAGANA, /*"Hiragana" The Hiragana (Japanese Kana characters) key.*/ - GF_KEY_HOME, /*"Home" The Home key.*/ - GF_KEY_INSERT, /*"Insert" The Insert (Ins) key.*/ - GF_KEY_JAPANESEHIRAGANA, /*"JapaneseHiragana" The Japanese-Hiragana key.*/ - GF_KEY_JAPANESEKATAKANA, /*"JapaneseKatakana" The Japanese-Katakana key.*/ - GF_KEY_JAPANESEROMAJI, /*"JapaneseRomaji" The Japanese-Romaji key.*/ - GF_KEY_JUNJAMODE, /*"JunjaMode" The Junja Mode key.*/ - GF_KEY_KANAMODE, /*"KanaMode" The Kana Mode (Kana Lock) key.*/ - GF_KEY_KANJIMODE, /*"KanjiMode" The Kanji (Japanese name for ideographic characters of Chinese origin) Mode key.*/ - GF_KEY_KATAKANA, /*"Katakana" The Katakana (Japanese Kana characters) key.*/ - GF_KEY_LAUNCHAPPLICATION1, /*"LaunchApplication1" The Start Application One key.*/ - GF_KEY_LAUNCHAPPLICATION2, /*"LaunchApplication2" The Start Application Two key.*/ - GF_KEY_LAUNCHMAIL, /*"LaunchMail" The Start Mail key.*/ - GF_KEY_LEFT, /*"Left" The Left Arrow key.*/ - GF_KEY_META, /*"Meta" The Meta key.*/ - GF_KEY_MEDIANEXTTRACK, /*"MediaNextTrack" The Media Next Track key.*/ - GF_KEY_MEDIAPLAYPAUSE, /*"MediaPlayPause" The Media Play Pause key.*/ - GF_KEY_MEDIAPREVIOUSTRACK, /*"MediaPreviousTrack" The Media Previous Track key.*/ - GF_KEY_MEDIASTOP, /*"MediaStop" The Media Stok key.*/ - GF_KEY_MODECHANGE, /*"ModeChange" The Mode Change key.*/ - GF_KEY_NONCONVERT, /*"Nonconvert" The Nonconvert (Don't Convert) key.*/ - GF_KEY_NUMLOCK, /*"NumLock" The Num Lock key.*/ - GF_KEY_PAGEDOWN, /*"PageDown" The Page Down (Next) key.*/ - GF_KEY_PAGEUP, /*"PageUp" The Page Up key.*/ - GF_KEY_PASTE, /*"Paste" The Paste key.*/ - GF_KEY_PAUSE, /*"Pause" The Pause key.*/ - GF_KEY_PLAY, /*"Play" The Play key.*/ - GF_KEY_PREVIOUSCANDIDATE, /*"PreviousCandidate" The Previous Candidate function key.*/ - GF_KEY_PRINTSCREEN, /*"PrintScreen" The Print Screen (PrintScrn, SnapShot) key.*/ - GF_KEY_PROCESS, /*"Process" The Process key.*/ - GF_KEY_PROPS, /*"Props" The Props key.*/ - GF_KEY_RIGHT, /*"Right" The Right Arrow key.*/ - GF_KEY_ROMANCHARACTERS, /*"RomanCharacters" The Roman Characters function key.*/ - GF_KEY_SCROLL, /*"Scroll" The Scroll Lock key.*/ - GF_KEY_SELECT, /*"Select" The Select key.*/ - GF_KEY_SELECTMEDIA, /*"SelectMedia" The Select Media key.*/ - GF_KEY_SHIFT, /*"Shift" The Shift key.*/ - GF_KEY_STOP, /*"Stop" The Stop key.*/ - GF_KEY_UP, /*"Up" The Up Arrow key.*/ - GF_KEY_UNDO, /*"Undo" The Undo key.*/ - GF_KEY_VOLUMEDOWN, /*"VolumeDown" The Volume Down key.*/ - GF_KEY_VOLUMEMUTE, /*"VolumeMute" The Volume Mute key.*/ - GF_KEY_VOLUMEUP, /*"VolumeUp" The Volume Up key.*/ - GF_KEY_WIN, /*"Win" The Windows Logo key.*/ - GF_KEY_ZOOM, /*"Zoom" The Zoom key.*/ - GF_KEY_BACKSPACE, /*"U+0008" The Backspace (Back) key.*/ - GF_KEY_TAB, /*"U+0009" The Horizontal Tabulation (Tab) key.*/ - GF_KEY_CANCEL, /*"U+0018" The Cancel key.*/ - GF_KEY_ESCAPE, /*"U+001B" The Escape (Esc) key.*/ - GF_KEY_SPACE, /*"U+0020" The Space (Spacebar) key.*/ - GF_KEY_EXCLAMATION, /*"U+0021" The Exclamation Mark (Factorial, Bang) key (!).*/ - GF_KEY_QUOTATION, /*"U+0022" The Quotation Mark (Quote Double) key (").*/ - GF_KEY_NUMBER, /*"U+0023" The Number Sign (Pound Sign, Hash, Crosshatch, Octothorpe) key (#).*/ - GF_KEY_DOLLAR, /*"U+0024" The Dollar Sign (milreis, escudo) key ($).*/ - GF_KEY_AMPERSAND, /*"U+0026" The Ampersand key (&).*/ - GF_KEY_APOSTROPHE, /*"U+0027" The Apostrophe (Apostrophe-Quote, APL Quote) key (').*/ - GF_KEY_LEFTPARENTHESIS, /*"U+0028" The Left Parenthesis (Opening Parenthesis) key (().*/ - GF_KEY_RIGHTPARENTHESIS, /*"U+0029" The Right Parenthesis (Closing Parenthesis) key ()).*/ - GF_KEY_STAR, /*"U+002A" The Asterix (Star) key (*).*/ - GF_KEY_PLUS, /*"U+002B" The Plus Sign (Plus) key (+).*/ - GF_KEY_COMMA, /*"U+002C" The Comma (decimal separator) sign key (,).*/ - GF_KEY_HYPHEN, /*"U+002D" The Hyphen-minus (hyphen or minus sign) key (-).*/ - GF_KEY_FULLSTOP, /*"U+002E" The Full Stop (period, dot, decimal point) key (.).*/ - GF_KEY_SLASH, /*"U+002F" The Solidus (slash, virgule, shilling) key (/).*/ - GF_KEY_0, /*"U+0030" The Digit Zero key (0).*/ - GF_KEY_1, /*"U+0031" The Digit One key (1).*/ - GF_KEY_2, /*"U+0032" The Digit Two key (2).*/ - GF_KEY_3, /*"U+0033" The Digit Three key (3).*/ - GF_KEY_4, /*"U+0034" The Digit Four key (4).*/ - GF_KEY_5, /*"U+0035" The Digit Five key (5).*/ - GF_KEY_6, /*"U+0036" The Digit Six key (6).*/ - GF_KEY_7, /*"U+0037" The Digit Seven key (7).*/ - GF_KEY_8, /*"U+0038" The Digit Eight key (8).*/ - GF_KEY_9, /*"U+0039" The Digit Nine key (9).*/ - GF_KEY_COLON, /*"U+003A" The Colon key (:).*/ - GF_KEY_SEMICOLON, /*"U+003B" The Semicolon key (;).*/ - GF_KEY_LESSTHAN, /*"U+003C" The Less-Than Sign key (<).*/ - GF_KEY_EQUALS, /*"U+003D" The Equals Sign key (=).*/ - GF_KEY_GREATERTHAN, /*"U+003E" The Greater-Than Sign key (>).*/ - GF_KEY_QUESTION, /*"U+003F" The Question Mark key (?).*/ - GF_KEY_AT, /*"U+0040" The Commercial At (@) key.*/ - GF_KEY_A, /*"U+0041" The Latin Capital Letter A key (A).*/ - GF_KEY_B, /*"U+0042" The Latin Capital Letter B key (B).*/ - GF_KEY_C, /*"U+0043" The Latin Capital Letter C key (C).*/ - GF_KEY_D, /*"U+0044" The Latin Capital Letter D key (D).*/ - GF_KEY_E, /*"U+0045" The Latin Capital Letter E key (E).*/ - GF_KEY_F, /*"U+0046" The Latin Capital Letter F key (F).*/ - GF_KEY_G, /*"U+0047" The Latin Capital Letter G key (G).*/ - GF_KEY_H, /*"U+0048" The Latin Capital Letter H key (H).*/ - GF_KEY_I, /*"U+0049" The Latin Capital Letter I key (I).*/ - GF_KEY_J, /*"U+004A" The Latin Capital Letter J key (J).*/ - GF_KEY_K, /*"U+004B" The Latin Capital Letter K key (K).*/ - GF_KEY_L, /*"U+004C" The Latin Capital Letter L key (L).*/ - GF_KEY_M, /*"U+004D" The Latin Capital Letter M key (M).*/ - GF_KEY_N, /*"U+004E" The Latin Capital Letter N key (N).*/ - GF_KEY_O, /*"U+004F" The Latin Capital Letter O key (O).*/ - GF_KEY_P, /*"U+0050" The Latin Capital Letter P key (P).*/ - GF_KEY_Q, /*"U+0051" The Latin Capital Letter Q key (Q).*/ - GF_KEY_R, /*"U+0052" The Latin Capital Letter R key (R).*/ - GF_KEY_S, /*"U+0053" The Latin Capital Letter S key (S).*/ - GF_KEY_T, /*"U+0054" The Latin Capital Letter T key (T).*/ - GF_KEY_U, /*"U+0055" The Latin Capital Letter U key (U).*/ - GF_KEY_V, /*"U+0056" The Latin Capital Letter V key (V).*/ - GF_KEY_W, /*"U+0057" The Latin Capital Letter W key (W).*/ - GF_KEY_X, /*"U+0058" The Latin Capital Letter X key (X).*/ - GF_KEY_Y, /*"U+0059" The Latin Capital Letter Y key (Y).*/ - GF_KEY_Z, /*"U+005A" The Latin Capital Letter Z key (Z).*/ - GF_KEY_LEFTSQUAREBRACKET, /*"U+005B" The Left Square Bracket (Opening Square Bracket) key ([).*/ - GF_KEY_BACKSLASH, /*"U+005C" The Reverse Solidus (Backslash) key (\).*/ - GF_KEY_RIGHTSQUAREBRACKET, /*"U+005D" The Right Square Bracket (Closing Square Bracket) key (]).*/ - GF_KEY_CIRCUM, /*"U+005E" The Circumflex Accent key (^).*/ - GF_KEY_UNDERSCORE, /*"U+005F" The Low Sign (Spacing Underscore, Underscore) key (_).*/ - GF_KEY_GRAVEACCENT, /*"U+0060" The Grave Accent (Back Quote) key (`).*/ - GF_KEY_LEFTCURLYBRACKET, /*"U+007B" The Left Curly Bracket (Opening Curly Bracket, Opening Brace, Brace Left) key ({).*/ - GF_KEY_PIPE, /*"U+007C" The Vertical Line (Vertical Bar, Pipe) key (|).*/ - GF_KEY_RIGHTCURLYBRACKET, /*"U+007D" The Right Curly Bracket (Closing Curly Bracket, Closing Brace, Brace Right) key (}).*/ - GF_KEY_DEL, /*"U+007F" The Delete (Del) Key.*/ - GF_KEY_INVERTEXCLAMATION, /*"U+00A1" The Inverted Exclamation Mark key (�).*/ - GF_KEY_DEADGRAVE, /*"U+0300" The Combining Grave Accent (Greek Varia, Dead Grave) key.*/ - GF_KEY_DEADEACUTE, /*"U+0301" The Combining Acute Accent (Stress Mark, Greek Oxia, Tonos, Dead Eacute) key.*/ - GF_KEY_DEADCIRCUM, /*"U+0302" The Combining Circumflex Accent (Hat, Dead Circumflex) key.*/ - GF_KEY_DEADTILDE, /*"U+0303" The Combining Tilde (Dead Tilde) key.*/ - GF_KEY_DEADMACRON, /*"U+0304" The Combining Macron (Long, Dead Macron) key.*/ - GF_KEY_DEADBREVE, /*"U+0306" The Combining Breve (Short, Dead Breve) key.*/ - GF_KEY_DEADABOVEDOT, /*"U+0307" The Combining Dot Above (Derivative, Dead Above Dot) key.*/ - GF_KEY_DEADDIARESIS, /*"U+0308" The Combining Diaeresis (Double Dot Abode, Umlaut, Greek Dialytika, Double Derivative, Dead Diaeresis) key.*/ - GF_KEY_DEADRINGABOVE, /*"U+030A" The Combining Ring Above (Dead Above Ring) key.*/ - GF_KEY_DEADDOUBLEACUTE, /*"U+030B" The Combining Double Acute Accent (Dead Doubleacute) key.*/ - GF_KEY_DEADCARON, /*"U+030C" The Combining Caron (Hacek, V Above, Dead Caron) key.*/ - GF_KEY_DEADCEDILLA, /*"U+0327" The Combining Cedilla (Dead Cedilla) key.*/ - GF_KEY_DEADOGONEK, /*"U+0328" The Combining Ogonek (Nasal Hook, Dead Ogonek) key.*/ - GF_KEY_DEADIOTA, /*"U+0345" The Combining Greek Ypogegrammeni (Greek Non-Spacing Iota Below, Iota Subscript, Dead Iota) key.*/ - GF_KEY_EURO, /*"U+20AC" The Euro Currency Sign key (�).*/ - GF_KEY_DEADVOICESOUND, /*"U+3099" The Combining Katakana-Hiragana Voiced Sound Mark (Dead Voiced Sound) key.*/ - GF_KEY_DEADSEMIVOICESOUND, /*"U+309A" The Combining Katakana-Hiragana Semi-Voiced Sound Mark (Dead Semivoiced Sound) key. */ - /* STB */ - GF_KEY_CHANNELUP, /*ChannelUp*/ - GF_KEY_CHANNELDOWN, /*ChannelDown*/ - GF_KEY_TEXT, /*Text*/ - GF_KEY_INFO, /*Info*/ - GF_KEY_EPG, /*EPG*/ - GF_KEY_RECORD, /*Record*/ - GF_KEY_BEGINPAGE, /*BeginPage*/ - /* end STB */ - - /*non-dom keys, used in LASeR*/ - GF_KEY_CELL_SOFT1, /*soft1 key of cell phones*/ - GF_KEY_CELL_SOFT2, /*soft2 key of cell phones*/ - - /*for joystick handling*/ - GF_KEY_JOYSTICK -}; - - -/*key modifiers state - set by terminal (not set by video driver)*/ -enum -{ - GF_KEY_MOD_SHIFT = (1), - GF_KEY_MOD_CTRL = (1<<2), - GF_KEY_MOD_ALT = (1<<3), - - GF_KEY_EXT_NUMPAD = (1<<4), - GF_KEY_EXT_LEFT = (1<<5), - GF_KEY_EXT_RIGHT = (1<<6) -}; /*mouse button modifiers*/ enum @@ -504,7 +98,7 @@ typedef struct u8 type; /*width and height of visual surface to allocate*/ u16 width, height; - /*indicates whether double buffering is desired*/ + /*indicates whether double buffering is desired - when sent from plugin to player, indicates the backbuffer has been destroyed*/ Bool back_buffer; /*indicates whether system memory for the backbuffer is desired (no video blitting)*/ Bool system_memory; @@ -527,24 +121,6 @@ typedef struct u32 show_type; } GF_EventShow; - -/*sensor signaling*/ -enum -{ - GF_CURSOR_NORMAL = 0x00, - GF_CURSOR_ANCHOR, - GF_CURSOR_TOUCH, - /*discSensor, cylinderSensor, sphereSensor*/ - GF_CURSOR_ROTATE, - /*proximitySensor & proximitySensor2D*/ - GF_CURSOR_PROXIMITY, - /*planeSensor & planeSensor2D*/ - GF_CURSOR_PLANE, - /*collision*/ - GF_CURSOR_COLLIDE, - GF_CURSOR_HIDE, -}; - /*event proc return value: ignored*/ typedef struct { @@ -672,13 +248,6 @@ typedef struct u32 sys_colors[28]; } GF_EventSysColors; -/*Mutation AttrChangeType Signaling*/ -enum -{ - GF_MUTATION_ATTRCHANGE_MODIFICATION = 0x01, - GF_MUTATION_ATTRCHANGE_ADDITION = 0x02, - GF_MUTATION_ATTRCHANGE_REMOVAL = 0x03, -}; typedef struct { /* GF_EVENT_TREE_MODIFIED, GF_EVENT_NODE_INSERTED, GF_EVENT_NODE_REMOVED, GF_EVENT_NODE_INSERTED_DOC, GF_EVENT_NODE_REMOVED_DOC, GF_EVENT_ATTR_MODIFIED, GF_EVENT_CHAR_DATA_MODIFIED */ @@ -690,13 +259,6 @@ typedef struct { u8 attrChange; } GF_EventMutation; -enum -{ - /*events forwarded from MPEG-2 stack*/ - GF_EVT_FORWARDED_MPEG2 = 0, - /*events forwarded from RTP/RTSP/IP stack (not used yet)*/ - GF_EVT_FORWARDED_RTP_RTSP -}; typedef struct { /* GF_EVENT_OPENFILE*/ diff --git a/include/gpac/events_constants.h b/include/gpac/events_constants.h new file mode 100644 index 0000000..f1693fd --- /dev/null +++ b/include/gpac/events_constants.h @@ -0,0 +1,500 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * Copyright (c) Telecom-Paristech 2005-20xx + * All rights reserved + * + * This file is part of GPAC / Events management + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#ifndef _GF_EVENTS_CONSTANTS_H_ +#define _GF_EVENTS_CONSTANTS_H_ + + + +/* + minimal event system + + DO NOT CHANGE THEIR POSITION IN THE LIST, USED TO SPEED UP FILTERING OF USER INPUT EVENTS +*/ + +/*events*/ +enum { + + /****************************************************** + + Events used for both GPAC internals and DOM Events + + *******************************************************/ + /*MouseEvents*/ + GF_EVENT_CLICK, + GF_EVENT_MOUSEUP, + GF_EVENT_MOUSEDOWN, + GF_EVENT_MOUSEOVER, + GF_EVENT_MOUSEOUT, + /*!! ALL MOUSE EVENTS SHALL BE DECLARED BEFORE MOUSEMOVE !! */ + GF_EVENT_MOUSEMOVE, + /*mouse wheel event*/ + GF_EVENT_MOUSEWHEEL, + + /*Key Events*/ + GF_EVENT_KEYUP, + GF_EVENT_KEYDOWN, /* covers KeyDown, KeyPress and AccessKey */ + GF_EVENT_LONGKEYPRESS, + /*character input*/ + GF_EVENT_TEXTINPUT, + + + /****************************************************** + + Events used for DOM Events only + + *******************************************************/ + GF_EVENT_TEXTSELECT, + + /*DOM UIEvents*/ + GF_EVENT_FOCUSIN, + GF_EVENT_FOCUSOUT, + GF_EVENT_ACTIVATE, + GF_EVENT_CHANGE, + GF_EVENT_FOCUS, + GF_EVENT_BLUR, + /*SVG (HTML) Events*/ + GF_EVENT_LOAD, + GF_EVENT_UNLOAD, + GF_EVENT_ABORT, + GF_EVENT_ERROR, + GF_EVENT_RESIZE, + GF_EVENT_SCROLL, + GF_EVENT_ZOOM, + GF_EVENT_BEGIN, /*this is a fake event, it is NEVER fired, only used in SMIL begin*/ + GF_EVENT_BEGIN_EVENT, + GF_EVENT_END, /*this is a fake event, it is NEVER fired, only used in SMIL end*/ + GF_EVENT_END_EVENT, + GF_EVENT_REPEAT, /*this is a fake event, it is NEVER fired, only used in SMIL repeat*/ + GF_EVENT_REPEAT_EVENT, + + /*DOM MutationEvents - NOT SUPPORTED YET*/ + GF_EVENT_TREE_MODIFIED, + GF_EVENT_NODE_INSERTED, + GF_EVENT_NODE_REMOVED, + GF_EVENT_NODE_INSERTED_DOC, + GF_EVENT_NODE_REMOVED_DOC, + GF_EVENT_ATTR_MODIFIED, + GF_EVENT_CHAR_DATA_MODIFIED, + GF_EVENT_NODE_NAME_CHANGED, + GF_EVENT_ATTR_NAME_CHANGED, + + GF_EVENT_DCCI_PROP_CHANGE, + + /*LASeR events*/ + GF_EVENT_ACTIVATED, + GF_EVENT_DEACTIVATED, + GF_EVENT_PAUSE, + GF_EVENT_PAUSED_EVENT, + GF_EVENT_PLAY, + GF_EVENT_REPEAT_KEY, + GF_EVENT_RESUME_EVENT, + GF_EVENT_SHORT_ACCESSKEY, + /*pseudo-event, only used in LASeR coding*/ + GF_EVENT_EXECUTION_TIME, + + /*MediaAccess events - cf http://www.w3.org/TR/MediaAccessEvents*/ +#if 0 + GF_EVENT_MEDIA_BEGIN_SESSION_SETUP, + GF_EVENT_MEDIA_END_SESSION_SETUP, + GF_EVENT_MEDIA_DATA_REQUEST, + GF_EVENT_MEDIA_PLAYABLE, + GF_EVENT_MEDIA_NOT_PLAYABLE, + GF_EVENT_MEDIA_DATA_PROGRESS, + GF_EVENT_MEDIA_END_OF_DATA, + GF_EVENT_MEDIA_STOP, + GF_EVENT_MEDIA_ERROR, +#endif + + /*HTML5 media events*/ + + GF_EVENT_MEDIA_SETUP_BEGIN, /*not HTML5 but should be :)*/ + GF_EVENT_MEDIA_SETUP_DONE, /*not HTML5 but should be :)*/ + GF_EVENT_MEDIA_LOAD_START, + GF_EVENT_MEDIA_LOAD_DONE, /*not HTML5 but should be :)*/ + GF_EVENT_MEDIA_PROGRESS, + GF_EVENT_MEDIA_SUSPEND, + GF_EVENT_MEDIA_EMPTIED, + GF_EVENT_MEDIA_STALLED, + GF_EVENT_MEDIA_LOADED_METADATA, + GF_EVENT_MEDIA_LODADED_DATA, + GF_EVENT_MEDIA_CANPLAY, + GF_EVENT_MEDIA_CANPLAYTHROUGH, + GF_EVENT_MEDIA_PLAYING, + GF_EVENT_MEDIA_WAITING, + GF_EVENT_MEDIA_SEEKING, + GF_EVENT_MEDIA_SEEKED, + GF_EVENT_MEDIA_ENDED, + GF_EVENT_MEDIA_DURATION_CHANGED, + GF_EVENT_MEDIA_TIME_UPDATE, + GF_EVENT_MEDIA_RATECHANGE, + GF_EVENT_MEDIA_VOLUME_CHANGED, + + GF_EVENT_BATTERY, + GF_EVENT_CPU, + GF_EVENT_UNKNOWN, + + + /****************************************************** + + Events used for GPAC internals only + + *******************************************************/ + + /*same as mousedown, generated internally by GPAC*/ + GF_EVENT_DBLCLICK, + + /*scene attached event, dispatched when the root node of a scene is loaded and + attached to the window or parent object (animation, inline, ...)*/ + GF_EVENT_SCENE_ATTACHED, + + /*VP resize attached event, dispatched when viewport of a scene is being modified + attached to the window or parent object (animation, inline, ...)*/ + GF_EVENT_VP_RESIZE, + + /*window events*/ + /*size has changed - indicate new w & h in .x end .y fields of event. + When sent from gpac to a video plugin, indicates the output size should be changed. This is only sent when the plugin + manages the output video himself + When sent from a video plugin to gpac, indicates the output size has been changed. This is only sent when the plugin + manages the output video himself + */ + GF_EVENT_SIZE, + /*signals the scene size (if indicated in scene) upon connection (sent to the user event proc only) + if scene size hasn't changed (seeking or other) this event is not sent + */ + GF_EVENT_SCENE_SIZE, + GF_EVENT_SHOWHIDE, /*window show/hide (minimized or other). This is also sent to the user to signal focus switch in fullscreen*/ + GF_EVENT_SET_CURSOR, /*set mouse cursor*/ + GF_EVENT_SET_CAPTION, /*set window caption*/ + GF_EVENT_MOVE, /*move window*/ + GF_EVENT_REFRESH, /*window needs repaint (whenever needed, eg restore, hide->show, background refresh, paint)*/ + GF_EVENT_QUIT, /*window is being closed*/ + /*video hw setup message: + - when sent from gpac to plugin, indicates that the plugin should re-setup hardware context due to a window resize: + * for 2D output, this means resizing the backbuffer if needed (depending on HW constraints) + * for 3D output, this means re-setup of OpenGL context (depending on HW constraints). Depending on windowing systems + and implementations, it could be possible to resize a window without destroying the GL context. + + - when sent from plugin to gpac, indicates that hardware resources must be resetup before next render step (this is mainly + due to discard all openGL textures and cached objects) + */ + GF_EVENT_VIDEO_SETUP, + /*queries the list of system colors - only exchanged between compositor and video output*/ + GF_EVENT_SYS_COLORS, + + /*indicates some text has been pasted - from video output to compositor only*/ + GF_EVENT_PASTE_TEXT, + /*queries for text to be copied - from video output to compositor only*/ + GF_EVENT_COPY_TEXT, + + /*terminal events*/ + GF_EVENT_CONNECT, /*signal URL is connected*/ + GF_EVENT_DURATION, /*signal duration of presentation*/ + GF_EVENT_EOS, /*signal End of scene playback*/ + GF_EVENT_AUTHORIZATION, /*indicates a user and pass is queried*/ + GF_EVENT_NAVIGATE, /*indicates the user app should load or jump to the given URL.*/ + GF_EVENT_NAVIGATE_INFO, /*indicates the link or its description under the mouse pointer*/ + GF_EVENT_MESSAGE, /*message from the MPEG-4 terminal*/ + GF_EVENT_PROGRESS, /*progress message from the MPEG-4 terminal*/ + GF_EVENT_FORWARDED, /*event forwarded by service (MPEG-2, RTP, ...)*/ + GF_EVENT_VIEWPOINTS, /*indicates viewpoint list has changed - no struct associated*/ + GF_EVENT_STREAMLIST, /*indicates stream list has changed - no struct associated - only used when no scene info is present*/ + GF_EVENT_METADATA, /*indicates a change in associated metadata*/ + GF_EVENT_MIGRATE, /*indicates a session migration request*/ + GF_EVENT_DISCONNECT, /*indicates the current url should be disconnected*/ + GF_EVENT_RESOLUTION, /*indicates the screen resolution has changed*/ + GF_EVENT_OPENFILE, + /* Events for Keyboad */ + GF_EVENT_TEXT_EDITING_START, + GF_EVENT_TEXT_EDITING_END +}; + +/*GPAC/DOM3 key codes*/ +enum { + GF_KEY_UNIDENTIFIED = 0, + GF_KEY_ACCEPT = 1, /* "Accept" The Accept (Commit) key.*/ + GF_KEY_AGAIN, /* "Again" The Again key.*/ + GF_KEY_ALLCANDIDATES, /* "AllCandidates" The All Candidates key.*/ + GF_KEY_ALPHANUM, /*"Alphanumeric" The Alphanumeric key.*/ + GF_KEY_ALT, /*"Alt" The Alt (Menu) key.*/ + GF_KEY_ALTGRAPH, /*"AltGraph" The Alt-Graph key.*/ + GF_KEY_APPS, /*"Apps" The Application key.*/ + GF_KEY_ATTN, /*"Attn" The ATTN key.*/ + GF_KEY_BROWSERBACK, /*"BrowserBack" The Browser Back key.*/ + GF_KEY_BROWSERFAVORITES, /*"BrowserFavorites" The Browser Favorites key.*/ + GF_KEY_BROWSERFORWARD, /*"BrowserForward" The Browser Forward key.*/ + GF_KEY_BROWSERHOME, /*"BrowserHome" The Browser Home key.*/ + GF_KEY_BROWSERREFRESH, /*"BrowserRefresh" The Browser Refresh key.*/ + GF_KEY_BROWSERSEARCH, /*"BrowserSearch" The Browser Search key.*/ + GF_KEY_BROWSERSTOP, /*"BrowserStop" The Browser Stop key.*/ + GF_KEY_CAPSLOCK, /*"CapsLock" The Caps Lock (Capital) key.*/ + GF_KEY_CLEAR, /*"Clear" The Clear key.*/ + GF_KEY_CODEINPUT, /*"CodeInput" The Code Input key.*/ + GF_KEY_COMPOSE, /*"Compose" The Compose key.*/ + GF_KEY_CONTROL, /*"Control" The Control (Ctrl) key.*/ + GF_KEY_CRSEL, /*"Crsel" The Crsel key.*/ + GF_KEY_CONVERT, /*"Convert" The Convert key.*/ + GF_KEY_COPY, /*"Copy" The Copy key.*/ + GF_KEY_CUT, /*"Cut" The Cut key.*/ + GF_KEY_DOWN, /*"Down" The Down Arrow key.*/ + GF_KEY_END, /*"End" The End key.*/ + GF_KEY_ENTER, /*"Enter" The Enter key. + Note: This key identifier is also used for the Return (Macintosh numpad) key.*/ + GF_KEY_ERASEEOF, /*"EraseEof" The Erase EOF key.*/ + GF_KEY_EXECUTE, /*"Execute" The Execute key.*/ + GF_KEY_EXSEL, /*"Exsel" The Exsel key.*/ + GF_KEY_F1, /*"F1" The F1 key.*/ + GF_KEY_F2, /*"F2" The F2 key.*/ + GF_KEY_F3, /*"F3" The F3 key.*/ + GF_KEY_F4, /*"F4" The F4 key.*/ + GF_KEY_F5, /*"F5" The F5 key.*/ + GF_KEY_F6, /*"F6" The F6 key.*/ + GF_KEY_F7, /*"F7" The F7 key.*/ + GF_KEY_F8, /*"F8" The F8 key.*/ + GF_KEY_F9, /*"F9" The F9 key.*/ + GF_KEY_F10, /*"F10" The F10 key.*/ + GF_KEY_F11, /*"F11" The F11 key.*/ + GF_KEY_F12, /*"F12" The F12 key.*/ + GF_KEY_F13, /*"F13" The F13 key.*/ + GF_KEY_F14, /*"F14" The F14 key.*/ + GF_KEY_F15, /*"F15" The F15 key.*/ + GF_KEY_F16, /*"F16" The F16 key.*/ + GF_KEY_F17, /*"F17" The F17 key.*/ + GF_KEY_F18, /*"F18" The F18 key.*/ + GF_KEY_F19, /*"F19" The F19 key.*/ + GF_KEY_F20, /*"F20" The F20 key.*/ + GF_KEY_F21, /*"F21" The F21 key.*/ + GF_KEY_F22, /*"F22" The F22 key.*/ + GF_KEY_F23, /*"F23" The F23 key.*/ + GF_KEY_F24, /*"F24" The F24 key.*/ + GF_KEY_FINALMODE, /*"FinalMode" The Final Mode (Final) key used on some asian keyboards.*/ + GF_KEY_FIND, /*"Find" The Find key.*/ + GF_KEY_FULLWIDTH, /*"FullWidth" The Full-Width Characters key.*/ + GF_KEY_HALFWIDTH, /*"HalfWidth" The Half-Width Characters key.*/ + GF_KEY_HANGULMODE, /*"HangulMode" The Hangul (Korean characters) Mode key.*/ + GF_KEY_HANJAMODE, /*"HanjaMode" The Hanja (Korean characters) Mode key.*/ + GF_KEY_HELP, /*"Help" The Help key.*/ + GF_KEY_HIRAGANA, /*"Hiragana" The Hiragana (Japanese Kana characters) key.*/ + GF_KEY_HOME, /*"Home" The Home key.*/ + GF_KEY_INSERT, /*"Insert" The Insert (Ins) key.*/ + GF_KEY_JAPANESEHIRAGANA, /*"JapaneseHiragana" The Japanese-Hiragana key.*/ + GF_KEY_JAPANESEKATAKANA, /*"JapaneseKatakana" The Japanese-Katakana key.*/ + GF_KEY_JAPANESEROMAJI, /*"JapaneseRomaji" The Japanese-Romaji key.*/ + GF_KEY_JUNJAMODE, /*"JunjaMode" The Junja Mode key.*/ + GF_KEY_KANAMODE, /*"KanaMode" The Kana Mode (Kana Lock) key.*/ + GF_KEY_KANJIMODE, /*"KanjiMode" The Kanji (Japanese name for ideographic characters of Chinese origin) Mode key.*/ + GF_KEY_KATAKANA, /*"Katakana" The Katakana (Japanese Kana characters) key.*/ + GF_KEY_LAUNCHAPPLICATION1, /*"LaunchApplication1" The Start Application One key.*/ + GF_KEY_LAUNCHAPPLICATION2, /*"LaunchApplication2" The Start Application Two key.*/ + GF_KEY_LAUNCHMAIL, /*"LaunchMail" The Start Mail key.*/ + GF_KEY_LEFT, /*"Left" The Left Arrow key.*/ + GF_KEY_META, /*"Meta" The Meta key.*/ + GF_KEY_MEDIANEXTTRACK, /*"MediaNextTrack" The Media Next Track key.*/ + GF_KEY_MEDIAPLAYPAUSE, /*"MediaPlayPause" The Media Play Pause key.*/ + GF_KEY_MEDIAPREVIOUSTRACK, /*"MediaPreviousTrack" The Media Previous Track key.*/ + GF_KEY_MEDIASTOP, /*"MediaStop" The Media Stok key.*/ + GF_KEY_MODECHANGE, /*"ModeChange" The Mode Change key.*/ + GF_KEY_NONCONVERT, /*"Nonconvert" The Nonconvert (Don't Convert) key.*/ + GF_KEY_NUMLOCK, /*"NumLock" The Num Lock key.*/ + GF_KEY_PAGEDOWN, /*"PageDown" The Page Down (Next) key.*/ + GF_KEY_PAGEUP, /*"PageUp" The Page Up key.*/ + GF_KEY_PASTE, /*"Paste" The Paste key.*/ + GF_KEY_PAUSE, /*"Pause" The Pause key.*/ + GF_KEY_PLAY, /*"Play" The Play key.*/ + GF_KEY_PREVIOUSCANDIDATE, /*"PreviousCandidate" The Previous Candidate function key.*/ + GF_KEY_PRINTSCREEN, /*"PrintScreen" The Print Screen (PrintScrn, SnapShot) key.*/ + GF_KEY_PROCESS, /*"Process" The Process key.*/ + GF_KEY_PROPS, /*"Props" The Props key.*/ + GF_KEY_RIGHT, /*"Right" The Right Arrow key.*/ + GF_KEY_ROMANCHARACTERS, /*"RomanCharacters" The Roman Characters function key.*/ + GF_KEY_SCROLL, /*"Scroll" The Scroll Lock key.*/ + GF_KEY_SELECT, /*"Select" The Select key.*/ + GF_KEY_SELECTMEDIA, /*"SelectMedia" The Select Media key.*/ + GF_KEY_SHIFT, /*"Shift" The Shift key.*/ + GF_KEY_STOP, /*"Stop" The Stop key.*/ + GF_KEY_UP, /*"Up" The Up Arrow key.*/ + GF_KEY_UNDO, /*"Undo" The Undo key.*/ + GF_KEY_VOLUMEDOWN, /*"VolumeDown" The Volume Down key.*/ + GF_KEY_VOLUMEMUTE, /*"VolumeMute" The Volume Mute key.*/ + GF_KEY_VOLUMEUP, /*"VolumeUp" The Volume Up key.*/ + GF_KEY_WIN, /*"Win" The Windows Logo key.*/ + GF_KEY_ZOOM, /*"Zoom" The Zoom key.*/ + GF_KEY_BACKSPACE, /*"U+0008" The Backspace (Back) key.*/ + GF_KEY_TAB, /*"U+0009" The Horizontal Tabulation (Tab) key.*/ + GF_KEY_CANCEL, /*"U+0018" The Cancel key.*/ + GF_KEY_ESCAPE, /*"U+001B" The Escape (Esc) key.*/ + GF_KEY_SPACE, /*"U+0020" The Space (Spacebar) key.*/ + GF_KEY_EXCLAMATION, /*"U+0021" The Exclamation Mark (Factorial, Bang) key (!).*/ + GF_KEY_QUOTATION, /*"U+0022" The Quotation Mark (Quote Double) key (").*/ + GF_KEY_NUMBER, /*"U+0023" The Number Sign (Pound Sign, Hash, Crosshatch, Octothorpe) key (#).*/ + GF_KEY_DOLLAR, /*"U+0024" The Dollar Sign (milreis, escudo) key ($).*/ + GF_KEY_AMPERSAND, /*"U+0026" The Ampersand key (&).*/ + GF_KEY_APOSTROPHE, /*"U+0027" The Apostrophe (Apostrophe-Quote, APL Quote) key (').*/ + GF_KEY_LEFTPARENTHESIS, /*"U+0028" The Left Parenthesis (Opening Parenthesis) key (().*/ + GF_KEY_RIGHTPARENTHESIS, /*"U+0029" The Right Parenthesis (Closing Parenthesis) key ()).*/ + GF_KEY_STAR, /*"U+002A" The Asterix (Star) key (*).*/ + GF_KEY_PLUS, /*"U+002B" The Plus Sign (Plus) key (+).*/ + GF_KEY_COMMA, /*"U+002C" The Comma (decimal separator) sign key (,).*/ + GF_KEY_HYPHEN, /*"U+002D" The Hyphen-minus (hyphen or minus sign) key (-).*/ + GF_KEY_FULLSTOP, /*"U+002E" The Full Stop (period, dot, decimal point) key (.).*/ + GF_KEY_SLASH, /*"U+002F" The Solidus (slash, virgule, shilling) key (/).*/ + GF_KEY_0, /*"U+0030" The Digit Zero key (0).*/ + GF_KEY_1, /*"U+0031" The Digit One key (1).*/ + GF_KEY_2, /*"U+0032" The Digit Two key (2).*/ + GF_KEY_3, /*"U+0033" The Digit Three key (3).*/ + GF_KEY_4, /*"U+0034" The Digit Four key (4).*/ + GF_KEY_5, /*"U+0035" The Digit Five key (5).*/ + GF_KEY_6, /*"U+0036" The Digit Six key (6).*/ + GF_KEY_7, /*"U+0037" The Digit Seven key (7).*/ + GF_KEY_8, /*"U+0038" The Digit Eight key (8).*/ + GF_KEY_9, /*"U+0039" The Digit Nine key (9).*/ + GF_KEY_COLON, /*"U+003A" The Colon key (:).*/ + GF_KEY_SEMICOLON, /*"U+003B" The Semicolon key (;).*/ + GF_KEY_LESSTHAN, /*"U+003C" The Less-Than Sign key (<).*/ + GF_KEY_EQUALS, /*"U+003D" The Equals Sign key (=).*/ + GF_KEY_GREATERTHAN, /*"U+003E" The Greater-Than Sign key (>).*/ + GF_KEY_QUESTION, /*"U+003F" The Question Mark key (?).*/ + GF_KEY_AT, /*"U+0040" The Commercial At (@) key.*/ + GF_KEY_A, /*"U+0041" The Latin Capital Letter A key (A).*/ + GF_KEY_B, /*"U+0042" The Latin Capital Letter B key (B).*/ + GF_KEY_C, /*"U+0043" The Latin Capital Letter C key (C).*/ + GF_KEY_D, /*"U+0044" The Latin Capital Letter D key (D).*/ + GF_KEY_E, /*"U+0045" The Latin Capital Letter E key (E).*/ + GF_KEY_F, /*"U+0046" The Latin Capital Letter F key (F).*/ + GF_KEY_G, /*"U+0047" The Latin Capital Letter G key (G).*/ + GF_KEY_H, /*"U+0048" The Latin Capital Letter H key (H).*/ + GF_KEY_I, /*"U+0049" The Latin Capital Letter I key (I).*/ + GF_KEY_J, /*"U+004A" The Latin Capital Letter J key (J).*/ + GF_KEY_K, /*"U+004B" The Latin Capital Letter K key (K).*/ + GF_KEY_L, /*"U+004C" The Latin Capital Letter L key (L).*/ + GF_KEY_M, /*"U+004D" The Latin Capital Letter M key (M).*/ + GF_KEY_N, /*"U+004E" The Latin Capital Letter N key (N).*/ + GF_KEY_O, /*"U+004F" The Latin Capital Letter O key (O).*/ + GF_KEY_P, /*"U+0050" The Latin Capital Letter P key (P).*/ + GF_KEY_Q, /*"U+0051" The Latin Capital Letter Q key (Q).*/ + GF_KEY_R, /*"U+0052" The Latin Capital Letter R key (R).*/ + GF_KEY_S, /*"U+0053" The Latin Capital Letter S key (S).*/ + GF_KEY_T, /*"U+0054" The Latin Capital Letter T key (T).*/ + GF_KEY_U, /*"U+0055" The Latin Capital Letter U key (U).*/ + GF_KEY_V, /*"U+0056" The Latin Capital Letter V key (V).*/ + GF_KEY_W, /*"U+0057" The Latin Capital Letter W key (W).*/ + GF_KEY_X, /*"U+0058" The Latin Capital Letter X key (X).*/ + GF_KEY_Y, /*"U+0059" The Latin Capital Letter Y key (Y).*/ + GF_KEY_Z, /*"U+005A" The Latin Capital Letter Z key (Z).*/ + GF_KEY_LEFTSQUAREBRACKET, /*"U+005B" The Left Square Bracket (Opening Square Bracket) key ([).*/ + GF_KEY_BACKSLASH, /*"U+005C" The Reverse Solidus (Backslash) key (\).*/ + GF_KEY_RIGHTSQUAREBRACKET, /*"U+005D" The Right Square Bracket (Closing Square Bracket) key (]).*/ + GF_KEY_CIRCUM, /*"U+005E" The Circumflex Accent key (^).*/ + GF_KEY_UNDERSCORE, /*"U+005F" The Low Sign (Spacing Underscore, Underscore) key (_).*/ + GF_KEY_GRAVEACCENT, /*"U+0060" The Grave Accent (Back Quote) key (`).*/ + GF_KEY_LEFTCURLYBRACKET, /*"U+007B" The Left Curly Bracket (Opening Curly Bracket, Opening Brace, Brace Left) key ({).*/ + GF_KEY_PIPE, /*"U+007C" The Vertical Line (Vertical Bar, Pipe) key (|).*/ + GF_KEY_RIGHTCURLYBRACKET, /*"U+007D" The Right Curly Bracket (Closing Curly Bracket, Closing Brace, Brace Right) key (}).*/ + GF_KEY_DEL, /*"U+007F" The Delete (Del) Key.*/ + GF_KEY_INVERTEXCLAMATION, /*"U+00A1" The Inverted Exclamation Mark key (�).*/ + GF_KEY_DEADGRAVE, /*"U+0300" The Combining Grave Accent (Greek Varia, Dead Grave) key.*/ + GF_KEY_DEADEACUTE, /*"U+0301" The Combining Acute Accent (Stress Mark, Greek Oxia, Tonos, Dead Eacute) key.*/ + GF_KEY_DEADCIRCUM, /*"U+0302" The Combining Circumflex Accent (Hat, Dead Circumflex) key.*/ + GF_KEY_DEADTILDE, /*"U+0303" The Combining Tilde (Dead Tilde) key.*/ + GF_KEY_DEADMACRON, /*"U+0304" The Combining Macron (Long, Dead Macron) key.*/ + GF_KEY_DEADBREVE, /*"U+0306" The Combining Breve (Short, Dead Breve) key.*/ + GF_KEY_DEADABOVEDOT, /*"U+0307" The Combining Dot Above (Derivative, Dead Above Dot) key.*/ + GF_KEY_DEADDIARESIS, /*"U+0308" The Combining Diaeresis (Double Dot Abode, Umlaut, Greek Dialytika, Double Derivative, Dead Diaeresis) key.*/ + GF_KEY_DEADRINGABOVE, /*"U+030A" The Combining Ring Above (Dead Above Ring) key.*/ + GF_KEY_DEADDOUBLEACUTE, /*"U+030B" The Combining Double Acute Accent (Dead Doubleacute) key.*/ + GF_KEY_DEADCARON, /*"U+030C" The Combining Caron (Hacek, V Above, Dead Caron) key.*/ + GF_KEY_DEADCEDILLA, /*"U+0327" The Combining Cedilla (Dead Cedilla) key.*/ + GF_KEY_DEADOGONEK, /*"U+0328" The Combining Ogonek (Nasal Hook, Dead Ogonek) key.*/ + GF_KEY_DEADIOTA, /*"U+0345" The Combining Greek Ypogegrammeni (Greek Non-Spacing Iota Below, Iota Subscript, Dead Iota) key.*/ + GF_KEY_EURO, /*"U+20AC" The Euro Currency Sign key (�).*/ + GF_KEY_DEADVOICESOUND, /*"U+3099" The Combining Katakana-Hiragana Voiced Sound Mark (Dead Voiced Sound) key.*/ + GF_KEY_DEADSEMIVOICESOUND, /*"U+309A" The Combining Katakana-Hiragana Semi-Voiced Sound Mark (Dead Semivoiced Sound) key. */ + /* STB */ + GF_KEY_CHANNELUP, /*ChannelUp*/ + GF_KEY_CHANNELDOWN, /*ChannelDown*/ + GF_KEY_TEXT, /*Text*/ + GF_KEY_INFO, /*Info*/ + GF_KEY_EPG, /*EPG*/ + GF_KEY_RECORD, /*Record*/ + GF_KEY_BEGINPAGE, /*BeginPage*/ + /* end STB */ + + /*non-dom keys, used in LASeR*/ + GF_KEY_CELL_SOFT1, /*soft1 key of cell phones*/ + GF_KEY_CELL_SOFT2, /*soft2 key of cell phones*/ + + /*for joystick handling*/ + GF_KEY_JOYSTICK +}; + + +/*key modifiers state - set by terminal (not set by video driver)*/ +enum +{ + GF_KEY_MOD_SHIFT = (1), + GF_KEY_MOD_CTRL = (1<<2), + GF_KEY_MOD_ALT = (1<<3), + + GF_KEY_EXT_NUMPAD = (1<<4), + GF_KEY_EXT_LEFT = (1<<5), + GF_KEY_EXT_RIGHT = (1<<6) +}; + +/*sensor signaling*/ +enum +{ + GF_CURSOR_NORMAL = 0x00, + GF_CURSOR_ANCHOR, + GF_CURSOR_TOUCH, + /*discSensor, cylinderSensor, sphereSensor*/ + GF_CURSOR_ROTATE, + /*proximitySensor & proximitySensor2D*/ + GF_CURSOR_PROXIMITY, + /*planeSensor & planeSensor2D*/ + GF_CURSOR_PLANE, + /*collision*/ + GF_CURSOR_COLLIDE, + GF_CURSOR_HIDE, +}; + +/*Mutation AttrChangeType Signaling*/ +enum +{ + GF_MUTATION_ATTRCHANGE_MODIFICATION = 0x01, + GF_MUTATION_ATTRCHANGE_ADDITION = 0x02, + GF_MUTATION_ATTRCHANGE_REMOVAL = 0x03, +}; + +enum +{ + /*events forwarded from MPEG-2 stack*/ + GF_EVT_FORWARDED_MPEG2 = 0, + /*events forwarded from RTP/RTSP/IP stack (not used yet)*/ + GF_EVT_FORWARDED_RTP_RTSP +}; + +#endif diff --git a/include/gpac/internal/bifs_dev.h b/include/gpac/internal/bifs_dev.h index b7e897d..2920427 100644 --- a/include/gpac/internal/bifs_dev.h +++ b/include/gpac/internal/bifs_dev.h @@ -136,9 +136,9 @@ struct __tag_bifs_dec GF_Node *gf_bifs_dec_node(GF_BifsDecoder * codec, GF_BitStream *bs, u32 NDT_Tag); /*decodes an SFField (to get a ptr to the field, use gf_node_get_field ) the FieldIndex is used for Quantzation*/ -GF_Err gf_bifs_dec_sf_field(GF_BifsDecoder * codec, GF_BitStream *bs, GF_Node *node, GF_FieldInfo *field); +GF_Err gf_bifs_dec_sf_field(GF_BifsDecoder * codec, GF_BitStream *bs, GF_Node *node, GF_FieldInfo *field, Bool is_mem_com); /*decodes a Field (either SF or MF). The field MUST BE EMPTY*/ -GF_Err gf_bifs_dec_field(GF_BifsDecoder * codec, GF_BitStream *bs, GF_Node *node, GF_FieldInfo *field); +GF_Err gf_bifs_dec_field(GF_BifsDecoder * codec, GF_BitStream *bs, GF_Node *node, GF_FieldInfo *field, Bool is_mem_com); /*decodes a route*/ GF_Err gf_bifs_dec_route(GF_BifsDecoder * codec, GF_BitStream *bs, Bool is_insert); /*get name*/ diff --git a/include/gpac/internal/bifs_tables.h b/include/gpac/internal/bifs_tables.h index b3c384d..e07aae3 100644 --- a/include/gpac/internal/bifs_tables.h +++ b/include/gpac/internal/bifs_tables.h @@ -24,7 +24,7 @@ /* - DO NOT MOFIFY - File generated on GMT Wed Jul 20 05:50:21 2011 + DO NOT MOFIFY - File generated on GMT Tue Nov 08 09:10:57 2011 BY MPEG4Gen for GPAC Version 0.4.6-DEV */ diff --git a/include/gpac/internal/compositor_dev.h b/include/gpac/internal/compositor_dev.h index f6510f1..c1b6a6e 100644 --- a/include/gpac/internal/compositor_dev.h +++ b/include/gpac/internal/compositor_dev.h @@ -363,7 +363,8 @@ struct __tag_compositor /*user rotation angle - ALWAYS CENTERED*/ Fixed rotation; - Bool skip_flush; + /*0: flush to be done - 1: flush can be skipped - 2: forces flush*/ + u32 skip_flush; #ifndef GPAC_DISABLE_SVG u32 num_clicks; #endif @@ -771,6 +772,7 @@ struct _traversing_state #ifndef GPAC_DISABLE_SVG SVG_Number *parent_use_opacity; SVGAllAttributes *parent_anim_atts; + Bool parent_is_use; /*SVG text rendering state*/ Bool in_svg_text; @@ -1064,7 +1066,7 @@ void gf_sc_audio_setup(GF_AudioInput *ai, GF_Compositor *sr, GF_Node *node); /*unregister interface from renderer/mixer and stops source - deleteing the interface is the caller responsability*/ void gf_sc_audio_predestroy(GF_AudioInput *ai); /*open audio object*/ -GF_Err gf_sc_audio_open(GF_AudioInput *ai, MFURL *url, Double clipBegin, Double clipEnd); +GF_Err gf_sc_audio_open(GF_AudioInput *ai, MFURL *url, Double clipBegin, Double clipEnd, Bool lock_timeline); /*closes audio object*/ void gf_sc_audio_stop(GF_AudioInput *ai); /*restarts audio object (cf note in MediaObj)*/ @@ -1155,6 +1157,7 @@ const char *gf_scene_get_service_url(GF_SceneGraph *sg); Bool gf_scene_is_over(GF_SceneGraph *sg); +GF_SceneGraph *gf_scene_enum_extra_scene(GF_SceneGraph *sg, u32 *i); #ifndef GPAC_DISABLE_SVG diff --git a/include/gpac/internal/dvb_mpe_dev.h b/include/gpac/internal/dvb_mpe_dev.h index b943f14..9949d07 100644 --- a/include/gpac/internal/dvb_mpe_dev.h +++ b/include/gpac/internal/dvb_mpe_dev.h @@ -30,6 +30,9 @@ #include +#ifndef GPAC_DISABLE_MPEG2TS + + /*INT object*/ typedef struct { @@ -255,4 +258,7 @@ typedef enum { void descriptor_PRIVATE (u8 *b, DTAG_SCOPE tag_scope, GF_List * descriptors ); + +#endif //GPAC_DISABLE_MPEG2TS + #endif //_GF_DVB_MPE_DEV_H_ diff --git a/include/gpac/internal/ietf_dev.h b/include/gpac/internal/ietf_dev.h index 03d0040..e02a7ee 100644 --- a/include/gpac/internal/ietf_dev.h +++ b/include/gpac/internal/ietf_dev.h @@ -80,7 +80,7 @@ void gf_rtp_reorderer_del(GF_RTPReorder *po); void gf_rtp_reorderer_reset(GF_RTPReorder *po); /*Adds a packet to the queue. Packet Data is memcopied*/ -GF_Err gf_rtp_reorderer_add(GF_RTPReorder *po, void *pck, u32 pck_size, u32 pck_seqnum); +GF_Err gf_rtp_reorderer_add(GF_RTPReorder *po, const void * pck, u32 pck_size, u32 pck_seqnum); /*gets the output of the queue. Packet Data IS YOURS to delete*/ void *gf_rtp_reorderer_get(GF_RTPReorder *po, u32 *pck_size); diff --git a/include/gpac/internal/isomedia_dev.h b/include/gpac/internal/isomedia_dev.h index 9b68c46..41fd6bc 100644 --- a/include/gpac/internal/isomedia_dev.h +++ b/include/gpac/internal/isomedia_dev.h @@ -133,6 +133,10 @@ enum GF_ISOM_BOX_TYPE_PDIN = GF_4CC( 'p', 'd', 'i', 'n' ), GF_ISOM_BOX_TYPE_SDTP = GF_4CC( 's', 'd', 't', 'p' ), + GF_ISOM_BOX_TYPE_SBGP = GF_4CC( 's', 'b', 'g', 'p' ), + GF_ISOM_BOX_TYPE_SGPD = GF_4CC( 's', 'g', 'p', 'd' ), + + #ifndef GPAC_DISABLE_ISOM_FRAGMENTS /*Movie Fragments*/ GF_ISOM_BOX_TYPE_MVEX = GF_4CC( 'm', 'v', 'e', 'x' ), @@ -398,6 +402,8 @@ typedef struct { GF_ISOM_BOX GF_EditListBox *editList; + + Bool last_is_empty; } GF_EditBox; @@ -1085,6 +1091,9 @@ typedef struct GF_SubSampleInformationBox *SubSamples; + GF_List *sampleGroups; + GF_List *sampleGroupsDescription; + u32 MaxSamplePerChunk; u16 groupID; u16 trackPriority; @@ -1525,6 +1534,10 @@ typedef struct GF_TrackExtendsBox *trex; GF_SampleDependencyTypeBox *sdtp; GF_SubSampleInformationBox *subs; + + GF_List *sampleGroups; + GF_List *sampleGroupsDescription; + /*when data caching is on*/ u32 DataCache; GF_TFBaseMediaDecodeTimeBox *tfdt; @@ -1859,8 +1872,9 @@ typedef struct Bool reference_type; u32 reference_size; u32 subsegment_duration; - Bool contains_RAP; - u32 RAP_delta_time; + Bool starts_with_SAP; + u32 SAP_type; + u32 SAP_delta_time; } GF_SIDXReference; typedef struct __sidx_box @@ -1876,6 +1890,58 @@ typedef struct __sidx_box } GF_SegmentIndexBox; #endif + + +/*********************************************************** + Sample Groups +***********************************************************/ +typedef struct +{ + u32 sample_count; + u32 group_description_index; +} GF_SampleGroupEntry; + +typedef struct +{ + GF_ISOM_FULL_BOX + u32 grouping_type; + u32 grouping_type_parameter; + + u32 entry_count; + GF_SampleGroupEntry *sample_entries; + +} GF_SampleGroupBox; + +typedef struct +{ + GF_ISOM_FULL_BOX + u32 grouping_type; + u32 default_length; + + GF_List *group_descriptions; +} GF_SampleGroupDescriptionBox; + +/*default entry */ +typedef struct +{ + u32 length; + u8 *data; +} GF_DefaultSampleGroupDescriptionEntry; + +/*VisualRandomAccessEntry - 'rap ' type*/ +typedef struct +{ + u8 num_leading_samples_known; + u8 num_leading_samples; +} GF_VisualRandomAccessEntry; + +/*RollRecoveryEntry - 'roll' type*/ +typedef struct +{ + s16 roll_distance; +} GF_RollRecoveryEntry; + + /* Data Map (media storage) stuff */ @@ -2037,6 +2103,11 @@ struct __tag_isom { GF_List *moof_list; Bool use_segments, moof_first, append_segment; + /*used when building single-indexed self initializing media segments*/ + GF_SegmentIndexBox *root_sidx; + u64 root_sidx_offset; + u32 root_sidx_index; + Bool is_index_segment; #endif @@ -2045,13 +2116,14 @@ struct __tag_isom { /*default track for sync of MPEG4 streams - this is the first accessed stream without OCR info - only set in READ mode*/ s32 es_id_default_sync; + }; /*time function*/ u64 gf_isom_get_mp4time(); /*set the last error of the file. if file is NULL, set the static error (used for IO errors*/ void gf_isom_set_last_error(GF_ISOFile *the_file, GF_Err error); -GF_Err gf_isom_parse_movie_boxes(GF_ISOFile *mov, u64 *bytesMissing); +GF_Err gf_isom_parse_movie_boxes(GF_ISOFile *mov, u64 *bytesMissing, Bool progressive_mode); GF_ISOFile *gf_isom_new_movie(); /*Movie and Track access functions*/ GF_TrackBox *gf_isom_get_track_from_file(GF_ISOFile *the_file, u32 trackNumber); @@ -2077,7 +2149,7 @@ Bool IsMP4Description(u32 entryType); /*Find a reference of a given type*/ GF_Err Track_FindRef(GF_TrackBox *trak, u32 ReferenceType, GF_TrackReferenceTypeBox **dpnd); /*Time and sample*/ -GF_Err GetMediaTime(GF_TrackBox *trak, u64 movieTime, u64 *MediaTime, s64 *SegmentStartTime, s64 *MediaOffset, u8 *useEdit); +GF_Err GetMediaTime(GF_TrackBox *trak, Bool force_non_empty, u64 movieTime, u64 *MediaTime, s64 *SegmentStartTime, s64 *MediaOffset, u8 *useEdit); GF_Err Media_GetSample(GF_MediaBox *mdia, u32 sampleNumber, GF_ISOSample **samp, u32 *sampleDescriptionIndex, Bool no_data, u64 *out_offset); GF_Err Media_CheckDataEntry(GF_MediaBox *mdia, u32 dataEntryIndex); GF_Err Media_FindSyncSample(GF_SampleTableBox *stbl, u32 searchFromTime, u32 *sampleNumber, u8 mode); @@ -2099,6 +2171,8 @@ GF_Err stbl_GetSampleDTS_and_Duration(GF_TimeToSampleBox *stts, u32 SampleNumber /*find a RAP or set the prev / next RAPs if vars are passed*/ GF_Err stbl_GetSampleRAP(GF_SyncSampleBox *stss, u32 SampleNumber, u8 *IsRAP, u32 *prevRAP, u32 *nextRAP); +/*same as above but only look for open-gop RAPs and GDR (roll)*/ +GF_Err stbl_SearchSAPs(GF_SampleTableBox *stbl, u32 SampleNumber, u8 *IsRAP, u32 *prevRAP, u32 *nextRAP); GF_Err stbl_GetSampleInfos(GF_SampleTableBox *stbl, u32 sampleNumber, u64 *offset, u32 *chunkNumber, u32 *descIndex, u8 *isEdited); GF_Err stbl_GetSampleShadow(GF_ShadowSyncBox *stsh, u32 *sampleNumber, u32 *syncNum); GF_Err stbl_GetPaddingBits(GF_PaddingBitsBox *padb, u32 SampleNumber, u8 *PadBits); @@ -2106,6 +2180,7 @@ u32 stbl_GetSampleFragmentCount(GF_SampleFragmentBox *stsf, u32 sampleNumber); u32 stbl_GetSampleFragmentSize(GF_SampleFragmentBox *stsf, u32 sampleNumber, u32 FragmentIndex); GF_Err stbl_GetSampleDepType(GF_SampleDependencyTypeBox *stbl, u32 SampleNumber, u32 *dependsOn, u32 *dependedOn, u32 *redundant); + /*unpack sample2chunk and chunk offset so that we have 1 sample per chunk (edition mode only)*/ GF_Err stbl_UnpackOffsets(GF_SampleTableBox *stbl); GF_Err SetTrackDuration(GF_TrackBox *trak); @@ -2176,6 +2251,9 @@ GF_Err stbl_RemovePaddingBits(GF_SampleTableBox *stbl, u32 SampleNumber); GF_Err stbl_RemoveSampleFragments(GF_SampleTableBox *stbl, u32 sampleNumber); GF_Err stbl_RemoveRedundant(GF_SampleTableBox *stbl, u32 SampleNumber); +/*expands sampleGroup table for the given grouping type and sample_number. If sample_number is 0, just appends an entry at the end of the table*/ +GF_Err gf_isom_add_sample_group_entry(GF_List *sampleGroups, u32 sample_number, u32 grouping_type, u32 sampleGroupDescriptionIndex); + #ifndef GPAC_DISABLE_ISOM_FRAGMENTS GF_Err gf_isom_close_fragments(GF_ISOFile *movie); #endif @@ -3357,6 +3435,21 @@ GF_Err rvcc_Size(GF_Box *s); GF_Err rvcc_Read(GF_Box *s, GF_BitStream *bs); GF_Err rvcc_dump(GF_Box *a, FILE * trace); + +GF_Box *sbgp_New(); +void sbgp_del(GF_Box *); +GF_Err sbgp_Write(GF_Box *s, GF_BitStream *bs); +GF_Err sbgp_Size(GF_Box *s); +GF_Err sbgp_Read(GF_Box *s, GF_BitStream *bs); +GF_Err sbgp_dump(GF_Box *a, FILE * trace); + +GF_Box *sgpd_New(); +void sgpd_del(GF_Box *); +GF_Err sgpd_Write(GF_Box *s, GF_BitStream *bs); +GF_Err sgpd_Size(GF_Box *s); +GF_Err sgpd_Read(GF_Box *s, GF_BitStream *bs); +GF_Err sgpd_dump(GF_Box *a, FILE * trace); + #endif /*GPAC_DISABLE_ISOM*/ #ifdef __cplusplus diff --git a/include/gpac/internal/m3u8.h b/include/gpac/internal/m3u8.h index aaf37ab..d8709e4 100644 --- a/include/gpac/internal/m3u8.h +++ b/include/gpac/internal/m3u8.h @@ -73,6 +73,7 @@ typedef struct s_playList { int target_duration; int mediaSequenceMin; int mediaSequenceMax; + int computed_duration; char is_ended; GF_List * elements; } Playlist; @@ -84,6 +85,7 @@ typedef enum e_playlistElementType { TYPE_PLAYLIST, TYPE_STREAM, TYPE_UNKNOWN} */ typedef struct s_playlistElement { int durationInfo; + u64 byteRangeStart, byteRangeEnd; int bandwidth; char * title; char * codecs; @@ -99,6 +101,7 @@ typedef struct s_program { int programId; GF_List * bitrates; int currentBitrateIndex; + int computed_duration; } Program; @@ -143,7 +146,7 @@ GF_Err program_del(Program * program); * This element can be either a playlist of a stream according to first parameter. * @return NULL if element could not be created. Element deletion will be deleted recusivly by #playlist_del(Playlist*) */ -PlaylistElement * playlist_element_new(PlaylistElementType elementType, const char * url, const char * title, const char *codecs, int durationInfo); +PlaylistElement * playlist_element_new(PlaylistElementType elementType, const char * url, const char * title, const char *codecs, int durationInfo, u64 byteRangeStart, u64 byteRangeEnd); /** * Creates a new VariantPlaylist diff --git a/include/gpac/internal/mpd.h b/include/gpac/internal/mpd.h index d481434..df14691 100644 --- a/include/gpac/internal/mpd.h +++ b/include/gpac/internal/mpd.h @@ -22,99 +22,292 @@ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. * */ -#ifndef _MPD_IN_H_ -#define _MPD_IN_H_ +#ifndef _MPD_H_ +#define _MPD_H_ #include #include #include #include -typedef struct + +/*TODO*/ +typedef struct +{ + u32 dummy; +} GF_MPD_Metrics; + +/*TODO*/ +typedef struct +{ + u32 dummy; +} GF_MPD_ContentComponent; + +/*TODO*/ +typedef struct +{ + char *scheme_id_uri; /*MANDATORY*/ + char *value; +} GF_MPD_Descriptor; + +/*TODO*/ +typedef struct +{ + u32 dummy; +} GF_MPD_Subset; + +/*TODO*/ +typedef struct +{ + u32 start_time; + u32 duration; /*MANDATORY*/ + u32 repeat_count; +} GF_MPD_SegmentTimelineEntry; + +typedef struct +{ + GF_List *entries; +} GF_MPD_SegmentTimeline; + +typedef struct +{ + u64 start_range, end_range; +} GF_MPD_ByteRange; + + +typedef struct +{ + char *URL; + char *service_location; + GF_MPD_ByteRange *byte_range; +} GF_MPD_BaseURL; + +typedef struct +{ + char *sourceURL; + GF_MPD_ByteRange *byte_range; +} GF_MPD_URL; + +typedef struct +{ + u32 num, den; +} GF_MPD_Fractional; + + +#define GF_MPD_SEGMENT_BASE \ + u32 timescale; \ + u32 presentation_time_offset; \ + u32 index_range; \ + Bool index_range_exact; \ + GF_MPD_URL *initialization_segment; \ + GF_MPD_URL *representation_index; \ + + +typedef struct { - char *url; - Bool use_byterange; - u32 byterange_start; - u32 byterange_end; -} GF_MPD_SegmentInfo; + GF_MPD_SEGMENT_BASE +} GF_MPD_SegmentBase; + +/*WARNING: duration is expressed in GF_MPD_SEGMENT_BASE@timescale unit*/ +#define GF_MPD_MULTIPLE_SEGMENT_BASE \ + GF_MPD_SEGMENT_BASE \ + u64 duration; \ + u32 start_number; \ + GF_MPD_SegmentTimeline *segment_timeline; \ + GF_MPD_URL *bitstream_switching_url; \ + +typedef struct +{ + GF_MPD_MULTIPLE_SEGMENT_BASE +} GF_MPD_MultipleSegmentBase; + +typedef struct +{ + char *media; + GF_MPD_ByteRange *media_range; + char *index; + GF_MPD_ByteRange *index_range; +} GF_MPD_SegmentURL; + +typedef struct +{ + GF_MPD_MULTIPLE_SEGMENT_BASE + /*list of segments - can be NULL if no segment*/ + GF_List *segment_URLs; +} GF_MPD_SegmentList; + +typedef struct +{ + GF_MPD_MULTIPLE_SEGMENT_BASE + char *media; + char *index; + char *initialization; + char *bitstream_switching; +} GF_MPD_SegmentTemplate; + +typedef enum +{ + GF_MPD_SCANTYPE_UNKNWON, + GF_MPD_SCANTYPE_PROGRESSIVE, + GF_MPD_SCANTYPE_INTERLACED +} GF_MPD_ScanType; + +/*MANDATORY: + mime_type + codecs +*/ +#define GF_MPD_COMMON_ATTRIBUTES_ELEMENTS \ + char *profiles; \ + u32 width; \ + u32 height; \ + GF_MPD_Fractional *sar; \ + GF_MPD_Fractional *framerate; \ + u32 samplerate; \ + char *mime_type; \ + char *segmentProfiles; \ + char *codecs; \ + u32 maximum_sap_period; \ + Bool starts_with_sap; \ + Double max_playout_rate; \ + Bool coding_dependency; \ + GF_MPD_ScanType scan_type; \ + GF_List *frame_packing; \ + GF_List *audio_channels; \ + GF_List *content_protection; \ + typedef struct { - char *id; - u32 bandwidth; - u32 width; - u32 height; - char *lang; - char *mime; - u32 groupID; - Bool disabled; - Bool startWithRap; - /* TODO: maximumRAPPeriod */ - /* TODO: depid*/ - /* TODO: default rep*/ - - u32 qualityRanking; - char *content_protection_type; - char *content_protection_uri; - double alternatePlayoutRate; - u32 default_segment_duration; - /*TODO: multiple views */ - char *default_base_url; - - /* initialization segment */ - char *init_url; - Bool init_use_range; - u32 init_byterange_start; - u32 init_byterange_end; - - /* other segments */ - char *url_template; - u32 startIndex; - u32 endIndex; - - GF_List *segments; -} GF_MPD_Representation; + GF_MPD_COMMON_ATTRIBUTES_ELEMENTS +} GF_MPD_CommonAttributes; typedef struct { - u32 start; /* expressed in seconds, relative to the start of the MPD */ - u32 duration; /* TODO */ - char *id; /* TODO */ - u8 flags; - Bool segment_alignment_flag; /* to be merged into real flags */ - Bool bitstream_switching_flag; - - u32 default_segment_duration; /* milliseconds */ - char *default_base_url; - /* TODO: default timeline */ - char *url_template; - - /* TODO: xlink:href & xlink:actuate */ - - GF_List *representations; - /* TODO: representation groups */ - /* TODO: subset */ -} GF_MPD_Period; + GF_MPD_COMMON_ATTRIBUTES_ELEMENTS + + u32 level; + char *dependecy_level; + u32 bandwidth; /*MANDATORY if level set*/ + char *content_components; +} GF_MPD_SubRepresentation; -typedef enum { - GF_MPD_TYPE_ON_DEMAND, - GF_MPD_TYPE_LIVE, -} GF_MPD_Type; typedef struct { - GF_MPD_Type type; - char *base_url; - /* TODO: add alternate URL */ - u32 duration; /* expressed in milliseconds */ - u32 min_update_time; /* expressed in milliseconds */ - u32 min_buffer_time; /* expressed in milliseconds */ - /*start time*/ - /*end time*/ - u32 time_shift_buffer_depth; /* expressed in milliseconds */ + GF_MPD_COMMON_ATTRIBUTES_ELEMENTS + + char *id; /*MANDATORY*/ + u32 bandwidth; /*MANDATORY*/ + u32 quality_ranking; + char *dependency_id; + char *media_stream_structure_id; + + GF_List *base_URLs; + GF_MPD_SegmentBase *segment_base; + GF_MPD_SegmentList *segment_list; + GF_MPD_SegmentTemplate *segment_template; + + GF_List *sub_representations; + + /*GPAC playback implementation*/ + Bool disabled; + +} GF_MPD_Representation; + + +typedef struct +{ + GF_MPD_COMMON_ATTRIBUTES_ELEMENTS + + u32 id; + /*default value is -1: not set in MPD*/ + s32 group; + + char *lang; + char *content_type; + GF_MPD_Fractional *par; + u32 min_bandwidth; + u32 max_bandwidth; + u32 min_width; + u32 max_width; + u32 min_height; + u32 max_height; + u32 min_framerate; + u32 max_framerate; + Bool segment_alignment; + Bool bitstream_switching; + Bool subsegment_alignment; + Bool subsegment_starts_with_sap; + + GF_List *accessibility; + GF_List *role; + GF_List *rating; + GF_List *viewpoint; + GF_List *content_component; + + GF_List *base_URLs; + GF_MPD_SegmentBase *segment_base; + GF_MPD_SegmentList *segment_list; + GF_MPD_SegmentTemplate *segment_template; + + GF_List *representations; + +} GF_MPD_AdaptationSet; + + +typedef struct +{ + char *ID; + u32 start; /* expressed in ms, relative to the start of the MPD */ + u32 duration; /* expressed in ms*/ + Bool bitstream_switching; + + GF_List *base_URLs; + GF_MPD_SegmentBase *segment_base; + GF_MPD_SegmentList *segment_list; + GF_MPD_SegmentTemplate *segment_template; + + GF_List *adaptation_sets; + GF_List *subsets; +} GF_MPD_Period; + +typedef struct +{ + char *lang; char *title; char *source; char *copyright; char *more_info_url; +} GF_MPD_ProgramInfo; + + +typedef enum { + GF_MPD_TYPE_STATIC, + GF_MPD_TYPE_DYNAMIC, +} GF_MPD_Type; + +typedef struct { + char *ID; + char *profiles; /*MANDATORY*/ + GF_MPD_Type type; + u64 availabilityStartTime; /*MANDATORY if type=dynamic*/ + u64 availabilityEndTime; + u32 media_presentation_duration; /* expressed in milliseconds */ /*MANDATORY if type=static*/ + u32 minimum_update_period; /* expressed in milliseconds */ + u32 min_buffer_time; /* expressed in milliseconds */ /*MANDATORY*/ + + u32 time_shift_buffer_depth; /* expressed in milliseconds */ + u32 suggested_presentaton_delay; /* expressed in milliseconds */ + + u32 max_segment_duration; /* expressed in milliseconds */ + u32 max_subsegment_duration; /* expressed in milliseconds */ - /* the number of periods is dynamic since we may update the MPD from time to time, cannot avoid GF_List */ + /*list of GF_MPD_ProgramInfo */ + GF_List *program_infos; + /*list of GF_MPD_BaseURL */ + GF_List *base_URLs; + /*list of strings */ + GF_List *locations; + /*list of Metrics */ + GF_List *metrics; + /*list of GF_MPD_Period */ GF_List *periods; } GF_MPD; @@ -122,10 +315,10 @@ GF_Err gf_mpd_init_from_dom(GF_XMLNode *root, GF_MPD *mpd, const char *base_url) GF_MPD *gf_mpd_new(); void gf_mpd_del(GF_MPD *mpd); +/*frees a SegmentURL*/ +void gf_mpd_segment_url_free(void *_item); -GF_Err gf_m3u8_to_mpd(GF_ClientService *service, const char *m3u8_file, const char *base_url, - const char *mpd_file, - u32 reload_count, char *mimeTypeForM3U8Segments); +GF_Err gf_m3u8_to_mpd(const char *m3u8_file, const char *base_url, const char *mpd_file, u32 reload_count, char *mimeTypeForM3U8Segments, GF_ClientService *service, Bool do_import, Bool use_mpd_templates); -#endif // _MPD_IN_H_ +#endif // _MPD_H_ diff --git a/include/gpac/internal/scenegraph_dev.h b/include/gpac/internal/scenegraph_dev.h index 8df8523..d371882 100644 --- a/include/gpac/internal/scenegraph_dev.h +++ b/include/gpac/internal/scenegraph_dev.h @@ -261,7 +261,7 @@ struct __tag_scene_graph u32 nb_evts_smil; u32 nb_evts_mutation; u32 nb_evts_laser; - u32 nb_evts_mae; + u32 nb_evts_media; u32 nb_evts_svg; u32 dom_evt_filter; diff --git a/include/gpac/internal/terminal_dev.h b/include/gpac/internal/terminal_dev.h index 6cd6469..84d830f 100644 --- a/include/gpac/internal/terminal_dev.h +++ b/include/gpac/internal/terminal_dev.h @@ -74,6 +74,17 @@ struct _net_service /*quick hack for avoiding double session creation*/ GF_DownloadSession *pending_service_session; + /*rebuffer window for http/...*/ + u32 download_rebuffer; + Bool auto_rebuffer; + + Bool is_paused; + + /*used by DASH until we rewrite the input module API: + if set to 1 during a disconnect() call, the root scene of the service and all sub-objects will be disconnected + if set to 2 during a disconnect() call, the call will be skiped + */ + u32 subservice_disconnect; }; @@ -356,10 +367,10 @@ struct _tag_terminal /*net services*/ GF_List *net_services; - /*net services to be connected*/ - GF_List *net_services_to_connect; /*net services to be destroyed*/ GF_List *net_services_to_remove; + /*connection tasks pending*/ + GF_List *connection_tasks; /*channels waiting for service CONNECT ack to be setup*/ GF_List *channels_pending; /*media objects pending for stop/play*/ @@ -397,6 +408,9 @@ struct _tag_terminal GF_Mutex *evt_mx; u32 in_event_filter; + /*ID of the thread currently in handle_services routine, 0 if none*/ + u32 thread_id_handling_services; + /*static URI relocator for locales*/ GF_TermLocales locales; @@ -422,8 +436,8 @@ Bool gf_term_forward_event(GF_Terminal *term, GF_Event *evt, Bool consumed, Bool /*error report function*/ void gf_term_message(GF_Terminal *app, const char *service, const char *message, GF_Err error); -/*creates service for given OD / URL*/ -void gf_term_connect_object(GF_Terminal *app, GF_ObjectManager *odm, char *serviceURL, char *parent_url); +/*posts a request to connect a given object*/ +void gf_term_post_connect_object(GF_Terminal *term, GF_ObjectManager *odm, char *serviceURL, char *parent_url); /*creates service for given channel / URL*/ GF_Err gf_term_connect_remote_channel(GF_Terminal *app, GF_Channel *ch, char *URL); @@ -438,10 +452,11 @@ void gf_term_close_service(GF_Terminal *app, GF_ClientService *service); /*locks media quaue*/ void gf_term_lock_media_queue(GF_Terminal *app, Bool LockIt); - /*locks net manager*/ void gf_term_lock_net(GF_Terminal *app, Bool LockIt); +/*checks no connection on the ODM are pending - of so, remove them*/ +void gf_term_check_connections_for_delete(GF_Terminal *term, GF_ObjectManager *odm); /*locks scene compositor*/ void gf_term_lock_compositor(GF_Terminal *app, Bool LockIt); @@ -615,6 +630,9 @@ struct _es_channel /* used in Carousel, to skip packets until the end of AU */ u8 carousel_type; Bool skip_carousel_au; + + /*discard clock initialization when dispatching pending AU - this is used when TS discontinuities / MPA_TIME happen*/ + Bool skip_time_check_for_pending; /* TimeStamp to Media Time mapping*/ /*TS (in TSResolution) corresponding to the SeedTime of the decoder. Delivered by net, otherwise 0*/ @@ -692,6 +710,8 @@ void gf_es_init_dummy(GF_Channel *ch); void gf_es_config_drm(GF_Channel *ch, GF_NetComDRMConfig *isma_cryp); /*dispatch raw media AU to the composition buffer and BLOCKS until the AU is consumed by the decoder*/ void gf_es_dispatch_raw_media_au(GF_Channel *ch, char *payload, u32 payload_size, u32 cts); +/*returns true if this stream owns its clock, false if it simply refers to it*/ +Bool gf_es_owns_clock(GF_Channel *ch); /* decoder stuff @@ -819,6 +839,9 @@ enum /*flag set if associated subscene must be regenerated*/ GF_ODM_REGENERATE_SCENE = (1<<10), + + /*flag set for first play request*/ + GF_ODM_INITIAL_BROADCAST_PLAY = (1<<11), }; enum @@ -1017,6 +1040,7 @@ Bool gf_term_send_event(GF_Terminal *term, GF_Event *evt); /*media access events */ void gf_term_service_media_event(GF_ObjectManager *odm, u32 event_type); +void gf_term_service_media_event_with_download(GF_ObjectManager *odm, u32 event_type, u64 loaded_size, u64 total_size, u32 bytes_per_sec); /*checks the URL and returns the ODID (MPEG-4 od://) or GF_MEDIA_EXTERNAL_ID for all regular URLs*/ u32 gf_mo_get_od_id(MFURL *url); diff --git a/include/gpac/isomedia.h b/include/gpac/isomedia.h index 03bf281..c64d5d7 100644 --- a/include/gpac/isomedia.h +++ b/include/gpac/isomedia.h @@ -327,6 +327,8 @@ Bool gf_isom_is_JPEG2000(GF_ISOFile *mov); u64 gf_isom_get_file_size(GF_ISOFile *the_file); +Bool gf_isom_moov_first(GF_ISOFile *movie); + /******************************************************************** STREAMING API FUNCTIONS ********************************************************************/ @@ -336,11 +338,14 @@ to use for http streaming & co NOTE: you must buffer the data to a local file, this mode DOES NOT handle http/ftp/... streaming +start_range and end_range restricts the media byte range in the URL (used by DASH) +if 0 or end_range<=start_range, the entire URL is used when parsing + BytesMissing is the predicted number of bytes missing for the file to be loaded Note that if the file is not optimized for streaming, this number is not accurate If the movie is successfully loaded (the_file non-NULL), BytesMissing is zero */ -GF_Err gf_isom_open_progressive(const char *fileName, GF_ISOFile **the_file, u64 *BytesMissing); +GF_Err gf_isom_open_progressive(const char *fileName, u64 start_range, u64 end_range, GF_ISOFile **the_file, u64 *BytesMissing); /*If requesting a sample fails with error GF_ISOM_INCOMPLETE_FILE, use this function to get the number of bytes missing to retrieve the sample*/ @@ -431,8 +436,8 @@ u64 gf_isom_get_media_duration(GF_ISOFile *the_file, u32 trackNumber); /*Get the timeScale of the media. */ u32 gf_isom_get_media_timescale(GF_ISOFile *the_file, u32 trackNumber); -/*return the maximum chunk duration of the track in milliseconds*/ -u32 gf_isom_get_max_chunk_duration(GF_ISOFile *the_file, u32 trackNumber); +/*gets min, average and max maximum chunk durations (each of them s optional) of the track in media timescale*/ +GF_Err gf_isom_get_chunks_infos(GF_ISOFile *movie, u32 trackNumber, u32 *dur_min, u32 *dur_avg, u32 *dur_max, u32 *size_min, u32 *size_avg, u32 *size_max); /*Get the HandlerDescription name. The outName must be: (outName != NULL && *outName == NULL) @@ -1054,6 +1059,9 @@ special shortcut for stream description cloning from a given input file (this av */ GF_Err gf_isom_clone_sample_description(GF_ISOFile *the_file, u32 trackNumber, GF_ISOFile *orig_file, u32 orig_track, u32 orig_desc_index, char *URLname, char *URNname, u32 *outDescriptionIndex); +/*clones all sampleDescription entries in new track, after an optional reset of existing entries*/ +GF_Err gf_isom_clone_sample_descriptions(GF_ISOFile *the_file, u32 trackNumber, GF_ISOFile *orig_file, u32 orig_track, Bool reset_existing); + /*special shortcut: clones a track (everything except media data and sample info (DTS? CTS, RAPs, etc...) also clones sampleDescriptions @keep_data_ref: if set, external data references are kept, otherwise they are removed (track media data will be self-contained) @@ -1071,7 +1079,7 @@ GF_Err gf_isom_clone_movie(GF_ISOFile *orig_file, GF_ISOFile *dest_file, Bool cl /*returns true if same set of sample description in both tracks - this does include self-contained checking and reserved flags. The specific media cfg (DSI & co) is not analysed, only a brutal memory comparaison is done*/ -Bool gf_isom_is_same_sample_description(GF_ISOFile *f1, u32 tk1, GF_ISOFile *f2, u32 tk2); +Bool gf_isom_is_same_sample_description(GF_ISOFile *f1, u32 tk1, u32 sdesc_index1, GF_ISOFile *f2, u32 tk2, u32 sdesc_index2); GF_Err gf_isom_set_JPEG2000(GF_ISOFile *mov, Bool set_on); @@ -1079,8 +1087,9 @@ GF_Err gf_isom_set_JPEG2000(GF_ISOFile *mov, Bool set_on); If reset_tables is set, sample information for all tracks setup as segment are destroyed. This allows keeping the memory footprint low when playing segments. Note however that seeking in the file is then no longer possible*/ GF_Err gf_isom_release_segment(GF_ISOFile *movie, Bool reset_tables); -/*opens a new segment file. Access to samples in previous segments is no longer possible*/ -GF_Err gf_isom_open_segment(GF_ISOFile *movie, const char *fileName); +/*opens a new segment file. Access to samples in previous segments is no longer possible +if end_range>start_range, restricts the URL to the given byterange when parsing*/ +GF_Err gf_isom_open_segment(GF_ISOFile *movie, const char *fileName, u64 start_range, u64 end_range); #ifndef GPAC_DISBALE_ISOM_FRAGMENTS @@ -1133,8 +1142,19 @@ GF_Err gf_isom_setup_track_fragment(GF_ISOFile *the_file, u32 TrackID, u8 DefaultSamplePadding, u16 DefaultDegradationPriority); -/*flushes data to disk and prepare movie fragmentation*/ -GF_Err gf_isom_finalize_for_fragment(GF_ISOFile *the_file, Bool use_segments); +/*change the default parameters of an existing trak fragment - should not be used if samples have +already been added - semantics are the same as in gf_isom_setup_track_fragment*/ +GF_Err gf_isom_change_track_fragment_defaults(GF_ISOFile *movie, u32 TrackID, + u32 DefaultSampleDescriptionIndex, + u32 DefaultSampleDuration, + u32 DefaultSampleSize, + u8 DefaultSampleIsSync, + u8 DefaultSamplePadding, + u16 DefaultDegradationPriority); + +/*flushes data to disk and prepare movie fragmentation +@media_segment_type: 0 if no segments, 1 if regular segment, 2 if single segment*/ +GF_Err gf_isom_finalize_for_fragment(GF_ISOFile *the_file, u32 media_segment_type); /*starts a new movie fragment - if force_cache is set, fragment metadata will be written before fragment media data for all tracks*/ @@ -1147,7 +1167,14 @@ GF_Err gf_isom_start_segment(GF_ISOFile *movie, char *SegName); GF_Err gf_isom_set_traf_base_media_decode_time(GF_ISOFile *movie, u32 TrackID, u64 decode_time); /*closes current segment - if fragments_per_sidx is <0, no sidx is used - if fragments_per_sidx is ==0, a single sidx is used*/ -GF_Err gf_isom_close_segment(GF_ISOFile *movie, s32 fragments_per_sidx, u32 referenceTrackID, u64 ref_track_decode_time, Bool daisy_chain_sidx, Bool last_segment); +GF_Err gf_isom_close_segment(GF_ISOFile *movie, s32 subsegs_per_sidx, u32 referenceTrackID, u64 ref_track_decode_time, u64 ref_track_next_cts, Bool daisy_chain_sidx, Bool last_segment, u64 *index_start_range, u64 *index_end_range); + +/*writes an empty sidx in the current movie. The SIDX will be forced to have nb_segs entries - nb_segs shall match the number of calls to +gf_isom_close_segment that will follow. This avoids wasting time and disk space moving data around. Once gf_isom_close_segment has then been called nb_segs times, +the pre-allocated SIDX is destroyed and sucessive calls to gf_isom_close_segment will create their own sidx (unless gf_isom_allocate_sidx is called again). +frags_per_sidx, daisy_chain_sidx and frags_per_segment are currently ignored and reserved for future usages where multiple SIDX could be written +if not NULL, start_range and end_range will contain the byte range of the SIDX box in the movie*/ +GF_Err gf_isom_allocate_sidx(GF_ISOFile *movie, s32 subsegs_per_sidx, Bool daisy_chain_sidx, u32 nb_segs, u32 *frags_per_segment, u32 *start_range, u32 *end_range); enum { @@ -1194,6 +1221,8 @@ GF_Err gf_isom_fragment_add_sample(GF_ISOFile *the_file, u32 TrackID, GF_ISOSamp CANNOT be used with OD tracks*/ GF_Err gf_isom_fragment_append_data(GF_ISOFile *the_file, u32 TrackID, char *data, u32 data_size, u8 PaddingBits); +void gf_isom_reset_fragment_info(GF_ISOFile *movie); + #endif /*GPAC_DISBALE_ISOM_FRAGMENTS*/ @@ -1965,7 +1994,8 @@ GF_Err gf_isom_add_subsample(GF_ISOFile *movie, u32 track, u32 sampleNumber, u32 #endif /*add subsample information for the latest sample added to the current track fragment*/ GF_Err gf_isom_fragment_add_subsample(GF_ISOFile *movie, u32 TrackID, u32 subSampleSize, u8 priority, u32 reserved, Bool discardable); -/*copy over the subsample information of the given sample from the source track/file to the last sample added to the current track fragment of the destination file*/ + +/*copy over the subsample and sampleToGroup information of the given sample from the source track/file to the last sample added to the current track fragment of the destination file*/ GF_Err gf_isom_fragment_copy_subsample(GF_ISOFile *dest, u32 TrackID, GF_ISOFile *orig, u32 track, u32 sampleNumber); /*gets the number of the next moof to be produced*/ @@ -1973,6 +2003,21 @@ u32 gf_isom_get_next_moof_number(GF_ISOFile *movie); /*Sets the number of the next moof to be produced*/ void gf_isom_set_next_moof_number(GF_ISOFile *movie, u32 value); + +/*returns 'rap ' and 'roll' group info for the given sample*/ +GF_Err gf_isom_get_sample_rap_roll_info(GF_ISOFile *the_file, u32 trackNumber, u32 sample_number, Bool *is_rap, Bool *has_roll, s32 *roll_distance); + +/*sample groups information*/ +#ifndef GPAC_DISABLE_ISOM_WRITE +/*sets rap flag for sample_number - this is used by non-IDR RAPs in AVC (also in USAC) were SYNC flag (stss table) cannot be used +num_leading_sample is the number of samples to after this RAP that have dependences on samples before this RAP and hence should be discarded +- currently sample group info MUST be added in order (no insertion in the tables)*/ +GF_Err gf_isom_set_sample_rap_group(GF_ISOFile *movie, u32 track, u32 sample_number, u32 num_leading_samples); +/*sets roll_distance info for sample_number (number of frames before (<0) or after (>0) this sample to have a complete refresh of the decoded data (used by GDR in AVC) +- currently sample group info MUST be added in order (no insertion in the tables)*/ +GF_Err gf_isom_set_sample_roll_group(GF_ISOFile *movie, u32 track, u32 sample_number, s16 roll_distance); +#endif + #endif /*GPAC_DISABLE_ISOM*/ #ifdef __cplusplus diff --git a/include/gpac/list.h b/include/gpac/list.h index 1b9a8e4..9187fdc 100644 --- a/include/gpac/list.h +++ b/include/gpac/list.h @@ -157,6 +157,15 @@ GF_Err gf_list_rem_last(GF_List *ptr); */ void *gf_list_enum(GF_List *ptr, u32 *pos); +/*! + * \brief list swap + * + * Swaps content of two lists + * \param l1 first list object + * \param l2 second list object + */ +GF_Err gf_list_swap(GF_List *l1, GF_List *l2); + /*! @} */ #ifdef __cplusplus diff --git a/include/gpac/math.h b/include/gpac/math.h index 6316945..3c61046 100644 --- a/include/gpac/math.h +++ b/include/gpac/math.h @@ -250,6 +250,13 @@ typedef struct __vec2f *\return length of the vector */ Fixed gf_v2d_len(GF_Point2D *vec); +/*! + *\brief get distance between 2 points + * + *Gets the distance between the 2 points + *\return distance + */ +Fixed gf_v2d_distance(GF_Point2D *a, GF_Point2D *b); /*! *\brief 2D vector from polar coordinates * diff --git a/include/gpac/media_tools.h b/include/gpac/media_tools.h index 1f6bb73..8c4a3f3 100644 --- a/include/gpac/media_tools.h +++ b/include/gpac/media_tools.h @@ -41,6 +41,8 @@ GF_ESD *gf_media_map_esd(GF_ISOFile *mp4, u32 track); #endif +#define GF_IMPORT_DEFAULT_FPS 25.0 + #ifndef GPAC_DISABLE_MEDIA_IMPORT /* @@ -94,6 +96,9 @@ enum /* set subsample information with SVC*/ GF_IMPORT_SET_SUBSAMPLES = 1<<15, + /* force to mark non-IDR frmaes with sync data (I slices,) to be marked as sync points points + THE RESULTING FILE IS NOT COMPLIANT*/ + GF_IMPORT_FORCE_SYNC = 1<<16, /*when set, only updates tracks info and return*/ GF_IMPORT_PROBE_ONLY = 1<<20, @@ -109,7 +114,8 @@ enum GF_IMPORT_DO_ABORT = 1<<31 }; -#define GF_IMPORT_DEFAULT_FPS 25.0 + + #define GF_IMPORT_AUTO_FPS 10000.0 #define GF_IMPORT_MAX_TRACKS 100 @@ -136,6 +142,8 @@ struct __track_import_info struct __track_video_info video_info; struct __track_audio_info audio_info; + char szCodecProfile[20]; + u32 lang; /*for MPEG4 on MPEG-2 TS: mpeg4 ES-ID*/ u32 mpeg4_es_id; @@ -219,7 +227,7 @@ GF_Err gf_media_import_chapters(GF_ISOFile *file, char *chap_file, Double import /*save file as fragmented movie @dash_mode: 0 = DASH not used, 1 = DASH used without GOP spliting, 2 = DASH used with GOP spliting, */ -GF_Err gf_media_fragment_file(GF_ISOFile *input, char *output_file, Double max_duration_sec, u32 dash_mode, Double dash_duration_sec, char *seg_rad_name, char *seg_ext, s32 fragments_per_sidx, Bool daisy_chain_sidx, Bool use_url_template, const char *dash_ctx); +GF_Err gf_media_fragment_file(GF_ISOFile *input, const char *output_file_radical, const char *mpd_name, Double max_duration_sec, u32 dash_mode, Double dash_duration_sec, char *seg_rad_name, char *seg_ext, s32 subsegs_per_sidx, Bool daisy_chain_sidx, Bool use_url_template, Bool use_single_segment, const char *dash_ctx, GF_ISOFile *sample_descs, u32 rep_id); /*make the file ISMA compliant: creates ISMA BIFS / OD tracks if needed, and update audio/video IDs the file should not contain more than one audio and one video track @@ -383,6 +391,9 @@ if force_end_of_session is set, this flushes the SAF Session - no more operation GF_Err gf_saf_mux_for_time(GF_SAFMuxer *mux, u32 time_ms, Bool force_end_of_session, char **out_data, u32 *out_size); +GF_Err gf_media_mpd_start(char *mpd_name, char *title, Bool use_url_template, Bool single_segment, char *dash_ctx, char *init_segment, Double period_duration); +GF_Err gf_media_mpd_end(char *mpd_name); + #ifdef __cplusplus } #endif diff --git a/include/gpac/mediaobject.h b/include/gpac/mediaobject.h index 897edf5..fd330eb 100644 --- a/include/gpac/mediaobject.h +++ b/include/gpac/mediaobject.h @@ -119,8 +119,9 @@ Bool gf_mo_get_audio_info(GF_MediaObject *mo, u32 *sample_rate, u32 *bits_per_sa Fixed gf_mo_get_current_speed(GF_MediaObject *mo); -/*checks if the service associated withthis object has an audio stream*/ -Bool gf_mo_has_audio(GF_MediaObject *mo); +/*checks if the service associated withthis object has an audio stream +returns 0 if no audio is associated, 1 if there is an audio object associated, 2 if the service is not yet ready (not connected)*/ +u32 gf_mo_has_audio(GF_MediaObject *mo); /*checks if the service associated withthis object has an audio stream*/ Bool gf_mo_is_private_media(GF_MediaObject *mo); diff --git a/include/gpac/modules/service.h b/include/gpac/modules/service.h index fb74d09..bff5805 100644 --- a/include/gpac/modules/service.h +++ b/include/gpac/modules/service.h @@ -100,6 +100,9 @@ typedef enum /*When using DASH or playlists, query the next file to concatenate to thecurrent one net->proxy only*/ GF_NET_SERVICE_QUERY_NEXT, + /*When using DASH, query the media range of the url passed in COnnectService - this is only used for local + playback/validation of DASH sequences*/ + GF_NET_SERVICE_QUERY_INIT_RANGE, } GF_NET_CHAN_CMD; /*channel command for all commands that don't need params: @@ -123,6 +126,9 @@ typedef struct Double start_range, end_range; /*params for GF_NET_CHAN_PLAY and GF_NET_CHAN_SPEED*/ Double speed; + Bool dash_segment_switch; + /*indicates theis is the first PLAY on an elemnt inserted from bcast*/ + Bool initial_broadcast_play; } GF_NetComPlay; @@ -307,6 +313,15 @@ typedef struct u32 command_type; /*out: next url to play after current one*/ const char *next_url; + /*out: range in given URL to be played - usually 0-0 as segments are downloaded to cache + but can be non-zero when playing local files*/ + u64 start_range, end_range; + /*indicates discontinuity type at segment switch: + 0: no discontinuity (eg, follow-up of previous segment + 1: segment switch discontinuity (eg, bitrate/codec change) + 2: time discontinuity - seeking has occured and some segments were skipped + */ + u32 discontinuity_type; } GF_NetURLQuery; /*GF_NET_SERVICE_QUALITY_SWITCH*/ diff --git a/include/gpac/mpeg4_odf.h b/include/gpac/mpeg4_odf.h index 787a542..9bc29fa 100644 --- a/include/gpac/mpeg4_odf.h +++ b/include/gpac/mpeg4_odf.h @@ -498,7 +498,7 @@ typedef struct { /*input stream format (not required, guessed from file_name)*/ char *streamFormat; /*time offset in ms from first TS (appends an edit list in mp4)*/ - u32 startTime; + s32 startTime; /*media length to import in ms (from 0)*/ u32 duration; diff --git a/include/gpac/mpegts.h b/include/gpac/mpegts.h index f119b12..542d563 100644 --- a/include/gpac/mpegts.h +++ b/include/gpac/mpegts.h @@ -190,6 +190,8 @@ enum GF_M2TS_EVT_CAT_UPDATE, /*AIT has been found (carousel) */ GF_M2TS_EVT_AIT_FOUND, + /*DSCM-CC has been found (carousel) */ + GF_M2TS_EVT_DSMCC_FOUND, }; @@ -256,6 +258,9 @@ typedef struct GF_M2TS_SectionFilter not parsed by the TS demuxer and left for the application */ Bool direct_dispatch; + /* this field is used for AIT sections, to link the AIT with the program */ + u32 service_id; + gf_m2ts_section_callback process_section; } GF_M2TS_SectionFilter; @@ -313,6 +318,9 @@ enum GF_M2TS_ES_SEND_REPEATED_SECTIONS = 1<<16, /*Flag used by importers*/ GF_M2TS_ES_FIRST_DTS = 1<<17, + + /*flag used to signal next discontinuity on stream should be ignored*/ + GF_M2TS_ES_IGNORE_NEXT_DISCONTINUITY = 1<<18 }; /*Abstract Section/PES stream object, only used for type casting*/ @@ -325,11 +333,12 @@ enum GF_SLConfig *slcfg; \ s16 component_tag; \ void *user; \ - u64 first_dts; + u64 first_dts; \ + u32 service_id; struct tag_m2ts_es { - ABSTRACT_ES + ABSTRACT_ES }; @@ -375,7 +384,7 @@ typedef struct tag_m2ts_pes u32 lang; /*object info*/ - u32 vid_w, vid_h, vid_par, aud_sr, aud_nb_ch; + u32 vid_w, vid_h, vid_par, aud_sr, aud_nb_ch, aud_obj_type; /*user private*/ @@ -406,12 +415,15 @@ typedef struct tag_m2ts_pes /*PES reframer - if NULL, pes processing is skiped*/ - u32 frame_state; /*returns the number of bytes NOT consummed from the input data buffer - these bytes are kept when reassembling the next PES packet*/ - u32 (*reframe)(struct tag_m2ts_demux *ts, struct tag_m2ts_pes *pes, u64 DTS, u64 CTS, unsigned char *data, u32 data_len); + u32 (*reframe)(struct tag_m2ts_demux *ts, struct tag_m2ts_pes *pes, Bool same_pts, unsigned char *data, u32 data_len); + + /*used by several reframers to store their parsing state*/ + u32 frame_state; /*LATM stuff - should be moved out of mpegts*/ unsigned char *buf; u32 buf_len; + u64 prev_PTS; GF_M2TS_DVB_Subtitling_Descriptor sub; } GF_M2TS_PES; @@ -570,6 +582,7 @@ struct tag_m2ts_demux char filename[GF_MAX_PATH]; u32 start_range, end_range; u64 file_size; + u64 start_byterange, end_byterange; Double duration; u32 nb_playing; Bool file_regulate; @@ -589,6 +602,7 @@ struct tag_m2ts_demux GF_List *programs; u32 nb_prog_pmt_received; Bool all_prog_pmt_received; + Bool all_prog_processed; /*keep it seperate for now - TODO check if we're sure of the order*/ GF_List *SDTs; GF_M2TS_TDT_TOT *TDT_time; /*UTC time from both TDT and TOT (we currently ignore local offset)*/ @@ -624,8 +638,18 @@ struct tag_m2ts_demux const char *dvb_channels_conf_path; - const char *(*query_next)(void *udta); - void *udta_query; + /*for DASH*/ + GF_Err (*query_next)(void *udta, Bool query_init_range, const char **next_url, u64 *next_start_range, u64 *next_end_range); + void *query_udta; + Bool segment_switch; + + /*AIT*/ + GF_List* ChannelAppList; + + /*Carousel*/ + Bool process_dmscc; + char* dsmcc_root_dir; + GF_List* dsmcc_controler; }; GF_M2TS_Demuxer *gf_m2ts_demux_new(); @@ -635,6 +659,7 @@ GF_ESD *gf_m2ts_get_esd(GF_M2TS_ES *es); GF_Err gf_m2ts_set_pes_framing(GF_M2TS_PES *pes, u32 mode); GF_Err gf_m2ts_process_data(GF_M2TS_Demuxer *ts, char *data, u32 data_size); u32 gf_dvb_get_freq_from_url(const char *channels_config_path, const char *url); +void gf_m2ts_demux_dmscc_init(GF_M2TS_Demuxer *ts); @@ -712,8 +737,7 @@ enum GF_M2TS_DVB_TIME_SLICE_FEC_DESCRIPTOR = 0x77, /* ... */ GF_M2TS_DVB_EAC3_DESCRIPTOR = 0x7A, - GF_M2TS_DVB_LOGICAL_CHANNEL_DESCRIPTOR = 0x83, - + GF_M2TS_DVB_LOGICAL_CHANNEL_DESCRIPTOR = 0x83, }; /* Reserved PID values */ @@ -748,7 +772,11 @@ enum { GF_M2TS_TABLE_ID_IPMP_CONTROL = 0x07, /* 0x08 - 0x37 reserved */ /* 0x38 - 0x3D DSM-CC defined */ - GF_M2TS_TABLE_ID_DSM_CC_PRIVATE = 0x3E, /* used for MPE (only, not MPE-FEC) */ + GF_M2TS_TABLE_ID_DSM_CC_ENCAPSULATED_DATA = 0x3A, + GF_M2TS_TABLE_ID_DSM_CC_UN_MESSAGE = 0x3B, /* used for MPE (only, not MPE-FEC) */ + GF_M2TS_TABLE_ID_DSM_CC_DOWNLOAD_DATA_MESSAGE = 0x3C, /* used for MPE (only, not MPE-FEC) */ + GF_M2TS_TABLE_ID_DSM_CC_STREAM_DESCRIPTION = 0x3D, /* used for MPE (only, not MPE-FEC) */ + GF_M2TS_TABLE_ID_DSM_CC_PRIVATE = 0x3E, /* used for MPE (only, not MPE-FEC) */ /* 0x3F DSM-CC defined */ GF_M2TS_TABLE_ID_NIT_ACTUAL = 0x40, /* max size for section 1024 */ GF_M2TS_TABLE_ID_NIT_OTHER = 0x41, @@ -912,6 +940,9 @@ typedef struct __m2ts_mux_stream { Bool table_needs_update; Bool table_needs_send; + /*minimal amount of bytes we are allowed to copy frome next AU in the current PES. If no more than this + is available in PES, don't copy from next*/ + u32 min_bytes_copy_from_next; /*process PES or table update/framing returns the priority of the stream, 0 meaning not scheduled, 1->N highest priority sent first*/ u32 (*process)(struct __m2ts_mux *muxer, struct __m2ts_mux_stream *stream); @@ -934,6 +965,8 @@ typedef struct __m2ts_mux_stream { u32 reframe_overhead; + Bool start_pes_at_rap, prevent_two_au_start_in_pes; + struct __elementary_stream_ifce *ifce; Double ts_scale; @@ -995,6 +1028,13 @@ struct __m2ts_mux_program { GF_List *loop_descriptors; Bool mpeg4_signaling; Bool mpeg4_signaling_for_scene_only; + + /* + 1: signals to force pat/pmt after current PES + 2: forces pat to be sent + 3: forces pmt to be sent after PAT + */ + u32 force_pat_pmt_state; }; struct __m2ts_mux { @@ -1031,11 +1071,13 @@ struct __m2ts_mux { /* System time when the muxer is started */ u32 init_sys_time; + Bool force_pat; + Bool one_au_per_pes; Bool eos_found; u32 pck_sent_over_br_window, last_br_time, avg_br; - u64 tot_pck_sent, tot_pad_sent; + u64 tot_pck_sent, tot_pad_sent, tot_pes_pad_bytes; }; @@ -1075,8 +1117,8 @@ GF_Err gf_m2ts_program_stream_update_ts_scale(GF_ESInterface *_self, u32 time_sc #endif /*GPAC_DISABLE_MPEG2TS_MUX*/ /******************* Demux DVB ****************************/ -#include - +#include +#include #define UDP_BUFFER_SIZE 0x40000 #define M2TS_BUFFER_MAX 400 diff --git a/include/gpac/nodes_mpeg4.h b/include/gpac/nodes_mpeg4.h index 04640a8..7652b0f 100644 --- a/include/gpac/nodes_mpeg4.h +++ b/include/gpac/nodes_mpeg4.h @@ -24,7 +24,7 @@ /* - DO NOT MOFIFY - File generated on GMT Wed Jul 20 05:50:21 2011 + DO NOT MOFIFY - File generated on GMT Tue Nov 08 09:10:57 2011 BY MPEG4Gen for GPAC Version 0.4.6-DEV */ @@ -2115,6 +2115,8 @@ typedef struct _tagBitWrapper SFInt32 type; /*field*/ MFURL url; /*field*/ SFString buffer; /*field*/ + /*GPAC private*/ + u32 buffer_len; } M_BitWrapper; diff --git a/include/gpac/path2d.h b/include/gpac/path2d.h index a27fa1b..ed767f6 100644 --- a/include/gpac/path2d.h +++ b/include/gpac/path2d.h @@ -38,7 +38,7 @@ extern "C" { #endif #include -#include +#include /*! diff --git a/include/gpac/scenegraph.h b/include/gpac/scenegraph.h index 3539980..7d933dc 100644 --- a/include/gpac/scenegraph.h +++ b/include/gpac/scenegraph.h @@ -569,6 +569,10 @@ enum GF_JSAPI_OP_PAUSE_SVG, /*!resumes an SVG ELEMENT*/ GF_JSAPI_OP_RESUME_SVG, + /*!restarts an SVG ELEMENT: this restarts all the media tunning on the main timeline*/ + GF_JSAPI_OP_RESTART_SVG, + /*!sets scene speed*/ + GF_JSAPI_OP_SET_SCENE_SPEED, /*!gets the DPI*/ GF_JSAPI_OP_GET_DPI_X, GF_JSAPI_OP_GET_DPI_Y, diff --git a/include/gpac/scenegraph_svg.h b/include/gpac/scenegraph_svg.h index 3811286..168fc06 100644 --- a/include/gpac/scenegraph_svg.h +++ b/include/gpac/scenegraph_svg.h @@ -241,9 +241,8 @@ typedef struct Fixed remaining_time; u16 status; const char *session_name; - u32 nb_streams; - struct mae_item {u32 streamType; u32 mediaType; u32 transport; } streams[20]; -} GF_DOMMediaAccessEvent; + u64 loaded_size, total_size; +} GF_DOMMediaEvent; /* DOM event handling @@ -334,7 +333,8 @@ typedef struct /*DOM event used in VRML (GPAC's internal)*/ Bool is_vrml; - GF_DOMMediaAccessEvent *mae; + /*media event*/ + GF_DOMMediaEvent *media_event; /*number of listeners triggered by the event*/ u32 consumed; @@ -405,8 +405,12 @@ enum GF_DOM_EVENT_SMIL = 1<<8, /*LASeR events*/ GF_DOM_EVENT_LASER = 1<<9, + /*HTML Media events*/ + GF_DOM_EVENT_MEDIA = 1<<10, +#if 0 /*MediaAccess events*/ - GF_DOM_EVENT_MEDIA_ACCESS = 1<<10, + GF_DOM_EVENT_MEDIA_ACCESS = 1<<11, +#endif /*fake events - these events are NEVER fired*/ GF_DOM_EVENT_FAKE = 1<<31, @@ -625,6 +629,7 @@ void gf_svg_flatten_attributes(SVG_Element *e, SVGAllAttributes *all_atts); const char *gf_svg_get_attribute_name(GF_Node *elt, u32 tag); u32 gf_svg_apply_inheritance(SVGAllAttributes *all_atts, SVGPropertiesPointers *render_svg_props) ; +/*creates a DOMAttribute for the given tag - THE ATTRIOBUTE IS NOT ADDED TO THE NODE'S ATTRIBUTES*/ GF_DOMAttribute *gf_xml_create_attribute(GF_Node *node, u32 tag); u32 gf_xml_get_attribute_type(u32 tag); u32 gf_xml_get_attribute_tag(GF_Node *node, char *attribute_name, u32 ns); diff --git a/include/gpac/setup.h b/include/gpac/setup.h index 3fbe32d..437f8dd 100644 --- a/include/gpac/setup.h +++ b/include/gpac/setup.h @@ -345,7 +345,7 @@ void gf_memory_print(void); /*prints the state of current allocations*/ /*end GPAC memory tracking*/ -#if (defined (WIN32) || defined (_WIN32_WCE)) && !defined(__GNUC__) +#if (defined (WIN32) || defined (_WIN32_WCE)) && (defined(__MINGW32__) || !defined(__GNUC__)) #define LLD "%I64d" #define LLU "%I64u" diff --git a/include/gpac/terminal.h b/include/gpac/terminal.h index cfd5c0b..9e3ccb7 100644 --- a/include/gpac/terminal.h +++ b/include/gpac/terminal.h @@ -139,8 +139,10 @@ GF_Err gf_term_paste_text(GF_Terminal *term, const char *txt, Bool probe_only); /*decodes pending media and render frame. NOTE: This can only be used when the terminal runs without visual thread (GF_TERM_NO_VISUAL_THREAD flag set) +returns estimated time left until next frame should be drawn. If GF_TERM_NO_REGULATION is not set, the function will sleep +for until next frame should be drawn before returning. */ -GF_Err gf_term_process_step(GF_Terminal *term); +u32 gf_term_process_step(GF_Terminal *term); /*decodes all pending media and render frame until no scene changes are detected. NOTE: This can only be used when the terminal runs without visual thread (GF_TERM_NO_VISUAL_THREAD flag set) @@ -165,7 +167,7 @@ void gf_term_mouse_input(GF_Terminal *term, GF_EventMouse *event); @isKeyUp: set if key is released */ /*NOT NEEDED WHEN THE TERMINAL IS HANDLING THE DISPLAY WINDOW (cf user.h)*/ -void gf_term_keyboard_input(GF_Terminal *term, u32 key_code, u32 hw_code, Bool isKeyUp); +Bool gf_term_keyboard_input(GF_Terminal *term, u32 key_code, u32 hw_code, Bool isKeyUp); /*post extended user character interaction to terminal @character: unicode character input diff --git a/include/gpac/thread.h b/include/gpac/thread.h index 0786530..0dddee6 100644 --- a/include/gpac/thread.h +++ b/include/gpac/thread.h @@ -216,6 +216,14 @@ void gf_mx_v(GF_Mutex *mx); */ Bool gf_mx_try_lock(GF_Mutex *mx); +/* + *\brief get mutex number of locks + * + *Returns the number of locks on the mutex if the caller thread is holding the mutex. + *\param mx the mutex object + *\return -1 if the mutex is not hold by the calling thread, or the number of locks (possibly 0) otherwise. + */ +s32 gf_mx_get_num_locks(GF_Mutex *mx); /********************************************************************* Semaphore Object diff --git a/include/gpac/tools.h b/include/gpac/tools.h index abc0f09..72b2460 100644 --- a/include/gpac/tools.h +++ b/include/gpac/tools.h @@ -93,8 +93,9 @@ extern "C" { * * Macro formating a 4-character code (or 4CC) "abcd" as 0xAABBCCDD */ +#ifndef GF_4CC #define GF_4CC(a,b,c,d) (((a)<<24)|((b)<<16)|((c)<<8)|(d)) - +#endif /*! * \brief GPAC feature list @@ -118,6 +119,16 @@ const char *gf_4cc_to_str(u32 type); */ int gf_asprintf(char **buffer, const char *fmt, ...); +/*! + * \brief file writing helper + * + * Wrapper to properly handle calls to fwrite() + * Ensures proper error handling is invoked when it fails. + * \return Same as gf_fwrite + * +*/ +size_t gf_fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream); + /*! * \brief large file opening * @@ -170,7 +181,7 @@ u64 gf_f64_seek(FILE *f, s64 pos, s32 whence); typedef enum { /*!Message from any scripting engine used in the presentation (ECMAScript, MPEG-J, ...) (Info).*/ - GF_SCRIPT_INFO = 3, + GF_SCRIPT_INFO = 3, /*!Indicates an data frame has several AU packed (not MPEG-4 compliant). This is used by decoders to force multiple decoding of the same data frame (Info).*/ GF_PACKED_FRAMES = 2, @@ -277,8 +288,10 @@ const char *gf_error_to_string(GF_Err e); */ enum { + /*! Disable all Log message*/ + GF_LOG_QUIET = 0, /*! Log message describes an error*/ - GF_LOG_ERROR = 1, + GF_LOG_ERROR, /*! Log message describes a warning*/ GF_LOG_WARNING, /*! Log message is informational (state, etc..)*/ @@ -287,15 +300,6 @@ enum GF_LOG_DEBUG }; -/*! - * \brief Log level assignment - * - * Sets the level used for log filtering. By default no log is performed - * \param level log level used. - * - */ -void gf_log_set_level(u32 level); - /*! * \brief Log exits at first error assignment * @@ -305,6 +309,14 @@ void gf_log_set_level(u32 level); */ void gf_log_set_strict_error(Bool strict); +/*! + * \brief gets string-formated log tools + * + * Returns the string-formatted log tools and levels. Returned string shall be freed by the caller. + * \return string-formatted log tools. + * + */ +char *gf_log_get_tools_levels(); /*! * GPAC Log tools @@ -315,61 +327,66 @@ void gf_log_set_strict_error(Bool strict); enum { /*! Log message from the core library (init, threads, network calls, etc)*/ - GF_LOG_CORE = 1, + GF_LOG_CORE = 0, /*! Log message from a raw media parser (BIFS, LASeR, A/V formats)*/ - GF_LOG_CODING= 1<<1, + GF_LOG_CODING, /*! Log message from a bitstream parser (IsoMedia, MPEG-2 TS, OGG, ...)*/ - GF_LOG_CONTAINER = 1<<2, + GF_LOG_CONTAINER, /*! Log message from the network/service stack (messages & co)*/ - GF_LOG_NETWORK = 1<<3, + GF_LOG_NETWORK, /*! Log message from the RTP/RTCP stack (TS info) and packet structure & hinting (debug)*/ - GF_LOG_RTP = 1<<4, + GF_LOG_RTP, /*! Log message from authoring subsystem (file manip, import/export)*/ - GF_LOG_AUTHOR = 1<<5, + GF_LOG_AUTHOR, /*! Log message from the sync layer of the terminal*/ - GF_LOG_SYNC = 1<<6, + GF_LOG_SYNC, /*! Log message from a codec*/ - GF_LOG_CODEC = 1<<7, + GF_LOG_CODEC, /*! Log message from any XML parser (context loading, etc)*/ - GF_LOG_PARSER = 1<<8, + GF_LOG_PARSER, /*! Log message from the terminal/compositor, indicating media object state*/ - GF_LOG_MEDIA = 1<<9, + GF_LOG_MEDIA, /*! Log message from the scene graph/scene manager (handling of nodes and attribute modif, DOM core)*/ - GF_LOG_SCENE = 1<<10, - /*! Log message from the scripting engine*/ - GF_LOG_SCRIPT = 1<<11, + GF_LOG_SCENE, + /*! Log message from the scripting engine APIs - does not cover alert() in the script code itself*/ + GF_LOG_SCRIPT, /*! Log message from event handling*/ - GF_LOG_INTERACT = 1<<12, + GF_LOG_INTERACT, /*! Log message from compositor*/ - GF_LOG_COMPOSE = 1<<13, + GF_LOG_COMPOSE, /*! Log for video object cache */ - GF_LOG_CACHE = 1<<14, + GF_LOG_CACHE, /*! Log message from multimedia I/O devices (audio/video input/output, ...)*/ - GF_LOG_MMIO = 1<<15, + GF_LOG_MMIO, /*! Log for runtime info (times, memory, CPU usage)*/ - GF_LOG_RTI = 1<<16, + GF_LOG_RTI, /*! Log for SMIL timing and animation*/ - GF_LOG_SMIL = 1<<17, + GF_LOG_SMIL, /*! Log for memory tracker*/ - GF_LOG_MEMORY = 1<<18, + GF_LOG_MEMORY, /*! Log for audio compositor*/ - GF_LOG_AUDIO = 1<<19, - /*! generic Log for modules*/ - GF_LOG_MODULE = 1<<20, - /*! log for threads and mutexes */ - GF_LOG_MUTEX = 1<<21, - /*! All logs*/ - GF_LOG_ALL = (1<<22)-1 + GF_LOG_AUDIO, + /*! Generic Log for modules*/ + GF_LOG_MODULE, + /*! Log for threads and mutexes */ + GF_LOG_MUTEX, + /*! Log for all messages coming from GF_Terminal or script alert()*/ + GF_LOG_CONSOLE, + + /*! special value used to set a level for all tools*/ + GF_LOG_ALL, + GF_LOG_TOOL_MAX = GF_LOG_ALL, }; /*! * \brief Log modules assignment * - * Sets the modules to be checked for log filtering. By default no modules are logged. - * \param tools log tools filtered. This is an OR'ed combinaison of log module flags + * Sets the tools to be checked for log filtering. By default no logging is performed. + * \param tool tool to be logged. + * \param level level of logging for this tool. * */ -void gf_log_set_tools(u32 tools); +void gf_log_set_tool_level(u32 tool, u32 level); /*! * \brief Log Message Callback @@ -394,10 +411,10 @@ typedef void (*gf_log_cbk)(void *cbck, u32 log_level, u32 log_tool, const char* */ gf_log_cbk gf_log_set_callback(void *usr_cbk, gf_log_cbk cbk); + /*! \cond DUMMY_DOXY_SECTION */ - #ifndef GPAC_DISABLE_LOG /*note: to turn log on, change to GPAC_ENABLE_LOG @@ -416,11 +433,43 @@ gf_log_cbk gf_log_set_callback(void *usr_cbk, gf_log_cbk cbk); void gf_log(const char *fmt, ...); void gf_log_lt(u32 ll, u32 lt); -u32 gf_log_get_level(); -u32 gf_log_get_tools(); -u32 gf_log_parse_level(const char *val); -u32 gf_log_parse_tools(const char *val); +/*! + * \brief Log level checking + * + * Checks if a given tool is logged for the given level + * \param log_tool tool to check + * \param log_level level to check + * \return 1 if logged, 0 otherwise +*/ +Bool gf_log_tool_level_on(u32 log_tool, u32 log_level); + +/*! + * \brief Set log tools and levels + * + * Set log tools and levels according to the log_tools_levels string. All previous log settings are discarded. + * \param log_tools_levels string specifying the tools and levels. It is formatted as logToolX@logLevelX:logToolZ@logLevelZ:... + * \return GF_OK or GF_BAD_PARAM +*/ +GF_Err gf_log_set_tools_levels(const char *log_tools_levels); + +/*! + * \brief Modify log tools and levels + * + * Modify log tools and levels according to the log_tools_levels string. Previous log settings are kept. + * \param log_tools_levels string specifying the tools and levels. It is formatted as logToolX@logLevelX:logToolZ@logLevelZ:... + * \return GF_OK or GF_BAD_PARAM +*/ +GF_Err gf_log_modify_tools_levels(const char *val); + +/*! + * \brief Set log level for a given tool + * + * Set log level for a given tool. + * \param tool tool to log + * \param level log level for this tool +*/ +void gf_log_set_tool_level(u32 tool, u32 level); #ifdef GPAC_DISABLE_LOG #define GF_LOG(_ll, _lm, __args) @@ -431,7 +480,7 @@ u32 gf_log_parse_tools(const char *val); * * Macro for logging messages. Usage is GF_LOG(log_lev, log_module, (fmt, ...)). The log function is only called if log filtering allows it. This avoids fetching logged parameters when the tool is not being logged. */ -#define GF_LOG(_log_level, _log_tools, __args) if ((gf_log_get_level() >= (_log_level)) && (gf_log_get_tools() & (_log_tools))) { gf_log_lt(_log_level, _log_tools); gf_log __args ;} +#define GF_LOG(_log_level, _log_tools, __args) if (gf_log_tool_level_on(_log_tools, _log_level) ) { gf_log_lt(_log_level, _log_tools); gf_log __args ;} #endif @@ -624,6 +673,29 @@ u32 gf_sys_clock(); */ void gf_sleep(u32 ms); +/*! + * \brief Delete Directory + * + * Delete a dir within the full path. + * \param DirPathName the file path name. + */ +GF_Err gf_rmdir(char *DirPathName); + +/*! + * \brief Create Directory + * + * Create a directory within the full path. + * \param DirPathName the dir path name. + */ +GF_Err gf_mkdir(char* DirPathName); + +/*! + * \brief Create Directory + * + * Cleanup a directory within the full path, removing all the files and the directories. + * \param DirPathName the dir path name. + */ +GF_Err gf_cleanup_dir(char* DirPathName); /*! * \brief CRC32 compute * @@ -752,6 +824,13 @@ GF_Err gf_gz_compress_payload(char **data, u32 data_len, u32 *out_size); */ GF_Err gf_gz_decompress_payload(char *data, u32 data_len, char **uncompressed_data, u32 *out_size); + +#ifdef GPAC_ANDROID +typedef void (*fm_callback_func)(void *cbk_obj, u32 type, u32 param, int *value); +extern void gf_fm_request_set_callback(void *cbk_obj, fm_callback_func cbk_func); +void gf_fm_request_call(u32 type, u32 param, int *value); +#endif //GPAC_ANDROID + /*! @} */ diff --git a/include/gpac/xml.h b/include/gpac/xml.h index 3265eeb..41d1374 100644 --- a/include/gpac/xml.h +++ b/include/gpac/xml.h @@ -136,6 +136,7 @@ typedef struct _tag_dom_parser GF_DOMParser; GF_DOMParser *gf_xml_dom_new(); void gf_xml_dom_del(GF_DOMParser *parser); GF_Err gf_xml_dom_parse(GF_DOMParser *parser, const char *file, gf_xml_sax_progress OnProgress, void *cbk); +GF_Err gf_xml_dom_parse_string(GF_DOMParser *dom, char *string); GF_XMLNode *gf_xml_dom_get_root(GF_DOMParser *parser); const char *gf_xml_dom_get_error(GF_DOMParser *parser); u32 gf_xml_dom_get_line(GF_DOMParser *parser); diff --git a/modules/Makefile b/modules/Makefile index 872cfaf..acd7af0 100644 --- a/modules/Makefile +++ b/modules/Makefile @@ -1,7 +1,6 @@ include ../config.mak #all OS and lib independent -#PLUGDIRS=aac_in ac3_in audio_filter bifs_dec ctx_load dummy_in soft_raster mp3_in isom_in odf_dec rtp_in timedtext img_in svg_in saf_in mpegts_in ismacryp widgetman redirect_av mpd_in PLUGDIRS=aac_in ac3_in audio_filter bifs_dec ctx_load dummy_in soft_raster mp3_in isom_in odf_dec rtp_in timedtext img_in svg_in saf_in mpegts_in ismacryp widgetman mpd_in osd ifeq ($(DISABLE_SVG), no) @@ -28,6 +27,11 @@ else PLUGDIRS+=ogg endif +ifeq ($(CONFIG_FREENECT), no) +else +PLUGDIRS+=freenect +endif + ifeq ($(CONFIG_AMR_NB), yes) PLUGDIRS+=amr_dec endif @@ -73,6 +77,17 @@ else PLUGDIRS+=gpac_js endif +ifeq ($(CONFIG_PLATINUM),yes) +PLUGDIRS+=platinum +endif + +ifeq ($(CONFIG_AVCAP),yes) +PLUGDIRS+=avcap +endif + +ifeq ($(CONFIG_OPENSVC),yes) +PLUGDIRS+=opensvc_dec +endif ifeq ($(RENOIR_ENABLE),yes) PLUGDIRS+=viren_out diff --git a/modules/aac_in/aac_in.c b/modules/aac_in/aac_in.c index d812ea5..dcd70a4 100644 --- a/modules/aac_in/aac_in.c +++ b/modules/aac_in/aac_in.c @@ -543,6 +543,12 @@ void aac_download_file(AACReader *read, char *url) gf_term_on_connect(read->service, NULL, GF_NOT_SUPPORTED); #endif } + +#ifndef DONT_USE_TERMINAL_MODULE_API + /*start our download (threaded)*/ + gf_dm_sess_process(read->dnload); +#endif + /*service confirm is done once fetched*/ } diff --git a/modules/aac_in/faad_dec.c b/modules/aac_in/faad_dec.c index b7a1999..fd5c029 100644 --- a/modules/aac_in/faad_dec.c +++ b/modules/aac_in/faad_dec.c @@ -24,6 +24,9 @@ #ifdef GPAC_HAS_FAAD +#ifdef __GNUC__ +#pragma GCC diagnostic ignored "-Wunknown-pragmas" +#endif #include #include diff --git a/modules/ac3_in/ac3_in.c b/modules/ac3_in/ac3_in.c index 8fa1bc2..df933f7 100644 --- a/modules/ac3_in/ac3_in.c +++ b/modules/ac3_in/ac3_in.c @@ -330,6 +330,9 @@ void ac3_download_file(GF_InputService *plug, char *url) if (!read->dnload ) { read->needs_connection = 0; gf_term_on_connect(read->service, NULL, GF_NOT_SUPPORTED); + } else { + /*start our download (threaded)*/ + gf_dm_sess_process(read->dnload); } /*service confirm is done once fetched*/ } diff --git a/modules/amr_dec/amr_in.c b/modules/amr_dec/amr_in.c index b9625b0..bce7442 100644 --- a/modules/amr_dec/amr_in.c +++ b/modules/amr_dec/amr_in.c @@ -262,6 +262,9 @@ static void AMR_DownloadFile(GF_InputService *plug, char *url) if (!read->dnload) { read->needs_connection = 0; gf_term_on_connect(read->service, NULL, GF_NOT_SUPPORTED); + } else { + /*start our download (threaded)*/ + gf_dm_sess_process(read->dnload); } /*service confirm is done once fetched*/ } @@ -464,17 +467,17 @@ fetch_next: switch (read->mtype) { case TYPE_AMR: ft = (toc >> 3) & 0x0F; - read->data_size = GF_AMR_FRAME_SIZE[ft]; + read->data_size = (u32)GF_AMR_FRAME_SIZE[ft]; break; case TYPE_AMR_WB: ft = (toc >> 3) & 0x0F; - read->data_size = GF_AMR_WB_FRAME_SIZE[ft]; + read->data_size = (u32)GF_AMR_WB_FRAME_SIZE[ft]; break; default: for (i=0; idata_size = GF_SMV_EVRC_RATE_TO_SIZE[2*i+1] - 1; + read->data_size = (u32)GF_SMV_EVRC_RATE_TO_SIZE[2*i+1] - 1; break; } } diff --git a/modules/amr_float_dec/amr_float_dec.c b/modules/amr_float_dec/amr_float_dec.c index c4d45b3..67a8dfd 100644 --- a/modules/amr_float_dec/amr_float_dec.c +++ b/modules/amr_float_dec/amr_float_dec.c @@ -229,14 +229,14 @@ static GF_Err AMR_ProcessData(GF_MediaDecoder *ifcg, D_IF_decode(ctx->wb_destate, inBuffer, (s16 *) outBuffer, 0); *outBufferLength += 320*2; outBuffer += 320*2; - offset = GF_AMR_WB_FRAME_SIZE[ft] + 1; + offset = (u32)GF_AMR_WB_FRAME_SIZE[ft] + 1; #endif } else { #ifdef GPAC_HAS_AMR_FT Decoder_Interface_Decode(ctx->nb_destate, inBuffer, (s16 *) outBuffer, 0); *outBufferLength += 160*2; outBuffer += 160*2; - offset = GF_AMR_FRAME_SIZE[ft] + 1; + offset = (u32)GF_AMR_FRAME_SIZE[ft] + 1; #endif } /*don't complain but...*/ diff --git a/modules/avcap/Makefile b/modules/avcap/Makefile new file mode 100644 index 0000000..81456ee --- /dev/null +++ b/modules/avcap/Makefile @@ -0,0 +1,57 @@ +include ../../config.mak + +vpath %.c $(SRC_PATH)/modules/avcap + +CFLAGS= $(CPPFLAGS) -I"$(SRC_PATH)/include" $(AVCAP_CFLAGS) + +ifeq ($(DEBUGBUILD), yes) +CFLAGS+=-g +LDFLAGS+=-g +endif + +ifeq ($(GPROFBUILD), yes) +CFLAGS+=-pg +LDFLAGS+=-pg +endif + +LINKLIBS= $(AVCAP_LDFLAGS) -L../../bin/gcc -lgpac + + +#common objects +OBJS=avcap.o + +SRCS := $(OBJS:.o=.c) + +LIB=gm_avcap.$(DYN_LIB_SUFFIX) + + +all: $(LIB) + + +$(LIB): $(OBJS) + $(CXX) -w $(SHFLAGS) $(LDFLAGS) -o ../../bin/gcc/$@ $(OBJS) $(LINKLIBS) + + +%.o: %.cpp + $(CXX) $(CFLAGS) -c -o $@ $< + + +clean: + rm -f $(OBJS) ../../bin/gcc/$(LIB) + +dep: depend + +depend: + rm -f .depend + $(CXX) -MM $(CFLAGS) $(SRCS) 1>.depend + +distclean: clean + rm -f Makefile.bak .depend + + + +# include dependency files if they exist +# +ifneq ($(wildcard .depend),) +include .depend +endif diff --git a/modules/avcap/avcap.cpp b/modules/avcap/avcap.cpp new file mode 100644 index 0000000..8d69650 --- /dev/null +++ b/modules/avcap/avcap.cpp @@ -0,0 +1,423 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Authors: Jean Le Feuvre + * Copyright (c) Telecom ParisTech 2011-20XX + * All rights reserved + * + * This file is part of GPAC / AVCAP video input module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include +#include +/*for GF_STREAM_PRIVATE_SCENE definition*/ +#include +#include + +#if !defined(__GNUC__)&& (defined(_WIN32_WCE) || defined (WIN32)) +# pragma comment(lib, "strmiids") +#ifdef _DEBUG +# pragma comment(lib, "avcapd") +# pragma comment(lib, "strmbasd") +#else +# pragma comment(lib, "avcap") +# pragma comment(lib, "strmbase") +#endif +# pragma comment(lib, "winmm") +#endif + + + +#include +using namespace avcap; + +class GPACCaptureHandler : public CaptureHandler +{ +public: + GPACCaptureHandler(GF_ClientService *service, LPNETCHANNEL channel) + : m_pService(service), m_pChannel(channel) + { + memset(&m_pSLHeader, 0, sizeof(GF_SLHeader)); + m_pSLHeader.compositionTimeStampFlag = 1; + } + virtual ~GPACCaptureHandler() {} + + GF_ClientService *m_pService; + LPNETCHANNEL m_pChannel; + GF_SLHeader m_pSLHeader; + +public: + /* This method is called by the CaptureManager, when new data was captured. + * \param io_buf The buffer, that contains the captured data. */ + void handleCaptureEvent(IOBuffer* io_buf); +}; + + +void GPACCaptureHandler::handleCaptureEvent(IOBuffer* io_buf) +{ + m_pSLHeader.compositionTimeStamp = io_buf->getTimestamp(); + gf_term_on_sl_packet(m_pService, m_pChannel, (char *) io_buf->getPtr(), io_buf->getValidBytes(), &m_pSLHeader, GF_OK); + io_buf->release(); +} + +DeviceDescriptor* get_device_descriptor(char *name) +{ + // find the descriptor of the device in the device-list by index + const DeviceCollector::DeviceList& dl = DEVICE_COLLECTOR::instance().getDeviceList(); + DeviceDescriptor* dd = 0; + int index = 0; + + for(DeviceCollector::DeviceList::const_iterator i = dl.begin(); i != dl.end(); i++, index++) { + dd = *i; + + if (!name || !stricmp(name, "default") ) + return dd; + if (strstr((char *) dd->getName().c_str(), name) != NULL) + return dd; + } + GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[VideoCapture] Cannot find capture driver %s\n", name)); + return NULL; +} + + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct +{ + /*the service we're responsible for*/ + GF_ClientService *service; + u32 state; + + DeviceDescriptor *device_desc; + CaptureDevice* device; + + GPACCaptureHandler *video_handler; + GPACCaptureHandler *audio_handler; + + u32 width, height, pixel_format, stride, out_size, fps; +} AVCapIn; + + +Bool AVCap_CanHandleURL(GF_InputService *plug, const char *url) +{ + if (!strnicmp(url, "camera://", 9)) return 1; + if (!strnicmp(url, "video://", 8)) return 1; + return 0; +} + + +GF_Err AVCap_ConnectService(GF_InputService *plug, GF_ClientService *serv, const char *url) +{ + GF_ESD *esd; + GF_BitStream *bs; + GF_ObjectDescriptor *od; + AVCapIn *vcap = (AVCapIn *) plug->priv; + + if (!vcap || !serv || !url) return GF_BAD_PARAM; + + vcap->state = 0; + vcap->service = serv; + + if (!vcap->device_desc) { + Format *format; + char *name; + char *params = (char *) strchr(url, '?'); + if (params) params[0] = 0; + name = (char *) strstr(url, "://"); + if (name) name += 3; + /* get device by name */ + vcap->device_desc = get_device_descriptor(name); + if (params) { + params[0] = '?'; + params ++; + } + + if (!vcap->device_desc) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[VideoCapture] Failed to instanciate AVCap\n")); + gf_term_on_connect(serv, NULL, GF_REMOTE_SERVICE_ERROR); + return GF_OK; + } + + vcap->device_desc->open(); + if ( (!strnicmp(url, "camera://", 9) || !strnicmp(url, "video://", 8)) && !vcap->device_desc->isVideoCaptureDev()) { + vcap->device_desc->close(); + gf_term_on_connect(serv, NULL, GF_URL_ERROR); + return GF_OK; + } + else if (!strnicmp(url, "audio://", 8) && !vcap->device_desc->isAudioDev()) { + vcap->device_desc->close(); + gf_term_on_connect(serv, NULL, GF_URL_ERROR); + return GF_OK; + } + vcap->device = vcap->device_desc->getDevice(); + if (!vcap->device) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[VideoCapture] Failed to initialize capture device\n")); + vcap->device_desc->close(); + gf_term_on_connect(serv, NULL, GF_SERVICE_ERROR); + return GF_OK; + } + vcap->device->getFormatMgr()->setFramerate(30); + + while (params) { + char *sep = (char *) strchr(params, '&'); + if (sep) sep[0] = 0; + + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("[VideoCapture] Set camera option %s\n", params)); + + if (!strnicmp(params, "resolution=", 11)) { + u32 w, h; + if (sscanf(params+11, "%dx%d", &w, &h)==2) { + vcap->device->getFormatMgr()->setResolution(w, h); + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("[VideoCapture] Set resolution to %dx%d\n", w, h)); + } + } + else if (!strnicmp(params, "fps=", 4)) { + u32 fps; + if (sscanf(params+4, "%d", &fps)==1) { + vcap->device->getFormatMgr()->setFramerate(fps); + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("[VideoCapture] Set framerate to %d\n", fps)); + } + } + else if (!strnicmp(params, "stereo=", 7)) { + } + else if (!strnicmp(params, "mode=", 5)) { + } + + if (!sep) break; + sep[0] = '&'; + params = sep+1; + } + vcap->width = vcap->device->getFormatMgr()->getWidth(); + vcap->height = vcap->device->getFormatMgr()->getHeight(); + vcap->fps = vcap->device->getFormatMgr()->getFramerate(); + + format = vcap->device->getFormatMgr()->getFormat(); + switch (format->getFourcc()) { + case GF_4CC('V', 'Y', 'U', 'Y'): + case GF_4CC('2', 'Y', 'U', 'Y'): + vcap->pixel_format = GF_PIXEL_YUY2; + vcap->stride = 2*vcap->width; + vcap->out_size = 2*vcap->width*vcap->height; + break; + default: + GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[VideoCapture] Unsupported 4CC %s (%08x) from capture device\n", gf_4cc_to_str(format->getFourcc()), format->getFourcc())); + vcap->device_desc->close(); + gf_term_on_connect(serv, NULL, GF_NOT_SUPPORTED); + return GF_OK; + } + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("[VideoCapture] Device configured - resolution %dx%d - Frame Rate %d - Pixel Format %s (Device 4CC %08x) \n", vcap->width, vcap->height, vcap->fps, gf_4cc_to_str(vcap->pixel_format), format->getFourcc())); + } + + /*ACK connection is OK*/ + gf_term_on_connect(serv, NULL, GF_OK); + + + /*setup object descriptor*/ + od = (GF_ObjectDescriptor *) gf_odf_desc_new(GF_ODF_OD_TAG); + + esd = gf_odf_desc_esd_new(0); + esd->slConfig->timestampResolution = 1000; + if (!strnicmp(url, "camera://", 9) || !strnicmp(url, "video://", 8)) { + od->objectDescriptorID = 1; + esd->ESID = 1; + esd->decoderConfig->streamType = GF_STREAM_VISUAL; + } else { + od->objectDescriptorID = 2; + esd->ESID = 2; + esd->decoderConfig->streamType = GF_STREAM_AUDIO; + } + esd->decoderConfig->objectTypeIndication = GPAC_OTI_RAW_MEDIA_STREAM; + + bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + gf_bs_write_u32(bs, vcap->pixel_format); + gf_bs_write_u16(bs, vcap->width); + gf_bs_write_u16(bs, vcap->height); + gf_bs_write_u32(bs, vcap->out_size); + gf_bs_write_u32(bs, vcap->stride); + gf_bs_get_content(bs, &esd->decoderConfig->decoderSpecificInfo->data, &esd->decoderConfig->decoderSpecificInfo->dataLength); + gf_bs_del(bs); + + gf_list_add(od->ESDescriptors, esd); + gf_term_add_media(vcap->service, (GF_Descriptor*)od, 0); + + return GF_OK; +} + +GF_Err AVCap_CloseService(GF_InputService *plug) +{ + AVCapIn *vcap = (AVCapIn *) plug->priv; + if (vcap->device_desc) { + vcap->device_desc->close(); + vcap->device_desc = NULL; + } + + vcap->state = 0; + gf_term_on_disconnect(vcap->service, NULL, GF_OK); + return GF_OK; +} + +/*Dummy input just send a file name, no multitrack to handle so we don't need to check sub_url nor expected type*/ +static GF_Descriptor *AVCap_GetServiceDesc(GF_InputService *plug, u32 expect_type, const char *sub_url) +{ + return NULL; +} + + +GF_Err AVCap_ServiceCommand(GF_InputService *plug, GF_NetworkCommand *com) +{ + AVCapIn *vcap = (AVCapIn *) plug->priv; + + if (!com->base.on_channel) return GF_NOT_SUPPORTED; + + switch (com->command_type) { + case GF_NET_CHAN_SET_PULL: return GF_NOT_SUPPORTED; + case GF_NET_CHAN_INTERACTIVE: return GF_OK; + /*since data is file-based, no padding is needed (decoder plugin will handle it itself)*/ + case GF_NET_CHAN_SET_PADDING: return GF_OK; + case GF_NET_CHAN_BUFFER: + com->buffer.max = com->buffer.min = 500; + return GF_OK; + case GF_NET_CHAN_DURATION: + /*this module is not made for updates, use undefined duration*/ + com->duration.duration = 0; + return GF_OK; + case GF_NET_CHAN_PLAY: + if (vcap->state==0) { + /*start capture*/ + if (vcap->video_handler) + vcap->device->getVidCapMgr()->registerCaptureHandler(vcap->video_handler); + + if (vcap->device->getVidCapMgr()->startCapture() != -1) + vcap->state = 1; + else + vcap->device->getVidCapMgr()->removeCaptureHandler(); + } + return GF_OK; + case GF_NET_CHAN_STOP: + if (vcap->state==1) { + /*stop capture*/ + vcap->device->getVidCapMgr()->removeCaptureHandler(); + vcap->device->getVidCapMgr()->stopCapture(); + vcap->state = 0; + } + return GF_OK; + case GF_NET_CHAN_CONFIG: return GF_OK; + case GF_NET_CHAN_GET_DSI: + com->get_dsi.dsi = NULL; + com->get_dsi.dsi_len = 0; + return GF_OK; + } + return GF_OK; +} + +GF_Err AVCap_ConnectChannel(GF_InputService *plug, LPNETCHANNEL channel, const char *url, Bool upstream) +{ + u32 ESID; + AVCapIn *vcap = (AVCapIn *) plug->priv; + + sscanf(url, "ES_ID=%u", &ESID); + if (ESID == 1) { + /*video connect*/ + vcap->video_handler = new GPACCaptureHandler(vcap->service, channel); + gf_term_on_connect(vcap->service, channel, GF_OK); + } else if (ESID == 2) { + /*audio connect*/ + vcap->audio_handler = new GPACCaptureHandler(vcap->service, channel); + gf_term_on_connect(vcap->service, channel, GF_OK); + } else { + gf_term_on_connect(vcap->service, channel, GF_STREAM_NOT_FOUND); + } + return GF_OK; +} + +GF_Err AVCap_DisconnectChannel(GF_InputService *plug, LPNETCHANNEL channel) +{ + AVCapIn *vcap = (AVCapIn *) plug->priv; + if (vcap->video_handler && vcap->video_handler->m_pChannel==channel) { + delete vcap->video_handler; + vcap->video_handler = NULL; + } + else if (vcap->audio_handler && vcap->audio_handler->m_pChannel==channel) { + delete vcap->audio_handler; + vcap->audio_handler = NULL; + } + gf_term_on_disconnect(vcap->service, channel, GF_OK); + return GF_OK; +} + +Bool AVCap_CanHandleURLInService(GF_InputService *plug, const char *url) +{ + return 0; +} + + +GF_EXPORT +const u32 *QueryInterfaces() +{ + static u32 si [] = { + GF_NET_CLIENT_INTERFACE, + 0 + }; + return si; +} + +GF_EXPORT +GF_BaseInterface *LoadInterface(u32 InterfaceType) +{ + if (InterfaceType == GF_NET_CLIENT_INTERFACE) { + AVCapIn *vcap; + GF_InputService *plug; + GF_SAFEALLOC(plug, GF_InputService); + memset(plug, 0, sizeof(GF_InputService)); + GF_REGISTER_MODULE_INTERFACE(plug, GF_NET_CLIENT_INTERFACE, "Video Capture using libavcap", "gpac distribution") + + plug->RegisterMimeTypes = NULL; + plug->CanHandleURL = AVCap_CanHandleURL; + plug->ConnectService = AVCap_ConnectService; + plug->CloseService = AVCap_CloseService; + plug->GetServiceDescriptor = AVCap_GetServiceDesc; + plug->ConnectChannel = AVCap_ConnectChannel; + plug->DisconnectChannel = AVCap_DisconnectChannel; + plug->ServiceCommand = AVCap_ServiceCommand; + plug->CanHandleURLInService = AVCap_CanHandleURLInService; + + GF_SAFEALLOC(vcap, AVCapIn); + plug->priv = vcap; + return (GF_BaseInterface *)plug; + } + return NULL; +} + +GF_EXPORT +void ShutdownInterface(GF_BaseInterface *bi) +{ + if (bi->InterfaceType==GF_NET_CLIENT_INTERFACE) { + GF_InputService *ifcn = (GF_InputService*)bi; + AVCapIn *vcap = (AVCapIn*)ifcn->priv; + gf_free(vcap); + gf_free(bi); + } +} + + +#ifdef __cplusplus +} +#endif diff --git a/modules/directfb_out/Makefile b/modules/directfb_out/Makefile index dff6a44..48ec61c 100644 --- a/modules/directfb_out/Makefile +++ b/modules/directfb_out/Makefile @@ -20,7 +20,7 @@ endif #common obj -OBJS=directfb_out.o +OBJS=directfb_out.o directfb_wrapper.o SRCS := $(OBJS:.o=.c) @@ -31,7 +31,7 @@ all: $(LIB) $(LIB): $(OBJS) - $(CC) $(SHFLAGS) $(LDFLAGS) -L../../bin/gcc -lgpac -o ../../bin/gcc/$@ $(OBJS) + $(CC) $(SHFLAGS) -o ../../bin/gcc/$@ $(OBJS) $(LDFLAGS) -L../../bin/gcc -lgpac %.o: %.c $(CC) $(CFLAGS) -c -o $@ $< diff --git a/modules/directfb_out/directfb_out.c b/modules/directfb_out/directfb_out.c index a751bd7..a194bdf 100755 --- a/modules/directfb_out/directfb_out.c +++ b/modules/directfb_out/directfb_out.c @@ -1,777 +1,365 @@ -#include "directfb_out.h" - -#define DirectFBVID() DirectFBVidCtx *ctx = (DirectFBVidCtx *)driv->opaque -// this was supposed to contain argc and argv from main !!!!! -int argc; -char **argv = {"toto"}; - -//static const PredefineKeyID(keyIDs); - -u32 DirectFBVid_TranslatePixelFormatToGPAC(u32 dfbpf) -{ - switch (dfbpf) { - case DSPF_RGB16: return GF_PIXEL_RGB_565; - case DSPF_RGB555: return GF_PIXEL_RGB_555; - case DSPF_RGB24: return GF_PIXEL_RGB_24; - case DSPF_RGB32: return GF_PIXEL_RGB_32; - case DSPF_ARGB: return GF_PIXEL_ARGB; - default: return 0; - } -} - -u32 DirectFBVid_TranslatePixelFormatFromGPAC(u32 gpacpf) -{ - switch (gpacpf) { - case GF_PIXEL_RGB_565: return DSPF_RGB16; - case GF_PIXEL_RGB_555 : return DSPF_RGB555; - case GF_PIXEL_BGR_24 : return DSPF_RGB24; - case GF_PIXEL_RGB_24 : return DSPF_RGB24; - case GF_PIXEL_RGB_32 : return DSPF_RGB32; - case GF_PIXEL_ARGB: return DSPF_ARGB; - case GF_PIXEL_RGBA: return DSPF_ARGB; - case GF_PIXEL_YUY2 : return DSPF_YUY2; - case GF_PIXEL_YV12 : return DSPF_YV12; - default: - GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[DirectFB] pixel format %s not supported\n", gf_4cc_to_str(gpacpf))); - return 0; - } -} - -static DFBEnumerationResult enum_input_device(DFBInputDeviceID device_id, DFBInputDeviceDescription desc, void *data ) -{ - DeviceInfo **devices = data; - DeviceInfo *device; - - device = malloc( sizeof(DeviceInfo) ); - - device->device_id = device_id; - device->desc = desc; - device->next = *devices; - - *devices = device; - - return GF_OK; -} - -static void directfb_translate_key(DFBInputDeviceKeySymbol DirectFBkey, GF_EventKey *evt) -{ - evt->flags = 0; - evt->hw_code = DirectFBkey; - switch (DirectFBkey){ - case DIKS_BACKSPACE: - evt->key_code = GF_KEY_BACKSPACE; break; - case DIKS_RETURN: - evt->key_code = GF_KEY_ENTER; break; - case DIKS_CANCEL: - evt->key_code = GF_KEY_CANCEL; break; - case DIKS_ESCAPE: - evt->key_code = GF_KEY_ESCAPE; break; - case DIKS_SPACE: - evt->key_code = GF_KEY_SPACE; break; - case DIKS_EXCLAMATION_MARK: - evt->key_code = GF_KEY_EXCLAMATION; break; - case DIKS_QUOTATION: - evt->key_code = GF_KEY_QUOTATION; break; - case DIKS_NUMBER_SIGN: - evt->key_code = GF_KEY_NUMBER; break; - case DIKS_DOLLAR_SIGN: - evt->key_code = GF_KEY_DOLLAR; break; -#if 0 - case DIKS_PERCENT_SIGN: - evt->key_code = GF_KEY_PERCENT; break; -#endif - case DIKS_AMPERSAND: - evt->key_code = GF_KEY_AMPERSAND; break; - case DIKS_APOSTROPHE: - evt->key_code = GF_KEY_APOSTROPHE; break; - case DIKS_PARENTHESIS_LEFT: - evt->key_code = GF_KEY_LEFTPARENTHESIS; break; - case DIKS_PARENTHESIS_RIGHT: - evt->key_code = GF_KEY_RIGHTPARENTHESIS; break; - case DIKS_ASTERISK: - evt->key_code = GF_KEY_STAR; break; - case DIKS_PLUS_SIGN: - evt->key_code = GF_KEY_PLUS; break; - case DIKS_COMMA: - evt->key_code = GF_KEY_COMMA; break; - case DIKS_MINUS_SIGN: - evt->key_code = GF_KEY_HYPHEN; break; - case DIKS_PERIOD: - evt->key_code = GF_KEY_FULLSTOP; break; - case DIKS_SLASH: - evt->key_code = GF_KEY_SLASH; break; - case DIKS_0: - evt->flags = GF_KEY_EXT_NUMPAD; evt->key_code = GF_KEY_0; break; - case DIKS_1: - //fprintf(stderr, "DIKS_1: %d\n", GF_KEY_1); - evt->flags = GF_KEY_EXT_NUMPAD; evt->key_code = GF_KEY_1; break; - case DIKS_2: - evt->flags = GF_KEY_EXT_NUMPAD; evt->key_code = GF_KEY_2; break; - case DIKS_3: - evt->flags = GF_KEY_EXT_NUMPAD; evt->key_code = GF_KEY_3; break; - case DIKS_4: - evt->flags = GF_KEY_EXT_NUMPAD; evt->key_code = GF_KEY_4; break; - case DIKS_5: - // fprintf(stderr, "DIKS_5\n"); - evt->flags = GF_KEY_EXT_NUMPAD; evt->key_code = GF_KEY_5; break; - case DIKS_6: - evt->flags = GF_KEY_EXT_NUMPAD; evt->key_code = GF_KEY_6; break; - case DIKS_7: - evt->flags = GF_KEY_EXT_NUMPAD; evt->key_code = GF_KEY_7; break; - case DIKS_8: - evt->flags = GF_KEY_EXT_NUMPAD; evt->key_code = GF_KEY_8; break; - case DIKS_9: - evt->flags = GF_KEY_EXT_NUMPAD; evt->key_code = GF_KEY_9; break; - case DIKS_COLON: - evt->key_code = GF_KEY_COLON; break; - case DIKS_SEMICOLON: - evt->key_code = GF_KEY_SEMICOLON; break; - case DIKS_LESS_THAN_SIGN: - evt->key_code = GF_KEY_LESSTHAN; break; - case DIKS_EQUALS_SIGN: - evt->key_code = GF_KEY_EQUALS; break; - case DIKS_GREATER_THAN_SIGN: - evt->key_code = GF_KEY_GREATERTHAN; break; - case DIKS_QUESTION_MARK: - evt->key_code = GF_KEY_QUESTION; break; - case DIKS_AT: - evt->key_code = GF_KEY_AT; break; - case DIKS_CAPITAL_A: - evt->key_code = GF_KEY_A; break; - case DIKS_CAPITAL_B: - evt->key_code = GF_KEY_B; break; - case DIKS_CAPITAL_C: - evt->key_code = GF_KEY_C; break; - case DIKS_CAPITAL_D: - evt->key_code = GF_KEY_D; break; - case DIKS_CAPITAL_E: - evt->key_code = GF_KEY_E; break; - case DIKS_CAPITAL_F: - evt->key_code = GF_KEY_F; break; - case DIKS_CAPITAL_G: - evt->key_code = GF_KEY_G; break; - case DIKS_CAPITAL_H: - evt->key_code = GF_KEY_H; break; - case DIKS_CAPITAL_I: - evt->key_code = GF_KEY_I; break; - case DIKS_CAPITAL_J: - evt->key_code = GF_KEY_J; break; - case DIKS_CAPITAL_K: - evt->key_code = GF_KEY_K; break; - case DIKS_CAPITAL_L: - evt->key_code = GF_KEY_L; break; - case DIKS_CAPITAL_M: - evt->key_code = GF_KEY_M; break; - case DIKS_CAPITAL_N: - evt->key_code = GF_KEY_N; break; - case DIKS_CAPITAL_O: - evt->key_code = GF_KEY_O; break; - case DIKS_CAPITAL_P: - evt->key_code = GF_KEY_P; break; - case DIKS_CAPITAL_Q: - evt->key_code = GF_KEY_Q; break; - case DIKS_CAPITAL_R: - evt->key_code = GF_KEY_R; break; - case DIKS_CAPITAL_S: - evt->key_code = GF_KEY_S; break; - case DIKS_CAPITAL_T: - evt->key_code = GF_KEY_T; break; - case DIKS_CAPITAL_U: - evt->key_code = GF_KEY_U; break; - case DIKS_CAPITAL_V: - evt->key_code = GF_KEY_V; break; - case DIKS_CAPITAL_W: - evt->key_code = GF_KEY_W; break; - case DIKS_CAPITAL_X: - evt->key_code = GF_KEY_X; break; - case DIKS_CAPITAL_Y: - evt->key_code = GF_KEY_Y; break; - case DIKS_CAPITAL_Z: - evt->key_code = GF_KEY_Z; break; - case DIKS_SQUARE_BRACKET_LEFT: - evt->key_code = GF_KEY_LEFTSQUAREBRACKET; break; - case DIKS_BACKSLASH: - evt->key_code = GF_KEY_BACKSLASH; break; - case DIKS_SQUARE_BRACKET_RIGHT: - evt->key_code = GF_KEY_RIGHTSQUAREBRACKET; break; - case DIKS_CIRCUMFLEX_ACCENT: - evt->key_code = GF_KEY_CIRCUM; break; - case DIKS_UNDERSCORE: - evt->key_code = GF_KEY_UNDERSCORE; break; - case DIKS_GRAVE_ACCENT: - evt->key_code = GF_KEY_GRAVEACCENT; break; - case DIKS_CURLY_BRACKET_LEFT: - evt->key_code = GF_KEY_LEFTCURLYBRACKET; break; - case DIKS_VERTICAL_BAR: - evt->key_code = GF_KEY_PIPE; break; - case DIKS_CURLY_BRACKET_RIGHT: - evt->key_code = GF_KEY_RIGHTCURLYBRACKET; break; - case DIKS_TILDE: break; - case DIKS_DELETE: - evt->key_code = GF_KEY_DEL; break; - case DIKS_CURSOR_LEFT: - evt->key_code = GF_KEY_LEFT; break; - case DIKS_CURSOR_RIGHT: - evt->key_code = GF_KEY_RIGHT; break; - case DIKS_CURSOR_UP: - evt->key_code = GF_KEY_UP; break; - case DIKS_CURSOR_DOWN: - evt->key_code = GF_KEY_DOWN; break; - case DIKS_INSERT: - evt->key_code = GF_KEY_INSERT; break; - case DIKS_HOME: - evt->key_code = GF_KEY_HOME; break; - case DIKS_END: - evt->key_code = GF_KEY_END; break; - case DIKS_PAGE_UP: - evt->key_code = GF_KEY_PAGEUP; break; - case DIKS_PAGE_DOWN: - evt->key_code = GF_KEY_PAGEDOWN; break; - case DIKS_PRINT: - evt->key_code = GF_KEY_PRINTSCREEN; break; - case DIKS_SELECT: - evt->key_code = GF_KEY_SELECT; break; - case DIKS_CLEAR: - evt->key_code = GF_KEY_CLEAR; break; - case DIKS_HELP: - evt->key_code = GF_KEY_HELP; break; - case DIKS_ZOOM: - evt->key_code = GF_KEY_ZOOM; break; - case DIKS_VOLUME_UP: - evt->key_code = GF_KEY_VOLUMEUP; break; - case DIKS_VOLUME_DOWN: - evt->key_code = GF_KEY_VOLUMEDOWN; break; - case DIKS_MUTE: - evt->key_code = GF_KEY_VOLUMEMUTE; break; - case DIKS_PLAYPAUSE: - case DIKS_PAUSE: - evt->key_code = GF_KEY_MEDIAPLAYPAUSE; break; - case DIKS_PLAY: - evt->key_code = GF_KEY_PLAY; break; - case DIKS_STOP: - evt->key_code = GF_KEY_MEDIASTOP; break; - case DIKS_PREVIOUS: - evt->key_code = GF_KEY_MEDIAPREVIOUSTRACK; break; - case DIKS_F1: - evt->key_code = GF_KEY_F1; break; - case DIKS_F2: - evt->key_code = GF_KEY_F2; break; - case DIKS_F3: - evt->key_code = GF_KEY_F3; break; - case DIKS_F4: - evt->key_code = GF_KEY_F4; break; - case DIKS_F5: - evt->key_code = GF_KEY_F5; break; - case DIKS_F6: - evt->key_code = GF_KEY_F6; break; - case DIKS_F7: - evt->key_code = GF_KEY_F7; break; - case DIKS_F8: - evt->key_code = GF_KEY_F8; break; - case DIKS_F9: - evt->key_code = GF_KEY_F9; break; - case DIKS_F10: - evt->key_code = GF_KEY_F10; break; - case DIKS_F11: - evt->key_code = GF_KEY_F11; break; - case DIKS_F12: - evt->key_code = GF_KEY_F12; break; - case DIKS_SHIFT: - evt->key_code = GF_KEY_SHIFT; break; - case DIKS_CONTROL: - evt->key_code = GF_KEY_CONTROL; break; - case DIKS_ALT: - evt->key_code = GF_KEY_ALT; break; - case DIKS_ALTGR: - evt->key_code = GF_KEY_ALTGRAPH; break; - case DIKS_META: - evt->key_code = GF_KEY_META; break; - case DIKS_CAPS_LOCK: - evt->key_code = GF_KEY_CAPSLOCK; break; - case DIKS_NUM_LOCK: - evt->key_code = GF_KEY_NUMLOCK; break; - case DIKS_SCROLL_LOCK: - evt->key_code = GF_KEY_SCROLL; break; - case DIKS_FAVORITES: - evt->key_code = GF_KEY_BROWSERFAVORITES; break; - case DIKS_CUSTOM0: - evt->key_code = GF_KEY_BROWSERREFRESH; break; - case DIKS_MENU: - evt->key_code = GF_KEY_BROWSERHOME; break; - case DIKS_POWER: - evt->key_code = GF_KEY_ENTER; break; - case DIKS_RED: - evt->key_code = GF_KEY_TAB; break; - case DIKS_GREEN: - evt->key_code = GF_KEY_CANCEL; break; - case DIKS_YELLOW: - evt->key_code = GF_KEY_COPY; break; - case DIKS_BLUE: - evt->key_code = GF_KEY_CUT; break; - case DIKS_MODE: - evt->key_code = GF_KEY_MODECHANGE; break; - case DIKS_BACK: - evt->key_code = GF_KEY_BROWSERBACK; break; - case DIKS_TV: - evt->key_code = GF_KEY_CLEAR; break; - case DIKS_OK: - evt->key_code = GF_KEY_SELECT; break; - case DIKS_REWIND: - evt->key_code = GF_KEY_BROWSERBACK; break; - case DIKS_FASTFORWARD: - evt->key_code = GF_KEY_BROWSERFORWARD; break; - case DIKS_SUBTITLE: - evt->key_code = GF_KEY_DEL; break; - case DIKS_CHANNEL_UP: - evt->key_code = GF_KEY_CHANNELUP; break; - case DIKS_CHANNEL_DOWN: - evt->key_code = GF_KEY_CHANNELDOWN; break; - case DIKS_TEXT: - evt->key_code = GF_KEY_TEXT; break; - case DIKS_INFO: - evt->key_code = GF_KEY_INFO; break; - case DIKS_EPG: - evt->key_code = GF_KEY_EPG; break; - case DIKS_RECORD: - evt->key_code = GF_KEY_RECORD; break; - case DIKS_AUDIO: - evt->key_code = GF_KEY_BEGINPAGE; break; - default: - evt->key_code = GF_KEY_UNIDENTIFIED; break; - } -} -#if 0 - -static int compare_symbol( const void *a, const void *b ) -{ - u32 *keycode = (u32 *) a; - struct predef_keyid *idname = (struct predef_keyid *) b; - - return *keycode - idname->key_code; -} - -static const char *get_local_key_name(u32 key_code) { - struct predef_keyid *predefine_keyid; - - predefine_keyid = bsearch(&key_code, keyIDs, sizeof(keyIDs) / sizeof(keyIDs[0]) - 1, sizeof(keyIDs[0]), compare_symbol ); - if (predefine_keyid) return predefine_keyid->name; - else return NULL; -} - -static void show_key_event(DirectFBVidCtx *ctx, GF_EventKey *evt ) -{ - //DirectFBVID(); - char buf[16]; - struct predef_keyid *predefine_keyid; - - predefine_keyid = bsearch(&evt->key_code, keyIDs, sizeof(keyIDs) / sizeof(keyIDs[0]) - 1, sizeof(keyIDs[0]), compare_symbol ); - - - if (predefine_keyid) { - GF_LOG(GF_LOG_INFO, GF_LOG_MMIO, ("[DirectFB] Predefined key ID from show_key_event():%s\n",predefine_keyid->name)); - } - ctx->primary->SetColor( ctx->primary, 0x60, 0x60, 0x60, 0xFF ); - snprintf (buf, sizeof(buf), "0x%X", evt->key_code); - ctx->primary->DrawString( ctx->primary, buf, -1, - ctx->width - 40, ctx->height/3, - DSTF_RIGHT ); +/* + * GPAC Multimedia Framework + * + * Copyright (c) 2005-20XX Telecom-Paristech + * All rights reserved + * + * This file is part of GPAC / DirectFB video output module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details.0 + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include +#include +#include "directfb_out.h" -} -#endif +#define DirectFBVID() DirectFBVidCtx *ctx = (DirectFBVidCtx *)driv->opaque +/** + * function DirectFBVid_DrawHLine + * - using hardware accelerator to a draw horizontal line + **/ static void DirectFBVid_DrawHLine(GF_VideoOutput *driv, u32 x, u32 y, u32 length, GF_Color color) { + u8 r = GF_COL_R(color); + u8 g = GF_COL_G(color); + u8 b = GF_COL_B(color); DirectFBVID(); - u8 r, g, b; - - //GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[DirectFB] in DirectFBVid_DrawHLine(). Drawing line x %d y %d length %d color %08X\n", x, y, length, color)); - SET_DRAWING_FLAGS( DSDRAW_NOFX ); - - r = GF_COL_R(color); - g = GF_COL_G(color); - b = GF_COL_B(color); - - ctx->primary->SetColor(ctx->primary, r, g, b, 0xFF); // no alpha - //ctx->primary->DrawLine(ctx->primary, x, y, x+length, y); // no acceleration - ctx->primary->FillRectangle(ctx->primary, x, y,length, 1); + DirectFBVid_DrawHLineWrapper(ctx, x, y, length, r, g, b); } + +/** + * function DirectFBVid_DrawHLineAlpha + * - using hardware accelerator to draw a horizontal line with alpha + **/ static void DirectFBVid_DrawHLineAlpha(GF_VideoOutput *driv, u32 x, u32 y, u32 length, GF_Color color, u8 alpha) { + u8 r = GF_COL_R(color); + u8 g = GF_COL_G(color); + u8 b = GF_COL_B(color); DirectFBVID(); - u8 r, g, b; - - //GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[DirectFB] in DirectFBVid_DrawHLineAlpha(). Alpha line drawing x %d y %d length %d color %08X alpha %d\n", x, y, length, color, alpha)); - - SET_DRAWING_FLAGS( DSDRAW_BLEND ); // use alpha - r = GF_COL_R(color); - g = GF_COL_G(color); - b = GF_COL_B(color); - - ctx->primary->SetColor(ctx->primary, r, g, b, alpha); - //ctx->primary->DrawLine(ctx->primary, x, y, x+length, y); - ctx->primary->FillRectangle(ctx->primary, x, y, length, 1); // with acceleration + DirectFBVid_DrawHLineAlphaWrapper(ctx, x, y, length, r, g, b, alpha); } + +/** + * function DirectFBVid_DrawRectangle + * - using hardware accelerator to fill a rectangle + **/ static void DirectFBVid_DrawRectangle(GF_VideoOutput *driv, u32 x, u32 y, u32 width, u32 height, GF_Color color) { + u8 r = GF_COL_R(color); + u8 g = GF_COL_G(color); + u8 b = GF_COL_B(color); + u8 a = GF_COL_A(color); DirectFBVID(); - u8 r, g, b, a; - - //GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[DirectFB] in DirectFBVid_DrawRectangle(). Drawing rectangle x %d y %d width %d height %d color %08x\n", x, y, width, height, color)); - - r = GF_COL_R(color); - g = GF_COL_G(color); - b = GF_COL_B(color); - a = GF_COL_A(color); - - SET_DRAWING_FLAGS( DSDRAW_NOFX ); - - ctx->primary->SetColor(ctx->primary, r, g, b, a); - ctx->primary->FillRectangle(ctx->primary, x, y, width, height); - //ctx->primary->Blit( ctx->primary, ctx->primary, NULL, x, y ); + DirectFBVid_DrawRectangleWrapper(ctx, x, y, width, height, r, g, b, a); } - +/** + * function DirectFBVid_Setup + * - DirectFB setup + **/ GF_Err DirectFBVid_Setup(GF_VideoOutput *driv, void *os_handle, void *os_display, u32 init_flags) { - const char* opt; - DFBResult err; - DFBSurfaceDescription dsc; - DFBSurfacePixelFormat dfbpf; - DFBAccelerationMask mask; - DeviceInfo *devices = NULL; - - DirectFBVID(); - ctx->is_init = 0; - argc=0; - DFBCHECK(DirectFBInit(&argc, & (argv) )); - - DirectFBSetOption ("bg-none", NULL); - DirectFBSetOption ("no-init-layer", NULL); - - /* create the super interface */ - DFBCHECK(DirectFBCreate( &(ctx->dfb) )); - - GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[DirectFB] Initialization\n")); - - /* create a list of input devices */ - ctx->dfb->EnumInputDevices(ctx->dfb, enum_input_device, &devices ); - - /* create an event buffer for all devices */ - DFBCHECK(ctx->dfb->CreateInputEventBuffer(ctx->dfb, DICAPS_KEYS, DFB_FALSE, &(ctx->events) )); - - /* Set the cooperative level */ - err = ctx->dfb->SetCooperativeLevel( ctx->dfb, DFSCL_FULLSCREEN ); - if (err) - DirectFBError( "Failed to set cooperative level", err ); - - /* Get the primary surface, i.e. the surface of the primary layer. */ - dsc.flags = DSDESC_CAPS; - dsc.caps = DSCAPS_PRIMARY | DSCAPS_DOUBLE; - - if (ctx->use_systems_memory) dsc.caps |= DSCAPS_SYSTEMONLY; - - DFBCHECK(ctx->dfb->CreateSurface( ctx->dfb, &dsc, &(ctx->primary) )); - - ctx->primary->GetPixelFormat( ctx->primary, &dfbpf ); - ctx->pixel_format = DirectFBVid_TranslatePixelFormatToGPAC(dfbpf); - ctx->primary->GetSize( ctx->primary, &(ctx->width), &(ctx->height) ); - ctx->primary->Clear( ctx->primary, 0, 0, 0, 0xFF); - - ctx->disable_acceleration = 0; - opt = gf_modules_get_option((GF_BaseInterface *)driv, "DirectFB", "DisableAcceleration"); - if (!opt) gf_modules_set_option((GF_BaseInterface *)driv, "DirectFB", "DisableAcceleration", "no"); - if (opt && !strcmp(opt, "yes")) ctx->disable_acceleration = 1; - - ctx->disable_display = 0; - opt = gf_modules_get_option((GF_BaseInterface *)driv, "DirectFB", "DisableDisplay"); - if (!opt) gf_modules_set_option((GF_BaseInterface *)driv, "DirectFB", "DisableDisplay", "no"); - if (opt && !strcmp(opt, "yes")) ctx->disable_display = 1; - - - ctx->flip_mode = DSFLIP_BLIT; - opt = gf_modules_get_option((GF_BaseInterface *)driv, "DirectFB", "FlipSyncMode"); - if (!opt) gf_modules_set_option((GF_BaseInterface *)driv, "DirectFB", "FlipSyncMode", "waitsync"); - if (!opt || !strcmp(opt, "waitsync")) ctx->flip_mode |= DSFLIP_WAITFORSYNC; - else if (opt && !strcmp(opt, "wait")) ctx->flip_mode |= DSFLIP_WAIT; - else if (opt && !strcmp(opt, "sync")) ctx->flip_mode |= DSFLIP_ONSYNC; - else if (opt && !strcmp(opt, "swap")) ctx->flip_mode &= ~DSFLIP_BLIT; - - opt = gf_modules_get_option((GF_BaseInterface *)driv, "DirectFB", "DisableBlit"); - if (!opt) gf_modules_set_option((GF_BaseInterface *)driv, "DirectFB", "DisableBlit", "no"); - if (opt && !strcmp(opt, "all")) { - driv->hw_caps &= ~(GF_VIDEO_HW_HAS_RGB | GF_VIDEO_HW_HAS_RGBA | GF_VIDEO_HW_HAS_YUV); - } - else if (opt && !strcmp(opt, "yuv")) driv->hw_caps &= ~GF_VIDEO_HW_HAS_YUV; - else if (opt && !strcmp(opt, "rgb")) driv->hw_caps &= ~GF_VIDEO_HW_HAS_RGB; - else if (opt && !strcmp(opt, "rgba")) driv->hw_caps &= ~GF_VIDEO_HW_HAS_RGBA; - + const char* opt; + + DirectFBVID(); + DirectFBVid_CtxSetIsInit(ctx, 0); + + // initialisation and surface creation + GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[DirectFB] Initialization\n")); + // check window mode used - SDL or X11 + { + WINDOW_MODE window_mode = 0; + opt = gf_modules_get_option((GF_BaseInterface *)driv, "DirectFB", "WindowMode"); + if (!opt) gf_modules_set_option((GF_BaseInterface *)driv, "DirectFB", "WindowMode", "X11"); + if (!opt || !strcmp(opt, "X11")) window_mode = WINDOW_X11; + else if (opt && !strcmp(opt, "SDL")) window_mode = WINDOW_SDL; + DirectFBVid_InitAndCreateSurface(ctx, window_mode); + } + + // check hardware accelerator configuration + DirectFBVid_CtxSetDisableAcceleration(ctx, 0); + opt = gf_modules_get_option((GF_BaseInterface *)driv, "DirectFB", "DisableAcceleration"); + if (!opt) gf_modules_set_option((GF_BaseInterface *)driv, "DirectFB", "DisableAcceleration", "no"); + if (opt && !strcmp(opt, "yes")) DirectFBVid_CtxSetDisableAcceleration(ctx, 1); + + // check for display configuration + DirectFBVid_CtxSetDisableDisplay(ctx, 0); + opt = gf_modules_get_option((GF_BaseInterface *)driv, "DirectFB", "DisableDisplay"); + if (!opt) gf_modules_set_option((GF_BaseInterface *)driv, "DirectFB", "DisableDisplay", "no"); + if (opt && !strcmp(opt, "yes")) DirectFBVid_CtxSetDisableDisplay(ctx, 1); + + // set flip mode + { + FLIP_MODE flip_mode = 0; + opt = gf_modules_get_option((GF_BaseInterface *)driv, "DirectFB", "FlipSyncMode"); + if (!opt) gf_modules_set_option((GF_BaseInterface *)driv, "DirectFB", "FlipSyncMode", "waitsync"); + if (!opt || !strcmp(opt, "waitsync")) flip_mode |= FLIP_WAITFORSYNC; + else if (opt && !strcmp(opt, "wait")) flip_mode |= FLIP_WAIT; + else if (opt && !strcmp(opt, "sync")) flip_mode |= FLIP_ONSYNC; + else if (opt && !strcmp(opt, "swap")) flip_mode |= FLIP_SWAP; + + DirectFBVid_CtxSetFlipMode(ctx, flip_mode); + } - if (!ctx->disable_acceleration){ - ctx->primary->GetAccelerationMask( ctx->primary, NULL, &mask ); - GF_LOG(GF_LOG_INFO, GF_LOG_MMIO, ("[DirectFB] hardware acceleration mask %08x \n", mask)); + // enable/disable blit + opt = gf_modules_get_option((GF_BaseInterface *)driv, "DirectFB", "DisableBlit"); + if (!opt) gf_modules_set_option((GF_BaseInterface *)driv, "DirectFB", "DisableBlit", "no"); + if (opt && !strcmp(opt, "all")) { + driv->hw_caps &= ~(GF_VIDEO_HW_HAS_RGB | GF_VIDEO_HW_HAS_RGBA | GF_VIDEO_HW_HAS_YUV); + } + else if (opt && !strcmp(opt, "yuv")) driv->hw_caps &= ~GF_VIDEO_HW_HAS_YUV; + else if (opt && !strcmp(opt, "rgb")) driv->hw_caps &= ~GF_VIDEO_HW_HAS_RGB; + else if (opt && !strcmp(opt, "rgba")) driv->hw_caps &= ~GF_VIDEO_HW_HAS_RGBA; - if (mask & DFXL_DRAWLINE ) // DrawLine() is accelerated. DFXL_DRAWLINE - ctx->accel_drawline = 1; - if (mask & DFXL_FILLRECTANGLE) // FillRectangle() is accelerated. - ctx->accel_fillrect = 1; + if (!DirectFBVid_CtxGetDisableAcceleration(ctx)) { + // check for functions that are hardware accelerated + DirectFBVid_CtxPrimaryProcessGetAccelerationMask(ctx); - GF_LOG(GF_LOG_INFO, GF_LOG_MMIO, ("[DirectFB] hardware acceleration mask %08x - Line: %d Rectangle: %d\n", mask, ctx->accel_drawline, ctx->accel_fillrect)); + driv->hw_caps |= GF_VIDEO_HW_HAS_LINE_BLIT; + driv->DrawHLine = DirectFBVid_DrawHLine; + driv->DrawHLineAlpha = DirectFBVid_DrawHLineAlpha; + driv->DrawRectangle = DirectFBVid_DrawRectangle; - driv->hw_caps |= GF_VIDEO_HW_HAS_LINE_BLIT; - driv->DrawHLine = DirectFBVid_DrawHLine; - driv->DrawHLineAlpha = DirectFBVid_DrawHLineAlpha; - driv->DrawRectangle = DirectFBVid_DrawRectangle; - } + //GF_LOG(GF_LOG_INFO, GF_LOG_MMIO, ("[DirectFB] hardware acceleration mask %08x - Line: %d Rectangle: %d\n", mask, ctx->accel_drawline, ctx->accel_fillrect)); + } - ctx->is_init = 1; - GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[DirectFB] Initialization success - HW caps %08x\n", driv->hw_caps)); + // end of initialization + DirectFBVid_CtxSetIsInit(ctx, 1); + GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[DirectFB] Initialization success - HW caps %08x\n", driv->hw_caps)); -// GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[DirectFB] Pixel format %s\n", gf_4cc_to_str(ctx->pixel_format))); - return GF_OK; +// GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[DirectFB] Pixel format %s\n", gf_4cc_to_str(ctx->pixel_format))); + return GF_OK; } + +/** + * function DirectFBVid_Shutdown + * - shutdown DirectFB module + **/ static void DirectFBVid_Shutdown(GF_VideoOutput *driv) { - DirectFBVID(); - if (!ctx->is_init) return; - //ctx->primary->Clear(ctx->primary,0,0,0,0); - //ctx->primary->Flip( ctx->primary, NULL, DSFLIP_NONE); - //ctx->primary->Clear(ctx->primary,0,0,0,0); - ctx->primary->Release( ctx->primary ); - ctx->events->Release( ctx->events ); - ctx->dfb->Release( ctx->dfb ); - ctx->is_init = 0; - system("stgfb_control /dev/fb0 a 0"); + u32 ret; + DirectFBVID(); + ret = DirectFBVid_ShutdownWrapper(ctx); + if (ret) { + GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[DirectFB] Failed to shutdown properly\n")); + } } + +/** + * function DirectFBVid_Flush + * - flushing buffer + **/ static GF_Err DirectFBVid_Flush(GF_VideoOutput *driv, GF_Window *dest) { - const char* opt; - - DirectFBVID(); - GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[DirectFB] Flipping backbuffer\n")); - if (ctx->disable_display) return GF_OK; + DirectFBVID(); + GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[DirectFB] Flipping backbuffer\n")); + // if display is disabled, nothing to be done + if (DirectFBVid_CtxGetDisableDisplay(ctx)) return GF_OK; - ctx->primary->Flip( ctx->primary, NULL, ctx->flip_mode); + return DirectFBVid_CtxPrimaryFlip(ctx); } +/** + * function DirectFBVid_SetFullScreen + * - set fullscreen mode + **/ GF_Err DirectFBVid_SetFullScreen(GF_VideoOutput *driv, u32 bFullScreenOn, u32 *screen_width, u32 *screen_height) { - DFBResult err; - DirectFBVID(); + DirectFBVID(); - *screen_width = ctx->width; - *screen_height = ctx->height; + *screen_width = DirectFBVid_CtxGetWidth(ctx); + *screen_height = DirectFBVid_CtxGetHeight(ctx); - return GF_OK; + return GF_OK; } +/** + * function DirectFBVid_ProcessMessageQueue + * - handle DirectFB events + **/ Bool DirectFBVid_ProcessMessageQueue(DirectFBVidCtx *ctx, GF_VideoOutput *driv) { - DFBInputEvent directfb_evt; - GF_Event gpac_evt; - - while (ctx->events->GetEvent( ctx->events, DFB_EVENT(&directfb_evt) ) == DFB_OK) - { - u32 i; - switch (directfb_evt.type){ - case DIET_KEYPRESS: - case DIET_KEYRELEASE: - //GF_LOG(GF_LOG_INFO, GF_LOG_MMIO, ("[DirectFB] in ProcessMessageQueue\n")); - directfb_translate_key(directfb_evt.key_symbol, &gpac_evt.key); - gpac_evt.type = (directfb_evt.type == DIET_KEYPRESS) ? GF_EVENT_KEYDOWN : GF_EVENT_KEYUP; -#if 0 - fprintf(stderr, "before %d %s\n", gpac_evt.key.key_code, - gf_dom_get_key_name(gpac_evt.key.key_code)); - - for (i=0;i<200; i++) { - fprintf(stderr, "%03d %s\n", i, gf_dom_get_key_name(i)); - } -#endif - driv->on_event(driv->evt_cbk_hdl, &gpac_evt); - //fprintf(stderr, "after %d %s\n", gpac_evt.key.key_code, gf_dom_get_key_name(gpac_evt.key.key_code)); - default: - break; + GF_Event gpac_evt; + memset(&gpac_evt, 0, sizeof(gpac_evt)); + while (DirectFBVid_ProcessMessageQueueWrapper(ctx, &gpac_evt.type, &gpac_evt.key.flags, &gpac_evt.key.key_code, &gpac_evt.mouse.x, &gpac_evt.mouse.y, &gpac_evt.mouse.button) == GF_OK) + { + driv->on_event(driv->evt_cbk_hdl, &gpac_evt); + gpac_evt.key.flags = 0; } - } - - return GF_OK; + return GF_OK; } +/** + * function DirectFBVid_ProcessEvent + * - process events + **/ static GF_Err DirectFBVid_ProcessEvent(GF_VideoOutput *driv, GF_Event *evt) { - DirectFBVID(); + DirectFBVID(); - if (!evt) { - DirectFBVid_ProcessMessageQueue(ctx, driv); - return GF_OK; - } + if (!evt) { + DirectFBVid_ProcessMessageQueue(ctx, driv); + return GF_OK; + } - switch (evt->type) { - case GF_EVENT_SIZE: - if ((ctx->width !=evt->size.width) || (ctx->height != evt->size.height)) { - GF_Event gpac_evt; - gpac_evt.type = GF_EVENT_SIZE; - gpac_evt.size.width = ctx->width; - gpac_evt.size.height = ctx->height; - driv->on_event(driv->evt_cbk_hdl, &gpac_evt); - } - return GF_OK; + switch (evt->type) { + case GF_EVENT_SIZE: + if ((DirectFBVid_CtxGetWidth(ctx) !=evt->size.width) || (DirectFBVid_CtxGetHeight(ctx) != evt->size.height)) { + GF_Event gpac_evt; + gpac_evt.type = GF_EVENT_SIZE; + gpac_evt.size.width = DirectFBVid_CtxGetWidth(ctx); + gpac_evt.size.height = DirectFBVid_CtxGetHeight(ctx); + driv->on_event(driv->evt_cbk_hdl, &gpac_evt); + } + return GF_OK; - case GF_EVENT_VIDEO_SETUP: - if (evt->setup.opengl_mode) return GF_NOT_SUPPORTED; + case GF_EVENT_VIDEO_SETUP: + if (evt->setup.opengl_mode) return GF_NOT_SUPPORTED; - if ((ctx->width !=evt->setup.width) || (ctx->height != evt->setup.height)) { - GF_Event gpac_evt; - gpac_evt.type = GF_EVENT_SIZE; - gpac_evt.size.width = ctx->width; - gpac_evt.size.height = ctx->height; - driv->on_event(driv->evt_cbk_hdl, &gpac_evt); - } - return GF_OK; - default: - return GF_OK; - } + if ((DirectFBVid_CtxGetWidth(ctx) !=evt->setup.width) || (DirectFBVid_CtxGetHeight(ctx) != evt->setup.height)) { + GF_Event gpac_evt; + gpac_evt.type = GF_EVENT_SIZE; + gpac_evt.size.width = DirectFBVid_CtxGetWidth(ctx); + gpac_evt.size.height = DirectFBVid_CtxGetHeight(ctx); + driv->on_event(driv->evt_cbk_hdl, &gpac_evt); + } + return GF_OK; + default: + return GF_OK; + } } + +/** + * function DirectFBVid_LockBackBuffer + * - lock the surface to access certain data + **/ static GF_Err DirectFBVid_LockBackBuffer(GF_VideoOutput *driv, GF_VideoSurface *video_info, u32 do_lock) { - DFBResult ret; - u32 pitch; - void *buf; - u32 width, height; - DFBSurfacePixelFormat format; - - DirectFBVID(); - if (!ctx->primary) return GF_BAD_PARAM; - if (do_lock) - { - if (!video_info) return GF_BAD_PARAM; - ret = ctx->primary->Lock(ctx->primary, DSLF_READ | DSLF_WRITE, &buf, &pitch); - if (ret != DFB_OK) return GF_IO_ERR; - - video_info->width = ctx->width; - video_info->height = ctx->height; - video_info->pitch_x = 0; - video_info->pitch_y = pitch; - video_info->video_buffer = buf; - video_info->pixel_format = ctx->pixel_format; - video_info->is_hardware_memory = !ctx->use_systems_memory; - - GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[DirectFB] backbuffer locked\n")); - } else { - ctx->primary->Unlock(ctx->primary); - GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[DirectFB] backbuffer unlocked\n")); - } - return GF_OK; + void *buf; + u32 pitch, ret; + DirectFBVID(); + if (!DirectFBVid_CtxGetPrimary(ctx)) return GF_BAD_PARAM; + if (do_lock) + { + if (!video_info) return GF_BAD_PARAM; + // lock surface first in order to access data below + ret = DirectFBVid_CtxPrimaryLock(ctx, &buf, &pitch); + if (ret != 0) return GF_IO_ERR; + + // fetch data + video_info->width = DirectFBVid_CtxGetWidth(ctx); + video_info->height = DirectFBVid_CtxGetHeight(ctx); + video_info->pitch_x = 0; + video_info->pitch_y = pitch; + video_info->video_buffer = buf; + video_info->pixel_format = DirectFBVid_CtxGetPixelFormat(ctx); + video_info->is_hardware_memory = !DirectFBVid_CtxIsHwMemory(ctx); + + GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[DirectFB] backbuffer locked\n")); + } else { + // unlock the surface after direct access + DirectFBVid_CtxPrimaryUnlock(ctx); + GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[DirectFB] backbuffer unlocked\n")); + } + + return GF_OK; } + +/** + * function DirectFBVid_Blit + * - blit a surface + **/ static GF_Err DirectFBVid_Blit(GF_VideoOutput *driv, GF_VideoSurface *video_src, GF_Window *src_wnd, GF_Window *dst_wnd, u32 overlay_type) { - DirectFBVID(); - - DFBResult res; - DFBSurfaceDescription srcdesc; - IDirectFBSurface *src; - DFBRectangle dfbsrc, dfbdst; - DFBAccelerationMask mask; - - if (overlay_type != 0) return GF_NOT_SUPPORTED; - if (ctx->disable_display) return GF_OK; - - memset(&srcdesc, 0, sizeof(srcdesc)); - - srcdesc.flags = DSDESC_WIDTH | DSDESC_HEIGHT | DSDESC_PIXELFORMAT | DSDESC_PREALLOCATED; - srcdesc.width = video_src->width; - srcdesc.height = video_src->height; - srcdesc.pixelformat = DirectFBVid_TranslatePixelFormatFromGPAC(video_src->pixel_format); - srcdesc.preallocated[0].data = video_src->video_buffer; - srcdesc.preallocated[0].pitch = video_src->pitch_y; - - - switch (video_src->pixel_format){ - case GF_PIXEL_ARGB: //return DSPF_ARGB; - case GF_PIXEL_RGBA: //return DSPF_ARGB; - ctx->primary->SetBlittingFlags(ctx->primary, DSBLIT_BLEND_ALPHACHANNEL); - break; - default: - ctx->primary->SetBlittingFlags(ctx->primary, DSBLIT_NOFX); - } - - - res = ctx->dfb->CreateSurface(ctx->dfb, &srcdesc, &src); - if (res != DFB_OK) { - GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[DirectFB] cannot create blit source surface for pixel format %s: %s (%d)\n", gf_4cc_to_str(video_src->pixel_format), DirectFBErrorString(res), res)); - return GF_IO_ERR; - } - - - dfbsrc.x = src_wnd->x; - dfbsrc.y = src_wnd->y; - dfbsrc.w = src_wnd->w; - dfbsrc.h = src_wnd->h; - - if (!src_wnd->x && !src_wnd->y && (dst_wnd->w==src_wnd->w) && (dst_wnd->h==src_wnd->h)) { - ctx->primary->Blit(ctx->primary, src, &dfbsrc, dst_wnd->x, dst_wnd->y); - } else { - dfbdst.x = dst_wnd->x; - dfbdst.y = dst_wnd->y; - dfbdst.w = dst_wnd->w; - dfbdst.h = dst_wnd->h; - ctx->primary->StretchBlit(ctx->primary, src, &dfbsrc, &dfbdst); - } - - src->Release(src); - GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[DirectFB] blit successful\n")); - - return GF_OK; + u32 ret; + DirectFBVID(); + + ret = DirectFBVid_BlitWrapper(ctx, video_src->width, video_src->height, video_src->pixel_format, video_src->video_buffer, video_src->pitch_y, src_wnd->x, src_wnd->y, src_wnd->w, src_wnd->h, dst_wnd->x, dst_wnd->y, dst_wnd->w, dst_wnd->h, overlay_type); + if (ret) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[DirectFB] cannot create blit source surface for pixel format %s\n", gf_4cc_to_str(video_src->pixel_format))); + return GF_NOT_SUPPORTED; + } + + GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[DirectFB] blit successful\n")); + + return GF_OK; } + +/** + * function DirectFBNewVideo + * - creates a DirectFb module + **/ void *DirectFBNewVideo() { - DirectFBVidCtx *ctx; - GF_VideoOutput *driv; - - driv = gf_malloc(sizeof(GF_VideoOutput)); - memset(driv, 0, sizeof(GF_VideoOutput)); - GF_REGISTER_MODULE_INTERFACE(driv, GF_VIDEO_OUTPUT_INTERFACE, "DirectFB Video Output", "gpac distribution"); - - ctx = gf_malloc(sizeof(DirectFBVidCtx)); - memset(ctx, 0, sizeof(DirectFBVidCtx)); - - /* GF_VideoOutput */ - driv->opaque = ctx; - driv->Setup = DirectFBVid_Setup; - driv->Shutdown = DirectFBVid_Shutdown; - driv->Flush = DirectFBVid_Flush; - driv->SetFullScreen = DirectFBVid_SetFullScreen; - driv->ProcessEvent = DirectFBVid_ProcessEvent; - driv->LockBackBuffer = DirectFBVid_LockBackBuffer; - driv->LockOSContext = NULL; - driv->Blit = DirectFBVid_Blit; - driv->hw_caps |= GF_VIDEO_HW_HAS_RGB | GF_VIDEO_HW_HAS_RGBA | GF_VIDEO_HW_HAS_YUV | GF_VIDEO_HW_HAS_STRETCH; - - return driv; + DirectFBVidCtx *ctx; + GF_VideoOutput *driv; + + driv = gf_malloc(sizeof(GF_VideoOutput)); + memset(driv, 0, sizeof(GF_VideoOutput)); + GF_REGISTER_MODULE_INTERFACE(driv, GF_VIDEO_OUTPUT_INTERFACE, "DirectFB Video Output", "gpac distribution"); + + ctx = gf_malloc(DirectFBVid_GetCtxSizeOf()); + memset(ctx, 0, DirectFBVid_GetCtxSizeOf()); + + /* GF_VideoOutput */ + driv->opaque = ctx; + driv->Setup = DirectFBVid_Setup; + driv->Shutdown = DirectFBVid_Shutdown; + driv->Flush = DirectFBVid_Flush; + driv->SetFullScreen = DirectFBVid_SetFullScreen; + driv->ProcessEvent = DirectFBVid_ProcessEvent; + driv->LockBackBuffer = DirectFBVid_LockBackBuffer; + driv->LockOSContext = NULL; + driv->Blit = DirectFBVid_Blit; + driv->hw_caps |= GF_VIDEO_HW_HAS_RGB | GF_VIDEO_HW_HAS_RGBA | GF_VIDEO_HW_HAS_YUV | GF_VIDEO_HW_HAS_STRETCH; + + return driv; } + +/** + * function DirectFBDeleteVideo + * - delete video + **/ void DirectFBDeleteVideo(void *ifce) { - GF_VideoOutput *driv = (GF_VideoOutput *)ifce; - DirectFBVID(); - gf_free(ctx); - gf_free(driv); + GF_VideoOutput *driv = (GF_VideoOutput *)ifce; + DirectFBVID(); + gf_free(ctx); + gf_free(driv); } @@ -785,6 +373,7 @@ const u32 *QueryInterfaces() return si; } + /*interface create*/ GF_BaseInterface *LoadInterface(u32 InterfaceType) { @@ -792,6 +381,7 @@ GF_BaseInterface *LoadInterface(u32 InterfaceType) return NULL; } + /*interface destroy*/ void ShutdownInterface(GF_BaseInterface *ifce) { diff --git a/modules/directfb_out/directfb_out.h b/modules/directfb_out/directfb_out.h index ae24d61..30ac3cf 100755 --- a/modules/directfb_out/directfb_out.h +++ b/modules/directfb_out/directfb_out.h @@ -1,280 +1,73 @@ +/* + * GPAC Multimedia Framework + * + * Copyright (c) 2005-20XX Telecom-Paristech + * All rights reserved + * + * This file is part of GPAC / DirectFB video output module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + #ifndef _DIRECTFB_OUT_H_ #define _DIRECTFB_OUT_H_ +typedef struct __DirectFBVidCtx DirectFBVidCtx; -#include -#include - -/* DirectFB */ -#define __DIRECT__STDTYPES__ //prevent u8, s8, ... definitions by directFB as we have them in GPAC -#include -#include -#include -#include - -static int do_xor = 0; - -/* macro for a safe call to DirectFB functions */ -#define DFBCHECK(x...) \ - do { \ - err = x; \ - if (err != DFB_OK) { \ - fprintf( stderr, "%s <%d>:\n\t", __FILE__, __LINE__ ); \ - DirectFBErrorFatal( #x, err ); \ - } \ - } while (0) - -#define SET_DRAWING_FLAGS( flags ) \ - ctx->primary->SetDrawingFlags( ctx->primary, (flags) | (do_xor ? DSDRAW_XOR : 0) ) - -typedef struct _DeviceInfo DeviceInfo; - -struct _DeviceInfo { - DFBInputDeviceID device_id; - DFBInputDeviceDescription desc; - DeviceInfo *next; -}; - -struct predef_keyid { - u32 key_code; - const char *name; -}; +typedef enum { + WINDOW_X11 = 1, + WINDOW_SDL = 1 << 1, +} WINDOW_MODE; -#if 0 -#define PredefineKeyID(predefined_key_identifiers) struct predef_keyid predefined_key_identifiers[] = {\ - { GF_KEY_ACCEPT, "Accept" },\ - { GF_KEY_AGAIN, "Again" },\ - { GF_KEY_ALLCANDIDATES, "AllCandidates" },\ - { GF_KEY_ALPHANUM, "Alphanumeric" },\ - { GF_KEY_ALT, "Alt" },\ - { GF_KEY_ALTGRAPH, "AltGraph" },\ - { GF_KEY_APPS, "Apps" },\ - { GF_KEY_ATTN, "Attn" },\ - { GF_KEY_BROWSERBACK, "BrowserBack" },\ - { GF_KEY_BROWSERFAVORITES, "BrowserFavorites" },\ - { GF_KEY_BROWSERFORWARD, "BrowserForward" },\ - { GF_KEY_BROWSERHOME, "BrowserHome" },\ - { GF_KEY_BROWSERREFRESH, "BrowserRefresh" },\ - { GF_KEY_BROWSERSEARCH, "BrowserSearch" },\ - { GF_KEY_BROWSERSTOP, "BrowserStop" },\ - { GF_KEY_CAPSLOCK, "CapsLock" },\ - { GF_KEY_CLEAR, "Clear" },\ - { GF_KEY_CODEINPUT, "CodeInput" },\ - { GF_KEY_COMPOSE, "Compose" },\ - { GF_KEY_CONTROL, "Control" },\ - { GF_KEY_CRSEL, "Crsel" },\ - { GF_KEY_CONVERT, "Convert" },\ - { GF_KEY_COPY, "Copy" },\ - { GF_KEY_CUT, "Cut" },\ - { GF_KEY_DOWN, "Down" },\ - { GF_KEY_END, "End" },\ - { GF_KEY_ENTER, "Enter" },\ - { GF_KEY_EPG, "EPG" }, \ - { GF_KEY_ERASEEOF, "EraseEof" },\ - { GF_KEY_EXECUTE, "Execute" },\ - { GF_KEY_EXSEL, "Exsel" },\ - { GF_KEY_F1, "F1" },\ - { GF_KEY_F2, "F2" },\ - { GF_KEY_F3, "F3" },\ - { GF_KEY_F4, "F4" },\ - { GF_KEY_F5, "F5" },\ - { GF_KEY_F6, "F6" },\ - { GF_KEY_F7, "F7" },\ - { GF_KEY_F8, "F8" },\ - { GF_KEY_F9, "F9" },\ - { GF_KEY_F10, "F10" },\ - { GF_KEY_F11, "F11" },\ - { GF_KEY_F12, "F12" },\ - { GF_KEY_F13, "F13" },\ - { GF_KEY_F14, "F14" },\ - { GF_KEY_F15, "F15" },\ - { GF_KEY_F16, "F16" },\ - { GF_KEY_F17, "F17" },\ - { GF_KEY_F18, "F18" },\ - { GF_KEY_F19, "F19" },\ - { GF_KEY_F20, "F20" },\ - { GF_KEY_F21, "F21" },\ - { GF_KEY_F22, "F22" },\ - { GF_KEY_F23, "F23" },\ - { GF_KEY_F24, "F24" },\ - { GF_KEY_FINALMODE, "FinalMode" },\ - { GF_KEY_FIND, "Find" },\ - { GF_KEY_FULLWIDTH, "FullWidth" },\ - { GF_KEY_HALFWIDTH, "HalfWidth" },\ - { GF_KEY_HANGULMODE, "HangulMode" },\ - { GF_KEY_HANJAMODE, "HanjaMode" },\ - { GF_KEY_HELP, "Help" },\ - { GF_KEY_HIRAGANA, "Hiragana" },\ - { GF_KEY_HOME, "Home" },\ - { GF_KEY_INFO, "Info" }, \ - { GF_KEY_INSERT, "Insert" },\ - { GF_KEY_JAPANESEHIRAGANA, "JapaneseHiragana" },\ - { GF_KEY_JAPANESEKATAKANA, "JapaneseKatakana" },\ - { GF_KEY_JAPANESEROMAJI, "JapaneseRomaji" },\ - { GF_KEY_JUNJAMODE, "JunjaMode" },\ - { GF_KEY_KANAMODE, "KanaMode" },\ - { GF_KEY_KANJIMODE, "KanjiMode" },\ - { GF_KEY_KATAKANA, "Katakana" },\ - { GF_KEY_LAUNCHAPPLICATION1, "LaunchApplication1" },\ - { GF_KEY_LAUNCHAPPLICATION2, "LaunchApplication2" },\ - { GF_KEY_LAUNCHMAIL, "LaunchMail" },\ - { GF_KEY_LEFT, "Left" },\ - { GF_KEY_META, "Meta" },\ - { GF_KEY_MEDIANEXTTRACK, "MediaNextTrack" },\ - { GF_KEY_MEDIAPLAYPAUSE, "MediaPlayPause" },\ - { GF_KEY_MEDIAPREVIOUSTRACK, "MediaPreviousTrack" },\ - { GF_KEY_MEDIASTOP, "MediaStop" },\ - { GF_KEY_MODECHANGE, "ModeChange" },\ - { GF_KEY_NONCONVERT, "Nonconvert" },\ - { GF_KEY_NUMLOCK, "NumLock" },\ - { GF_KEY_PAGEDOWN, "PageDown" },\ - { GF_KEY_PAGEUP, "PageUp" },\ - { GF_KEY_PASTE, "Paste" },\ - { GF_KEY_PAUSE, "Pause" },\ - { GF_KEY_PLAY, "Play" },\ - { GF_KEY_PREVIOUSCANDIDATE, "PreviousCandidate" },\ - { GF_KEY_PRINTSCREEN, "PrintScreen" },\ - { GF_KEY_PROCESS, "Process" },\ - { GF_KEY_PROPS, "Props" },\ - { GF_KEY_RIGHT, "Right" },\ - { GF_KEY_ROMANCHARACTERS, "RomanCharacters" },\ - { GF_KEY_SCROLL, "Scroll" },\ - { GF_KEY_SELECT, "Select" },\ - { GF_KEY_SELECTMEDIA, "SelectMedia" },\ - { GF_KEY_SHIFT, "Shift" },\ - { GF_KEY_STOP, "Stop" },\ - { GF_KEY_UP, "Up" },\ - { GF_KEY_UNDO, "Undo" },\ - { GF_KEY_VOLUMEDOWN, "VolumeDown" },\ - { GF_KEY_VOLUMEMUTE, "VolumeMute" },\ - { GF_KEY_VOLUMEUP, "VolumeUp" },\ - { GF_KEY_WIN, "Win" },\ - { GF_KEY_ZOOM, "Zoom" },\ - { GF_KEY_BACKSPACE, "U+0008" },\ - { GF_KEY_TAB, "U+0009" },\ - { GF_KEY_CANCEL, "U+0018" },\ - { GF_KEY_ESCAPE, "U+001B" },\ - { GF_KEY_SPACE, "U+0020" },\ - { GF_KEY_EXCLAMATION, "U+0021" },\ - { GF_KEY_QUOTATION, "U+0022" },\ - { GF_KEY_NUMBER, "U+0023" },\ - { GF_KEY_DOLLAR, "U+0024" },\ - { GF_KEY_AMPERSAND, "U+0026" },\ - { GF_KEY_APOSTROPHE, "U+0027" },\ - { GF_KEY_LEFTPARENTHESIS, "U+0028" },\ - { GF_KEY_RIGHTPARENTHESIS, "U+0029" },\ - { GF_KEY_STAR, "U+002A" },\ - { GF_KEY_PLUS, "U+002B" },\ - { GF_KEY_COMMA, "U+002C" },\ - { GF_KEY_HYPHEN, "U+002D" },\ - { GF_KEY_FULLSTOP, "U+002E" },\ - { GF_KEY_SLASH, "U+002F" },\ - { GF_KEY_0, "U+0030" },\ - { GF_KEY_1, "U+0031" },\ - { GF_KEY_2, "U+0032" },\ - { GF_KEY_3, "U+0033" },\ - { GF_KEY_4, "U+0034" },\ - { GF_KEY_5, "U+0035" },\ - { GF_KEY_6, "U+0036" },\ - { GF_KEY_7, "U+0037" },\ - { GF_KEY_8, "U+0038" },\ - { GF_KEY_9, "U+0039" },\ - { GF_KEY_COLON, "U+003A" },\ - { GF_KEY_SEMICOLON, "U+003B" },\ - { GF_KEY_LESSTHAN, "U+003C" },\ - { GF_KEY_EQUALS, "U+003D" },\ - { GF_KEY_GREATERTHAN, "U+003E" },\ - { GF_KEY_QUESTION, "U+003F" },\ - { GF_KEY_AT, "U+0040" },\ - { GF_KEY_A, "U+0041" },\ - { GF_KEY_B, "U+0042" },\ - { GF_KEY_C, "U+0043" },\ - { GF_KEY_D, "U+0044" },\ - { GF_KEY_E, "U+0045" },\ - { GF_KEY_F, "U+0046" },\ - { GF_KEY_G, "U+0047" },\ - { GF_KEY_H, "U+0048" },\ - { GF_KEY_I, "U+0049" },\ - { GF_KEY_J, "U+004A" },\ - { GF_KEY_K, "U+004B" },\ - { GF_KEY_L, "U+004C" },\ - { GF_KEY_M, "U+004D" },\ - { GF_KEY_N, "U+004E" },\ - { GF_KEY_O, "U+004F" },\ - { GF_KEY_P, "U+0050" },\ - { GF_KEY_Q, "U+0051" },\ - { GF_KEY_R, "U+0052" },\ - { GF_KEY_S, "U+0053" },\ - { GF_KEY_T, "U+0054" },\ - { GF_KEY_U, "U+0055" },\ - { GF_KEY_V, "U+0056" },\ - { GF_KEY_W, "U+0057" },\ - { GF_KEY_X, "U+0058" },\ - { GF_KEY_Y, "U+0059" },\ - { GF_KEY_Z, "U+005A" },\ - { GF_KEY_LEFTSQUAREBRACKET, "U+005B" },\ - { GF_KEY_BACKSLASH, "U+005C" },\ - { GF_KEY_RIGHTSQUAREBRACKET, "U+005D" },\ - { GF_KEY_CIRCUM, "U+005E" },\ - { GF_KEY_UNDERSCORE, "U+005F" },\ - { GF_KEY_GRAVEACCENT, "U+0060" },\ - { GF_KEY_LEFTCURLYBRACKET, "U+007B" },\ - { GF_KEY_PIPE, "U+007C" },\ - { GF_KEY_RIGHTCURLYBRACKET, "U+007D" },\ - { GF_KEY_DEL, "U+007F" },\ - { GF_KEY_INVERTEXCLAMATION, "U+00A1" },\ - { GF_KEY_DEADGRAVE, "U+0300" },\ - { GF_KEY_DEADEACUTE, "U+0301" },\ - { GF_KEY_DEADCIRCUM, "U+0302" },\ - { GF_KEY_DEADTILDE, "U+0303" },\ - { GF_KEY_DEADMACRON, "U+0304" }, \ - { GF_KEY_DEADBREVE, "U+0306" },\ - { GF_KEY_DEADABOVEDOT, "U+0307" },\ - { GF_KEY_DEADDIARESIS, "U+0308" }, \ - { GF_KEY_DEADRINGABOVE, "U+030A" },\ - { GF_KEY_DEADDOUBLEACUTE, "U+030B" },\ - { GF_KEY_DEADCARON, "U+030C" },\ - { GF_KEY_DEADCEDILLA, "U+0327" },\ - { GF_KEY_DEADOGONEK, "U+0328" },\ - { GF_KEY_DEADIOTA, "U+0345" },\ - { GF_KEY_EURO, "U+20AC" },\ - { GF_KEY_DEADVOICESOUND, "U+3099" },\ - { GF_KEY_DEADSEMIVOICESOUND, "U+309A" }\ -}; -#endif -typedef struct -{ - /* the super interface */ - IDirectFB *dfb; - /* the primary surface */ - IDirectFBSurface *primary; +typedef enum { + FLIP_SWAP = 1, + FLIP_WAITFORSYNC = 1 << 1, + FLIP_WAIT = 1 << 2, + FLIP_ONSYNC = 1 << 3, +} FLIP_MODE; - /* screen width, height */ - u32 width, height, pixel_format; - Bool use_systems_memory, disable_acceleration, disable_aa, is_init, disable_display; - - /* acceleration */ - int accel_drawline, accel_fillrect; - DFBSurfaceFlipFlags flip_mode; - - /*===== for key events =====*/ - - /* Input interfaces: devices and its buffer */ - IDirectFBEventBuffer *events; - - /*============================= - if using window - ============================= */ - - /* DirectFB window */ - IDirectFBWindow *window; - - /* display layer */ - IDirectFBDisplayLayer *layer; +#ifndef Bool +#define Bool u32 +#endif -} DirectFBVidCtx; +u32 DirectFBVid_TranslatePixelFormatToGPAC(u32 dfbpf); +u32 DirectFBVid_TranslatePixelFormatFromGPAC(u32 gpacpf); +size_t DirectFBVid_GetCtxSizeOf(void); +void DirectFBVid_InitAndCreateSurface(DirectFBVidCtx *ctx, WINDOW_MODE window_mode); +void DirectFBVid_CtxSetFlipMode(DirectFBVidCtx *ctx, FLIP_MODE flip_mode); +void DirectFBVid_CtxPrimaryProcessGetAccelerationMask(DirectFBVidCtx *ctx); +u32 DirectFBVid_ProcessMessageQueueWrapper(DirectFBVidCtx *ctx, u8 *type, u32 *flags, u32 *key_code, s32 *x, s32 *y, u32 *button); +void DirectFBVid_DrawHLineWrapper(DirectFBVidCtx *ctx, u32 x, u32 y, u32 length, u8 r, u8 g, u8 b); +void DirectFBVid_DrawHLineAlphaWrapper(DirectFBVidCtx *ctx, u32 x, u32 y, u32 length, u8 r, u8 g, u8 b, u8 alpha); +void DirectFBVid_DrawRectangleWrapper(DirectFBVidCtx *ctx, u32 x, u32 y, u32 width, u32 height, u8 r, u8 g, u8 b, u8 a); +u32 DirectFBVid_CtxPrimaryLock(DirectFBVidCtx *ctx, void **buf, u32 *pitch); +void DirectFBVid_CtxPrimaryUnlock(DirectFBVidCtx *ctx); +u32 DirectFBVid_CtxGetWidth(DirectFBVidCtx *ctx); +u32 DirectFBVid_CtxGetHeight(DirectFBVidCtx *ctx); +void *DirectFBVid_CtxGetPrimary(DirectFBVidCtx *ctx); +u32 DirectFBVid_CtxGetPixelFormat(DirectFBVidCtx *ctx); +Bool DirectFBVid_CtxIsHwMemory(DirectFBVidCtx *ctx); +u32 DirectFBVid_CtxPrimaryFlip(DirectFBVidCtx *ctx); +void DirectFBVid_CtxSetDisableDisplay(DirectFBVidCtx *ctx, Bool val); +Bool DirectFBVid_CtxGetDisableDisplay(DirectFBVidCtx *ctx); +void DirectFBVid_CtxSetDisableAcceleration(DirectFBVidCtx *ctx, Bool val); +Bool DirectFBVid_CtxGetDisableAcceleration(DirectFBVidCtx *ctx); +void DirectFBVid_CtxSetIsInit(DirectFBVidCtx *ctx, Bool val); +u32 DirectFBVid_ShutdownWrapper(DirectFBVidCtx *ctx); +u32 DirectFBVid_BlitWrapper(DirectFBVidCtx *ctx, u32 video_src_width, u32 video_src_height, u32 video_src_pixel_format, char *video_src_buffer, s32 video_src_pitch_y, u32 src_wnd_x, u32 src_wnd_y, u32 src_wnd_w, u32 src_wnd_h, u32 dst_wnd_x, u32 dst_wnd_y, u32 dst_wnd_w, u32 dst_wnd_h, u32 overlay_type); -void *DirectFBNewVideo(); -void DirectFBDeleteVideo(void *ifce); #endif diff --git a/modules/directfb_out/directfb_wrapper.c b/modules/directfb_out/directfb_wrapper.c new file mode 100644 index 0000000..fa536ed --- /dev/null +++ b/modules/directfb_out/directfb_wrapper.c @@ -0,0 +1,830 @@ +/* + * GPAC Multimedia Framework + * + * Copyright (c) 2005-20XX Telecom-Paristech + * All rights reserved + * + * This file is part of GPAC / DirectFB video output module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +/* DirectFB */ +#include +#include +#include +#include + +#include +#include + +#include "directfb_out.h" + +enum +{ + GF_MOUSE_LEFT = 0, + GF_MOUSE_MIDDLE, + GF_MOUSE_RIGHT +}; + +static int do_xor = 0; + +/* macro for a safe call to DirectFB functions */ +#define DFBCHECK(x...) \ + do { \ + err = x; \ + if (err != DFB_OK) { \ + fprintf( stderr, "%s <%d>:\n\t", __FILE__, __LINE__ ); \ + DirectFBErrorFatal( #x, err ); \ + } \ + } while (0) + +#define SET_DRAWING_FLAGS( flags ) \ + ctx->primary->SetDrawingFlags( ctx->primary, (flags) | (do_xor ? DSDRAW_XOR : 0) ) + + +/* Structs */ + +struct __DirectFBVidCtx +{ + /* the super interface */ + IDirectFB *dfb; + /* the primary surface */ + IDirectFBSurface *primary; + + /* for keyboard input */ + //~ IDirectFBInputDevice *keyboard; + + /* screen width, height */ + u32 width, height, pixel_format; + Bool use_systems_memory, disable_acceleration, disable_aa, is_init, disable_display; + + /* acceleration */ + int accel_drawline, accel_fillrect; + DFBSurfaceFlipFlags flip_mode; + + /*===== for key events =====*/ + + /* Input interfaces: devices and its buffer */ + IDirectFBEventBuffer *events; + + /* mouse events */ + IDirectFBInputDevice *mouse; + + /*============================= + if using window + ============================= */ + + /* DirectFB window */ + IDirectFBWindow *window; + + /* display layer */ + IDirectFBDisplayLayer *layer; + +}; + + +size_t DirectFBVid_GetCtxSizeOf(void) +{ + return sizeof(DirectFBVidCtx); +} + + +typedef struct _DeviceInfo DeviceInfo; +struct _DeviceInfo { + DFBInputDeviceID device_id; + DFBInputDeviceDescription desc; + DeviceInfo *next; +}; + + + +/* Wrapper to DirectFB members */ + +/** + * function DirectFBVid_DrawHLineWrapper + * - using hardware accelerator to draw horizontal line + **/ +void DirectFBVid_DrawHLineWrapper(DirectFBVidCtx *ctx, u32 x, u32 y, u32 length, u8 r, u8 g, u8 b) +{ + //GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[DirectFB] in DirectFBVid_DrawHLine(). Drawing line x %d y %d length %d color %08X\n", x, y, length, color)); + + // DSDRAW_NOFX: flag controlling drawing command, drawing without using any effects + SET_DRAWING_FLAGS( DSDRAW_NOFX ); + + // set the color used without alpha + ctx->primary->SetColor(ctx->primary, r, g, b, 0xFF); + + // to draw a line using hardware accelerators, we can use either DrawLine (in our STB, DrawLine() function is not accelerated) or FillRectangle function with height equals to 1 + //ctx->primary->DrawLine(ctx->primary, x, y, x+length, y); // no acceleration + ctx->primary->FillRectangle(ctx->primary, x, y,length, 1); +} + + +/** + * function DirectFBVid_DrawHLineWrapper + * - using hardware accelerator to draw horizontal line with alpha + **/ +void DirectFBVid_DrawHLineAlphaWrapper(DirectFBVidCtx *ctx, u32 x, u32 y, u32 length, u8 r, u8 g, u8 b, u8 alpha) +{ + //GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[DirectFB] in DirectFBVid_DrawHLineAlpha(). Alpha line drawing x %d y %d length %d color %08X alpha %d\n", x, y, length, color, alpha)); + + // DSDRAW_BLEND: using alpha from color + SET_DRAWING_FLAGS( DSDRAW_BLEND ); + + ctx->primary->SetColor(ctx->primary, r, g, b, alpha); + //ctx->primary->DrawLine(ctx->primary, x, y, x+length, y); + ctx->primary->FillRectangle(ctx->primary, x, y, length, 1); // with acceleration +} + + +/** + * function DirectFBVid_DrawRectangleWrapper + * - using hardware accelerator to fill a rectangle with the given color + **/ +void DirectFBVid_DrawRectangleWrapper(DirectFBVidCtx *ctx, u32 x, u32 y, u32 width, u32 height, u8 r, u8 g, u8 b, u8 a) +{ + //GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[DirectFB] in DirectFBVid_DrawRectangle(). Drawing rectangle x %d y %d width %d height %d color %08x\n", x, y, width, height, color)); + + SET_DRAWING_FLAGS( DSDRAW_NOFX ); + + ctx->primary->SetColor(ctx->primary, r, g, b, a); + ctx->primary->FillRectangle(ctx->primary, x, y, width, height); + //ctx->primary->Blit( ctx->primary, ctx->primary, NULL, x, y ); +} + + +/** + * function DirectFBVid_CtxPrimaryLock + * - lock the surface (in order to access certain data) + **/ +u32 DirectFBVid_CtxPrimaryLock(DirectFBVidCtx *ctx, void **buf, u32 *pitch) +{ + DFBResult ret = ctx->primary->Lock(ctx->primary, DSLF_READ | DSLF_WRITE, buf, pitch); + if (ret != DFB_OK) return 1; + return 0; +} + + +static DFBEnumerationResult enum_input_device(DFBInputDeviceID device_id, DFBInputDeviceDescription desc, void *data ) +{ + DeviceInfo **devices = data; + + DeviceInfo *device = malloc(sizeof(DeviceInfo) ); + + device->device_id = device_id; + device->desc = desc; + device->next = *devices; + + *devices = device; + + return 0; +} + + +/** + * function DirectFBVid_InitAndCreateSurface + * - initialize and create DirectFB surface + **/ +u32 DirectFBVid_TranslatePixelFormatToGPAC(u32 dfbpf); +void DirectFBVid_InitAndCreateSurface(DirectFBVidCtx *ctx, u32 window_mode) +{ + DFBResult err; + DFBSurfaceDescription dsc; + DFBSurfacePixelFormat dfbpf; + DeviceInfo *devices = NULL; + + //fake arguments and DirectFBInit() + { + int i, argc=2, argc_ro=2; + char **argv = malloc(argc*sizeof(char*)); + char *argv_ro[2]; + //http://directfb.org/wiki/index.php/Configuring_DirectFB + argv_ro[0]=argv[0]=strdup("gpac"); + // graphis system used is X11 + if (window_mode == WINDOW_SDL) { + argv_ro[1]=argv[1]=strdup("--dfb:system=sdl"); + } else { + argv_ro[1]=argv[1]=strdup("--dfb:system=x11"); + } + + // screen resolution 640x480 + //~ argv_ro[2]=argv[2]=strdup("--dfb:mode=640x480"); + //~ argv_ro[2]=argv[2]=strdup(""); + + /* create the super interface */ + DFBCHECK(DirectFBInit(&argc, &argv)); + + for (i=0; idfb) )); + + /* create a list of input devices */ + ctx->dfb->EnumInputDevices(ctx->dfb, enum_input_device, &devices ); + if (devices->desc.type & DIDTF_JOYSTICK) { + // for mouse + DFBCHECK(ctx->dfb->GetInputDevice(ctx->dfb, devices->device_id, &(ctx->mouse))); + } + + /* create an event buffer for all devices */ + DFBCHECK(ctx->dfb->CreateInputEventBuffer(ctx->dfb, DICAPS_KEYS, DFB_FALSE, &(ctx->events) )); + + /* Set the cooperative level */ + DFBCHECK(ctx->dfb->SetCooperativeLevel( ctx->dfb, DFSCL_FULLSCREEN )); + + /* Get the primary surface, i.e. the surface of the primary layer. */ + // capabilities field is valid + dsc.flags = DSDESC_CAPS; + // primary, double-buffered surface + dsc.caps = DSCAPS_PRIMARY | DSCAPS_DOUBLE; + + // if using system memory, data is permanently stored in this memory (no video memory allocation) + if (ctx->use_systems_memory) dsc.caps |= DSCAPS_SYSTEMONLY; + + DFBCHECK(ctx->dfb->CreateSurface( ctx->dfb, &dsc, &(ctx->primary) )); + + // fetch pixel format + ctx->primary->GetPixelFormat( ctx->primary, &dfbpf ); + // translate DirectFB pixel format to GPAC + ctx->pixel_format = DirectFBVid_TranslatePixelFormatToGPAC(dfbpf); + // surface width and height in pixel + ctx->primary->GetSize( ctx->primary, &(ctx->width), &(ctx->height) ); + ctx->primary->Clear( ctx->primary, 0, 0, 0, 0xFF); +} + + +/** + * function DirectFBVid_CtxPrimaryUnlock + * - unlock a surface after direct access (in order to fetch data) + **/ +void DirectFBVid_CtxPrimaryUnlock(DirectFBVidCtx *ctx) +{ + ctx->primary->Unlock(ctx->primary); +} + + +/** + * function DirectFBVid_CtxGetWidth + * - returns screen width + **/ +u32 DirectFBVid_CtxGetWidth(DirectFBVidCtx *ctx) +{ + return ctx->width; +} + + +/** + * function DirectFBVid_CtxGetHeight + * - returns screen height + **/ +u32 DirectFBVid_CtxGetHeight(DirectFBVidCtx *ctx) +{ + return ctx->height; +} + + +/** + * function DirectFBVid_CtxGetPrimary + * - return the primary surface + **/ +void *DirectFBVid_CtxGetPrimary(DirectFBVidCtx *ctx) +{ + return ctx->primary; +} + + +/** + * function DirectFBVid_CtxGetPixelFormat + * - get pixel format + **/ +u32 DirectFBVid_CtxGetPixelFormat(DirectFBVidCtx *ctx) +{ + return ctx->pixel_format; +} + + +/** + * function DirectFBVid_CtxIsHwMemory + * - return value whether system memory is used or not + **/ +Bool DirectFBVid_CtxIsHwMemory(DirectFBVidCtx *ctx) +{ + return ctx->use_systems_memory; +} + + +/** + * function DirectFBVid_CtxPrimaryFlip + * - flipping buffers + **/ +u32 DirectFBVid_CtxPrimaryFlip(DirectFBVidCtx *ctx) +{ + return ctx->primary->Flip(ctx->primary, NULL, ctx->flip_mode); +} + + +/** + * function DirectFBVid_CtxSetDisableDisplay + * - set disable display value + **/ +void DirectFBVid_CtxSetDisableDisplay(DirectFBVidCtx *ctx, Bool val) +{ + ctx->disable_display = val; +} + + +/** + * function DirectFBVid_CtxGetDisableDisplay + * - boolean showing whether display is enabled/disabled + **/ +Bool DirectFBVid_CtxGetDisableDisplay(DirectFBVidCtx *ctx) +{ + return ctx->disable_display; +} + + +/** + * function DirectFBVid_CtxSetDisableAcceleration + * - boolean showing whether hardware accelerator is enabled/disabled + **/ +void DirectFBVid_CtxSetDisableAcceleration(DirectFBVidCtx *ctx, Bool val) +{ + ctx->disable_acceleration = val; +} + + +/** + * function DirectFBVid_CtxGetDisableAcceleration + * - return disable_acceleration value + **/ +Bool DirectFBVid_CtxGetDisableAcceleration(DirectFBVidCtx *ctx) +{ + return ctx->disable_acceleration; +} + + +/** + * function DirectFBVid_CtxSetIsInit + * - boolean showing whether DirectFB is initialized + **/ +void DirectFBVid_CtxSetIsInit(DirectFBVidCtx *ctx, Bool val) +{ + ctx->is_init = val; +} + + +/** + * function DirectFBVid_CtxSetFlipMode + * - set flip mode + **/ +void DirectFBVid_CtxSetFlipMode(DirectFBVidCtx *ctx, u32 flip_mode) +{ + ctx->flip_mode = DSFLIP_BLIT; + switch(flip_mode) { + case FLIP_WAITFORSYNC: + ctx->flip_mode |= DSFLIP_WAITFORSYNC; + break; + case FLIP_WAIT: + ctx->flip_mode |= DSFLIP_WAIT; + break; + case FLIP_ONSYNC: + ctx->flip_mode |= DSFLIP_ONSYNC; + break; + case FLIP_SWAP: + ctx->flip_mode &= ~DSFLIP_BLIT; + break; + } +} + + +/** + * function DirectFBVid_CtxPrimaryProcessGetAccelerationMask + * - Get a mask of drawing functions that are hardware accelerated with the current settings. + **/ +void DirectFBVid_CtxPrimaryProcessGetAccelerationMask(DirectFBVidCtx *ctx) +{ + DFBAccelerationMask mask; + ctx->primary->GetAccelerationMask( ctx->primary, NULL, &mask ); + + if (mask & DFXL_DRAWLINE ) // DrawLine() is accelerated. DFXL_DRAWLINE + ctx->accel_drawline = 1; + if (mask & DFXL_FILLRECTANGLE) // FillRectangle() is accelerated. + ctx->accel_fillrect = 1; +} + + +/** + * function DirectFBVid_ShutdownWrapper + * - shutdown DirectFB module + **/ +u32 DirectFBVid_ShutdownWrapper(DirectFBVidCtx *ctx) +{ + // case where initialization is not done + if (!ctx->is_init) return 1; + + //~ ctx->keyboard->Release( ctx->keyboard ); + ctx->primary->Release( ctx->primary ); + ctx->events->Release( ctx->events ); + ctx->dfb->Release( ctx->dfb ); + ctx->is_init = 0; + //we use x11 thus it should be useless: + //system("stgfb_control /dev/fb0 a 0") + return 0; +} + + +/** + * Blit a surface + **/ +u32 DirectFBVid_TranslatePixelFormatFromGPAC(u32 gpacpf); +u32 DirectFBVid_BlitWrapper(DirectFBVidCtx *ctx, u32 video_src_width, u32 video_src_height, u32 video_src_pixel_format, char *video_src_buffer, s32 video_src_pitch_y, u32 src_wnd_x, u32 src_wnd_y, u32 src_wnd_w, u32 src_wnd_h, u32 dst_wnd_x, u32 dst_wnd_y, u32 dst_wnd_w, u32 dst_wnd_h, u32 overlay_type) +{ + DFBResult res; + DFBSurfaceDescription srcdesc; + IDirectFBSurface *src; + DFBRectangle dfbsrc, dfbdst; + + if (overlay_type != 0) return 1; + if (ctx->disable_display) return 0; + + memset(&srcdesc, 0, sizeof(srcdesc)); + + srcdesc.flags = DSDESC_WIDTH | DSDESC_HEIGHT | DSDESC_PIXELFORMAT | DSDESC_PREALLOCATED; + srcdesc.width = video_src_width; + srcdesc.height = video_src_height; + srcdesc.pixelformat = DirectFBVid_TranslatePixelFormatFromGPAC(video_src_pixel_format); + srcdesc.preallocated[0].data = video_src_buffer; + srcdesc.preallocated[0].pitch = video_src_pitch_y; + + + switch (video_src_pixel_format) { + case GF_PIXEL_ARGB: //return DSPF_ARGB; + case GF_PIXEL_RGBA: //return DSPF_ARGB; + ctx->primary->SetBlittingFlags(ctx->primary, DSBLIT_BLEND_ALPHACHANNEL); + break; + default: + ctx->primary->SetBlittingFlags(ctx->primary, DSBLIT_NOFX); + } + + // create a surface with the new surface description + res = ctx->dfb->CreateSurface(ctx->dfb, &srcdesc, &src); + if (res != DFB_OK) { + //GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[DirectFB] cannot create blit source surface for pixel format %s: %s (%d)\n", gf_4cc_to_str(video_src->pixel_format), DirectFBErrorString(res), res)); + return 1; + } + + + dfbsrc.x = src_wnd_x; + dfbsrc.y = src_wnd_y; + dfbsrc.w = src_wnd_w; + dfbsrc.h = src_wnd_h; + + // blit on the surface + if (!src_wnd_x && !src_wnd_y && (dst_wnd_w==src_wnd_w) && (dst_wnd_h==src_wnd_h)) { + ctx->primary->Blit(ctx->primary, src, &dfbsrc, dst_wnd_x, dst_wnd_y); + // blit an area scaled from the source to the destination rectangle + } else { + dfbdst.x = dst_wnd_x; + dfbdst.y = dst_wnd_y; + dfbdst.w = dst_wnd_w; + dfbdst.h = dst_wnd_h; + ctx->primary->StretchBlit(ctx->primary, src, &dfbsrc, &dfbdst); + } + + src->Release(src); + + return 0; +} + + +/** + * function DirectFBVid_ProcessMessageQueueWrapper + * - handle DirectFB events + * - key events + **/ +void directfb_translate_key(DFBInputDeviceKeyIdentifier DirectFBkey, u32 *flags, u32 *key_code); +u32 DirectFBVid_ProcessMessageQueueWrapper(DirectFBVidCtx *ctx, u8 *type, u32 *flags, u32 *key_code, s32 *x, s32 *y, u32 *button) +{ + DFBInputEvent directfb_evt; + + if (ctx->events->GetEvent( ctx->events, DFB_EVENT(&directfb_evt) ) == DFB_OK) + { + switch (directfb_evt.type) { + case DIET_KEYPRESS: + case DIET_KEYRELEASE: + directfb_translate_key(directfb_evt.key_id, flags, key_code); + *type = (directfb_evt.type == DIET_KEYPRESS) ? GF_EVENT_KEYDOWN : GF_EVENT_KEYUP; + break; + case DIET_BUTTONPRESS: + case DIET_BUTTONRELEASE: + *type = (directfb_evt.type == DIET_BUTTONPRESS) ? GF_EVENT_MOUSEUP : GF_EVENT_MOUSEDOWN; + switch(directfb_evt.button) { + case DIBI_LEFT: + *button = GF_MOUSE_LEFT; + break; + case DIBI_RIGHT: + *button = GF_MOUSE_RIGHT; + break; + case DIBI_MIDDLE: + *button = GF_MOUSE_MIDDLE; + break; + default: + printf("in here for others\n"); + break; + } + break; + case DIET_AXISMOTION: + *type = GF_EVENT_MOUSEMOVE; + ctx->mouse->GetXY(ctx->mouse, x, y); + default: + break; + } + + return 0; + } + + return 1; +} + + +/* Events translation */ +/** + * function DirectFBVid_TranslatePixelFormatToGPAC + * - translate pixel from DirectFb to GPAC + **/ +u32 DirectFBVid_TranslatePixelFormatToGPAC(u32 dfbpf) +{ + switch (dfbpf) { + case DSPF_RGB16: return GF_PIXEL_RGB_565; + case DSPF_RGB555: return GF_PIXEL_RGB_555; + case DSPF_RGB24: return GF_PIXEL_RGB_24; + case DSPF_RGB32: return GF_PIXEL_RGB_32; + case DSPF_ARGB: return GF_PIXEL_ARGB; + default: return 0; + } +} + +/** + * function DirectFBVid_TranslatePixelFormatToGPAC + * - translate pixel from GPAC to DirectFB + **/ +u32 DirectFBVid_TranslatePixelFormatFromGPAC(u32 gpacpf) +{ + switch (gpacpf) { + case GF_PIXEL_RGB_565: return DSPF_RGB16; + case GF_PIXEL_RGB_555 : return DSPF_RGB555; + case GF_PIXEL_BGR_24 : return DSPF_RGB24; + case GF_PIXEL_RGB_24 : return DSPF_RGB24; + case GF_PIXEL_RGB_32 : return DSPF_RGB32; + case GF_PIXEL_ARGB: return DSPF_ARGB; + case GF_PIXEL_RGBA: return DSPF_ARGB; + case GF_PIXEL_YUY2 : return DSPF_YUY2; + case GF_PIXEL_YV12 : return DSPF_YV12; + default: + ;//GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[DirectFB] pixel format %s not supported\n", gf_4cc_to_str(gpacpf))); + } + + return 0; +} + + +/** + * function directfb_translate_key + * - translate key from DirectFB to GPAC + **/ +void directfb_translate_key(DFBInputDeviceKeyIdentifier DirectFBkey, u32 *flags, u32 *key_code) +{ + //~ printf("DirectFBkey=%d\n", DirectFBkey); + + switch (DirectFBkey) { + case DIKI_BACKSPACE: + *key_code = GF_KEY_BACKSPACE; + //~ printf("DIKI_BACKSPACE\n"); + break; + case DIKI_TAB: + *key_code = GF_KEY_TAB; + //~ printf("DIKI_TAB\n"); + break; + case DIKI_ENTER: + *key_code = GF_KEY_ENTER; + //~ printf("DIKI_ENTER\n"); + break; + case DIKI_ESCAPE: + *key_code = GF_KEY_ESCAPE; + //~ printf("DIKI_ESCAPE\n"); + break; + case DIKI_SPACE: + *key_code = GF_KEY_SPACE; + //~ printf("DIKI_SPACE\n"); + break; + case DIKI_SHIFT_L: + case DIKI_SHIFT_R: + *key_code = GF_KEY_SHIFT; + //~ printf("DIKI_SHIFT_R/DIKI_SHIFT_L\n"); + break; + case DIKI_CONTROL_L: + case DIKI_CONTROL_R: + *key_code = GF_KEY_CONTROL; + //~ printf("DIKI_CONTROL_L/DIKI_CONTROL_R\n"); + break; + case DIKI_ALT_L: + case DIKI_ALT_R: + *key_code = GF_KEY_ALT; + //~ printf("DIKI_ALT_L/DIKI_ALT_R\n"); + break; + case DIKI_CAPS_LOCK: + *key_code = GF_KEY_CAPSLOCK; + //~ printf("DIKI_CAPS_LOCK\n"); + break; + case DIKI_META_L: + case DIKI_META_R: + *key_code = GF_KEY_META; break; + case DIKI_KP_EQUAL: + *key_code = GF_KEY_EQUALS; break; + case DIKI_SUPER_L: + case DIKI_SUPER_R: + *key_code = GF_KEY_WIN; break; + + /* alphabets */ + case DIKI_A: + *key_code = GF_KEY_A; + //~ printf("DIKI_A\n"); + break; + case DIKI_B: + *key_code = GF_KEY_B; + //~ printf("DIKI_B\n"); + break; + case DIKI_C: + *key_code = GF_KEY_C; + //~ printf("DIKI_C\n"); + break; + case DIKI_D: + *key_code = GF_KEY_D; + //~ printf("DIKI_D\n"); + break; + case DIKI_E: + *key_code = GF_KEY_E; + //~ printf("DIKI_E\n"); + break; + case DIKI_F: + *key_code = GF_KEY_F; break; + case DIKI_G: + *key_code = GF_KEY_G; break; + case DIKI_H: + *key_code = GF_KEY_H; break; + case DIKI_I: + *key_code = GF_KEY_I; break; + case DIKI_J: + *key_code = GF_KEY_J; break; + case DIKI_K: + *key_code = GF_KEY_K; break; + case DIKI_L: + *key_code = GF_KEY_L; break; + case DIKI_M: + *key_code = GF_KEY_M; break; + case DIKI_N: + *key_code = GF_KEY_N; break; + case DIKI_O: + *key_code = GF_KEY_O; break; + case DIKI_P: + *key_code = GF_KEY_P; break; + case DIKI_Q: + *key_code = GF_KEY_Q; break; + case DIKI_R: + *key_code = GF_KEY_R; break; + case DIKI_S: + *key_code = GF_KEY_S; break; + case DIKI_T: + *key_code = GF_KEY_T; break; + case DIKI_U: + *key_code = GF_KEY_U; break; + case DIKI_V: + *key_code = GF_KEY_V; break; + case DIKI_W: + *key_code = GF_KEY_W; break; + case DIKI_X: + *key_code = GF_KEY_X; break; + case DIKI_Y: + *key_code = GF_KEY_Y; break; + case DIKI_Z: + *key_code = GF_KEY_Z; break; + + case DIKI_PRINT: + *key_code = GF_KEY_PRINTSCREEN; break; + case DIKI_SCROLL_LOCK: + *key_code = GF_KEY_SCROLL; break; + case DIKI_PAUSE: + *key_code = GF_KEY_PAUSE; break; + case DIKI_INSERT: + *key_code = GF_KEY_INSERT; break; + case DIKI_DELETE: + *key_code = GF_KEY_DEL; break; + case DIKI_HOME: + *key_code = GF_KEY_HOME; break; + case DIKI_END: + *key_code = GF_KEY_END; break; + case DIKI_PAGE_UP: + *key_code = GF_KEY_PAGEUP; break; + case DIKI_PAGE_DOWN: + *key_code = GF_KEY_PAGEDOWN; break; + + /* arrows */ + case DIKI_UP: + *key_code = GF_KEY_UP; break; + case DIKI_DOWN: + *key_code = GF_KEY_DOWN; break; + case DIKI_RIGHT: + *key_code = GF_KEY_RIGHT; break; + case DIKI_LEFT: + *key_code = GF_KEY_LEFT; break; + + /* extended numerical pad */ + case DIKI_NUM_LOCK: + *flags = GF_KEY_EXT_NUMPAD; *key_code = GF_KEY_NUMLOCK; break; + case DIKI_KP_DIV: + *flags = GF_KEY_EXT_NUMPAD; *key_code = GF_KEY_SLASH; break; + case DIKI_KP_MULT: + *flags = GF_KEY_EXT_NUMPAD; *key_code = GF_KEY_STAR; break; + case DIKI_KP_MINUS: + *flags = GF_KEY_EXT_NUMPAD; *key_code = GF_KEY_HYPHEN; break; + case DIKI_KP_PLUS: + *flags = GF_KEY_EXT_NUMPAD; *key_code = GF_KEY_PLUS; break; + case DIKI_KP_ENTER: + *flags = GF_KEY_EXT_NUMPAD; *key_code = GF_KEY_ENTER; break; + case DIKI_KP_DECIMAL: + *flags = GF_KEY_EXT_NUMPAD; *key_code = GF_KEY_FULLSTOP; break; + case DIKI_KP_0: + *flags = GF_KEY_EXT_NUMPAD; *key_code = GF_KEY_0; break; + case DIKI_KP_1: + *flags = GF_KEY_EXT_NUMPAD; *key_code = GF_KEY_1; break; + case DIKI_KP_2: + *flags = GF_KEY_EXT_NUMPAD; *key_code = GF_KEY_2; break; + case DIKI_KP_3: + *flags = GF_KEY_EXT_NUMPAD; *key_code = GF_KEY_3; break; + case DIKI_KP_4: + *flags = GF_KEY_EXT_NUMPAD; *key_code = GF_KEY_4; break; + case DIKI_KP_5: + *flags = GF_KEY_EXT_NUMPAD; *key_code = GF_KEY_5; break; + case DIKI_KP_6: + *flags = GF_KEY_EXT_NUMPAD; *key_code = GF_KEY_6; break; + case DIKI_KP_7: + *flags = GF_KEY_EXT_NUMPAD; *key_code = GF_KEY_7; break; + case DIKI_KP_8: + *flags = GF_KEY_EXT_NUMPAD; *key_code = GF_KEY_8; break; + case DIKI_KP_9: + *flags = GF_KEY_EXT_NUMPAD; *key_code = GF_KEY_9; break; + + /* Fn functions */ + case DIKI_F1: + *key_code = GF_KEY_F1; break; + case DIKI_F2: + *key_code = GF_KEY_F2; break; + case DIKI_F3: + *key_code = GF_KEY_F3; break; + case DIKI_F4: + *key_code = GF_KEY_F4; break; + case DIKI_F5: + *key_code = GF_KEY_F5; break; + case DIKI_F6: + *key_code = GF_KEY_F6; break; + case DIKI_F7: + *key_code = GF_KEY_F7; break; + case DIKI_F8: + *key_code = GF_KEY_F8; break; + case DIKI_F9: + *key_code = GF_KEY_F9; break; + case DIKI_F10: + *key_code = GF_KEY_F10; break; + case DIKI_F11: + *key_code = GF_KEY_F11; break; + case DIKI_F12: + *key_code = GF_KEY_F12; break; + + default: + *key_code = GF_KEY_UNIDENTIFIED; break; + } +} + diff --git a/modules/droid_cam/droid_cam.c b/modules/droid_cam/droid_cam.c new file mode 100644 index 0000000..ec6cbd7 --- /dev/null +++ b/modules/droid_cam/droid_cam.c @@ -0,0 +1,715 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Authors: Ivica Arsov + * Copyright (c) Institut Telecom 2011-20XX + * All rights reserved + * + * This file is part of GPAC / Android camera module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define LOG_TAG "ANDROID_CAMERA" +#ifdef ANDROID +# define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) +# define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) +#else +# define QUOTEME_(x) #x +# define QUOTEME(x) QUOTEME_(x) +# define LOGI(...) printf("I/" LOG_TAG " (" __FILE__ ":" QUOTEME(__LINE__) "): " __VA_ARGS__) +# define LOGE(...) printf("E/" LOG_TAG "(" ")" __VA_ARGS__) +#endif + +static JavaVM* javaVM = 0; +static jclass camCtrlClass; +static jmethodID cid; +static jmethodID startCamera; +static jmethodID stopCamera; +static jmethodID startProcessing; +static jmethodID stopProcessing; +static jmethodID getImageFormat; +static jmethodID getImageHeight; +static jmethodID getImageWidth; + +jint JNI_OnLoad(JavaVM* vm, void* reserved) +{ + JNIEnv* env = 0; + javaVM = vm; + + if ( (*javaVM)->GetEnv(javaVM, (void**)&env, JNI_VERSION_1_2) != JNI_OK ) + return -1; + + // Get the class and its methods in the main env + + // Get the CameraController class + // This is just a local refenrece. Cannot be used in the other JNI calls. + camCtrlClass = (*env)->FindClass(env, "com/gpac/Osmo4/Preview"); + if (camCtrlClass == 0) + { + LOGE("CameraController class = null [myCamera.c, startCameraNative()]"); + return -1; + } + + // Get Global Reference to be able to use the class + camCtrlClass = (*env)->NewGlobalRef(env, camCtrlClass); + if ( camCtrlClass == 0 ) + { + LOGE("[MPEG-V_IN] Cannot create Global Reference\n"); + return -1; + } + + // Get the method ID for the CameraController constructor. + cid = (*env)->GetMethodID(env, camCtrlClass, "", "()V"); + if (cid == 0) + { + LOGE("Method ID for CameraController constructor is null [myCamera.c, startCameraNative()]"); + return -1; + } + + // Get startCamera() method from class CameraController + startCamera = (*env)->GetMethodID(env, camCtrlClass, "initializeCamera", "(Z)Z"); + if (startCamera == 0) + { + LOGE("[ANDROID_CAMERA] Function startCamera not found"); + return -1; + } + + stopCamera = (*env)->GetMethodID(env, camCtrlClass, "stopCamera", "()V"); + if (stopCamera == 0) + { + LOGE("[ANDROID_CAMERA] Function stopCamera not found"); + return -1; + } + + startProcessing = (*env)->GetMethodID(env, camCtrlClass, "resumePreview", "()V"); + if (startProcessing == 0) + { + LOGE("[ANDROID_CAMERA] Function startProcessing not found"); + return -1; + } + + stopProcessing = (*env)->GetMethodID(env, camCtrlClass, "pausePreview", "()V"); + if (stopProcessing == 0) + { + LOGE("[ANDROID_CAMERA] Function stopProcessing not found"); + return -1; + } + + getImageFormat = (*env)->GetMethodID(env, camCtrlClass, "getPreviewFormat", "()I"); + if (getImageFormat == 0) + { + LOGE("[ANDROID_CAMERA] Function getImageFormat not found"); + return -1; + } + + getImageHeight = (*env)->GetMethodID(env, camCtrlClass, "getImageHeight", "()I"); + if (getImageHeight == 0) + { + LOGE("[ANDROID_CAMERA] Function getImageHeight not found"); + return -1; + } + + getImageWidth = (*env)->GetMethodID(env, camCtrlClass, "getImageWidth", "()I"); + if (getImageWidth == 0) + { + LOGE("[ANDROID_CAMERA] Function getImageWidth not found"); + return -1; + } + + return JNI_VERSION_1_2; +} +//---------------------------------------------------------------------- +JavaVM* GetJavaVM() +{ + return javaVM; +} + +JNIEnv* GetEnv() +{ + JNIEnv* env; + if ( (*GetJavaVM())->GetEnv(GetJavaVM(), (void**)&env, JNI_VERSION_1_2) != JNI_OK ) + return NULL; + + return env; +} + +void JNI_OnUnload(JavaVM *vm, void *reserved) +{ + JNIEnv* env = 0; + + if ( (*vm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_2) != JNI_OK ) + return; + + (*env)->DeleteGlobalRef(env, camCtrlClass); +} +//---------------------------------------------------------------------- + + +#define CAM_PIXEL_FORMAT GF_PIXEL_NV21 +//GF_PIXEL_RGB_32 +#define CAM_PIXEL_SIZE 1.5f +//4 +#define CAM_WIDTH 640 +#define CAM_HEIGHT 480 + +//GF_PIXEL_RGB_24; + +typedef struct +{ + GF_InputService *input; + /*the service we're responsible for*/ + GF_ClientService *service; + LPNETCHANNEL* channel; + + /*input file*/ + u32 time_scale; + + u32 base_track_id; + + struct _tag_terminal *term; + + u32 cntr; + + u32 width; + u32 height; + + Bool started; + + JNIEnv* env; + u8 isAttached; + jclass camCtrlClass; + jmethodID cid; + jobject camCtrlObj; + jmethodID startCamera; + jmethodID stopCamera; + jmethodID startProcessing; + jmethodID stopProcessing; + jmethodID getImageFormat; + jmethodID getImageHeight; + jmethodID getImageWidth; + +} ISOMReader; + +ISOMReader* globReader; + +void loadCameraControler(ISOMReader *read); +void camStartCamera(ISOMReader *read); +void camStopCamera(ISOMReader *read); + +Bool CAM_CanHandleURL(GF_InputService *plug, const char *url) +{ + if (!strnicmp(url, "hw://camera", 11)) return 1; + + return 0; +} + +void unloadCameraControler(ISOMReader *read) +{ + GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[ANDROID_CAMERA] unloadCameraControler: %d\n", gf_th_id())); + if ( read->isAttached ) + { + //(*rc->env)->PopLocalFrame(rc->env, NULL); + (*GetJavaVM())->DetachCurrentThread(GetJavaVM()); + read->isAttached = 0; + } + + read->env = NULL; +} + +u32 unregisterFunc(void* data) +{ + unloadCameraControler(globReader); +} + +void loadCameraControler(ISOMReader *read) +{ + JNIEnv* env = NULL; + jint res = 0; + + GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[ANDROID_CAMERA] loadCameraControler: %d\n", gf_th_id())); + + // Get the JNI interface pointer + res = (*GetJavaVM())->GetEnv(javaVM, (void**)&env, JNI_VERSION_1_2); + if ( res == JNI_EDETACHED ) + { + GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[ANDROID_CAMERA] The current thread is not attached to the VM, assuming native thread\n")); + if ( res = (*GetJavaVM())->AttachCurrentThread(GetJavaVM(), &env, NULL) ) + { + GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[ANDROID_CAMERA] Attach current thread failed: %d\n", res)); + return; + } + gf_register_before_exit_function(gf_th_current(), unregisterFunc); + read->isAttached = 1; + //(*rc->env)->PushLocalFrame(rc->env, 2); + } + else + if ( res == JNI_EVERSION ) + { + GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[ANDROID_CAMERA] The specified version is not supported\n")); + return; + } + + read->env = env; + read->camCtrlClass = camCtrlClass; + read->cid = cid; + read->startCamera = startCamera; + read->stopCamera = stopCamera; + read->startProcessing = startProcessing; + read->stopProcessing = stopProcessing; + read->getImageFormat = getImageFormat; + read->getImageHeight = getImageHeight; + read->getImageWidth = getImageWidth; + + // Create the object. + read->camCtrlObj = (*env)->NewObject(env, read->camCtrlClass, read->cid); + if (read->camCtrlObj == 0) + { + GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("CameraController object creation failed. [myCamera.c, startCameraNative()]")); + return; + } +} + +GF_Err CAM_ConnectService(GF_InputService *plug, GF_ClientService *serv, const char *url) +{ + ISOMReader *read; + if (!plug || !plug->priv || !serv) return GF_SERVICE_ERROR; + read = (ISOMReader *) plug->priv; + + read->input = plug; + read->service = serv; + read->base_track_id = 1; + read->time_scale = 1000; + + read->term = serv->term; + + loadCameraControler(read); + + /*reply to user*/ + gf_term_on_connect(serv, NULL, GF_OK); + //if (read->no_service_desc) isor_declare_objects(read); + + return GF_OK; +} + +GF_Err CAM_CloseService(GF_InputService *plug) +{ + GF_Err reply; + ISOMReader *read; + if (!plug || !plug->priv) return GF_SERVICE_ERROR; + read = (ISOMReader *) plug->priv; + reply = GF_OK; + + (*GetEnv())->DeleteLocalRef( GetEnv(), read->camCtrlObj ); + + //unloadCameraControler(read); + + gf_term_on_disconnect(read->service, NULL, reply); + return GF_OK; +} + +u32 getWidth(ISOMReader *read); +u32 getHeight(ISOMReader *read); + +static GF_Descriptor *CAM_GetServiceDesc(GF_InputService *plug, u32 expect_type, const char *sub_url) +{ + u32 trackID; + GF_ESD *esd; + ISOMReader *read; + GF_ObjectDescriptor *od; + GF_BitStream *bs; + char *buf; + u32 buf_size; + if (!plug || !plug->priv) return NULL; + read = (ISOMReader *) plug->priv; + + trackID = 0; + trackID = read->base_track_id; + read->base_track_id = 0; + + if (trackID && (expect_type==GF_MEDIA_OBJECT_VIDEO) ) { + od = (GF_ObjectDescriptor *) gf_odf_desc_new(GF_ODF_OD_TAG); + od->objectDescriptorID = 1; + + esd = gf_odf_desc_esd_new(0); + esd->slConfig->timestampResolution = 1000; + esd->decoderConfig->streamType = GF_STREAM_VISUAL; + esd->ESID = 1; + esd->decoderConfig->objectTypeIndication = GPAC_OTI_RAW_MEDIA_STREAM; + + bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + + read->width = getWidth(read); + read->height = getHeight(read); + + gf_bs_write_u32(bs, CAM_PIXEL_FORMAT); // fourcc + gf_bs_write_u16(bs, read->width); // width + gf_bs_write_u16(bs, read->height); // height + gf_bs_write_u32(bs, read->width * read->height * CAM_PIXEL_SIZE); // framesize + gf_bs_write_u32(bs, read->width * CAM_PIXEL_SIZE); // stride + + gf_bs_align(bs); + gf_bs_get_content(bs, &buf, &buf_size); + gf_bs_del(bs); + + esd->decoderConfig->decoderSpecificInfo->data = buf; + esd->decoderConfig->decoderSpecificInfo->dataLength = buf_size; + + gf_list_add(od->ESDescriptors, esd); + return (GF_Descriptor *) od; + } + + return NULL; +} + + + + +GF_Err CAM_ConnectChannel(GF_InputService *plug, LPNETCHANNEL channel, const char *url, Bool upstream) +{ + GF_Err e; + ISOMReader *read; + if (!plug || !plug->priv) return GF_SERVICE_ERROR; + read = (ISOMReader *) plug->priv; + + e = GF_OK; + if (upstream) { + e = GF_ISOM_INVALID_FILE; + } + + read->channel = channel; + + camStartCamera(read); + + gf_term_on_connect(read->service, channel, e); + return e; +} + +GF_Err CAM_DisconnectChannel(GF_InputService *plug, LPNETCHANNEL channel) +{ + GF_Err e; + ISOMReader *read; + if (!plug || !plug->priv) return GF_SERVICE_ERROR; + read = (ISOMReader *) plug->priv; + + e = GF_OK; + + camStopCamera(read); + + gf_term_on_disconnect(read->service, channel, e); + return e; +} + +int* decodeYUV420SP( char* yuv420sp, int width, int height) +{ + int frameSize = width * height; + int j, yp, uvp, i, y, y1192, r, g, b, u, v; + int ti, tj; + + int* rgb = (int*)gf_malloc(width*height*4); + for (j = 0, yp = 0, tj=height-1; j < height; j++, tj--) + { + uvp = frameSize + (j >> 1) * width, u = 0, v = 0; + for (i = 0, ti=0; i < width; i++, yp++, ti+=width) + { + y = (0xff & ((int) yuv420sp[yp])) - 16; + if (y < 0) y = 0; + if ((i & 1) == 0) + { + v = (0xff & yuv420sp[uvp++]) - 128; + u = (0xff & yuv420sp[uvp++]) - 128; + } + + y1192 = 1192 * y; + r = (y1192 + 1634 * v); + g = (y1192 - 833 * v - 400 * u); + b = (y1192 + 2066 * u); + + if (r < 0) + r = 0; + else if (r > 262143) + r = 262143; + if (g < 0) + g = 0; + else if (g > 262143) + g = 262143; + if (b < 0) + b = 0; + else if (b > 262143) + b = 262143; + + rgb[yp] = 0xff000000 | ((r << 6) & 0xff0000) + | ((g >> 2) & 0xff00) | ((b >> 10) & 0xff); +// rgb[ti+tj] = 0xff000000 | ((r << 6) & 0xff0000) +// | ((g >> 2) & 0xff00) | ((b >> 10) & 0xff); + } + } + return rgb; +} + +void Java_com_gpac_Osmo4_Preview_processFrameBuf( JNIEnv* env, jobject thiz, jbyteArray arr) +{ + u8* data; + u32 datasize; + ISOMReader* ctx = globReader; + GF_SLHeader hdr; + u32 cts = 0; + u32 convTime = 0; + u32 j = 0; + jbyte *jdata; + jsize len; + + if ( ctx->started ) + { + len = (*env)->GetArrayLength(env, arr); + jdata = (*env)->GetByteArrayElements(env,arr,0); + + convTime = gf_term_get_time(ctx->term); + + data = (u8*)jdata;//(u8*)decodeYUV420SP((char*)jdata, ctx->width, ctx->height); // + datasize = len;//ctx->width * ctx->height * CAM_PIXEL_SIZE;// + + cts = gf_term_get_time(ctx->term); + + convTime = cts - convTime; + + memset(&hdr, 0, sizeof(hdr)); + hdr.compositionTimeStampFlag = 1; + hdr.compositionTimeStamp = cts; + gf_term_on_sl_packet(ctx->service, ctx->channel, (void*)data, datasize, &hdr, GF_OK); + + //gf_free(data); + + (*env)->ReleaseByteArrayElements(env,arr,jdata,JNI_ABORT); + + //GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("Camera Frame Sent %d\n", gf_sys_clock())); + } +} + +void CallCamMethod(ISOMReader *read, jmethodID methodID) +{ + JNIEnv* env = NULL; + jint res = 0; + u8 isAttached = 0; + + // Get the JNI interface pointer + res = (*GetJavaVM())->GetEnv(javaVM, (void**)&env, JNI_VERSION_1_2); + if ( res == JNI_EDETACHED ) + { + GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[ANDROID_CAMERA] The current thread is not attached to the VM, assuming native thread\n")); + if ( res = (*GetJavaVM())->AttachCurrentThread(GetJavaVM(), &env, NULL) ) + { + GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[ANDROID_CAMERA] Attach current thread failed: %d\n", res)); + return; + } + if ( env != read->env ) + isAttached = 1; + } + + (*env)->CallNonvirtualVoidMethod(env, read->camCtrlObj, read->camCtrlClass, methodID); + + if (isAttached) + { + (*GetJavaVM())->DetachCurrentThread(GetJavaVM()); + } +} + +void camStartCamera(ISOMReader *read) +{ + JNIEnv* env = NULL; + jboolean isPortrait = JNI_FALSE; + + GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[ANDROID_CAMERA] startCamera: %d\n", gf_th_id())); + + // Get the JNI interface pointer + env = read->env; + + (*env)->CallNonvirtualBooleanMethod(env, read->camCtrlObj, read->camCtrlClass, read->startCamera, isPortrait); +} + +void camStopCamera(ISOMReader *read) +{ + GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[ANDROID_CAMERA] stopCamera: %d\n", gf_th_id())); + + CallCamMethod(read, read->stopCamera); +} + +void pauseCamera(ISOMReader *read) +{ + GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[ANDROID_CAMERA] pauseCamera: %d\n", gf_th_id())); + + read->started = 0; + CallCamMethod(read, read->stopProcessing); +} + +void resumeCamera(ISOMReader *read) +{ + GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[ANDROID_CAMERA] resumeCamera: %d\n", gf_th_id())); + + read->started = 1; + CallCamMethod(read, read->startProcessing); +} + +u32 getWidth(ISOMReader *read) +{ + JNIEnv* env = NULL; + + GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[ANDROID_CAMERA] getWidth: %d\n", gf_th_id())); + + // Get the JNI interface pointer + env = read->env; + + return (*env)->CallNonvirtualIntMethod(env, read->camCtrlObj, read->camCtrlClass, read->getImageWidth); +} + +u32 getHeight(ISOMReader *read) +{ + JNIEnv* env = NULL; + + GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[ANDROID_CAMERA] getHeight: %d\n", gf_th_id())); + + // Get the JNI interface pointer + env = read->env; + + return (*env)->CallNonvirtualIntMethod(env, read->camCtrlObj, read->camCtrlClass, read->getImageHeight); +} + +GF_Err CAM_ServiceCommand(GF_InputService *plug, GF_NetworkCommand *com) +{ + ISOMReader *read; + GF_BitStream *bs; + char *buf; + u32 buf_size; + if (!plug || !plug->priv || !com) return GF_SERVICE_ERROR; + read = (ISOMReader *) plug->priv; + + if (com->command_type==GF_NET_SERVICE_INFO) { + return GF_OK; + } + if (com->command_type==GF_NET_SERVICE_HAS_AUDIO) { + return GF_NOT_SUPPORTED; + } + if (!com->base.on_channel) return GF_NOT_SUPPORTED; + + switch (com->command_type) { + case GF_NET_CHAN_INTERACTIVE: + return GF_OK; + case GF_NET_CHAN_BUFFER: + com->buffer.max = com->buffer.min = 0; + return GF_OK; + case GF_NET_CHAN_PLAY: + resumeCamera(read); + return GF_OK; + case GF_NET_CHAN_STOP: + pauseCamera(read); + return GF_OK; + /*nothing to do on MP4 for channel config*/ + case GF_NET_CHAN_CONFIG: + return GF_OK; + case GF_NET_CHAN_GET_PIXEL_AR: + return 1<<16;//gf_isom_get_pixel_aspect_ratio(read->mov, ch->track, 1, &com->par.hSpacing, &com->par.vSpacing); + case GF_NET_CHAN_GET_DSI: + { + GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("Cam get DSI\n")); + /*it may happen that there are conflicting config when using ESD URLs...*/ + bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + + read->width = getWidth(read); + read->height = getHeight(read); + + gf_bs_write_u32(bs, CAM_PIXEL_FORMAT); // fourcc + gf_bs_write_u16(bs, read->width); // width + gf_bs_write_u16(bs, read->height); // height + gf_bs_write_u32(bs, read->width * read->height * CAM_PIXEL_SIZE); // framesize + gf_bs_write_u32(bs, read->width * CAM_PIXEL_SIZE); // stride + + gf_bs_align(bs); + gf_bs_get_content(bs, &buf, &buf_size); + gf_bs_del(bs); + + com->get_dsi.dsi = buf; + com->get_dsi.dsi_len = buf_size; + return GF_OK; + } + } + return GF_NOT_SUPPORTED; +} + +GF_InputService *CAM_client_load() +{ + ISOMReader *reader; + GF_InputService *plug; + GF_SAFEALLOC(plug, GF_InputService); + GF_REGISTER_MODULE_INTERFACE(plug, GF_NET_CLIENT_INTERFACE, "GPAC Camera Plugin", "gpac distribution") + plug->CanHandleURL = CAM_CanHandleURL; + plug->ConnectService = CAM_ConnectService; + plug->CloseService = CAM_CloseService; + plug->GetServiceDescriptor = CAM_GetServiceDesc; + plug->ConnectChannel = CAM_ConnectChannel; + plug->DisconnectChannel = CAM_DisconnectChannel; + plug->ServiceCommand = CAM_ServiceCommand; + + GF_SAFEALLOC(reader, ISOMReader); + plug->priv = reader; + globReader = reader; + return plug; +} + +void CAM_client_del(GF_BaseInterface *bi) +{ + GF_InputService *plug = (GF_InputService *) bi; + ISOMReader *read = (ISOMReader *)plug->priv; + + gf_free(read); + gf_free(bi); +} + +GF_EXPORT +const u32 *QueryInterfaces() +{ + static u32 si [] = { + GF_NET_CLIENT_INTERFACE, + 0 + }; + return si; +} + +GF_EXPORT +GF_BaseInterface *LoadInterface(u32 InterfaceType) +{ + if (InterfaceType == GF_NET_CLIENT_INTERFACE) + return (GF_BaseInterface *)CAM_client_load(); + return NULL; +} + +GF_EXPORT +void ShutdownInterface(GF_BaseInterface *ifce) +{ + switch (ifce->InterfaceType) { + case GF_NET_CLIENT_INTERFACE: CAM_client_del(ifce); break; + } +} diff --git a/modules/droid_mpegv/droid_mpegv.c b/modules/droid_mpegv/droid_mpegv.c new file mode 100644 index 0000000..e0bf366 --- /dev/null +++ b/modules/droid_mpegv/droid_mpegv.c @@ -0,0 +1,387 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Authors: Ivica Arsov + * Copyright (c) Institut Telecom 2011-20XX + * All rights reserved + * + * This file is part of GPAC / MPEG-V Input sensor for android + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include + +/*driver interfaces*/ + +#include +#include + +#include + +#include +#include + +#include + +#define LOG_TAG "MPEG-V_IN" +#ifdef ANDROID +# define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) +# define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) +#else +# define QUOTEME_(x) #x +# define QUOTEME(x) QUOTEME_(x) +# define LOGI(...) printf("I/" LOG_TAG " (" __FILE__ ":" QUOTEME(__LINE__) "): " __VA_ARGS__) +# define LOGE(...) printf("E/" LOG_TAG "(" ")" __VA_ARGS__) +#endif + +JNIEXPORT void JNICALL Java_com_gpac_Osmo4_MPEGVSensor_sendData( JNIEnv* env, jobject thiz, jint ptr, jstring data); + +static JavaVM* javaVM = 0; +static jclass sensCtrlClass; +static jmethodID cid; +static jmethodID startSensor; +static jmethodID stopSensor; + +//---------------------------------------------------------------------- +jint JNI_OnLoad(JavaVM* vm, void* reserved) +{ + JNIEnv* env = 0; + javaVM = vm; + + if ( (*javaVM)->GetEnv(javaVM, (void**)&env, JNI_VERSION_1_2) != JNI_OK ) + return -1; + + // Get the class and its methods in the main env + + // Get the CameraController class + // This is just a local refenrece. Cannot be used in the other JNI calls. + sensCtrlClass = (*env)->FindClass(env, "com/gpac/Osmo4/MPEGVSensor"); + if (sensCtrlClass == 0) + { + LOGE("[MPEG-V_IN] Class MPEGVSensor not found\n"); + return -1; + } + + // Get Global Reference to be able to use the class + sensCtrlClass = (*env)->NewGlobalRef(env, sensCtrlClass); + if ( sensCtrlClass == 0 ) + { + LOGE("[MPEG-V_IN] Caanot create Global Reference\n"); + return -1; + } + + // Get the method ID for the CameraController constructor. + cid = (*env)->GetMethodID(env, sensCtrlClass, "", "()V"); + if (cid == 0) + { + LOGE("[MPEG-V_IN] MPEGVSensor Constructor not found\n"); + return -1; + } + + // Get startCamera() method from class CameraController + startSensor = (*env)->GetMethodID(env, sensCtrlClass, "startSensor", "(II)V"); + if (startSensor == 0) + { + LOGE("[MPEG-V_IN] Function startSensor not found\n"); + return -1; + } + + stopSensor = (*env)->GetMethodID(env, sensCtrlClass, "stopSensor", "()V"); + if (stopSensor == 0) + { + LOGE("[MPEG-V_IN] Function stopSensor not found\n"); + return -1; + } + + return JNI_VERSION_1_2; +} + +void JNI_OnUnload(JavaVM *vm, void *reserved) +{ + JNIEnv* env = 0; + + if ( (*vm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_2) != JNI_OK ) + return; + + (*env)->DeleteGlobalRef(env, sensCtrlClass); +} +//---------------------------------------------------------------------- +JavaVM* GetJavaVM() +{ + return javaVM; +} +//---------------------------------------------------------------------- + +typedef struct +{ + char sensor[50]; + u16 sensorAndroidType; + u8 isAttached; + + GF_Thread *trd; + u8 stop; + + JNIEnv* env; + jclass sensCtrlClass; + jmethodID cid; + jobject sensCtrlObj; + jmethodID startSensor; + jmethodID stopSensor; +} MPEGVSensorContext; + +#define MPEGVSCTX MPEGVSensorContext *rc = (MPEGVSensorContext *)dr->udta + +void unloadSensorController(MPEGVSensorContext *rc) +{ + if ( rc->isAttached ) + { + (*rc->env)->PopLocalFrame(rc->env, NULL); + (*GetJavaVM())->DetachCurrentThread(GetJavaVM()); + rc->isAttached = 0; + } + + rc->env = NULL; +} + +void loadSensorControler(MPEGVSensorContext *rc) +{ + JNIEnv* env = NULL; + jint res = 0; + + // Here we can be in a thread + + res = (*GetJavaVM())->GetEnv(javaVM, (void**)&env, JNI_VERSION_1_2); + if ( res == JNI_EDETACHED ) + { + GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[MPEG-V_IN] The current thread is not attached to the VM, assuming native thread\n")); + if ( res = (*GetJavaVM())->AttachCurrentThread(GetJavaVM(), &env, NULL) ) + { + GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[MPEG-V_IN] Attach current thread failed: %d\n", res)); + return; + } + rc->isAttached = 1; + (*env)->PushLocalFrame(env, 2); + } + else + if ( res == JNI_EVERSION ) + { + GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[MPEG-V_IN] The specified version is not supported\n")); + return; + } + + rc->env = env; + rc->sensCtrlClass = sensCtrlClass; + rc->cid = cid; + rc->startSensor = startSensor; + rc->stopSensor = stopSensor; + + // Create the sensor object in the thread + rc->sensCtrlObj = (*rc->env)->NewObject(rc->env, rc->sensCtrlClass, rc->cid); + if (rc->sensCtrlObj == 0) + { + GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[MPEG-V_IN] Cannot create MPEGVSensor object\n")); + return; + } +} + +Bool MPEGVS_RegisterDevice(struct __input_device *dr, const char *urn, GF_BitStream *dsi, void (*AddField)(struct __input_device *_this, u32 fieldType, const char *name)) +{ + MPEGVSCTX; + + //"MPEG-V:siv:OrientationSensorType" + + if ( strnicmp(urn, "MPEG-V", 6) ) + return 0; + + if ( strlen(urn) <= 6 ) + { + GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[MPEG-V] No sensor type specified\n")); + return 0; + } + + if ( strnicmp(urn+6, ":siv:", 5) ) + { + GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[MPEG-V] Not valid sensor type specified\n")); + return 0; + } + + strcpy(rc->sensor, urn+11); + + if ( !strcmp(rc->sensor, "OrientationSensorType") ) + { + AddField(dr, GF_SG_VRML_SFVEC3F, "Orientation"); + + rc->sensorAndroidType = 3; + + AddField(dr, GF_SG_VRML_SFVEC3F, "pos"); + + return 1; + } + else + { + GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[MPEG-V_IN] Unsupported sensor type: %s\n", rc->sensor)); + return 0; + } +} + +u32 MPEGVS_OnData(struct __input_device * dr, const char* data) +{ + GF_BitStream *bs; + char *buf; + u32 buf_size; + float x, y, z; + + bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + + sscanf(data, "%f;%f;%f;", &x, &y, &z); + + gf_bs_write_int(bs, 1, 1); + gf_bs_write_float(bs, x); + gf_bs_write_float(bs, y); + gf_bs_write_float(bs, z); + + gf_bs_align(bs); + gf_bs_get_content(bs, &buf, &buf_size); + gf_bs_del(bs); + + dr->DispatchFrame(dr, (u8*)buf, buf_size); + gf_free(buf); + + return GF_OK; +} + +JNIEXPORT void Java_com_gpac_Osmo4_MPEGVSensor_sendData( JNIEnv* env, jobject thiz, jint ptr, jstring data) +{ + jboolean isCopy; + const char * cData = (*env)->GetStringUTFChars(env, data, &isCopy); + + MPEGVS_OnData((struct __input_device *)ptr, cData); + + (*env)->ReleaseStringUTFChars(env, data, cData); +} + +u32 ThreadRun(void* param) +{ + struct __input_device * dr = (struct __input_device *)param; + MPEGVSCTX; + + GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[MPEG-V_IN] Start: %d\n", gf_th_id())); + + loadSensorControler(rc); + + if (!rc->env || !rc->sensCtrlObj) + return; + + (*rc->env)->CallNonvirtualVoidMethod(rc->env, rc->sensCtrlObj, rc->sensCtrlClass, rc->startSensor, (s32)dr, rc->sensorAndroidType); + + while (!rc->stop) + gf_sleep(10); + + GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[MPEG-V_IN] Stop: %d\n", gf_th_id())); + + if (!rc->env) + return; + + if ( rc->sensCtrlObj ) + { + (*rc->env)->CallNonvirtualVoidMethod(rc->env, rc->sensCtrlObj, rc->sensCtrlClass, rc->stopSensor); + + (*rc->env)->DeleteLocalRef( rc->env, rc->sensCtrlObj ); + } + + unloadSensorController(rc); +} + +void MPEGVS_Start(struct __input_device * dr) +{ + MPEGVSCTX; + + rc->trd = gf_th_new("MPEG-V_IN"); + gf_th_run(rc->trd, ThreadRun, dr); +} + +void MPEGVS_Stop(struct __input_device * dr) +{ + MPEGVSCTX; + + if ( rc->trd ) + { + rc->stop = 1; + while ( gf_th_status(rc->trd) == GF_THREAD_STATUS_RUN ) + gf_sleep(5); + + gf_th_del(rc->trd); + rc->trd = NULL; + rc->stop = 0; + } +} + +GF_InputSensorDevice* NewMPEGVSInputSesor() +{ + MPEGVSensorContext* ctx = NULL; + GF_InputSensorDevice* driv = NULL; + + driv = (GF_InputSensorDevice *) gf_malloc(sizeof(GF_InputSensorDevice)); + memset(driv, 0, sizeof(GF_InputSensorDevice)); + GF_REGISTER_MODULE_INTERFACE(driv, GF_INPUT_DEVICE_INTERFACE, "MPEG-V Sensors Input Module", "gpac distribution"); + + driv->RegisterDevice = MPEGVS_RegisterDevice; + driv->Start = MPEGVS_Start; + driv->Stop = MPEGVS_Stop; + + ctx = (MPEGVSensorContext*) gf_malloc (sizeof(MPEGVSensorContext)); + memset(ctx, 0, sizeof(MPEGVSensorContext)); + + driv->udta = (void*)ctx; + + return driv; +} + +void DeleteMPEGVSInputSensor(GF_InputSensorDevice* dev) +{ + MPEGVS_Stop(dev); + gf_free(dev->udta); + gf_free(dev); +} + +/*interface query*/ +GF_EXPORT +const u32 *QueryInterfaces() +{ + static u32 si [] = { + GF_INPUT_DEVICE_INTERFACE, + 0 + }; + return si; +} +/*interface create*/ +GF_BaseInterface *LoadInterface(u32 InterfaceType) +{ + if (InterfaceType == GF_INPUT_DEVICE_INTERFACE) return (GF_BaseInterface *) NewMPEGVSInputSesor(); + return NULL; +} +/*interface destroy*/ +void ShutdownInterface(GF_BaseInterface *ifce) +{ + switch (ifce->InterfaceType) { + case GF_INPUT_DEVICE_INTERFACE: + DeleteMPEGVSInputSensor((GF_InputSensorDevice *)ifce); + break; + } +} + diff --git a/modules/dummy_in/dummy_in.c b/modules/dummy_in/dummy_in.c index 0b0cd49..6dc2605 100644 --- a/modules/dummy_in/dummy_in.c +++ b/modules/dummy_in/dummy_in.c @@ -186,7 +186,12 @@ void DC_DownloadFile(GF_InputService *plug, char *url) DCReader *read = (DCReader *) plug->priv; read->dnload = gf_term_download_new(read->service, url, 0, DC_NetIO, read); - if (!read->dnload) gf_term_on_connect(read->service, NULL, GF_NOT_SUPPORTED); + if (!read->dnload) { + gf_term_on_connect(read->service, NULL, GF_NOT_SUPPORTED); + } else { + /*start our download (threaded)*/ + gf_dm_sess_process(read->dnload); + } } diff --git a/modules/dx_hw/dx_2d.c b/modules/dx_hw/dx_2d.c index fc050db..0ce8e59 100644 --- a/modules/dx_hw/dx_2d.c +++ b/modules/dx_hw/dx_2d.c @@ -106,11 +106,9 @@ static GF_Err DD_ClearBackBuffer(GF_VideoOutput *dr, u32 color) rc.left = rc.top = 0; rc.right = dd->width; rc.bottom = dd->height; -#ifdef USE_DX_3 - hr = IDirectDrawSurface_Blt(dd->pBack, &rc, NULL, NULL, DDBLT_COLORFILL, &ddbltfx ); -#else - hr = IDirectDrawSurface7_Blt(dd->pBack, &rc, NULL, NULL, DDBLT_COLORFILL, &ddbltfx ); -#endif + + hr = dd->pBack->lpVtbl->Blt(dd->pBack, &rc, NULL, NULL, DDBLT_COLORFILL, &ddbltfx); + return FAILED(hr) ? GF_IO_ERR : GF_OK; } @@ -119,11 +117,7 @@ GF_Err CreateBackBuffer(GF_VideoOutput *dr, u32 Width, u32 Height, Bool use_syst Bool force_reinit; HRESULT hr; const char *opt; -#ifdef USE_DX_3 - DDSURFACEDESC ddsd; -#else - DDSURFACEDESC2 ddsd; -#endif + DDSURFDESC ddsd; DDCONTEXT; @@ -141,7 +135,7 @@ GF_Err CreateBackBuffer(GF_VideoOutput *dr, u32 Width, u32 Height, Bool use_syst return GF_OK; } - if (dd->pBack) SAFE_DD_RELEASE(dd->pBack); + SAFE_DD_RELEASE(dd->pBack); /*create backbuffer*/ ZeroMemory(&ddsd, sizeof(ddsd)); @@ -167,11 +161,8 @@ GF_Err CreateBackBuffer(GF_VideoOutput *dr, u32 Width, u32 Height, Bool use_syst ddsd.dwWidth = Width; ddsd.dwHeight = Height; -#ifdef USE_DX_3 - hr = IDirectDraw_CreateSurface(dd->pDD, &ddsd, &dd->pBack, NULL ); -#else - hr = IDirectDraw7_CreateSurface(dd->pDD, &ddsd, &dd->pBack, NULL ); -#endif + hr = dd->pDD->lpVtbl->CreateSurface(dd->pDD, &ddsd, &dd->pBack, NULL ); + if (FAILED(hr)) { if (!dd->systems_memory) { ddsd.ddsCaps.dwCaps &= ~DDSCAPS_VIDEOMEMORY; @@ -179,11 +170,7 @@ GF_Err CreateBackBuffer(GF_VideoOutput *dr, u32 Width, u32 Height, Bool use_syst dd->systems_memory = 2; GF_LOG(GF_LOG_INFO, GF_LOG_MMIO, ("[DX Out] Hardware Video not available for backbuffer)\n")); -#ifdef USE_DX_3 - hr = IDirectDraw_CreateSurface(dd->pDD, &ddsd, &dd->pBack, NULL ); -#else - hr = IDirectDraw7_CreateSurface(dd->pDD, &ddsd, &dd->pBack, NULL ); -#endif + hr = dd->pDD->lpVtbl->CreateSurface(dd->pDD, &ddsd, &dd->pBack, NULL ); } if (FAILED(hr)) return GF_IO_ERR; } @@ -208,27 +195,19 @@ GF_Err InitDirectDraw(GF_VideoOutput *dr, u32 Width, u32 Height) HRESULT hr; DWORD cooplev; LPDIRECTDRAW ddraw; -#ifdef USE_DX_3 - DDSURFACEDESC ddsd; -#else - DDSURFACEDESC2 ddsd; -#endif + DDSURFDESC ddsd; DDPIXELFORMAT pixelFmt; LPDIRECTDRAWCLIPPER pcClipper; DDCONTEXT; - if (!dd->cur_hwnd || !Width || !Height) return GF_BAD_PARAM; + if (!dd->cur_hwnd || !Width || !Height || !dd->DirectDrawCreate) return GF_BAD_PARAM; DestroyObjects(dd); - if( FAILED( hr = DirectDrawCreate(NULL, &ddraw, NULL ) ) ) + if( FAILED( hr = dd->DirectDrawCreate(NULL, &ddraw, NULL ) ) ) return GF_IO_ERR; -#ifdef USE_DX_3 - hr = IDirectDraw_QueryInterface(ddraw, &IID_IDirectDraw, (LPVOID *)&dd->pDD); -#else - hr = IDirectDraw_QueryInterface(ddraw, &IID_IDirectDraw7, (LPVOID *)&dd->pDD); -#endif - IDirectDraw_Release(ddraw); + hr = ddraw->lpVtbl->QueryInterface(ddraw, &IID_IDirectDraw7, (LPVOID *)&dd->pDD); + ddraw->lpVtbl->Release(ddraw); if (FAILED(hr)) return GF_IO_ERR; dd->switch_res = 0; @@ -239,9 +218,9 @@ GF_Err InitDirectDraw(GF_VideoOutput *dr, u32 Width, u32 Height) /*change display mode*/ if (dd->switch_res) { #ifdef USE_DX_3 - hr = IDirectDraw_SetDisplayMode(dd->pDD, dd->fs_width, dd->fs_height, dd->video_bpp); + hr = dd->pDD->lpVtbl->SetDisplayMode(dd->pDD, dd->fs_width, dd->fs_height, dd->video_bpp); #else - hr = IDirectDraw7_SetDisplayMode(dd->pDD, dd->fs_width, dd->fs_height, dd->video_bpp, 0, 0 ); + hr = dd->pDD->lpVtbl->SetDisplayMode(dd->pDD, dd->fs_width, dd->fs_height, dd->video_bpp, 0, 0 ); #endif if( FAILED(hr)) return GF_IO_ERR; } @@ -249,12 +228,7 @@ GF_Err InitDirectDraw(GF_VideoOutput *dr, u32 Width, u32 Height) cooplev = DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN; } - -#ifdef USE_DX_3 - hr = IDirectDraw_SetCooperativeLevel(dd->pDD, dd->cur_hwnd, cooplev); -#else - hr = IDirectDraw7_SetCooperativeLevel(dd->pDD, dd->cur_hwnd, cooplev); -#endif + hr = dd->pDD->lpVtbl->SetCooperativeLevel(dd->pDD, dd->cur_hwnd, cooplev); if( FAILED(hr) ) return GF_IO_ERR; /*create primary*/ @@ -263,18 +237,15 @@ GF_Err InitDirectDraw(GF_VideoOutput *dr, u32 Width, u32 Height) ddsd.dwFlags = DDSD_CAPS; ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE; -#ifdef USE_DX_3 - if( FAILED(IDirectDraw_CreateSurface(dd->pDD, &ddsd, &dd->pPrimary, NULL ) ) ) + hr = dd->pDD->lpVtbl->CreateSurface(dd->pDD, &ddsd, &dd->pPrimary, NULL); + if( FAILED(hr) ) return GF_IO_ERR; -#else - if( FAILED(hr = IDirectDraw7_CreateSurface(dd->pDD, &ddsd, &dd->pPrimary, NULL ) ) ) - return GF_IO_ERR; -#endif /*get pixel format of video board*/ memset (&pixelFmt, 0, sizeof(pixelFmt)); pixelFmt.dwSize = sizeof(pixelFmt); - hr = IDirectDrawSurface_GetPixelFormat(dd->pPrimary, &pixelFmt); + + hr = dd->pPrimary->lpVtbl->GetPixelFormat(dd->pPrimary, &pixelFmt); if( FAILED(hr) ) return GF_IO_ERR; switch(pixelFmt.dwRGBBitCount) { @@ -306,53 +277,40 @@ GF_Err InitDirectDraw(GF_VideoOutput *dr, u32 Width, u32 Height) return GF_IO_ERR; } + hr = dd->pDD->lpVtbl->CreateClipper(dd->pDD, 0, &pcClipper, NULL); + if( FAILED(hr)) + return GF_IO_ERR; -#ifdef USE_DX_3 - if( FAILED( hr = IDirectDraw_CreateClipper(dd->pDD, 0, &pcClipper, NULL ) ) ) - return GF_IO_ERR; -#else - if( FAILED( hr = IDirectDraw7_CreateClipper(dd->pDD, 0, &pcClipper, NULL ) ) ) - return GF_IO_ERR; -#endif - - if( FAILED( hr = IDirectDrawClipper_SetHWnd(pcClipper, 0, dd->cur_hwnd) ) ) { - IDirectDrawClipper_Release(pcClipper); + hr = pcClipper->lpVtbl->SetHWnd(pcClipper, 0, dd->cur_hwnd); + if( FAILED(hr) ) { + pcClipper->lpVtbl->Release(pcClipper); return GF_IO_ERR; } - if( FAILED( hr = IDirectDrawSurface_SetClipper(dd->pPrimary, pcClipper ) ) ) { - IDirectDrawClipper_Release(pcClipper); + hr = dd->pPrimary->lpVtbl->SetClipper(dd->pPrimary, pcClipper); + if( FAILED(hr)) { + pcClipper->lpVtbl->Release(pcClipper); return GF_IO_ERR; } - IDirectDrawClipper_Release(pcClipper); + pcClipper->lpVtbl->Release(pcClipper); dd->ddraw_init = 1; /*if YUV not initialize, init using HW video memory to setup HW caps*/ return CreateBackBuffer(dr, Width, Height, dd->yuv_init); } -static GF_Err DD_LockSurface(DDContext *dd, GF_VideoSurface *vi, void *surface) +static GF_Err DD_LockSurface(DDContext *dd, GF_VideoSurface *vi, LPDDRAWSURFACE surface) { HRESULT hr; -#ifdef USE_DX_3 - DDSURFACEDESC desc; -#else - DDSURFACEDESC2 desc; -#endif + DDSURFDESC desc; if (!dd || !vi || !surface) return GF_BAD_PARAM; -#ifdef USE_DX_3 - ZeroMemory(&desc, sizeof(DDSURFACEDESC)); + ZeroMemory(&desc, sizeof(desc)); desc.dwSize = sizeof(DDSURFACEDESC); - if (FAILED(hr = IDirectDrawSurface_Lock( (LPDIRECTDRAWSURFACE)surface, NULL, &desc, DDLOCK_SURFACEMEMORYPTR | /*DDLOCK_WRITEONLY | */ DDLOCK_WAIT, NULL))) { - return GF_IO_ERR; - } -#else - ZeroMemory(&desc, sizeof(DDSURFACEDESC2)); - desc.dwSize = sizeof(DDSURFACEDESC2); - if (FAILED(hr = IDirectDrawSurface7_Lock( (LPDIRECTDRAWSURFACE7) surface, NULL, &desc, DDLOCK_SURFACEMEMORYPTR | /*DDLOCK_WRITEONLY | */ DDLOCK_WAIT, NULL))) { + + hr = surface->lpVtbl->Lock(surface, NULL, &desc, DDLOCK_SURFACEMEMORYPTR | /*DDLOCK_WRITEONLY | */ DDLOCK_WAIT, NULL); + if (FAILED(hr)) return GF_IO_ERR; - } -#endif + vi->video_buffer = desc.lpSurface; vi->width = desc.dwWidth; vi->height = desc.dwHeight; @@ -362,15 +320,11 @@ static GF_Err DD_LockSurface(DDContext *dd, GF_VideoSurface *vi, void *surface) return GF_OK; } -static GF_Err DD_UnlockSurface(DDContext *dd, void *surface) +static GF_Err DD_UnlockSurface(DDContext *dd, LPDDRAWSURFACE surface) { HRESULT hr; if (!dd || !dd->ddraw_init) return GF_IO_ERR; -#ifdef USE_DX_3 - hr = IDirectDrawSurface_Unlock( (LPDIRECTDRAWSURFACE)surface, NULL); -#else - hr = IDirectDrawSurface7_Unlock((LPDIRECTDRAWSURFACE7)surface, NULL); -#endif + hr = surface->lpVtbl->Unlock(surface, NULL); return FAILED(hr) ? GF_IO_ERR : GF_OK; } @@ -393,12 +347,12 @@ static void *LockOSContext(GF_VideoOutput *dr, Bool do_lock) if (!dd->pBack) return NULL; if (do_lock) { - if (!dd->lock_hdc && ! IDirectDrawSurface_IsLost(dd->pBack)) { - if (FAILED(IDirectDrawSurface_GetDC(dd->pBack, &dd->lock_hdc)) ) + if (!dd->lock_hdc && ! dd->pBack->lpVtbl->IsLost(dd->pBack)) { + if (FAILED(dd->pBack->lpVtbl->GetDC(dd->pBack, &dd->lock_hdc)) ) dd->lock_hdc = NULL; } } else if (dd->lock_hdc) { - IDirectDrawSurface_ReleaseDC(dd->pBack, dd->lock_hdc); + dd->pBack->lpVtbl->ReleaseDC(dd->pBack, dd->lock_hdc); dd->lock_hdc = NULL; } return (void *)dd->lock_hdc ; @@ -424,7 +378,7 @@ static GF_Err DD_BlitSurface(DDContext *dd, DDSurface *src, GF_Window *src_wnd, DDCOLORKEY ck; col = GF_COL_ARGB(0xFF, key->r, key->g, key->b); ck.dwColorSpaceHighValue = ck.dwColorSpaceLowValue = col; - hr = IDirectDrawSurface_SetColorKey(src->pSurface, DDCKEY_SRCBLT, &ck); + hr = src->pSurface->lpVtbl->SetColorKey(src->pSurface, DDCKEY_SRCBLT, &ck); if (FAILED(hr)) return GF_IO_ERR; } @@ -433,11 +387,11 @@ static GF_Err DD_BlitSurface(DDContext *dd, DDSurface *src, GF_Window *src_wnd, flags = DDBLTFAST_WAIT; if (key) flags |= DDBLTFAST_SRCCOLORKEY; if (dst_wnd) { left = r_dst.left; top = r_dst.top; } else left = top = 0; - hr = IDirectDrawSurface_BltFast(dd->pBack, left, top, src->pSurface, src_wnd ? &r_src : NULL, flags); + hr = dd->pBack->lpVtbl->BltFast(dd->pBack, left, top, src->pSurface, src_wnd ? &r_src : NULL, flags); } else { flags = DDBLT_WAIT; if (key) flags |= DDBLT_KEYSRC; - hr = IDirectDrawSurface_Blt(dd->pBack, dst_wnd ? &r_dst : NULL, src->pSurface, src_wnd ? &r_src : NULL, flags, NULL); + hr = dd->pBack->lpVtbl->Blt(dd->pBack, dst_wnd ? &r_dst : NULL, src->pSurface, src_wnd ? &r_src : NULL, flags, NULL); } GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[DX Out] Hardware blit src w=%d,h=%d to dst w=%d,h=%d - result: %08x\n", src_w, src_h, dst_w, dst_h, hr)); return FAILED(hr) ? GF_IO_ERR : GF_OK; @@ -445,11 +399,7 @@ static GF_Err DD_BlitSurface(DDContext *dd, DDSurface *src, GF_Window *src_wnd, static DDSurface *DD_GetSurface(GF_VideoOutput *dr, u32 width, u32 height, u32 pixel_format, Bool check_caps) { -#ifdef USE_DX_3 - DDSURFACEDESC ddsd; -#else - DDSURFACEDESC2 ddsd; -#endif + DDSURFDESC ddsd; HRESULT hr; DDCONTEXT; @@ -487,11 +437,8 @@ static DDSurface *DD_GetSurface(GF_VideoOutput *dr, u32 width, u32 height, u32 p ddsd.dwHeight = height; ddsd.ddpfPixelFormat.dwFourCC = get_win_4CC(dr->yuv_pixel_format); -#ifdef USE_DX_3 - if( FAILED( hr = IDirectDraw_CreateSurface(dd->pDD, &ddsd, &yuvp->pSurface, NULL ) ) ) { -#else - if( FAILED( hr = IDirectDraw7_CreateSurface(dd->pDD, &ddsd, &yuvp->pSurface, NULL ) ) ) { -#endif + hr = dd->pDD->lpVtbl->CreateSurface(dd->pDD, &ddsd, &yuvp->pSurface, NULL); + if (FAILED(hr) ) { if (!check_caps) return NULL; /*try withou overlay cap*/ @@ -499,11 +446,8 @@ static DDSurface *DD_GetSurface(GF_VideoOutput *dr, u32 width, u32 height, u32 p dr->hw_caps &= ~GF_VIDEO_HW_HAS_YUV_OVERLAY; ddsd.ddsCaps.dwCaps = DDSCAPS_VIDEOMEMORY; -#ifdef USE_DX_3 - if( FAILED( hr = IDirectDraw_CreateSurface(dd->pDD, &ddsd, &yuvp->pSurface, NULL ) ) ) { -#else - if( FAILED( hr = IDirectDraw7_CreateSurface(dd->pDD, &ddsd, &yuvp->pSurface, NULL ) ) ) { -#endif + hr = dd->pDD->lpVtbl->CreateSurface(dd->pDD, &ddsd, &yuvp->pSurface, NULL); + if( FAILED(hr) ) { return NULL; } } @@ -536,14 +480,9 @@ static DDSurface *DD_GetSurface(GF_VideoOutput *dr, u32 width, u32 height, u32 p ddsd.dwWidth = width; ddsd.dwHeight = height; -#ifdef USE_DX_3 - if( FAILED( hr = IDirectDraw_CreateSurface(dd->pDD, &ddsd, &dd->rgb_pool.pSurface, NULL ) ) ) - return NULL; -#else - if( FAILED( hr = IDirectDraw7_CreateSurface(dd->pDD, &ddsd, &dd->rgb_pool.pSurface, NULL ) ) ) + hr = dd->pDD->lpVtbl->CreateSurface(dd->pDD, &ddsd, &dd->rgb_pool.pSurface, NULL); + if (FAILED(hr)) return NULL; -#endif - dd->rgb_pool.width = width; dd->rgb_pool.height = height; dd->rgb_pool.format = dd->pixelFormat; @@ -560,7 +499,8 @@ static GF_Err DD_Blit(GF_VideoOutput *dr, GF_VideoSurface *video_src, GF_Window DDCONTEXT; if (!video_src) { - if (overlay_type && dd->yuv_pool.pSurface) IDirectDrawSurface2_UpdateOverlay(dd->yuv_pool.pSurface, NULL, dd->pPrimary, NULL, DDOVER_HIDE, NULL); + if (overlay_type && dd->yuv_pool.pSurface) + dd->yuv_pool.pSurface->lpVtbl->UpdateOverlay(dd->yuv_pool.pSurface, NULL, dd->pPrimary, NULL, DDOVER_HIDE, NULL); return GF_OK; } if (src_wnd) { @@ -609,21 +549,21 @@ static GF_Err DD_Blit(GF_VideoOutput *dr, GF_VideoSurface *video_src, GF_Window #if 1 if (overlay_type==1) { - hr = IDirectDrawSurface2_UpdateOverlay(pool->pSurface, &src_rc, dd->pPrimary, &dst_rc, DDOVER_SHOW, NULL); + hr = pool->pSurface->lpVtbl->UpdateOverlay(pool->pSurface, &src_rc, dd->pPrimary, &dst_rc, DDOVER_SHOW, NULL); } else { DDOVERLAYFX ddofx; memset(&ddofx, 0, sizeof(DDOVERLAYFX)); ddofx.dwSize = sizeof(DDOVERLAYFX); ddofx.dckDestColorkey.dwColorSpaceLowValue = dr->overlay_color_key; ddofx.dckDestColorkey.dwColorSpaceHighValue = dr->overlay_color_key; - hr = IDirectDrawSurface2_UpdateOverlay(pool->pSurface, &src_rc, dd->pPrimary, &dst_rc, DDOVER_SHOW | DDOVER_KEYDESTOVERRIDE, &ddofx); + hr = pool->pSurface->lpVtbl->UpdateOverlay(pool->pSurface, &src_rc, dd->pPrimary, &dst_rc, DDOVER_SHOW | DDOVER_KEYDESTOVERRIDE, &ddofx); } if (FAILED(hr)) { - IDirectDrawSurface2_UpdateOverlay(pool->pSurface, NULL, dd->pPrimary, NULL, DDOVER_HIDE, NULL); + pool->pSurface->lpVtbl->UpdateOverlay(pool->pSurface, NULL, dd->pPrimary, NULL, DDOVER_HIDE, NULL); } #else if (overlay_type==1) { - hr = IDirectDrawSurface_Blt(dd->pPrimary, &dst_rc, pool->pSurface, &src_rc, 0, NULL); + hr = dd->pPrimary->lpVtbl->Blt(dd->pPrimary, &dst_rc, pool->pSurface, &src_rc, 0, NULL); } else { DDBLTFX ddfx; memset(&ddfx, 0, sizeof(DDBLTFX)); @@ -631,7 +571,7 @@ static GF_Err DD_Blit(GF_VideoOutput *dr, GF_VideoSurface *video_src, GF_Window ddfx.ddckDestColorkey.dwColorSpaceLowValue = dr->overlay_color_key; ddfx.ddckDestColorkey.dwColorSpaceHighValue = dr->overlay_color_key; - hr = IDirectDrawSurface_Blt(dd->pPrimary, &dst_rc, pool->pSurface, &src_rc, DDBLT_WAIT | DDBLT_KEYDESTOVERRIDE, &ddfx); + hr = dd->pPrimary->lpVtbl->Blt(dd->pPrimary, &dst_rc, pool->pSurface, &src_rc, DDBLT_WAIT | DDBLT_KEYDESTOVERRIDE, &ddfx); } #endif return FAILED(hr) ? GF_IO_ERR : GF_OK; @@ -720,17 +660,10 @@ void DD_InitYUV(GF_VideoOutput *dr) dr->hw_caps |= GF_VIDEO_HW_HAS_YUV | GF_VIDEO_HW_HAS_YUV_OVERLAY; -#ifdef USE_DX_3 - IDirectDraw_GetFourCCCodes(dd->pDD, &numCodes, NULL); + dd->pDD->lpVtbl->GetFourCCCodes(dd->pDD, &numCodes, NULL); if (!numCodes) return; codes = (DWORD *)gf_malloc(numCodes*sizeof(DWORD)); - IDirectDraw_GetFourCCCodes(dd->pDD, &numCodes, codes); -#else - IDirectDraw7_GetFourCCCodes(dd->pDD, &numCodes, NULL); - if (!numCodes) return; - codes = (DWORD *)gf_malloc(numCodes*sizeof(DWORD)); - IDirectDraw7_GetFourCCCodes(dd->pDD, &numCodes, codes); -#endif + dd->pDD->lpVtbl->GetFourCCCodes(dd->pDD, &numCodes, codes); num_yuv = 0; for (i=0; iopaque; @@ -84,9 +88,9 @@ static GF_Err DS_Setup(GF_AudioOutput *dr, void *os_handle, u32 num_buffers, u32 ctx->cfg_duration = total_duration; if (ctx->cfg_num_buffers <= 1) ctx->cfg_num_buffers = 2; - if ( FAILED( hr = DirectSoundCreate( NULL, &ctx->pDS, NULL ) ) ) return GF_IO_ERR; + if ( FAILED( hr = ctx->DirectSoundCreate( NULL, &ctx->pDS, NULL ) ) ) return GF_IO_ERR; flags = DSSCL_EXCLUSIVE; - if( FAILED( hr = IDirectSound_SetCooperativeLevel(ctx->pDS, ctx->hWnd, DSSCL_EXCLUSIVE) ) ) { + if( FAILED( hr = ctx->pDS->lpVtbl->SetCooperativeLevel(ctx->pDS, ctx->hWnd, DSSCL_EXCLUSIVE) ) ) { SAFE_DS_RELEASE( ctx->pDS ); return GF_IO_ERR; } @@ -99,10 +103,10 @@ void DS_ResetBuffer(DSContext *ctx) VOID *pLock = NULL; DWORD size; - if( FAILED(IDirectSoundBuffer_Lock(ctx->pOutput, 0, ctx->buffer_size*ctx->num_audio_buffer, &pLock, &size, NULL, NULL, 0 ) ) ) + if( FAILED(ctx->pOutput->lpVtbl->Lock(ctx->pOutput, 0, ctx->buffer_size*ctx->num_audio_buffer, &pLock, &size, NULL, NULL, 0 ) ) ) return; memset(pLock, 0, (size_t) size); - IDirectSoundBuffer_Unlock(ctx->pOutput, pLock, size, NULL, 0L); + ctx->pOutput->lpVtbl->Unlock(ctx->pOutput, pLock, size, NULL, 0L); } void DS_ReleaseBuffer(GF_AudioOutput *dr) @@ -111,7 +115,7 @@ void DS_ReleaseBuffer(GF_AudioOutput *dr) DSCONTEXT(); /*stop playing and notif proc*/ - if (ctx->pOutput) IDirectSoundBuffer_Stop(ctx->pOutput); + if (ctx->pOutput) ctx->pOutput->lpVtbl->Stop(ctx->pOutput); SAFE_DS_RELEASE(ctx->pOutput); /*use notif, shutdown notifier and event watcher*/ @@ -195,20 +199,20 @@ static GF_Err DS_ConfigureOutput(GF_AudioOutput *dr, u32 *SampleRate, u32 *NbCha #endif - hr = IDirectSound_CreateSoundBuffer(ctx->pDS, &dsbBufferDesc, &ctx->pOutput, NULL ); + hr = ctx->pDS->lpVtbl->CreateSoundBuffer(ctx->pDS, &dsbBufferDesc, &ctx->pOutput, NULL ); if (FAILED(hr)) { retry: if (ctx->use_notif) gf_modules_set_option((GF_BaseInterface *)dr, "Audio", "DisableNotification", "yes"); ctx->use_notif = 0; dsbBufferDesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_GLOBALFOCUS; - hr = IDirectSound_CreateSoundBuffer(ctx->pDS, &dsbBufferDesc, &ctx->pOutput, NULL ); + hr = ctx->pDS->lpVtbl->CreateSoundBuffer(ctx->pDS, &dsbBufferDesc, &ctx->pOutput, NULL ); if (FAILED(hr)) return GF_IO_ERR; } for (i=0; inum_audio_buffer; i++) ctx->frame_state[i] = 0; if (ctx->use_notif) { - hr = IDirectSoundBuffer_QueryInterface(ctx->pOutput, &IID_IDirectSoundNotify , (void **)&pNotify); + hr = ctx->pOutput->lpVtbl->QueryInterface(ctx->pOutput, &IID_IDirectSoundNotify , (void **)&pNotify); if (hr == S_OK) { /*setup the notification positions*/ for (i=0; inum_audio_buffer; i++) { @@ -218,16 +222,16 @@ retry: } /*Tell DirectSound when to notify us*/ - hr = IDirectSoundNotify_SetNotificationPositions(pNotify, ctx->num_audio_buffer, ctx->notif_events); + hr = pNotify->lpVtbl->SetNotificationPositions(pNotify, ctx->num_audio_buffer, ctx->notif_events); if (hr != S_OK) { - IDirectSoundNotify_Release(pNotify); + pNotify->lpVtbl->Release(pNotify); for (i=0; inum_audio_buffer; i++) CloseHandle(ctx->events[i]); SAFE_DS_RELEASE(ctx->pOutput); goto retry; } - IDirectSoundNotify_Release(pNotify); + pNotify->lpVtbl->Release(pNotify); } else { ctx->use_notif = 0; } @@ -243,18 +247,18 @@ retry: /*reset*/ DS_ResetBuffer(ctx); /*play*/ - IDirectSoundBuffer_Play(ctx->pOutput, 0, 0, DSBPLAY_LOOPING); + ctx->pOutput->lpVtbl->Play(ctx->pOutput, 0, 0, DSBPLAY_LOOPING); return GF_OK; } static Bool DS_RestoreBuffer(LPDIRECTSOUNDBUFFER pDSBuffer) { DWORD dwStatus; - IDirectSoundBuffer_GetStatus(pDSBuffer, &dwStatus ); + pDSBuffer->lpVtbl->GetStatus(pDSBuffer, &dwStatus ); if( dwStatus & DSBSTATUS_BUFFERLOST ) { GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[DirectSound] buffer lost\n")); - IDirectSoundBuffer_Restore(pDSBuffer); - IDirectSoundBuffer_GetStatus(pDSBuffer, &dwStatus); + pDSBuffer->lpVtbl->Restore(pDSBuffer); + pDSBuffer->lpVtbl->GetStatus(pDSBuffer, &dwStatus); if( dwStatus & DSBSTATUS_BUFFERLOST ) return 1; } return 0; @@ -279,7 +283,7 @@ void DS_FillBuffer(GF_AudioOutput *dr, u32 buffer) /*lock and fill from current pos*/ pos = buffer * ctx->buffer_size; pLock = NULL; - if( FAILED( hr = IDirectSoundBuffer_Lock(ctx->pOutput, pos, ctx->buffer_size, + if( FAILED( hr = ctx->pOutput->lpVtbl->Lock(ctx->pOutput, pos, ctx->buffer_size, &pLock, &size, NULL, NULL, 0L ) ) ) { GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[DirectSound] Error locking sound buffer\n")); return; @@ -290,7 +294,7 @@ void DS_FillBuffer(GF_AudioOutput *dr, u32 buffer) dr->FillBuffer(dr->audio_renderer, pLock, size); /*update current pos*/ - if( FAILED( hr = IDirectSoundBuffer_Unlock(ctx->pOutput, pLock, size, NULL, 0)) ) { + if( FAILED( hr = ctx->pOutput->lpVtbl->Unlock(ctx->pOutput, pLock, size, NULL, 0)) ) { GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[DirectSound] Error unlocking sound buffer\n")); } ctx->frame_state[buffer] = 1; @@ -323,14 +327,14 @@ void DS_WriteAudio(GF_AudioOutput *dr) DSCONTEXT(); /*wait for end of current play buffer*/ - if (IDirectSoundBuffer_GetCurrentPosition(ctx->pOutput, &in_play, NULL) != DS_OK ) { + if (ctx->pOutput->lpVtbl->GetCurrentPosition(ctx->pOutput, &in_play, NULL) != DS_OK ) { GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[DirectSound] error getting sound buffer poitions\n")); return; } in_play = (in_play / ctx->buffer_size); retry = 6; while (retry) { - if (IDirectSoundBuffer_GetCurrentPosition(ctx->pOutput, &cur_play, NULL) != DS_OK ) { + if (ctx->pOutput->lpVtbl->GetCurrentPosition(ctx->pOutput, &cur_play, NULL) != DS_OK ) { GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[DirectSound] error getting sound buffer poitions\n")); return; } @@ -358,12 +362,12 @@ static void DS_Play(GF_AudioOutput *dr, u32 PlayType) DSCONTEXT(); switch (PlayType) { case 0: - IDirectSoundBuffer_Stop(ctx->pOutput); + ctx->pOutput->lpVtbl->Stop(ctx->pOutput); break; case 2: DS_ResetBuffer(ctx); case 1: - IDirectSoundBuffer_Play(ctx->pOutput, 0, 0, DSBPLAY_LOOPING); + ctx->pOutput->lpVtbl->Play(ctx->pOutput, 0, 0, DSBPLAY_LOOPING); break; } } @@ -375,7 +379,7 @@ static void DS_SetVolume(GF_AudioOutput *dr, u32 Volume) if (Volume > 100) Volume = DSBVOLUME_MAX; else if(Volume == 0) Vol = DSBVOLUME_MIN; else Vol = DSBVOLUME_MIN/2 + Volume * (DSBVOLUME_MAX-DSBVOLUME_MIN/2) / 100; - IDirectSoundBuffer_SetVolume(ctx->pOutput, Vol); + ctx->pOutput->lpVtbl->SetVolume(ctx->pOutput, Vol); } static void DS_SetPan(GF_AudioOutput *dr, u32 Pan) @@ -391,7 +395,7 @@ static void DS_SetPan(GF_AudioOutput *dr, u32 Pan) } else { dspan = 0; } - IDirectSoundBuffer_SetPan(ctx->pOutput, dspan); + ctx->pOutput->lpVtbl->SetPan(ctx->pOutput, dspan); } @@ -422,6 +426,15 @@ void *NewAudioOutput() ctx = gf_malloc(sizeof(DSContext)); memset(ctx, 0, sizeof(DSContext)); + ctx->hDSoundLib = LoadLibrary("dsound.dll"); + if (ctx->hDSoundLib) { + ctx->DirectSoundCreate = (DIRECTSOUNDCREATEPROC) GetProcAddress(ctx->hDSoundLib, "DirectSoundCreate"); + } + if (!ctx->DirectSoundCreate) { + if (ctx->hDSoundLib) FreeLibrary(ctx->hDSoundLib); + gf_free(ctx);return NULL; + } + driv = gf_malloc(sizeof(GF_AudioOutput)); memset(driv, 0, sizeof(GF_AudioOutput)); @@ -450,6 +463,8 @@ void DeleteAudioOutput(void *ifce) GF_AudioOutput *dr = (GF_AudioOutput *)ifce; DSCONTEXT(); + if (ctx->hDSoundLib) + FreeLibrary(ctx->hDSoundLib); gf_free(ctx); gf_free(ifce); CoUninitialize(); diff --git a/modules/dx_hw/dx_hw.h b/modules/dx_hw/dx_hw.h index 0a89cda..d6ba1be 100644 --- a/modules/dx_hw/dx_hw.h +++ b/modules/dx_hw/dx_hw.h @@ -37,12 +37,16 @@ #include #include +#define INITGUID /* #include */ -#include +#define HAS_DDRAW_H + +#ifdef HAS_DDRAW_H +#include #include #include @@ -50,6 +54,27 @@ #include #endif +/*DirectDraw video output*/ +#if (DIRECTDRAW_VERSION < 0x0700) +#define USE_DX_3 +#endif + + +#ifdef USE_DX_3 +typedef LPDIRECTDRAWSURFACE LPDDRAWSURFACE; +typedef DDSURFACEDESC DDSURFDESC; +#else +typedef LPDIRECTDRAWSURFACE7 LPDDRAWSURFACE; +typedef DDSURFACEDESC2 DDSURFDESC; +#endif +typedef DDSURFDESC *LPDDSURFDESC; + +typedef HRESULT(WINAPI * DIRECTDRAWCREATEPROC) (GUID *, LPDIRECTDRAW *, IUnknown *); + +#endif + + + #ifdef _WIN32_WCE # ifndef SWP_ASYNCWINDOWPOS # define SWP_ASYNCWINDOWPOS 0 @@ -62,21 +87,9 @@ #include "GLES/egl.h" #endif -/* - DirectDraw video output -*/ - -#if (DIRECTDRAW_VERSION < 0x0700) -#define USE_DX_3 -#endif - typedef struct { -#ifdef USE_DX_3 - LPDIRECTDRAWSURFACE pSurface; -#else - LPDIRECTDRAWSURFACE7 pSurface; -#endif + LPDDRAWSURFACE pSurface; u32 width, height, format, pitch; } DDSurface; @@ -166,6 +179,13 @@ typedef struct u32 last_mouse_move, timer, cursor_type_backup; Bool windowless, hidden; + + Bool dd_lost; + + + HMODULE hDDrawLib; + DIRECTDRAWCREATEPROC DirectDrawCreate; + } DDContext; void DD_SetupWindow(GF_VideoOutput *dr, Bool hide); @@ -195,19 +215,16 @@ GF_Err DD_SetupOpenGL(GF_VideoOutput *dr, u32 offscreen_width, u32 offscreen_hei #endif -#ifdef USE_DX_3 -#define SAFE_DD_RELEASE(p) { if(p) { IDirectDraw_Release(p); (p)=NULL; } } -#else -#define SAFE_DD_RELEASE(p) { if(p) { IDirectDraw7_Release(p); (p)=NULL; } } -#endif +#define SAFE_DD_RELEASE(p) { if(p) { (p)->lpVtbl->Release( (p) ); (p)=NULL; } } void *NewAudioOutput(); void DeleteAudioOutput(void *); -#define SAFE_DS_RELEASE(p) { if(p) { IDirectSound_Release(p); (p)=NULL; } } +#define SAFE_DS_RELEASE(p) { if(p) { p->lpVtbl->Release(p); (p)=NULL; } } LRESULT APIENTRY DD_WindowProc(HWND hWnd, UINT msg, UINT wParam, LONG lParam); + #endif diff --git a/modules/dx_hw/dx_video.c b/modules/dx_hw/dx_video.c index d373886..1280ba3 100644 --- a/modules/dx_hw/dx_video.c +++ b/modules/dx_hw/dx_video.c @@ -151,14 +151,8 @@ static void RestoreWindow(DDContext *dd) } else #endif - { -#ifdef USE_DX_3 - IDirectDraw_SetCooperativeLevel(dd->pDD, dd->cur_hwnd, DDSCL_NORMAL); -#else - IDirectDraw7_SetCooperativeLevel(dd->pDD, dd->cur_hwnd, DDSCL_NORMAL); -#endif - dd->NeedRestore = 0; - } + dd->pDD->lpVtbl->SetCooperativeLevel(dd->pDD, dd->cur_hwnd, DDSCL_NORMAL); + dd->NeedRestore = 0; // SetForegroundWindow(dd->cur_hwnd); SetFocus(dd->cur_hwnd); @@ -386,6 +380,7 @@ GF_Err DD_SetupOpenGL(GF_VideoOutput *dr, u32 offscreen_width, u32 offscreen_hei if (dd->output_3d_type==1) { + memset(&evt, 0, sizeof(GF_Event)); evt.type = GF_EVENT_VIDEO_SETUP; evt.setup.opengl_mode = 1; dr->on_event(dr->evt_cbk_hdl, &evt); @@ -610,24 +605,20 @@ GF_Err DD_Flush(GF_VideoOutput *dr, GF_Window *dest) dest->x = pt.x; dest->y = pt.y; MAKERECT(rc, dest); - hr = IDirectDrawSurface_Blt(dd->pPrimary, &rc, dd->pBack, NULL, DDBLT_WAIT, NULL); + hr = dd->pPrimary->lpVtbl->Blt(dd->pPrimary, &rc, dd->pBack, NULL, DDBLT_WAIT, NULL); } else { - hr = IDirectDrawSurface_Blt(dd->pPrimary, NULL, dd->pBack, NULL, DDBLT_WAIT, NULL); + hr = dd->pPrimary->lpVtbl->Blt(dd->pPrimary, NULL, dd->pBack, NULL, DDBLT_WAIT, NULL); } if (hr == DDERR_SURFACELOST) { - IDirectDrawSurface_Restore(dd->pPrimary); - IDirectDrawSurface_Restore(dd->pBack); + dd->pPrimary->lpVtbl->Restore(dd->pPrimary); + dd->pBack->lpVtbl->Restore(dd->pBack); } return FAILED(hr) ? GF_IO_ERR : GF_OK; } -#ifdef USE_DX_3 -HRESULT WINAPI EnumDisplayModes( LPDDSURFACEDESC lpDDDesc, LPVOID lpContext) -#else -HRESULT WINAPI EnumDisplayModes( LPDDSURFACEDESC2 lpDDDesc, LPVOID lpContext) -#endif +HRESULT WINAPI EnumDisplayModes( LPDDSURFDESC lpDDDesc, LPVOID lpContext) { DDContext *dd = (DDContext *) lpContext; @@ -649,27 +640,20 @@ HRESULT WINAPI EnumDisplayModes( LPDDSURFACEDESC2 lpDDDesc, LPVOID lpContext) GF_Err GetDisplayMode(DDContext *dd) { - if (dd->switch_res) { + if (dd->switch_res && dd->DirectDrawCreate) { HRESULT hr; Bool temp_dd = 0;; if (!dd->pDD) { LPDIRECTDRAW ddraw; - DirectDrawCreate(NULL, &ddraw, NULL); -#ifdef USE_DX_3 - IDirectDraw_QueryInterface(ddraw, &IID_IDirectDraw, (LPVOID *)&dd->pDD); -#else - IDirectDraw_QueryInterface(ddraw, &IID_IDirectDraw7, (LPVOID *)&dd->pDD); -#endif + dd->DirectDrawCreate(NULL, &ddraw, NULL); + ddraw->lpVtbl->QueryInterface(ddraw, &IID_IDirectDraw7, (LPVOID *)&dd->pDD); temp_dd = 1; } //we start with a hugde res and downscale dd->fs_width = dd->fs_height = 50000; -#ifdef USE_DX_3 - hr = IDirectDraw_EnumDisplayModes(dd->pDD, 0L, NULL, dd, (LPDDENUMMODESCALLBACK) EnumDisplayModes); -#else - hr = IDirectDraw7_EnumDisplayModes(dd->pDD, 0L, NULL, dd, (LPDDENUMMODESCALLBACK2) EnumDisplayModes); -#endif + hr = dd->pDD->lpVtbl->EnumDisplayModes(dd->pDD, 0L, NULL, dd, EnumDisplayModes); + if (temp_dd) SAFE_DD_RELEASE(dd->pDD); if (FAILED(hr)) return GF_IO_ERR; } else { @@ -697,6 +681,11 @@ static void *NewDXVideoOutput() driv->SetFullScreen = DD_SetFullScreen; driv->ProcessEvent = DD_ProcessEvent; + pCtx->hDDrawLib = LoadLibrary("ddraw.dll"); + if (pCtx->hDDrawLib) { + pCtx->DirectDrawCreate = (DIRECTDRAWCREATEPROC) GetProcAddress(pCtx->hDDrawLib, "DirectDrawCreate"); + } + driv->max_screen_width = GetSystemMetrics(SM_CXSCREEN); driv->max_screen_height = GetSystemMetrics(SM_CYSCREEN); driv->hw_caps = GF_VIDEO_HW_OPENGL | GF_VIDEO_HW_OPENGL_OFFSCREEN | GF_VIDEO_HW_OPENGL_OFFSCREEN_ALPHA | GF_VIDEO_HW_HAS_HWND_HDC; @@ -710,6 +699,11 @@ static void DeleteVideoOutput(void *ifce) { GF_VideoOutput *driv = (GF_VideoOutput *) ifce; DDContext *dd = (DDContext *)driv->opaque; + + if (dd->hDDrawLib) { + FreeLibrary(dd->hDDrawLib); + } + gf_free(dd); gf_free(driv); } diff --git a/modules/dx_hw/dx_window.c b/modules/dx_hw/dx_window.c index 20cc9a7..3939c4c 100644 --- a/modules/dx_hw/dx_window.c +++ b/modules/dx_hw/dx_window.c @@ -372,6 +372,13 @@ LRESULT APIENTRY DD_WindowProc(HWND hWnd, UINT msg, UINT wParam, LONG lParam) ctx->orig_wnd_proc = 0L; } break; + case WM_DISPLAYCHANGE: + ctx->dd_lost = 1; + memset(&evt, 0, sizeof(GF_Event)); + evt.type = GF_EVENT_VIDEO_SETUP; + evt.setup.back_buffer = 1; + vout->on_event(vout->evt_cbk_hdl, &evt); + break; case WM_ACTIVATE: if (!ctx->on_secondary_screen && ctx->fullscreen && (LOWORD(wParam)==WA_INACTIVE) @@ -1015,19 +1022,23 @@ GF_Err DD_ProcessEvent(GF_VideoOutput*dr, GF_Event *evt) /*if scene resize resize window*/ case GF_EVENT_SIZE: if (ctx->owns_hwnd) { - if (ctx->windowless) - SetWindowPos(ctx->os_hwnd, NULL, 0, 0, evt->size.width, evt->size.height, SWP_NOZORDER | SWP_NOMOVE | SWP_ASYNCWINDOWPOS); - else - SetWindowPos(ctx->os_hwnd, NULL, 0, 0, evt->size.width + ctx->off_w, evt->size.height + ctx->off_h, SWP_NOZORDER | SWP_NOMOVE | SWP_ASYNCWINDOWPOS); - if (ctx->fullscreen) { ctx->store_width = evt->size.width; ctx->store_height = evt->size.height; + } else { + if (ctx->windowless) + SetWindowPos(ctx->os_hwnd, NULL, 0, 0, evt->size.width, evt->size.height, SWP_NOZORDER | SWP_NOMOVE | SWP_ASYNCWINDOWPOS); + else + SetWindowPos(ctx->os_hwnd, NULL, 0, 0, evt->size.width + ctx->off_w, evt->size.height + ctx->off_h, SWP_NOZORDER | SWP_NOMOVE | SWP_ASYNCWINDOWPOS); } } break; /*HW setup*/ case GF_EVENT_VIDEO_SETUP: + if (ctx->dd_lost) { + ctx->dd_lost = 0; + DestroyObjects(ctx); + } ctx->is_setup=1; switch (evt->setup.opengl_mode) { case 0: diff --git a/modules/ffmpeg_in/ffmpeg_decode.c b/modules/ffmpeg_in/ffmpeg_decode.c index 19808a9..c2ff738 100644 --- a/modules/ffmpeg_in/ffmpeg_decode.c +++ b/modules/ffmpeg_in/ffmpeg_decode.c @@ -130,7 +130,7 @@ static void FFDEC_LoadDSI(FFDec *ffd, GF_BitStream *bs, AVCodec *codec, AVCodecC static GF_Err FFDEC_AttachStream(GF_BaseDecoder *plug, GF_ESD *esd) { - u32 codec_id; + u32 codec_id = 0; int gotpic; GF_BitStream *bs; AVCodecContext **ctx; @@ -192,8 +192,8 @@ static GF_Err FFDEC_AttachStream(GF_BaseDecoder *plug, GF_ESD *esd) (*ctx)->height = gf_bs_read_u16(bs); } (*ctx)->bit_rate = gf_bs_read_u32(bs); - (*ctx)->codec_tag = gf_bs_read_u32(bs); + ffd->raw_pix_fmt = gf_bs_read_u32(bs); *codec = avcodec_find_decoder(codec_id); FFDEC_LoadDSI(ffd, bs, *codec, *ctx, 1); @@ -312,12 +312,16 @@ static GF_Err FFDEC_AttachStream(GF_BaseDecoder *plug, GF_ESD *esd) } *frame = avcodec_alloc_frame(); } - - if (avcodec_open((*ctx), (*codec) )<0) return GF_NON_COMPLIANT_BITSTREAM; + if (codec_id == CODEC_ID_RAWVIDEO) { + (*ctx)->codec_id = CODEC_ID_RAWVIDEO; + (*ctx)->pix_fmt = ffd->raw_pix_fmt; + if ((*ctx)->extradata && strstr((*ctx)->extradata, "BottomUp")) ffd->flipped = 1; + } else { + if (avcodec_open((*ctx), (*codec) )<0) return GF_NON_COMPLIANT_BITSTREAM; + } /*setup audio streams*/ if (ffd->st==GF_STREAM_AUDIO) { - /* souchay : test was wrong I think, was codec->type but must be codec->id */ if ((*codec)->id == CODEC_ID_MP2) { (*ctx)->frame_size = ((*ctx)->sample_rate > 24000) ? 1152 : 576; } @@ -330,11 +334,13 @@ static GF_Err FFDEC_AttachStream(GF_BaseDecoder *plug, GF_ESD *esd) case CODEC_ID_MJPEG: case CODEC_ID_MJPEGB: case CODEC_ID_LJPEG: -#if (LIBAVCODEC_VERSION_INT > ((51<<16)+(20<<8)+0) ) +#if (LIBAVCODEC_VERSION_INT > AV_VERSION_INT(51, 20, 0)) case CODEC_ID_GIF: #endif + case CODEC_ID_RAWVIDEO: ffd->pix_fmt = GF_PIXEL_RGB_24; break; + case CODEC_ID_DVD_SUBTITLE: *frame = avcodec_alloc_frame(); #ifdef USE_AVCODEC2 @@ -401,7 +407,7 @@ static GF_Err FFDEC_DetachStream(GF_BaseDecoder *plug, u16 ES_ID) if (*ctx) { if ((*ctx)->extradata) gf_free((*ctx)->extradata); (*ctx)->extradata = NULL; - avcodec_close((*ctx)); + if ((*ctx)->codec) avcodec_close((*ctx)); *ctx = NULL; } *codec = NULL; @@ -517,8 +523,8 @@ static GF_Err FFDEC_SetCapabilities(GF_BaseDecoder *plug, GF_CodecCapability cap case GF_CODEC_WAIT_RAP: ffd->frame_start = 0; if (ffd->st==GF_STREAM_VISUAL) { - if (ffd->base_ctx) avcodec_flush_buffers(ffd->base_ctx); - if (ffd->depth_ctx) avcodec_flush_buffers(ffd->depth_ctx); + if (ffd->base_ctx && ffd->base_ctx->codec) avcodec_flush_buffers(ffd->base_ctx); + if (ffd->depth_ctx && ffd->depth_ctx->codec) avcodec_flush_buffers(ffd->depth_ctx); } return GF_OK; default: @@ -649,6 +655,41 @@ redecode: return GF_PACKED_FRAMES; } + + if ( ctx->codec_id == CODEC_ID_RAWVIDEO) { + if (*outBufferLength != ffd->out_size) { + *outBufferLength = ffd->out_size; + return GF_BUFFER_TOO_SMALL; + } + if (inBufferLength) { + *outBufferLength = ffd->out_size; + assert(inBufferLength==ffd->out_size); + + if (ffd->raw_pix_fmt==PIX_FMT_BGR24) { + s32 i, j; + for (j=0; jheight; j++) { + u8 *src = inBuffer + j*3*ctx->width; + u8 *dst = outBuffer + j*3*ctx->width; + if (ffd->flipped) { + dst = outBuffer + (ctx->height-j-1) * 3*ctx->width; + } + for (i=0; iwidth; i++) { + dst[0] = src[2]; + dst[1] = src[1]; + dst[2] = src[2]; + src += 3; + dst += 3; + } + } + } else { + memcpy(outBuffer, inBuffer, ffd->out_size); + } + } else { + *outBufferLength = 0; + } + return GF_OK; + } + *outBufferLength = 0; /*visual stream*/ w = ctx->width; @@ -846,8 +887,12 @@ redecode: ctx->width, ctx->height, ctx->pix_fmt, ctx->width, ctx->height, pix_out, SWS_BICUBIC, NULL, NULL, NULL); - if ((*cached_sws)){ + if ((*cached_sws)) { +#if LIBSWSCALE_VERSION_INT < AV_VERSION_INT(0, 9, 0) + int sz = sws_scale((*cached_sws), frame->data, frame->linesize, 0, ctx->height, pict.data, pict.linesize); +#else int sz = sws_scale((*cached_sws), (const uint8_t * const*)frame->data, frame->linesize, 0, ctx->height, pict.data, pict.linesize); +#endif assert( sz > 0 ); } #endif @@ -1019,22 +1064,22 @@ void FFDEC_Delete(void *ifce) { GF_BaseDecoder *dec = ifce; FFDec *ffd; - if (!ifce) - return; - ffd = dec->privateStack; + if (!ifce) + return; + ffd = dec->privateStack; dec->privateStack = NULL; - if (ffd){ - if (ffd->base_ctx) avcodec_close(ffd->base_ctx); - ffd->base_ctx = NULL; - if (ffd->depth_ctx) avcodec_close(ffd->depth_ctx); - ffd->depth_ctx = NULL; + if (ffd){ + if (ffd->base_ctx && ffd->base_ctx->codec) avcodec_close(ffd->base_ctx); + ffd->base_ctx = NULL; + if (ffd->depth_ctx && ffd->depth_ctx->codec) avcodec_close(ffd->depth_ctx); + ffd->depth_ctx = NULL; #ifdef FFMPEG_SWSCALE - if (ffd->base_sws) sws_freeContext(ffd->base_sws); - ffd->base_sws = NULL; - if (ffd->depth_sws) sws_freeContext(ffd->base_sws); - ffd->depth_sws = NULL; + if (ffd->base_sws) sws_freeContext(ffd->base_sws); + ffd->base_sws = NULL; + if (ffd->depth_sws) sws_freeContext(ffd->base_sws); + ffd->depth_sws = NULL; #endif - gf_free(ffd); - } - gf_free(dec); + gf_free(ffd); + } + gf_free(dec); } diff --git a/modules/ffmpeg_in/ffmpeg_demux.c b/modules/ffmpeg_in/ffmpeg_demux.c index 6f87ae2..6f0e71f 100644 --- a/modules/ffmpeg_in/ffmpeg_demux.c +++ b/modules/ffmpeg_in/ffmpeg_demux.c @@ -212,6 +212,14 @@ static u32 FFD_RegisterMimeTypes(const GF_InputService *plug){ return i/3; } +static int open_file(AVFormatContext ** ic_ptr, const char * filename, AVInputFormat * fmt){ +#ifdef USE_PRE_0_7 + return av_open_input_file(ic_ptr, filename, fmt, 0, NULL); +#else + return avformat_open_input(ic_ptr, filename, fmt, NULL); +#endif +} + static Bool FFD_CanHandleURL(GF_InputService *plug, const char *url) { @@ -273,12 +281,12 @@ static Bool FFD_CanHandleURL(GF_InputService *plug, const char *url) } ctx = NULL; - if (av_open_input_file(&ctx, szName, NULL, 0, NULL)<0) { + if (open_file(&ctx, szName, NULL)<0) { AVInputFormat *av_in = NULL;; /*some extensions not supported by ffmpeg*/ if (ext && !strcmp(szExt, "cmp")) av_in = av_find_input_format("m4v"); - if (av_open_input_file(&ctx, szName, av_in, 0, NULL)<0) { + if (open_file(&ctx, szName, av_in)<0) { return 0; } } @@ -421,6 +429,7 @@ opaque_video: /*ffmpeg specific*/ gf_bs_write_u32(bs, dec->bit_rate); gf_bs_write_u32(bs, dec->codec_tag); + gf_bs_write_u32(bs, dec->pix_fmt); if (dec->extradata_size) { gf_bs_write_data(bs, dec->extradata, dec->extradata_size); @@ -482,7 +491,7 @@ static int ff_url_read(void *h, unsigned char *buf, int size) ffd->buffer_used-=size; memcpy(ffd->buffer, ffd->buffer+size, sizeof(char)*ffd->buffer_used); #ifdef FFMPEG_DUMP_REMOTE - if (ffd->outdbg) fwrite(buf, size, 1, ffd->outdbg); + if (ffd->outdbg) gf_fwrite(buf, size, 1, ffd->outdbg); #endif return size; } @@ -515,7 +524,7 @@ static int ff_url_read(void *h, unsigned char *buf, int size) buf += read; } #ifdef FFMPEG_DUMP_REMOTE - if (ffd->outdbg) fwrite(ffd->buffer, full_size, 1, ffd->outdbg); + if (ffd->outdbg) gf_fwrite(ffd->buffer, full_size, 1, ffd->outdbg); #endif return full_size ? (int) full_size : -1; } @@ -570,8 +579,16 @@ static GF_Err FFD_ConnectService(GF_InputService *plug, GF_ClientService *serv, #ifdef FFMPEG_DUMP_REMOTE ffd->outdbg = gf_f64_open("ffdeb.raw", "wb"); #endif +/*#ifdef USE_PRE_0_7*/ init_put_byte(&ffd->io, ffd->buffer, ffd->buffer_size, 0, ffd, ff_url_read, NULL, NULL); +/*#else + ffio_init_context(&ffd->io, ffd->buffer, ffd->buffer_size, 0, ffd, ff_url_read, NULL, NULL); +#endif*/ +#ifdef USE_PRE_0_7 ffd->io.is_streamed = 1; +#else + ffd->io.seekable = 1; +#endif ffd->dnload = gf_term_download_new(ffd->service, url, GF_NETIO_SESSION_NOT_THREADED | GF_NETIO_SESSION_NOT_CACHED, NULL, ffd); if (!ffd->dnload) return GF_URL_ERROR; @@ -587,7 +604,7 @@ static GF_Err FFD_ConnectService(GF_InputService *plug, GF_ClientService *serv, } if (e==GF_EOS) { const char *cache_file = gf_dm_sess_get_cache_name(ffd->dnload); - res = av_open_input_file(&ffd->ctx, cache_file, av_in, 0, NULL); + res = open_file(&ffd->ctx, cache_file, av_in); } else { pd.filename = szName; pd.buf_size = ffd->buffer_used; @@ -602,7 +619,7 @@ static GF_Err FFD_ConnectService(GF_InputService *plug, GF_ClientService *serv, res = av_open_input_stream(&ffd->ctx, &ffd->io, szName, av_in, NULL); } } else { - res = av_open_input_file(&ffd->ctx, szName, av_in, 0, NULL); + res = open_file(&ffd->ctx, szName, av_in); } switch (res) { @@ -679,7 +696,7 @@ static GF_Err FFD_ConnectService(GF_InputService *plug, GF_ClientService *serv, ffd->seekable = (av_seek_frame(ffd->ctx, -1, 0, AVSEEK_FLAG_BACKWARD)<0) ? 0 : 1; if (!ffd->seekable) { av_close_input_file(ffd->ctx); - av_open_input_file(&ffd->ctx, szName, av_in, 0, NULL); + open_file(&ffd->ctx, szName, av_in); av_find_stream_info(ffd->ctx); } } @@ -715,6 +732,7 @@ static GF_Descriptor *FFD_GetServiceDesc(GF_InputService *plug, u32 expect_type, /*since we don't handle multitrack in ffmpeg, we don't need to check sub_url, only use expected type*/ if (expect_type==GF_MEDIA_OBJECT_AUDIO) { + if (ffd->audio_st<0) return NULL; od = (GF_ObjectDescriptor *) gf_odf_desc_new(GF_ODF_OD_TAG); od->objectDescriptorID = 1; esd = FFD_GetESDescriptor(ffd, 1); @@ -725,6 +743,7 @@ static GF_Descriptor *FFD_GetServiceDesc(GF_InputService *plug, u32 expect_type, return (GF_Descriptor *) od; } if (expect_type==GF_MEDIA_OBJECT_VIDEO) { + if (ffd->video_st<0) return NULL; od = (GF_ObjectDescriptor *) gf_odf_desc_new(GF_ODF_OD_TAG); od->objectDescriptorID = 1; esd = FFD_GetESDescriptor(ffd, 0); @@ -751,7 +770,7 @@ static GF_Err FFD_CloseService(GF_InputService *plug) if (ffd->dnload) { if (ffd->is_running) { - while (!ffd->is_running) gf_sleep(0); + while (!ffd->is_running) gf_sleep(1); ffd->is_running = 0; } gf_term_download_del(ffd->dnload); diff --git a/modules/ffmpeg_in/ffmpeg_in.h b/modules/ffmpeg_in/ffmpeg_in.h index 57e6d66..cf2a20e 100644 --- a/modules/ffmpeg_in/ffmpeg_in.h +++ b/modules/ffmpeg_in/ffmpeg_in.h @@ -79,7 +79,7 @@ void gf_av_vlog(void* avcl, int level, const char *fmt, va_list vl); -#if LIBAVCODEC_VERSION_INT > ((52<<16)+(0<<8)+0) +#if LIBAVCODEC_VERSION_INT > AV_VERSION_INT(52, 0, 0) #define FFMPEG_SWSCALE #ifdef FFMPEG_OLD_HEADERS #include @@ -112,6 +112,9 @@ typedef struct u32 out_pix_fmt; Bool is_image; + u32 raw_pix_fmt; + Bool flipped; + /*for audio packed frames*/ u32 frame_start; char audio_buf[AVCODEC_MAX_AUDIO_FRAME_SIZE]; @@ -149,6 +152,10 @@ void FFDEC_Delete(void *ifce); //#define FFMPEG_DUMP_REMOTE +#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(52, 102, 0) +#define USE_PRE_0_7 1 +#endif + typedef struct { /*the service we're responsible for*/ @@ -181,7 +188,11 @@ typedef struct /*file downloader*/ GF_DownloadSession *dnload; +#ifdef USE_PRE_0_7 ByteIOContext io; +#else + AVIOContext io; +#endif char *buffer; u32 buffer_size; diff --git a/modules/freenect/Makefile b/modules/freenect/Makefile new file mode 100644 index 0000000..cb4810c --- /dev/null +++ b/modules/freenect/Makefile @@ -0,0 +1,65 @@ +include ../../config.mak + +vpath %.c $(SRC_PATH)/modules/freenect + +CFLAGS= $(CPPFLAGS) -I"$(SRC_PATH)/include" $(FREENECT_CFLAGS) + +ifeq ($(DEBUGBUILD), yes) +CFLAGS+=-g +LDFLAGS+=-g +endif + +ifeq ($(GPROFBUILD), yes) +CFLAGS+=-pg +LDFLAGS+=-pg +endif + + +LINKLIBS= + +ifeq ($(CONFIG_FREENECT), local) +LINKLIBS+=-L../../extra_lib/lib/gcc +CFLAGS+= -I"$(LOCAL_INC_PATH)" +endif + +LINKLIBS+=-lfreenect -L../../bin/gcc -lgpac + + +#common objects +OBJS=freenect.o + +SRCS := $(OBJS:.o=.c) + +LIB=gm_freenect.$(DYN_LIB_SUFFIX) + + +all: $(LIB) + + +$(LIB): $(OBJS) + $(CXX) -w $(SHFLAGS) $(LDFLAGS) -o ../../bin/gcc/$@ $(OBJS) $(LINKLIBS) + + +%.o: %.cpp + $(CXX) $(CFLAGS) -c -o $@ $< + + +clean: + rm -f $(OBJS) ../../bin/gcc/$(LIB) + +dep: depend + +depend: + rm -f .depend + $(CXX) -MM $(CFLAGS) $(SRCS) 1>.depend + +distclean: clean + rm -f Makefile.bak .depend + + + +# include dependency files if they exist +# +ifneq ($(wildcard .depend),) +include .depend +endif diff --git a/modules/freenect/freenect.c b/modules/freenect/freenect.c new file mode 100644 index 0000000..bf4b9cf --- /dev/null +++ b/modules/freenect/freenect.c @@ -0,0 +1,566 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Authors: Jean Le Feuvre + * Copyright (c) Telecom ParisTech 2011-20XX + * All rights reserved + * + * This file is part of GPAC / Freenect video input module + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include +#include +#include +#include + +#include + +#include + +typedef struct +{ + /*the service we're responsible for*/ + GF_ClientService *service; + + freenect_context *f_ctx; + freenect_device *f_dev; + + u32 width, height, fps, out_depth_size, out_color_size, color_stride, depth_stride, color_pixel_format, depth_pixel_format; + u32 depth_format; + + u8 *vid_buf; + u8 *depth_buf; + u16 gamma[2048]; + + GF_SLHeader depth_sl_header, color_sl_header; + LPNETCHANNEL depth_channel, color_channel; + + GF_Thread *th; + u32 nb_running; + Bool done; + +} FreenectIn; + + +void Freenect_DepthCallback_RGBD(freenect_device *dev, void *v_depth, uint32_t timestamp) +{ + FreenectIn *vcap = freenect_get_user(dev); + if (vcap->depth_channel) { + u32 i, j; + u16 *depth = (u16*)v_depth; + + for (i=0; iheight; i++) { + for (j=0; jwidth; j++) { + int idx_col = 3 * (j + i*vcap->width) ; + int idx_depth = 4*(j + i*vcap->width) ; + int pval = depth[i*vcap->width + j]; + pval = 255 - (255*pval) / 2048; + + vcap->depth_buf[idx_depth ] = vcap->vid_buf[idx_col]; + vcap->depth_buf[idx_depth + 1] = vcap->vid_buf[idx_col+1]; + vcap->depth_buf[idx_depth + 2] = vcap->vid_buf[idx_col+2]; + vcap->depth_buf[idx_depth + 3] = pval; + } + } + vcap->depth_sl_header.compositionTimeStamp = timestamp; + gf_term_on_sl_packet(vcap->service, vcap->depth_channel, (char *) vcap->depth_buf, vcap->out_depth_size, &vcap->depth_sl_header, GF_OK); + } +} + + +void Freenect_DepthCallback_GREY16(freenect_device *dev, void *v_depth, uint32_t timestamp) +{ + FreenectIn *vcap = freenect_get_user(dev); + if (vcap->depth_channel) { + memcpy(vcap->depth_buf, v_depth, vcap->out_depth_size); + + vcap->depth_sl_header.compositionTimeStamp = timestamp; + gf_term_on_sl_packet(vcap->service, vcap->depth_channel, (char *) vcap->depth_buf, vcap->out_depth_size, &vcap->depth_sl_header, GF_OK); + } +} + +void Freenect_DepthCallback_GREY8(freenect_device *dev, void *v_depth, uint32_t timestamp) +{ + FreenectIn *vcap = freenect_get_user(dev); + if (vcap->depth_channel) { + u32 i, j; + u16 *depth = (u16*)v_depth; + + for (i=0; iheight; i++) { + for (j=0; jwidth; j++) { + int pval = depth[j + i*vcap->width]; + pval = (255*pval) / 2048; + vcap->depth_buf[j + i*vcap->width] = pval; + } + } +// vcap->depth_sl_header.compositionTimeStamp = timestamp; + vcap->depth_sl_header.compositionTimeStamp ++; + gf_term_on_sl_packet(vcap->service, vcap->depth_channel, (char *) vcap->depth_buf, vcap->out_depth_size, &vcap->depth_sl_header, GF_OK); + } +} + +void Freenect_DepthCallback_ColorGradient(freenect_device *dev, void *v_depth, uint32_t timestamp) +{ + FreenectIn *vcap = freenect_get_user(dev); + if (vcap->depth_channel) { + u32 i; + u16 *depth = (u16*)v_depth; + /*remap to color RGB using freenect gamma*/ + for (i=0; iwidth*vcap->height; i++) { + int pval = vcap->gamma[depth[i]]; + int lb = pval & 0xff; + switch (pval>>8) { + case 0: + vcap->depth_buf[3*i+0] = 255; + vcap->depth_buf[3*i+1] = 255-lb; + vcap->depth_buf[3*i+2] = 255-lb; + break; + case 1: + vcap->depth_buf[3*i+0] = 255; + vcap->depth_buf[3*i+1] = lb; + vcap->depth_buf[3*i+2] = 0; + break; + case 2: + vcap->depth_buf[3*i+0] = 255-lb; + vcap->depth_buf[3*i+1] = 255; + vcap->depth_buf[3*i+2] = 0; + break; + case 3: + vcap->depth_buf[3*i+0] = 0; + vcap->depth_buf[3*i+1] = 255; + vcap->depth_buf[3*i+2] = lb; + break; + case 4: + vcap->depth_buf[3*i+0] = 0; + vcap->depth_buf[3*i+1] = 255-lb; + vcap->depth_buf[3*i+2] = 255; + break; + case 5: + vcap->depth_buf[3*i+0] = 0; + vcap->depth_buf[3*i+1] = 0; + vcap->depth_buf[3*i+2] = 255-lb; + break; + default: + vcap->depth_buf[3*i+0] = 0; + vcap->depth_buf[3*i+1] = 0; + vcap->depth_buf[3*i+2] = 0; + break; + } + } + vcap->depth_sl_header.compositionTimeStamp = timestamp; + gf_term_on_sl_packet(vcap->service, vcap->depth_channel, (char *) vcap->depth_buf, vcap->out_depth_size, &vcap->depth_sl_header, GF_OK); + } +} + +void Freenect_RGBCallback(freenect_device *dev, void *rgb, uint32_t timestamp) +{ + FreenectIn *vcap = freenect_get_user(dev); + if (vcap->color_channel) { + vcap->color_sl_header.compositionTimeStamp = timestamp; + gf_term_on_sl_packet(vcap->service, vcap->color_channel, (char *) rgb, vcap->out_color_size, &vcap->color_sl_header, GF_OK); + } +} + + +u32 FreenectRun(void *par) +{ + FreenectIn *vcap = par; + + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("[Freenect] Starting device thread\n")); + freenect_start_depth(vcap->f_dev); + freenect_start_video(vcap->f_dev); + vcap->done = 0; + while (vcap->nb_running && (freenect_process_events(vcap->f_ctx)>=0) ) { + gf_sleep(0); + } + freenect_stop_depth(vcap->f_dev); + freenect_stop_video(vcap->f_dev); + vcap->done = 1; + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("[Freenect] Stoping device thread\n")); + return 0; +} + +Bool Freenect_CanHandleURL(GF_InputService *plug, const char *url) +{ + if (!strnicmp(url, "camera://", 9)) return 1; + if (!strnicmp(url, "video://", 8)) return 1; + if (!strnicmp(url, "kinect://", 8)) return 1; + return 0; +} + +void Freenect_Logs(freenect_context *dev, freenect_loglevel level, const char *msg) +{ + switch (level) { + case FREENECT_LOG_ERROR: + GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[Freenect] %s", msg)); + break; + case FREENECT_LOG_WARNING: + GF_LOG(GF_LOG_WARNING, GF_LOG_MODULE, ("[Freenect] %s", msg)); + break; + case FREENECT_LOG_NOTICE: + case FREENECT_LOG_INFO: + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("[Freenect] %s", msg)); + break; + case FREENECT_LOG_DEBUG: + case FREENECT_LOG_SPEW: + case FREENECT_LOG_FLOOD: + default: + GF_LOG(GF_LOG_DEBUG, GF_LOG_MODULE, ("[Freenect] %s", msg)); + break; + } +} + + +GF_Err Freenect_ConnectService(GF_InputService *plug, GF_ClientService *serv, const char *url) +{ + GF_ESD *esd; + GF_BitStream *bs; + GF_ObjectDescriptor *od; + FreenectIn *vcap = (FreenectIn *) plug->priv; + + if (!vcap || !serv || !url) return GF_BAD_PARAM; + + vcap->service = serv; + + if (!vcap->f_ctx) { + int i; + Bool use_depth = 1; + freenect_frame_mode frame_mode; + freenect_resolution frame_format = FREENECT_RESOLUTION_MEDIUM; + char *name, *params; + int res = freenect_init(&vcap->f_ctx, NULL); + if (res < 0) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("freenect_init() failed - ret code %d\n", res)); + return GF_IO_ERR; + } + freenect_set_log_level(vcap->f_ctx, FREENECT_LOG_DEBUG); + freenect_set_log_callback(vcap->f_ctx, Freenect_Logs); + freenect_select_subdevices(vcap->f_ctx, FREENECT_DEVICE_CAMERA); + + res = freenect_num_devices (vcap->f_ctx); + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("[Freenect] %d devices found\n", res)); + if (res<1) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[Freenect] No device found\n")); + return GF_URL_ERROR; + } + + res = freenect_open_device(vcap->f_ctx, &vcap->f_dev, 0); + if (res < 0) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[Freenect] Could not open Kinect - error %d\n", res)); + return GF_SERVICE_ERROR; + } + + + params = (char *) strchr(url, '?'); + if (params) params[0] = 0; + name = (char *) strstr(url, "://"); + if (name) name += 3; + + if (!stricmp(name, "color")) { + use_depth = 0; + } + + if (params) { + params[0] = '?'; + params ++; + } + while (params) { + char *sep = (char *) strchr(params, '&'); + if (sep) sep[0] = 0; + + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("[VideoCapture] Set camera option %s\n", params)); + + if (!strnicmp(params, "resolution=", 11)) { + u32 w, h; + if (sscanf(params+11, "%dx%d", &w, &h)==2) { + if ((w<=320) || (h<=240)) frame_format = FREENECT_RESOLUTION_LOW; + else if ((w<=640) || (h<=480)) frame_format = FREENECT_RESOLUTION_MEDIUM; + else frame_format = FREENECT_RESOLUTION_HIGH; + } + } + else if (!strnicmp(params, "format=", 7)) { + if (!stricmp(params+7, "standard")) vcap->depth_format = 0; + else if (!stricmp(params+7, "grey")) vcap->depth_format = 1; + else if (!stricmp(params+7, "rgbd")) vcap->depth_format = 2; + else if (!stricmp(params+7, "grey16")) vcap->depth_format = 3; + else { + GF_LOG(GF_LOG_WARNING, GF_LOG_MODULE, ("[VideoCapture] Unrecognized value %s for parameter \"format\"\n", params+7)); + } + } + else { + GF_LOG(GF_LOG_WARNING, GF_LOG_MODULE, ("[VideoCapture] Unrecognized parameter %s\n", params)); + } + + if (!sep) break; + sep[0] = '&'; + params = sep+1; + } + + frame_mode = freenect_find_video_mode(frame_format, FREENECT_VIDEO_RGB); + res = freenect_set_video_mode(vcap->f_dev, frame_mode); + res = freenect_set_depth_mode(vcap->f_dev, freenect_find_depth_mode(frame_format, FREENECT_DEPTH_11BIT)); + + vcap->width = frame_mode.width; + vcap->height = frame_mode.height; + vcap->fps = frame_mode.framerate; + /*currently hardcoded*/ + vcap->color_pixel_format = GF_PIXEL_RGB_24; + vcap->color_stride = 3*vcap->width; + vcap->out_color_size = vcap->color_stride * vcap->height; + vcap->vid_buf = gf_malloc(sizeof(char) * vcap->out_color_size); + freenect_set_video_callback(vcap->f_dev, Freenect_RGBCallback); + + switch (vcap->depth_format) { + case 1: + vcap->depth_pixel_format = GF_PIXEL_GREYSCALE; + vcap->depth_stride = vcap->width; + freenect_set_depth_callback(vcap->f_dev, Freenect_DepthCallback_GREY8); + break; + case 2: + vcap->depth_pixel_format = GF_PIXEL_RGBD; + vcap->depth_stride = 4*vcap->width; + freenect_set_depth_callback(vcap->f_dev, Freenect_DepthCallback_RGBD); + break; + case 3: + vcap->depth_pixel_format = GF_PIXEL_RGB_565; + vcap->depth_stride = 2*vcap->width; + freenect_set_depth_callback(vcap->f_dev, Freenect_DepthCallback_GREY16); + break; + default: + vcap->depth_pixel_format = GF_PIXEL_RGB_24; + vcap->depth_stride = 3*vcap->width; + freenect_set_depth_callback(vcap->f_dev, Freenect_DepthCallback_ColorGradient); + } + vcap->out_depth_size = vcap->depth_stride * vcap->height; + vcap->depth_buf = gf_malloc(sizeof(char) * vcap->out_depth_size); + + + res = freenect_set_video_buffer(vcap->f_dev, vcap->vid_buf); + + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("[Freenect] Device configured - resolution %dx%d - Frame Rate %d - Depth Pixel Format %s\n", vcap->width, vcap->height, vcap->fps, gf_4cc_to_str(vcap->depth_pixel_format) )); + + + for (i=0; i<2048; i++) { + float v = i/2048.0f; + v = powf(v, 3)* 6; + vcap->gamma[i] = (u16) (v*6*256); + } + + freenect_set_user(vcap->f_dev, vcap); + + vcap->th = gf_th_new("Freenect"); + } + + /*ACK connection is OK*/ + gf_term_on_connect(serv, NULL, GF_OK); + + + /*setup object descriptor*/ + od = (GF_ObjectDescriptor *) gf_odf_desc_new(GF_ODF_OD_TAG); + + esd = gf_odf_desc_esd_new(0); + esd->slConfig->timestampResolution = 1000; + if (!strnicmp(url, "camera://", 9) || !strnicmp(url, "video://", 8) || !strnicmp(url, "kinect://", 8)) { + if (strstr(url, "color") || strstr(url, "COLOR")) { + od->objectDescriptorID = 2; + esd->ESID = 2; + esd->decoderConfig->streamType = GF_STREAM_VISUAL; + } else { + od->objectDescriptorID = 1; + esd->ESID = 1; + esd->decoderConfig->streamType = GF_STREAM_VISUAL; + } + } else { + od->objectDescriptorID = 3; + esd->ESID = 3; + esd->decoderConfig->streamType = GF_STREAM_AUDIO; + } + esd->decoderConfig->objectTypeIndication = GPAC_OTI_RAW_MEDIA_STREAM; + + bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + gf_bs_write_u32(bs, (esd->ESID==2) ? vcap->color_pixel_format : vcap->depth_pixel_format); + gf_bs_write_u16(bs, vcap->width); + gf_bs_write_u16(bs, vcap->height); + gf_bs_write_u32(bs, (esd->ESID==2) ? vcap->out_color_size : vcap->out_depth_size); + gf_bs_write_u32(bs, (esd->ESID==2) ? vcap->color_stride : vcap->depth_stride); + gf_bs_get_content(bs, &esd->decoderConfig->decoderSpecificInfo->data, &esd->decoderConfig->decoderSpecificInfo->dataLength); + gf_bs_del(bs); + + gf_list_add(od->ESDescriptors, esd); + gf_term_add_media(vcap->service, (GF_Descriptor*)od, 0); + + return GF_OK; +} + +GF_Err Freenect_CloseService(GF_InputService *plug) +{ + FreenectIn *vcap = (FreenectIn *) plug->priv; + if (vcap->f_dev) freenect_close_device(vcap->f_dev); + if (vcap->f_ctx) freenect_shutdown(vcap->f_ctx); + vcap->f_ctx = NULL; + vcap->f_dev = NULL; + gf_term_on_disconnect(vcap->service, NULL, GF_OK); + return GF_OK; +} + +/*Dummy input just send a file name, no multitrack to handle so we don't need to check sub_url nor expected type*/ +static GF_Descriptor *Freenect_GetServiceDesc(GF_InputService *plug, u32 expect_type, const char *sub_url) +{ + return NULL; +} + + +GF_Err Freenect_ServiceCommand(GF_InputService *plug, GF_NetworkCommand *com) +{ + FreenectIn *vcap = (FreenectIn *) plug->priv; + + if (!com->base.on_channel) return GF_NOT_SUPPORTED; + + switch (com->command_type) { + case GF_NET_CHAN_SET_PULL: return GF_NOT_SUPPORTED; + case GF_NET_CHAN_INTERACTIVE: return GF_OK; + /*since data is file-based, no padding is needed (decoder plugin will handle it itself)*/ + case GF_NET_CHAN_SET_PADDING: return GF_OK; + case GF_NET_CHAN_BUFFER: + com->buffer.max = com->buffer.min = 500; + return GF_OK; + case GF_NET_CHAN_DURATION: + /*this module is not made for updates, use undefined duration*/ + com->duration.duration = 0; + return GF_OK; + case GF_NET_CHAN_PLAY: + if (!vcap->nb_running) { + vcap->nb_running++; + gf_th_run(vcap->th, FreenectRun, vcap); + } + return GF_OK; + case GF_NET_CHAN_STOP: + if (vcap->nb_running) { + vcap->nb_running--; + if (!vcap->nb_running) { + while (! vcap->done) { + gf_sleep(10); + } + } + } + return GF_OK; + case GF_NET_CHAN_CONFIG: return GF_OK; + case GF_NET_CHAN_GET_DSI: + com->get_dsi.dsi = NULL; + com->get_dsi.dsi_len = 0; + return GF_OK; + } + return GF_OK; +} + +GF_Err Freenect_ConnectChannel(GF_InputService *plug, LPNETCHANNEL channel, const char *url, Bool upstream) +{ + u32 ESID; + FreenectIn *vcap = (FreenectIn *) plug->priv; + + sscanf(url, "ES_ID=%u", &ESID); + if (ESID == 1) { + vcap->depth_channel = channel; + memset(&vcap->depth_sl_header, 0, sizeof(GF_SLHeader)); + vcap->depth_sl_header.compositionTimeStampFlag = 1; + gf_term_on_connect(vcap->service, channel, GF_OK); + } else if (ESID == 2) { + vcap->color_channel = channel; + memset(&vcap->color_sl_header, 0, sizeof(GF_SLHeader)); + vcap->color_sl_header.compositionTimeStampFlag = 1; + gf_term_on_connect(vcap->service, channel, GF_OK); + } else { + /*TODO*/ + gf_term_on_connect(vcap->service, channel, GF_STREAM_NOT_FOUND); + } + return GF_OK; +} + +GF_Err Freenect_DisconnectChannel(GF_InputService *plug, LPNETCHANNEL channel) +{ + FreenectIn *vcap = (FreenectIn *) plug->priv; + if (vcap->depth_channel == channel) { + vcap->depth_channel = NULL; + } + else if (vcap->color_channel == channel) { + vcap->color_channel = NULL; + } + gf_term_on_disconnect(vcap->service, channel, GF_OK); + return GF_OK; +} + +Bool Freenect_CanHandleURLInService(GF_InputService *plug, const char *url) +{ + return 0; +} + + +GF_EXPORT +const u32 *QueryInterfaces() +{ + static u32 si [] = { + GF_NET_CLIENT_INTERFACE, + 0 + }; + return si; +} + +GF_EXPORT +GF_BaseInterface *LoadInterface(u32 InterfaceType) +{ + if (InterfaceType == GF_NET_CLIENT_INTERFACE) { + FreenectIn *vcap; + GF_InputService *plug; + GF_SAFEALLOC(plug, GF_InputService); + memset(plug, 0, sizeof(GF_InputService)); + GF_REGISTER_MODULE_INTERFACE(plug, GF_NET_CLIENT_INTERFACE, "Video Capture using libfreenect", "gpac distribution") + + plug->RegisterMimeTypes = NULL; + plug->CanHandleURL = Freenect_CanHandleURL; + plug->ConnectService = Freenect_ConnectService; + plug->CloseService = Freenect_CloseService; + plug->GetServiceDescriptor = Freenect_GetServiceDesc; + plug->ConnectChannel = Freenect_ConnectChannel; + plug->DisconnectChannel = Freenect_DisconnectChannel; + plug->ServiceCommand = Freenect_ServiceCommand; + plug->CanHandleURLInService = Freenect_CanHandleURLInService; + + GF_SAFEALLOC(vcap, FreenectIn); + plug->priv = vcap; + return (GF_BaseInterface *)plug; + } + return NULL; +} + +GF_EXPORT +void ShutdownInterface(GF_BaseInterface *bi) +{ + if (bi->InterfaceType==GF_NET_CLIENT_INTERFACE) { + GF_InputService *ifcn = (GF_InputService*)bi; + FreenectIn *vcap = (FreenectIn*)ifcn->priv; + + if (vcap->vid_buf) gf_free(vcap->vid_buf); + if (vcap->depth_buf) gf_free(vcap->depth_buf); + if (vcap->th) gf_th_del(vcap->th); + gf_free(vcap); + gf_free(bi); + } +} + + diff --git a/modules/ft_font/ft_font.c b/modules/ft_font/ft_font.c index 76ad5b0..22f8036 100644 --- a/modules/ft_font/ft_font.c +++ b/modules/ft_font/ft_font.c @@ -99,8 +99,9 @@ void setBestFont(const char * listOfFonts[], char ** currentBestFont, const char gf_free(*currentBestFont); *currentBestFont = NULL; } - if (! (*currentBestFont)) + if (! (*currentBestFont)){ *currentBestFont = gf_strdup(fontName); + } } static Bool ft_enum_fonts(void *cbck, char *file_name, char *file_path) @@ -228,15 +229,67 @@ static void ft_rescan_fonts(GF_FontReader *dr) font_default = ftpriv->font_dir; ftpriv->font_dir = font_dir; + + if (ftpriv->font_fixed) gf_free(ftpriv->font_fixed); + ftpriv->font_fixed = NULL; + if (ftpriv->font_sans) gf_free(ftpriv->font_sans); + ftpriv->font_sans = NULL; + if (ftpriv->font_serif) gf_free(ftpriv->font_serif); + ftpriv->font_serif = NULL; + + /* let's check we have fonts that match our default Bol/Italic/BoldItalic conventions*/ + count = gf_cfg_get_key_count(cfg, "FontEngine"); + for (i=0; ifont_fixed, key) || (!ftpriv->font_fixed && (strstr(fkey, "fixed") || strstr(fkey, "mono")) ) ) { + if (ftpriv->font_fixed) gf_free(ftpriv->font_fixed); + ftpriv->font_fixed = gf_strdup(key); + } + + if (isBestFontFor(BEST_SANS_FONTS, ftpriv->font_sans, key) || (!ftpriv->font_sans && strstr(fkey, "sans")) ) { + if (ftpriv->font_sans) gf_free(ftpriv->font_sans); + ftpriv->font_sans = gf_strdup(key); + } + + if (isBestFontFor(BEST_SERIF_FONTS, ftpriv->font_serif, key) || (!ftpriv->font_serif && strstr(fkey, "serif")) ) { + if (ftpriv->font_serif) gf_free(ftpriv->font_serif); + ftpriv->font_serif = gf_strdup(key); + } + } + if (!ftpriv->font_serif) ftpriv->font_serif = gf_strdup(font_default ? font_default : ""); if (!ftpriv->font_sans) ftpriv->font_sans = gf_strdup(font_default ? font_default : ""); if (!ftpriv->font_fixed) ftpriv->font_fixed = gf_strdup(font_default ? font_default : ""); - if (font_default) gf_free(font_default); - /* We try these fonts in this order */ - - gf_modules_set_option((GF_BaseInterface *)dr, "FontEngine", "FontFixed", ftpriv->font_fixed); gf_modules_set_option((GF_BaseInterface *)dr, "FontEngine", "FontSerif", ftpriv->font_serif); gf_modules_set_option((GF_BaseInterface *)dr, "FontEngine", "FontSans", ftpriv->font_sans); @@ -389,22 +442,41 @@ static GF_Err ft_set_font(GF_FontReader *dr, const char *OrigFontName, u32 style in the cfg file*/ if (!fontName || !strlen(fontName)) return GF_NOT_SUPPORTED; fname = gf_malloc(sizeof(char) * (strlen(fontName) + 50)); - strcpy(fname, fontName); - if (styles & GF_FONT_WEIGHT_BOLD) strcat(fname, " Bold"); - if (styles & GF_FONT_ITALIC) strcat(fname, " Italic"); - opt = gf_modules_get_option((GF_BaseInterface *)dr, "FontEngine", fname); - gf_free(fname); - if (opt) { - FT_Face face; - if (FT_New_Face(ftpriv->library, opt, 0, & face )) return GF_IO_ERR; - if (!face) return GF_IO_ERR; - gf_list_add(ftpriv->loaded_fonts, face); - ftpriv->active_face = face; - return GF_OK; + { + int checkStyles = (styles & GF_FONT_WEIGHT_BOLD) | (styles & GF_FONT_ITALIC); + checkFont: + strcpy(fname, fontName); + if (styles & GF_FONT_WEIGHT_BOLD & checkStyles) strcat(fname, " Bold"); + if (styles & GF_FONT_ITALIC & checkStyles) strcat(fname, " Italic"); + + opt = gf_modules_get_option((GF_BaseInterface *)dr, "FontEngine", fname); + + if (opt) { + FT_Face face; + gf_free(fname); + if (FT_New_Face(ftpriv->library, opt, 0, & face )) return GF_IO_ERR; + if (!face) return GF_IO_ERR; + gf_list_add(ftpriv->loaded_fonts, face); + ftpriv->active_face = face; + return GF_OK; + } + if (checkStyles){ + /* If we tried font + bold + italic -> we will try font + [bold | italic] + If we tried font + [bold | italic] -> we try font + */ + if (checkStyles == (GF_FONT_WEIGHT_BOLD | GF_FONT_ITALIC)) + checkStyles = GF_FONT_WEIGHT_BOLD; + else if (checkStyles == GF_FONT_WEIGHT_BOLD && (styles & GF_FONT_ITALIC)) + checkStyles = GF_FONT_ITALIC; + else if (checkStyles == GF_FONT_WEIGHT_BOLD || checkStyles == GF_FONT_ITALIC) + checkStyles = 0; + goto checkFont; + } } - GF_LOG(GF_LOG_WARNING, GF_LOG_PARSER, ("[FreeType] Font %s not found\n", fontName)); + GF_LOG(GF_LOG_WARNING, GF_LOG_PARSER, ("[FreeType] Font '%s' (%s) not found\n", fontName, fname)); + gf_free(fname); return GF_NOT_SUPPORTED; } @@ -523,7 +595,10 @@ static GF_Glyph *ft_load_glyph(GF_FontReader *dr, u32 glyph_name) glyph_idx = FT_Get_Char_Index(ftpriv->active_face, glyph_name); /*missing glyph*/ - if (!glyph_idx) return NULL; + if (!glyph_idx) { + GF_LOG(GF_LOG_WARNING, GF_LOG_PARSER, ("[FreeType] Glyph not found for char %d in font %s (style %s)\n", glyph_name, ftpriv->active_face->family_name, ftpriv->active_face->style_name)); + return NULL; + } /*work in design units*/ FT_Load_Glyph(ftpriv->active_face, glyph_idx, FT_LOAD_NO_SCALE | FT_LOAD_NO_BITMAP); diff --git a/modules/gapi/gapi.cpp b/modules/gapi/gapi.cpp index cd1e9b6..32e8d47 100644 --- a/modules/gapi/gapi.cpp +++ b/modules/gapi/gapi.cpp @@ -695,6 +695,7 @@ GF_Err GAPI_SetupOGL_ES(GF_VideoOutput *dr) return GF_IO_ERR; } GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[GAPI] OpenGL initialize - %d x %d \n", gctx->bb_width, gctx->bb_height)); + memset(&evt, 0, sizeof(GF_Event)); evt.type = GF_EVENT_VIDEO_SETUP; dr->on_event(dr->evt_cbk_hdl, &evt); return GF_OK; diff --git a/modules/gpac_js/gpac_js.c b/modules/gpac_js/gpac_js.c index 62e5046..0591097 100644 --- a/modules/gpac_js/gpac_js.c +++ b/modules/gpac_js/gpac_js.c @@ -38,6 +38,9 @@ #ifdef GPAC_HAS_SPIDERMONKEY +#if defined(GPAC_ANDROID) && !defined(XP_UNIX) +#define XP_UNIX +#endif #if !defined(__GNUC__) # pragma comment(lib, "js32") @@ -724,7 +727,12 @@ static Bool gjs_event_filter(void *udta, GF_Event *evt, Bool consumed_by_composi if (consumed_by_compositor) return 0; if (gjs->evt != NULL) return 0; - gf_sg_lock_javascript(gjs->c, 1); + res = 0; + while (gjs->nb_loaded) { + res = gf_sg_try_lock_javascript(gjs->c); + if (res) break; + } + if (!res) return 0; rval = JSVAL_VOID; gjs->evt = evt; diff --git a/modules/hyb_in/Makefile b/modules/hyb_in/Makefile new file mode 100644 index 0000000..bdc9531 --- /dev/null +++ b/modules/hyb_in/Makefile @@ -0,0 +1,57 @@ +include ../../config.mak + +vpath %.c $(SRC_PATH)/modules/hyb_in + +CFLAGS= $(OPTFLAGS) -I"$(SRC_PATH)/include" + +ifeq ($(DEBUGBUILD), yes) +CFLAGS+=-g +LDFLAGS+=-g +endif + +ifeq ($(GPROFBUILD), yes) +CFLAGS+=-pg +LDFLAGS+=-pg +endif + +#common obj +OBJS=hyb_in.o fm_fake_pull.o fm_fake_push.o + +SRCS := $(OBJS:.o=.c) + +LIB=gm_hyb_in.$(DYN_LIB_SUFFIX) + + +all: $(LIB) + + +$(LIB): $(OBJS) + $(CC) $(SHFLAGS) $(LDFLAGS) -o ../../bin/gcc/$@ $(OBJS) $(EXTRALIBS) -L../../bin/gcc -lgpac +ifeq ($(STATICBUILD),yes) + $(CC) $(SHFLAGS) $(LDFLAGS) -o ../../bin/gcc/gm_hyb_in-static.$(DYN_LIB_SUFFIX) $(OBJS) -L../../bin/gcc -lgpac_static +endif + + +%.o: %.c + $(CC) $(CFLAGS) -c -o $@ $< + + +clean: + rm -f $(OBJS) ../../bin/gcc/$(LIB) + +dep: depend + +depend: + rm -f .depend + $(CC) -MM $(CFLAGS) $(SRCS) 1>.depend + +distclean: clean + rm -f Makefile.bak .depend + + + +# include dependency files if they exist +# +ifneq ($(wildcard .depend),) +include .depend +endif diff --git a/modules/hyb_in/fm_fake_pull.c b/modules/hyb_in/fm_fake_pull.c index 2680791..e99920f 100644 --- a/modules/hyb_in/fm_fake_pull.c +++ b/modules/hyb_in/fm_fake_pull.c @@ -30,6 +30,10 @@ /**********************************************************************************************************************/ +#define EXT_MEDIA_LOAD_THREADED + +/**********************************************************************************************************************/ + #define FM_FAKE_PULL_AUDIO_FREQ 44100 #define FM_FAKE_PULL_CHAN_NUM 2 #define FM_FAKE_PULL_BITS 16 @@ -44,6 +48,9 @@ typedef struct s_FM_FAKE_PULL { unsigned char buffer10[FM_FAKE_PULL_FRAME_LEN]; /*played 10 percent of time*/ unsigned char buffer90[FM_FAKE_PULL_FRAME_LEN]; /*played 90 percent of time*/ +#ifdef EXT_MEDIA_LOAD_THREADED + GF_Thread *media_th; +#endif } FM_FAKE_PULL; FM_FAKE_PULL FM_FAKE_PULL_private_data; @@ -118,6 +125,17 @@ static GF_ObjectDescriptor* FM_FAKE_PULL_GetOD(void) /**********************************************************************************************************************/ +static u32 ext_media_load_th(void *par) { + GF_HYBMEDIA *self = (GF_HYBMEDIA*) par; + /*declare object to terminal*/ + GF_ObjectDescriptor *od = (GF_ObjectDescriptor*)gf_odf_desc_new(GF_ODF_OD_TAG); + od->URLString = gf_strdup("http://gpac.sourceforge.net/screenshots/lion.jpg"); + od->objectDescriptorID = 0; + gf_sleep(2000); //TODO: remove the sleep + gf_term_add_media(self->owner, (GF_Descriptor*)od, 0); + return 0; +} + static GF_Err FM_FAKE_PULL_Connect(GF_HYBMEDIA *self, GF_ClientService *service, const char *url) { u32 i; @@ -138,6 +156,20 @@ static GF_Err FM_FAKE_PULL_Connect(GF_HYBMEDIA *self, GF_ClientService *service, *((FM_FAKE_PULL_TYPE*)((FM_FAKE_PULL*)self->private_data)->buffer10+i) = 1 << (FM_FAKE_PULL_BITS-1); } + /*for hybrid scenarios: add an external media*/ + if (1) { +#ifdef EXT_MEDIA_LOAD_THREADED + GF_Thread **th = &((FM_FAKE_PULL*)self->private_data)->media_th; + assert(*th == NULL); //once at a time + *th = gf_th_new("HYB-FM fake external media load thread"); + gf_th_run(*th, ext_media_load_th, self); +#else + ext_media_load_th(self); +#endif + //wait for video to begin as late video creates desynchro. + //gf_sleep(5000); + } + return GF_OK; } @@ -145,7 +177,11 @@ static GF_Err FM_FAKE_PULL_Connect(GF_HYBMEDIA *self, GF_ClientService *service, static GF_Err FM_FAKE_PULL_Disconnect(GF_HYBMEDIA *self) { + FM_FAKE_PULL *priv = (FM_FAKE_PULL*)self->private_data; self->owner = NULL; +#ifdef EXT_MEDIA_LOAD_THREADED + gf_th_del(priv->media_th); +#endif return GF_OK; } diff --git a/modules/hyb_in/fm_fake_push.c b/modules/hyb_in/fm_fake_push.c index 1ef2c35..10e8950 100644 --- a/modules/hyb_in/fm_fake_push.c +++ b/modules/hyb_in/fm_fake_push.c @@ -32,7 +32,11 @@ /**********************************************************************************************************************/ -#define FM_FAKE_PUSH_AUDIO_FREQ 44100 +#define EXT_MEDIA_LOAD_THREADED + +/**********************************************************************************************************************/ + +#define FM_FAKE_PUSH_AUDIO_FREQ 22050 #define FM_FAKE_PUSH_CHAN_NUM 2 #define FM_FAKE_PUSH_BITS 16 #define FM_FAKE_PUSH_TYPE s16 @@ -47,7 +51,9 @@ typedef struct s_FM_FAKE_PUSH { unsigned char buffer90[FM_FAKE_PUSH_FRAME_LEN]; /*played 90 percent of time*/ GF_Thread *th; - +#ifdef EXT_MEDIA_LOAD_THREADED + GF_Thread *media_th; +#endif } FM_FAKE_PUSH; FM_FAKE_PUSH FM_FAKE_PUSH_private_data; @@ -146,71 +152,84 @@ static GF_Err GetData(const GF_HYBMEDIA *self, char **out_data_ptr, u32 *out_dat /**********************************************************************************************************************/ +u32 ext_media_load_th(void *par) { + GF_HYBMEDIA *self = (GF_HYBMEDIA*) par; + /*declare object to terminal*/ + GF_ObjectDescriptor *od = (GF_ObjectDescriptor*)gf_odf_desc_new(GF_ODF_OD_TAG); + od->URLString = gf_strdup("http://gpac.sourceforge.net/screenshots/lion.jpg"); + od->objectDescriptorID = 0; + gf_sleep(2000); //TODO: remove the sleep + gf_term_add_media(self->owner, (GF_Descriptor*)od, 0); + return 0; +} + static u32 audio_gen_th(void *par) { GF_Err e; char *data; u32 data_size, init_time; - s32 time_to_wait = 0; + volatile s32 time_to_wait = 0; GF_SLHeader slh; GF_HYBMEDIA *self = (GF_HYBMEDIA*) par; FM_FAKE_PUSH *fm_fake_push = (FM_FAKE_PUSH*)self->private_data; memset(&slh, 0, sizeof(GF_SLHeader)); - while (self->state >= 0) /*pause or play*/ { - if (self->state == HYB_STATE_PAUSE) { - gf_sleep(10); - init_time = (u32)(gf_sys_clock() - ((u64)slh.compositionTimeStamp*1000)/FM_FAKE_PUSH_AUDIO_FREQ); /*pause: won't wait at resume*/ - continue; + /*for hybrid scenarios: add an external media*/ + if (1) { +#ifdef EXT_MEDIA_LOAD_THREADED + GF_Thread **th = &((FM_FAKE_PUSH*)self->private_data)->media_th; + assert(*th == NULL); //once at a time + *th = gf_th_new("HYB-FM fake external media load thread"); + gf_th_run(*th, ext_media_load_th, par); +#else + ext_media_load_th(par); +#endif + gf_sleep(2000); //TODO: remove the sleep } - if (!time_to_wait) - { - /*TODO: remove the sleep*/ - gf_sleep(1000); - - /*for hybrid scenarios: add an external media*/ + if (0) { + time_t now; + struct tm *now_tm; + time(&now); + now_tm = gmtime(&now); { - /*declare object to terminal*/ - GF_ObjectDescriptor *od = (GF_ObjectDescriptor*)gf_odf_desc_new(GF_ODF_OD_TAG); - od->URLString = PUT_TOUR_URL_HERE; - od->objectDescriptorID = 0; - gf_term_add_media(self->owner, (GF_Descriptor*)od, 0); + GF_NetworkCommand com; + memset(&com, 0, sizeof(com)); + com.command_type = GF_NET_CHAN_MAP_TIME; + + com.map_time.media_time = now_tm->tm_hour*3600+now_tm->tm_min*60+now_tm->tm_sec; + com.map_time.timestamp = slh.compositionTimeStamp; + com.map_time.reset_buffers = 0; + com.base.on_channel = self->channel; + gf_term_on_command(self->owner, &com, GF_OK); + GF_LOG(GF_LOG_INFO, GF_LOG_MEDIA, ("[HYB In] Mapping WC Time %04d/%02d/%02d %02d:%02d:%02d and Hyb time "LLD"\n", + (now_tm->tm_year + 1900), (now_tm->tm_mon + 1), now_tm->tm_mday, now_tm->tm_hour, now_tm->tm_min, now_tm->tm_sec, + com.map_time.timestamp)); } + } - /*for hybrid scenarios: map clocks*/ - { - time_t now; - struct tm *now_tm; - time(&now); - now_tm = gmtime(&now); - { - GF_NetworkCommand com; - memset(&com, 0, sizeof(com)); - com.command_type = GF_NET_CHAN_MAP_TIME; - com.map_time.media_time = now_tm->tm_hour*3600+now_tm->tm_min*60+now_tm->tm_sec; - com.map_time.timestamp = slh.compositionTimeStamp; - com.map_time.reset_buffers = 0; - com.base.on_channel = self->channel; - gf_term_on_command(self->owner, &com, GF_OK); - GF_LOG(GF_LOG_INFO, GF_LOG_MEDIA, ("[HYB In] Mapping WC Time %04d/%02d/%02d %02d:%02d:%02d and Hyb time "LLD"\n", - (now_tm->tm_year + 1900), (now_tm->tm_mon + 1), now_tm->tm_mday, now_tm->tm_hour, now_tm->tm_min, now_tm->tm_sec, - com.map_time.timestamp)); - } - } + /*initialize clock*/ + init_time = gf_sys_clock(); + } - /*initialize clock*/ - init_time = gf_sys_clock(); + while (self->state >= 0) /*pause or play*/ + { + if (self->state == HYB_STATE_PAUSE) { + gf_sleep(10); + init_time = (u32)(gf_sys_clock() - ((u64)slh.compositionTimeStamp*1000)/FM_FAKE_PUSH_AUDIO_FREQ - 50/*50ms delay*/); /*pause: won't wait at resume*/ + continue; } - time_to_wait = (u32)(init_time + ((u64)slh.compositionTimeStamp*1000)/FM_FAKE_PUSH_AUDIO_FREQ - gf_sys_clock()); +#if 0 + time_to_wait = (s32)(init_time + ((u64)slh.compositionTimeStamp*1000)/FM_FAKE_PUSH_AUDIO_FREQ - gf_sys_clock()); GF_LOG(GF_LOG_DEBUG, GF_LOG_MODULE, ("[HYB_IN] FM_FAKE_PUSH - %d ms before next AU\n", time_to_wait)); if (time_to_wait > 0) { if (time_to_wait > 1000) /*TODO: understand the big shifts when playing icecasts contents*/ GF_LOG(GF_LOG_WARNING, GF_LOG_MODULE, ("[HYB_IN] FM_FAKE_PUSH - audio asked to sleep for %d ms\n", time_to_wait)); gf_sleep(time_to_wait); } +#endif e = GetData(self, &data, &data_size, &slh); gf_term_on_sl_packet(self->owner, self->channel, data, data_size, &slh, e); @@ -274,6 +293,9 @@ static GF_Err FM_FAKE_PUSH_Disconnect(GF_HYBMEDIA *self) audio_gen_stop(self); gf_th_del(priv->th); +#ifdef EXT_MEDIA_LOAD_THREADED + gf_th_del(priv->media_th); +#endif priv->th = NULL; self->owner = NULL; diff --git a/modules/hyb_in/fm_mmbtools.c b/modules/hyb_in/fm_mmbtools.c index bf598fc..6c3d009 100644 --- a/modules/hyb_in/fm_mmbtools.c +++ b/modules/hyb_in/fm_mmbtools.c @@ -26,79 +26,4 @@ /*hybrid media interface implementation using MMB Tools from the Communication Research Center Canada (http://mmbtools.crc.ca/)*/ -#include -#if 0 - - -GF_HYBMEDIA master_fm_mmbtools = { -}; - - -typedef struct { - Bool clock_found_time; /*simulate the discovery of a RDS clock: arrives within the first minutes with a 100ms jitter*/ - - GF_Thread *fm_th; - volatile u32 fm_th_state; -} FM_MMBTOOLS; - - - -static Bool FM_MMBTOOLS_CanHandleURL(const char *url) -{ - if (!strnicmp(url, "fm://", 5)) - return 1; - - return 0; -} - -static GF_ObjectDescriptor* FM_MMBTOOLS_GetOD() -{ - - return od; -} - -static GF_Err HYB_FM_Close(GF_HYB_In *hyb_in) -{ - /*stop the audio_generation thread*/ - hyb_in->fm_th_state = HYB_STATE_STOPPING; - while (hyb_in->fm_th_state != HYB_STATE_STOPPED) - gf_sleep(10); - - return GF_OK; -} - -static GF_Err HYB_FM_Connect(GF_HYB_In *hyb_in, const char *url) -{ - FM_MMBTOOLS* audio_gen; - if (hyb_in->fm_thread) - return GF_ERR; - GF_SAFEALLOC(audio_gen, FM_MMBTOOLS); - hyb_in->fm_thread = gf_th_new("HYB-FM fake audio generation thread"); - gf_th_run(hyb_in->fm_thread, audio_gen_th, audio_gen); - - return GF_OK; -} - -static u32 audio_gen_th(void *par) -{ - FM_MMBTOOLS audio_gen = (FM_MMBTOOLS*) par; - - /*RDS clock discovery time*/ - clock_found_time = rand() % XXX; //radom in the 1st minute - - while (!audio_gen->state) /*FM_MMBTOOLS_STATE_RUNNING*/ - { - gf_sleep (remaining); - - if (clock > clock_found_time && ) { - gf_term_clock clock(); - } else { - gf_term_clock utc_clock(); - } - } - - audio_gen->state = FM_MMBTOOLS_STATE_STOPPED; -} - -#endif diff --git a/modules/hyb_in/hyb_in.c b/modules/hyb_in/hyb_in.c index 5500f55..0fdd172 100644 --- a/modules/hyb_in/hyb_in.c +++ b/modules/hyb_in/hyb_in.c @@ -102,12 +102,12 @@ static GF_Err HYB_ConnectService(GF_InputService *plug, GF_ClientService *serv, GF_Err e = GF_OK; const size_t nb_masters = sizeof(hyb_masters) / sizeof(GF_HYBMEDIA*); - GF_HYB_In *hyb_in = (GF_HYB_In*)plug->priv; + GF_HYB_In *hyb_in = (GF_HYB_In*)plug->priv; - GF_LOG(GF_LOG_DEBUG, GF_LOG_MODULE, ("[HYB_IN] Received Connection request from service %p for %s\n", serv, url)); + GF_LOG(GF_LOG_DEBUG, GF_LOG_MODULE, ("[HYB_IN] Received Connection request from service %p for %s\n", serv, url)); - if (!hyb_in || !serv || !url) return GF_BAD_PARAM; - hyb_in->service = serv; + if (!hyb_in || !serv || !url) return GF_BAD_PARAM; + hyb_in->service = serv; /*choose the master service*/ for (i=0; imaster); if (e) { GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[HYB_IN] Error - object \"%s\" failed the sanity checks\n", hyb_in->master->name)); - gf_term_on_connect(hyb_in->service, NULL, e); + gf_term_on_connect(hyb_in->service, NULL, e); return e; } GF_LOG(GF_LOG_DEBUG, GF_LOG_MODULE, ("[HYB_IN] Selected master object \"%s\" for URL: %s\n", hyb_in->master->name, url)); @@ -131,7 +131,7 @@ static GF_Err HYB_ConnectService(GF_InputService *plug, GF_ClientService *serv, e = hyb_in->master->Connect(hyb_in->master, hyb_in->service, url); if (e) { GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[HYB_IN] Error - cannot connect service, wrong URL %s\n", url)); - gf_term_on_connect(hyb_in->service, NULL, GF_BAD_PARAM); + gf_term_on_connect(hyb_in->service, NULL, GF_BAD_PARAM); return e; } gf_term_on_connect(hyb_in->service, NULL, GF_OK); @@ -143,41 +143,43 @@ static GF_Err HYB_ConnectService(GF_InputService *plug, GF_ClientService *serv, static GF_Err HYB_CloseService(GF_InputService *plug) { GF_Err e; - GF_HYB_In *hyb_in = (GF_HYB_In*)plug->priv; + GF_HYB_In *hyb_in = (GF_HYB_In*)plug->priv; - GF_LOG(GF_LOG_DEBUG, GF_LOG_MODULE, ("[HYB_IN] Received Close Service (%p) request from terminal\n", ((GF_HYB_In*)plug->priv)->service)); + GF_LOG(GF_LOG_DEBUG, GF_LOG_MODULE, ("[HYB_IN] Received Close Service (%p) request from terminal\n", ((GF_HYB_In*)plug->priv)->service)); /*force to stop and disconnect the master*/ hyb_in->master->SetState(hyb_in->master, GF_NET_CHAN_STOP); e = hyb_in->master->Disconnect(hyb_in->master); if (e) { - GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[HYB_IN] Error - cannot disconnect service %p\n", hyb_in->service)); - gf_term_on_connect(hyb_in->service, NULL, GF_BAD_PARAM); + GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[HYB_IN] Error - cannot disconnect service %p\n", hyb_in->service)); + gf_term_on_connect(hyb_in->service, NULL, GF_BAD_PARAM); return e; } + gf_term_on_disconnect(hyb_in->service, NULL, GF_OK); + return GF_OK; } static GF_Descriptor *HYB_GetServiceDesc(GF_InputService *plug, u32 expect_type, const char *sub_url) { - GF_LOG(GF_LOG_DEBUG, GF_LOG_MODULE, ("[HYB_IN] Received Service Description request from terminal for %s\n", sub_url)); + GF_LOG(GF_LOG_DEBUG, GF_LOG_MODULE, ("[HYB_IN] Received Service Description request from terminal for %s\n", sub_url)); return NULL; } static GF_Err HYB_ConnectChannel(GF_InputService *plug, LPNETCHANNEL channel, const char *url, Bool upstream) { - GF_HYB_In *hyb_in; + GF_HYB_In *hyb_in; GF_HYBMEDIA *master; - if (!plug || !plug->priv) + if (!plug || !plug->priv) return GF_SERVICE_ERROR; hyb_in = (GF_HYB_In*)plug->priv; master = (GF_HYBMEDIA*)hyb_in->master; - GF_LOG(GF_LOG_DEBUG, GF_LOG_MODULE, ("[HYB_IN] Received Channel Connection request from service %p for %s\n", channel, url)); + GF_LOG(GF_LOG_DEBUG, GF_LOG_MODULE, ("[HYB_IN] Received Channel Connection request from service %p for %s\n", channel, url)); master->channel = channel; gf_term_on_connect(hyb_in->service, channel, GF_OK); @@ -187,19 +189,19 @@ static GF_Err HYB_ConnectChannel(GF_InputService *plug, LPNETCHANNEL channel, co static GF_Err HYB_DisconnectChannel(GF_InputService *plug, LPNETCHANNEL channel) { - GF_HYB_In *hyb_in = (GF_HYB_In*)plug->priv; + GF_HYB_In *hyb_in = (GF_HYB_In*)plug->priv; - GF_LOG(GF_LOG_DEBUG, GF_LOG_MODULE, ("[HYB_IN] Received Channel Disconnect Service (%p) request from terminal\n", hyb_in->service)); + GF_LOG(GF_LOG_DEBUG, GF_LOG_MODULE, ("[HYB_IN] Received Channel Disconnect Service (%p) request from terminal\n", hyb_in->service)); + gf_term_on_disconnect(hyb_in->service, channel, GF_OK); hyb_in->master->channel = NULL; - gf_term_on_disconnect(hyb_in->service, NULL, GF_OK); return GF_OK; } static GF_Err HYB_ServiceCommand(GF_InputService *plug, GF_NetworkCommand *com) { - GF_HYB_In *hyb_in = (GF_HYB_In*)plug->priv; + GF_HYB_In *hyb_in = (GF_HYB_In*)plug->priv; switch (com->command_type) { case GF_NET_CHAN_SET_SPEED: @@ -238,7 +240,7 @@ static Bool HYB_CanHandleURLInService(GF_InputService *plug, const char *url) static GF_Err HYB_ChannelGetSLP(GF_InputService *plug, LPNETCHANNEL channel, char **out_data_ptr, u32 *out_data_size, GF_SLHeader *out_sl_hdr, Bool *sl_compressed, GF_Err *out_reception_status, Bool *is_new_data) { - GF_HYB_In *hyb_in = (GF_HYB_In*)plug->priv; + GF_HYB_In *hyb_in = (GF_HYB_In*)plug->priv; assert(hyb_in->master->data_mode == HYB_PULL && hyb_in->master->GetData && hyb_in->master->ReleaseData); assert(((GF_HYB_In*)plug->priv)->master->channel == channel); @@ -251,7 +253,7 @@ static GF_Err HYB_ChannelGetSLP(GF_InputService *plug, LPNETCHANNEL channel, cha static GF_Err HYB_ChannelReleaseSLP(GF_InputService *plug, LPNETCHANNEL channel) { - GF_HYB_In *hyb_in = (GF_HYB_In*)plug->priv; + GF_HYB_In *hyb_in = (GF_HYB_In*)plug->priv; assert(((GF_HYB_In*)plug->priv)->master->channel == channel); return GF_OK; } @@ -259,38 +261,38 @@ static GF_Err HYB_ChannelReleaseSLP(GF_InputService *plug, LPNETCHANNEL channel) GF_EXPORT const u32 *QueryInterfaces() { - static u32 si [] = { - GF_NET_CLIENT_INTERFACE, - 0 - }; - return si; + static u32 si [] = { + GF_NET_CLIENT_INTERFACE, + 0 + }; + return si; } GF_EXPORT GF_BaseInterface *LoadInterface(u32 InterfaceType) { - GF_HYB_In *hyb_in; - GF_InputService *plug; - if (InterfaceType != GF_NET_CLIENT_INTERFACE) return NULL; - - GF_SAFEALLOC(plug, GF_InputService); - GF_REGISTER_MODULE_INTERFACE(plug, GF_NET_CLIENT_INTERFACE, "GPAC HYBRID MEDIA Loader", "gpac distribution") - plug->RegisterMimeTypes= HYB_RegisterMimeTypes; - plug->CanHandleURL= HYB_CanHandleURL; - plug->ConnectService= HYB_ConnectService; - plug->CloseService= HYB_CloseService; - plug->GetServiceDescriptor= HYB_GetServiceDesc; - plug->ConnectChannel= HYB_ConnectChannel; - plug->DisconnectChannel= HYB_DisconnectChannel; - plug->ServiceCommand= HYB_ServiceCommand; - plug->CanHandleURLInService=HYB_CanHandleURLInService; - plug->ChannelGetSLP= HYB_ChannelGetSLP; - plug->ChannelReleaseSLP= HYB_ChannelReleaseSLP; - - GF_SAFEALLOC(hyb_in, GF_HYB_In); - plug->priv = hyb_in; - - return (GF_BaseInterface *)plug; + GF_HYB_In *hyb_in; + GF_InputService *plug; + if (InterfaceType != GF_NET_CLIENT_INTERFACE) return NULL; + + GF_SAFEALLOC(plug, GF_InputService); + GF_REGISTER_MODULE_INTERFACE(plug, GF_NET_CLIENT_INTERFACE, "GPAC HYBRID MEDIA Loader", "gpac distribution") + plug->RegisterMimeTypes= HYB_RegisterMimeTypes; + plug->CanHandleURL= HYB_CanHandleURL; + plug->ConnectService= HYB_ConnectService; + plug->CloseService= HYB_CloseService; + plug->GetServiceDescriptor= HYB_GetServiceDesc; + plug->ConnectChannel= HYB_ConnectChannel; + plug->DisconnectChannel= HYB_DisconnectChannel; + plug->ServiceCommand= HYB_ServiceCommand; + plug->CanHandleURLInService=HYB_CanHandleURLInService; + plug->ChannelGetSLP= HYB_ChannelGetSLP; + plug->ChannelReleaseSLP= HYB_ChannelReleaseSLP; + + GF_SAFEALLOC(hyb_in, GF_HYB_In); + plug->priv = hyb_in; + + return (GF_BaseInterface *)plug; } GF_EXPORT @@ -306,3 +308,4 @@ void ShutdownInterface(GF_BaseInterface *ifce) GF_LOG(GF_LOG_MEDIA, GF_LOG_ERROR, ("DeleteLoaderInterface %p: 2\n", ifce)); } } + diff --git a/modules/img_in/img_in.c b/modules/img_in/img_in.c index 89789ee..7ce064f 100644 --- a/modules/img_in/img_in.c +++ b/modules/img_in/img_in.c @@ -73,9 +73,15 @@ GF_ESD *IMG_GetESD(IMGLoader *read) u32 mtype, w, h; GF_BitStream *bs = gf_bs_from_file(read->stream, GF_BITSTREAM_READ); gf_img_parse(bs, &OTI, &mtype, &w, &h, &esd->decoderConfig->decoderSpecificInfo->data, &esd->decoderConfig->decoderSpecificInfo->dataLength); - esd->decoderConfig->objectTypeIndication = OTI; gf_bs_del(bs); + if (!OTI) { + GF_LOG(GF_LOG_WARNING, GF_LOG_CODEC, ("[IMG_IN] Unable to guess format image - assigning from extension\n")); + if (read->img_type==IMG_JPEG) OTI = GPAC_OTI_IMAGE_JPEG; + else if (read->img_type==IMG_PNG) OTI = GPAC_OTI_IMAGE_PNG; + } + esd->decoderConfig->objectTypeIndication = OTI; + if (read->img_type == IMG_PNGD) { GF_Descriptor *d = gf_odf_desc_new(GF_ODF_AUX_VIDEO_DATA); ((GF_AuxVideoDescriptor*)d)->aux_video_type = 1; @@ -190,7 +196,12 @@ void jp_download_file(GF_InputService *plug, const char *url) IMGLoader *read = (IMGLoader *) plug->priv; read->dnload = gf_term_download_new(read->service, url, 0, IMG_NetIO, read); - if (!read->dnload) gf_term_on_connect(read->service, NULL, GF_NOT_SUPPORTED); + if (!read->dnload) { + gf_term_on_connect(read->service, NULL, GF_NOT_SUPPORTED); + } else { + /*start our download (threaded)*/ + gf_dm_sess_process(read->dnload); + } /*service confirm is done once fetched*/ } @@ -315,6 +326,8 @@ static GF_Err IMG_ServiceCommand(GF_InputService *plug, GF_NetworkCommand *com) { IMGLoader *read = (IMGLoader *)plug->priv; + if (com->command_type==GF_NET_SERVICE_HAS_AUDIO) return GF_NOT_SUPPORTED; + if (!com->base.on_channel) return GF_NOT_SUPPORTED; switch (com->command_type) { case GF_NET_CHAN_SET_PADDING: diff --git a/modules/img_in/jp2_dec.c b/modules/img_in/jp2_dec.c index 40a0398..e3a13c9 100644 --- a/modules/img_in/jp2_dec.c +++ b/modules/img_in/jp2_dec.c @@ -1,26 +1,26 @@ /* - * GPAC - Multimedia Framework C SDK - * - * Copyright (c) Jean Le Feuvre 2000-2005 - * All rights reserved - * - * This file is part of GPAC / image format module - * - * GPAC is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * GPAC is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; see the file COPYING. If not, write to - * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. - * - */ +* GPAC - Multimedia Framework C SDK +* +* Copyright (c) Jean Le Feuvre 2000-2005 +* All rights reserved +* +* This file is part of GPAC / image format module +* +* GPAC is free software; you can redistribute it and/or modify +* it under the terms of the GNU Lesser General Public License as published by +* the Free Software Foundation; either version 2, or (at your option) +* any later version. +* +* GPAC is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; see the file COPYING. If not, write to +* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +* +*/ #include "img_in.h" @@ -33,6 +33,7 @@ typedef struct /*no support for scalability with JPEG (progressive JPEG to test)*/ u32 bpp, nb_comp, width, height, out_size, pixel_format, dsi_size; char *dsi; + opj_image_t *image; } JP2Dec; #define JP2CTX() JP2Dec *ctx = (JP2Dec *) ((IMGDec *)ifcg->privateStack)->opaque @@ -59,7 +60,7 @@ static GF_Err JP2_AttachStream(GF_BaseDecoder *ifcg, GF_ESD *esd) case 1: ctx->pixel_format = GF_PIXEL_GREYSCALE; break; case 2: ctx->pixel_format = GF_PIXEL_ALPHAGREY; break; case 3: ctx->pixel_format = GF_PIXEL_RGB_24; break; - case 4: ctx->pixel_format = GF_PIXEL_ARGB; break; + case 4: ctx->pixel_format = GF_PIXEL_RGBA; break; default: return GF_NOT_SUPPORTED; } } else { @@ -92,6 +93,11 @@ static GF_Err JP2_AttachStream(GF_BaseDecoder *ifcg, GF_ESD *esd) } static GF_Err JP2_DetachStream(GF_BaseDecoder *ifcg, u16 ES_ID) { + JP2CTX(); + if (ctx->image) { + opj_image_destroy(ctx->image); + ctx->image = NULL; + } return GF_OK; } static GF_Err JP2_GetCapabilities(GF_BaseDecoder *ifcg, GF_CodecCapability *capability) @@ -167,26 +173,26 @@ void info_callback(const char *msg, void *client_data) } /* - * Divide an integer by a power of 2 and round upwards. - * - * a divided by 2^b - */ +* Divide an integer by a power of 2 and round upwards. +* +* a divided by 2^b +*/ static int int_ceildivpow2(int a, int b) { return (a + (1 << b) - 1) >> b; } static GF_Err JP2_ProcessData(GF_MediaDecoder *ifcg, - char *inBuffer, u32 inBufferLength, - u16 ES_ID, - char *outBuffer, u32 *outBufferLength, - u8 PaddingBits, u32 mmlevel) + char *inBuffer, u32 inBufferLength, + u16 ES_ID, + char *outBuffer, u32 *outBufferLength, + u8 PaddingBits, u32 mmlevel) { u32 i, w, wr, h, hr, wh; opj_dparameters_t parameters; /* decompression parameters */ opj_event_mgr_t event_mgr; /* event manager */ - opj_image_t *image = NULL; opj_dinfo_t* dinfo = NULL; /* handle to a decompressor */ opj_cio_t *cio = NULL; + opj_codestream_info_t cinfo; JP2CTX(); @@ -199,14 +205,15 @@ static GF_Err JP2_ProcessData(GF_MediaDecoder *ifcg, } #endif - /* configure the event callbacks (not required) */ - memset(&event_mgr, 0, sizeof(opj_event_mgr_t)); - event_mgr.error_handler = error_callback; - event_mgr.warning_handler = warning_callback; - event_mgr.info_handler = info_callback; + if (!ctx->image) { + /* configure the event callbacks (not required) */ + memset(&event_mgr, 0, sizeof(opj_event_mgr_t)); + event_mgr.error_handler = error_callback; + event_mgr.warning_handler = warning_callback; + event_mgr.info_handler = info_callback; - /* set decoding parameters to default values */ - opj_set_default_decoder_parameters(¶meters); + /* set decoding parameters to default values */ + opj_set_default_decoder_parameters(¶meters); /* get a decoder handle */ dinfo = opj_create_decompress(CODEC_JP2); @@ -218,126 +225,179 @@ static GF_Err JP2_ProcessData(GF_MediaDecoder *ifcg, opj_setup_decoder(dinfo, ¶meters); - /* open a byte stream */ - if (ctx->dsi) { - char *data; - - data = gf_malloc(sizeof(char) * (ctx->dsi_size+inBufferLength)); - memcpy(data, ctx->dsi, ctx->dsi_size); - memcpy(data+ctx->dsi_size, inBuffer, inBufferLength); - cio = opj_cio_open((opj_common_ptr)dinfo, data, ctx->dsi_size+inBufferLength); - /* decode the stream and fill the image structure */ - image = opj_decode(dinfo, cio); - gf_free(data); - } else { - cio = opj_cio_open((opj_common_ptr)dinfo, inBuffer, inBufferLength); - /* decode the stream and fill the image structure */ - image = opj_decode(dinfo, cio); - } + /* open a byte stream */ + if (ctx->dsi) { + char *data; - if(!image) { - opj_destroy_decompress(dinfo); + data = gf_malloc(sizeof(char) * (ctx->dsi_size+inBufferLength)); + memcpy(data, ctx->dsi, ctx->dsi_size); + memcpy(data+ctx->dsi_size, inBuffer, inBufferLength); + cio = opj_cio_open((opj_common_ptr)dinfo, data, ctx->dsi_size+inBufferLength); + /* decode the stream and fill the image structure */ + ctx->image = opj_decode(dinfo, cio); + gf_free(data); + } else { + cio = opj_cio_open((opj_common_ptr)dinfo, inBuffer, inBufferLength); + /* decode the stream and fill the image structure */ + ctx->image = opj_decode_with_info(dinfo, cio, &cinfo); + } + + //Fill the ctx info because dsi was not present + if (ctx->image) { + ctx->nb_comp = cinfo.numcomps; + ctx->width = cinfo.image_w; + ctx->height = cinfo.image_h; + ctx->bpp = ctx->nb_comp * 8; + ctx->out_size = ctx->width * ctx->height * ctx->nb_comp /* * ctx->bpp / 8 */; + + switch (ctx->nb_comp) { + case 1: ctx->pixel_format = GF_PIXEL_GREYSCALE; break; + case 2: ctx->pixel_format = GF_PIXEL_ALPHAGREY; break; + case 3: ctx->pixel_format = GF_PIXEL_RGB_24; break; + case 4: ctx->pixel_format = GF_PIXEL_RGBA; break; + default: return GF_NOT_SUPPORTED; + } + + if ( *outBufferLength < ctx->out_size ) { + *outBufferLength = ctx->out_size; + opj_destroy_decompress(dinfo); + opj_cio_close(cio); + return GF_BUFFER_TOO_SMALL; + } + } + + if(!ctx->image) { + opj_destroy_decompress(dinfo); + opj_cio_close(cio); + return GF_IO_ERR; + } + + /* close the byte stream */ opj_cio_close(cio); - return GF_IO_ERR; + cio = NULL; + + /* gf_free( remaining structures */ + if(dinfo) { + opj_destroy_decompress(dinfo); + dinfo = NULL; + } } - /* close the byte stream */ - opj_cio_close(cio); - - w = image->comps[0].w; - wr = int_ceildivpow2(image->comps[0].w, image->comps[0].factor); - h = image->comps[0].h; - hr = int_ceildivpow2(image->comps[0].h, image->comps[0].factor); + w = ctx->image->comps[0].w; + wr = int_ceildivpow2(ctx->image->comps[0].w, ctx->image->comps[0].factor); + h = ctx->image->comps[0].h; + hr = int_ceildivpow2(ctx->image->comps[0].h, ctx->image->comps[0].factor); wh = wr*hr; if (ctx->nb_comp==1) { if ((w==wr) && (h==hr)) { for (i=0; icomps[0].data[i]; + outBuffer[i] = ctx->image->comps[0].data[i]; } } else { for (i=0; icomps[0].data[w * hr - ((i) / (wr) + 1) * w + (i) % (wr)]; + outBuffer[i] = ctx->image->comps[0].data[w * hr - ((i) / (wr) + 1) * w + (i) % (wr)]; } } } else if (ctx->nb_comp==3) { - if ((image->comps[0].w==2*image->comps[1].w) && (image->comps[1].w==image->comps[2].w) - && (image->comps[0].h==2*image->comps[1].h) && (image->comps[1].h==image->comps[2].h)) { - - if (ctx->pixel_format != GF_PIXEL_YV12) { - ctx->pixel_format = GF_PIXEL_YV12; - ctx->out_size = 3*ctx->width*ctx->height/2; - *outBufferLength = ctx->out_size; - return GF_BUFFER_TOO_SMALL; - } + if ((ctx->image->comps[0].w==2*ctx->image->comps[1].w) && (ctx->image->comps[1].w==ctx->image->comps[2].w) + && (ctx->image->comps[0].h==2*ctx->image->comps[1].h) && (ctx->image->comps[1].h==ctx->image->comps[2].h)) { - if ((w==wr) && (h==hr)) { - for (i=0; icomps[0].data[i]; - outBuffer++; - } - w = image->comps[1].w; - wr = int_ceildivpow2(image->comps[1].w, image->comps[1].factor); - h = image->comps[1].h; - hr = int_ceildivpow2(image->comps[1].h, image->comps[1].factor); - wh = wr*hr; - for (i=0; icomps[1].data[i]; - outBuffer++; - } - for (i=0; icomps[2].data[i]; - outBuffer++; - } - } else { - for (i=0; icomps[0].data[w * hr - ((i) / (wr) + 1) * w + (i) % (wr)]; - } - w = image->comps[1].w; - wr = int_ceildivpow2(image->comps[1].w, image->comps[1].factor); - h = image->comps[1].h; - hr = int_ceildivpow2(image->comps[1].h, image->comps[1].factor); - wh = wr*hr; - for (i=0; icomps[1].data[w * hr - ((i) / (wr) + 1) * w + (i) % (wr)]; + if (ctx->pixel_format != GF_PIXEL_YV12) { + ctx->pixel_format = GF_PIXEL_YV12; + ctx->out_size = 3*ctx->width*ctx->height/2; + *outBufferLength = ctx->out_size; + return GF_BUFFER_TOO_SMALL; } - for (i=0; icomps[2].data[w * hr - ((i) / (wr) + 1) * w + (i) % (wr)]; - } - } + if ((w==wr) && (h==hr)) { + for (i=0; iimage->comps[0].data[i]; + outBuffer++; + } + w = ctx->image->comps[1].w; + wr = int_ceildivpow2(ctx->image->comps[1].w, ctx->image->comps[1].factor); + h = ctx->image->comps[1].h; + hr = int_ceildivpow2(ctx->image->comps[1].h, ctx->image->comps[1].factor); + wh = wr*hr; + for (i=0; iimage->comps[1].data[i]; + outBuffer++; + } + for (i=0; iimage->comps[2].data[i]; + outBuffer++; + } + } else { + for (i=0; iimage->comps[0].data[w * hr - ((i) / (wr) + 1) * w + (i) % (wr)]; + } + w = ctx->image->comps[1].w; + wr = int_ceildivpow2(ctx->image->comps[1].w, ctx->image->comps[1].factor); + h = ctx->image->comps[1].h; + hr = int_ceildivpow2(ctx->image->comps[1].h, ctx->image->comps[1].factor); + wh = wr*hr; + for (i=0; iimage->comps[1].data[w * hr - ((i) / (wr) + 1) * w + (i) % (wr)]; + } + for (i=0; iimage->comps[2].data[w * hr - ((i) / (wr) + 1) * w + (i) % (wr)]; + } + } - } else if ((image->comps[0].w==image->comps[1].w) && (image->comps[1].w==image->comps[2].w) - && (image->comps[0].h==image->comps[1].h) && (image->comps[1].h==image->comps[2].h)) { - if ((w==wr) && (h==hr)) { - for (i=0; icomps[0].data[i]; - outBuffer[idx+1] = image->comps[1].data[i]; - outBuffer[idx+2] = image->comps[2].data[i]; + } else if ((ctx->image->comps[0].w==ctx->image->comps[1].w) && (ctx->image->comps[1].w==ctx->image->comps[2].w) + && (ctx->image->comps[0].h==ctx->image->comps[1].h) && (ctx->image->comps[1].h==ctx->image->comps[2].h)) { + + if ((w==wr) && (h==hr)) { + for (i=0; iimage->comps[0].data[i]; + outBuffer[idx+1] = ctx->image->comps[1].data[i]; + outBuffer[idx+2] = ctx->image->comps[2].data[i]; + } + } else { + for (i=0; iimage->comps[0].data[w * hr - ((i) / (wr) + 1) * w + (i) % (wr)]; + outBuffer[idx+1] = ctx->image->comps[1].data[w * hr - ((i) / (wr) + 1) * w + (i) % (wr)]; + outBuffer[idx+2] = ctx->image->comps[2].data[w * hr - ((i) / (wr) + 1) * w + (i) % (wr)]; + } } - } else { - for (i=0; icomps[0].data[w * hr - ((i) / (wr) + 1) * w + (i) % (wr)]; - outBuffer[idx+1] = image->comps[1].data[w * hr - ((i) / (wr) + 1) * w + (i) % (wr)]; - outBuffer[idx+2] = image->comps[2].data[w * hr - ((i) / (wr) + 1) * w + (i) % (wr)]; - } - } } } + else if (ctx->nb_comp==4) { + if ((ctx->image->comps[0].w==ctx->image->comps[1].w) && (ctx->image->comps[1].w==ctx->image->comps[2].w) && (ctx->image->comps[2].w==ctx->image->comps[3].w) + && (ctx->image->comps[0].h==ctx->image->comps[1].h) && (ctx->image->comps[1].h==ctx->image->comps[2].h) && (ctx->image->comps[2].h==ctx->image->comps[3].h)) { + + if ((w==wr) && (h==hr)) { + for (i=0; iimage->comps[0].data[i]; + outBuffer[idx+1] = ctx->image->comps[1].data[i]; + outBuffer[idx+2] = ctx->image->comps[2].data[i]; + outBuffer[idx+3] = ctx->image->comps[3].data[i]; + } + } else { + for (i=0; iimage->comps[0].data[w * hr - ((i) / (wr) + 1) * w + (i) % (wr)]; + outBuffer[idx+1] = ctx->image->comps[1].data[w * hr - ((i) / (wr) + 1) * w + (i) % (wr)]; + outBuffer[idx+2] = ctx->image->comps[2].data[w * hr - ((i) / (wr) + 1) * w + (i) % (wr)]; + outBuffer[idx+3] = ctx->image->comps[3].data[w * hr - ((i) / (wr) + 1) * w + (i) % (wr)]; + } + } + } + } - /* gf_free( remaining structures */ - if(dinfo) { - opj_destroy_decompress(dinfo); + /* gf_free( image data structure */ + if (ctx->image) { + opj_image_destroy(ctx->image); + ctx->image = NULL; } - /* gf_free( image data structure */ - opj_image_destroy(image); - *outBufferLength = ctx->out_size; return GF_OK; } diff --git a/modules/ismacryp/ismacryp.c b/modules/ismacryp/ismacryp.c index 12ec5a9..db86ff8 100644 --- a/modules/ismacryp/ismacryp.c +++ b/modules/ismacryp/ismacryp.c @@ -80,6 +80,8 @@ static GF_Err ISMA_GetGPAC_KMS(ISMAEAPriv *priv, GF_Channel *ch, const char *kms sess = gf_term_download_new(ch->service, kms_url, 0, ISMA_KMS_NetIO, ch); if (!sess) return GF_IO_ERR; + /*start our download (threaded)*/ + gf_dm_sess_process(sess); while (1) { e = gf_dm_sess_get_stats(sess, NULL, NULL, NULL, NULL, NULL, NULL); diff --git a/modules/isom_in/isom_in.h b/modules/isom_in/isom_in.h index 539647c..2ef8bbd 100644 --- a/modules/isom_in/isom_in.h +++ b/modules/isom_in/isom_in.h @@ -28,6 +28,7 @@ #include #include +#include #ifndef GPAC_DISABLE_ISOM @@ -59,6 +60,9 @@ typedef struct /*0: not fragmented - 1 fragmented - 2 fragmented and last fragment received*/ u32 frag_type; + + GF_Mutex *segment_mutex; + } ISOMReader; diff --git a/modules/isom_in/load.c b/modules/isom_in/load.c index 26c05d9..3bc504d 100644 --- a/modules/isom_in/load.c +++ b/modules/isom_in/load.c @@ -140,7 +140,7 @@ void isor_declare_objects(ISOMReader *read) /*write cover data*/ assert(!(tlen & 0x80000000)); - fwrite(tag, tlen & 0x7FFFFFFF, 1, t); + gf_fwrite(tag, tlen & 0x7FFFFFFF, 1, t); fclose(t); /*don't display cover art when video is present*/ diff --git a/modules/isom_in/read.c b/modules/isom_in/read.c index 05b39f2..e844cb2 100644 --- a/modules/isom_in/read.c +++ b/modules/isom_in/read.c @@ -162,7 +162,7 @@ void isor_net_io(void *cbk, GF_NETIO_Parameter *param) return; } - e = gf_isom_open_progressive(local_name, &read->mov, &read->missing_bytes); + e = gf_isom_open_progressive(local_name, 0, 0, &read->mov, &read->missing_bytes); switch (e) { case GF_ISOM_INCOMPLETE_FILE: return; @@ -185,7 +185,12 @@ void isor_setup_download(GF_InputService *plug, const char *url) { ISOMReader *read = (ISOMReader *) plug->priv; read->dnload = gf_term_download_new(read->service, url, 0, isor_net_io, read); - if (!read->dnload) gf_term_on_connect(read->service, NULL, GF_NOT_SUPPORTED); + if (!read->dnload) { + gf_term_on_connect(read->service, NULL, GF_NOT_SUPPORTED); + } else { + /*start our download (threaded)*/ + gf_dm_sess_process(read->dnload); + } /*service confirm is done once IOD can be fetched*/ } @@ -221,7 +226,18 @@ GF_Err ISOR_ConnectService(GF_InputService *plug, GF_ClientService *serv, const } if (isor_is_local(szURL)) { - GF_Err e = gf_isom_open_progressive(szURL, &read->mov, &read->missing_bytes); + GF_Err e; + u64 start_range, end_range; + start_range = end_range = 0; + if (plug->query_proxy) { + GF_NetworkCommand param; + param.command_type = GF_NET_SERVICE_QUERY_INIT_RANGE; + if (read->input->query_proxy(read->input, ¶m)==GF_OK) { + start_range = param.url_query.start_range; + end_range = param.url_query.end_range; + } + } + e = gf_isom_open_progressive(szURL, start_range, end_range, &read->mov, &read->missing_bytes); if (e != GF_OK){ GF_LOG(GF_LOG_ERROR, GF_LOG_NETWORK, ("[IsoMedia] : error while opening %s, error=%s\n", szURL, gf_error_to_string(e))); gf_term_on_connect(serv, NULL, e); @@ -775,6 +791,7 @@ GF_Err ISOR_ServiceCommand(GF_InputService *plug, GF_NetworkCommand *com) if (com->play.end_range >= 0) ch->end = (u64) (s64) (com->play.end_range*ch->time_scale); } ch->is_playing = 1; + if (com->play.dash_segment_switch) ch->wait_for_segment_switch = 1; GF_LOG(GF_LOG_DEBUG, GF_LOG_NETWORK, ("[IsoMedia] Starting channel playback "LLD" to "LLD" (%g to %g)\n", ch->start, ch->end, com->play.start_range, com->play.end_range)); return GF_OK; case GF_NET_CHAN_STOP: @@ -846,6 +863,8 @@ GF_InputService *isor_client_load() GF_SAFEALLOC(reader, ISOMReader); reader->channels = gf_list_new(); + reader->segment_mutex = gf_mx_new("ISO Segment"); + plug->priv = reader; return plug; } diff --git a/modules/isom_in/read_ch.c b/modules/isom_in/read_ch.c index 801a02b..8328e3c 100644 --- a/modules/isom_in/read_ch.c +++ b/modules/isom_in/read_ch.c @@ -41,10 +41,72 @@ void isor_reset_reader(ISOMChannel *ch) ch->is_playing = 0; } +static void check_segment_switch(ISOMReader *read) +{ + GF_NetworkCommand param; + u32 i, count; + GF_Err e; + + /*access to the segment switching must be protected in case several decoders are threaded on the file using GetSLPacket */ + gf_mx_p(read->segment_mutex); + + if (!read->frag_type || !read->input->query_proxy) { + gf_mx_v(read->segment_mutex); + return; + } + + count = gf_list_count(read->channels); + for (i=0; ichannels, i); + /*check all playing channels are waiting for next segment*/ + if (ch->is_playing && !ch->wait_for_segment_switch) { + gf_mx_v(read->segment_mutex); + return; + } + } + /*close current segment*/ + gf_isom_release_segment(read->mov, 1); + GF_LOG(GF_LOG_DEBUG, GF_LOG_NETWORK, ("[IsoMedia] Done playing segment - querying new one\n")); + + /*update current fragment if any*/ + param.command_type = GF_NET_SERVICE_QUERY_NEXT; + if ((read->input->query_proxy(read->input, ¶m)==GF_OK) && param.url_query.next_url){ + if (param.url_query.discontinuity_type==2) + gf_isom_reset_fragment_info(read->mov); + + e = gf_isom_open_segment(read->mov, param.url_query.next_url, param.url_query.start_range, param.url_query.end_range); + +#ifndef GPAC_DISABLE_LOG + if (e<0) { + GF_LOG(GF_LOG_ERROR, GF_LOG_NETWORK, ("[IsoMedia] Error opening new segment %s: %s\n", param.url_query.next_url, gf_error_to_string(e) )); + } else if (param.url_query.end_range) { + GF_LOG(GF_LOG_DEBUG, GF_LOG_NETWORK, ("[IsoMedia] Playing new range in %s: "LLU"-"LLU"\n", param.url_query.next_url, param.url_query.start_range, param.url_query.end_range )); + } else { + GF_LOG(GF_LOG_DEBUG, GF_LOG_NETWORK, ("[IsoMedia] playing new segment %s\n", param.url_query.next_url)); + } +#endif + + for (i=0; ichannels, i); + ch->wait_for_segment_switch = 0; + GF_LOG(GF_LOG_DEBUG, GF_LOG_NETWORK, ("[IsoMedia] Track %d - cur sample %d - new sample count %d\n", ch->track, ch->sample_num, gf_isom_get_sample_count(ch->owner->mov, ch->track) )); + } + } else { + /*consider we are done*/ + read->frag_type = 2; + GF_LOG(GF_LOG_DEBUG, GF_LOG_NETWORK, ("[IsoMedia] No more segments - done playing file\n")); + } + gf_mx_v(read->segment_mutex); +} static void init_reader(ISOMChannel *ch) { u32 ivar; + if (ch->wait_for_segment_switch) { + check_segment_switch(ch->owner); + if (ch->wait_for_segment_switch) + return; + } ch->current_slh.accessUnitEndFlag = 1; ch->current_slh.accessUnitStartFlag = 1; @@ -64,7 +126,7 @@ static void init_reader(ISOMChannel *ch) ch->last_state=GF_OK; } else { /*take care of seeking out of the track range*/ - if (ch->durationstart) { + if (!ch->owner->frag_type && (ch->durationstart)) { ch->last_state = gf_isom_get_sample_for_movie_time(ch->owner->mov, ch->track, ch->duration, &ivar, GF_ISOM_SEARCH_SYNC_BACKWARD, &ch->sample, &ch->sample_num); } else { ch->last_state = gf_isom_get_sample_for_movie_time(ch->owner->mov, ch->track, ch->start, &ivar, GF_ISOM_SEARCH_SYNC_BACKWARD, &ch->sample, &ch->sample_num); @@ -100,44 +162,7 @@ static void init_reader(ISOMChannel *ch) } -static void check_segment_switch(ISOMReader *read) -{ - GF_NetworkCommand param; - u32 i, count; - GF_Err e; - if (!read->frag_type) return; - if (!read->input->query_proxy) return; - count = gf_list_count(read->channels); - for (i=0; ichannels, i); - /*check all playing channels are waiting for next segment*/ - if (ch->is_playing && !ch->wait_for_segment_switch) { - return; - } - } - /*close current segment*/ - gf_isom_release_segment(read->mov, 1); - GF_LOG(GF_LOG_DEBUG, GF_LOG_NETWORK, ("[IsoMedia] Done playing segment - querying new one\n")); - - /*update current fragment if any*/ - param.command_type = GF_NET_SERVICE_QUERY_NEXT; - if ((read->input->query_proxy(read->input, ¶m)==GF_OK) && param.url_query.next_url){ - - e = gf_isom_open_segment(read->mov, param.url_query.next_url); - GF_LOG(GF_LOG_DEBUG, GF_LOG_NETWORK, ("[IsoMedia] playing new segment %s: %s\n", param.url_query.next_url, gf_error_to_string(e) )); - - for (i=0; ichannels, i); - ch->wait_for_segment_switch = 0; - GF_LOG(GF_LOG_DEBUG, GF_LOG_NETWORK, ("[IsoMedia] Track %d - cur sample %d - new sample count %d\n", ch->track, ch->sample_num, gf_isom_get_sample_count(ch->owner->mov, ch->track) )); - } - } else { - /*consider we are done*/ - read->frag_type = 2; - GF_LOG(GF_LOG_DEBUG, GF_LOG_NETWORK, ("[IsoMedia] No more segments - done playing file\n")); - } -} void isor_reader_get_sample(ISOMChannel *ch) { @@ -151,43 +176,66 @@ void isor_reader_get_sample(ISOMChannel *ch) u32 prev_sample = ch->sample_num; e = gf_isom_get_sample_for_movie_time(ch->owner->mov, ch->track, ch->sample_time + 1, &ivar, GF_ISOM_SEARCH_FORWARD, &ch->sample, &ch->sample_num); - /*we are in forced seek mode: fetch all samples before the one matching the sample time*/ - if (ch->edit_sync_frame) { - ch->edit_sync_frame++; - if (ch->edit_sync_frame < ch->sample_num) { - gf_isom_sample_del(&ch->sample); - ch->sample = gf_isom_get_sample(ch->owner->mov, ch->track, ch->edit_sync_frame, &ivar); - ch->sample->DTS = ch->sample_time; - ch->sample->CTS_Offset = 0; - } else { - ch->edit_sync_frame = 0; - if (ch->sample) ch->sample_time = ch->sample->DTS; - } - } else { - /*we jumped to another segment - if RAP is needed look for closest rap in decoding order and - force seek mode*/ - if (ch->sample && !ch->sample->IsRAP && ch->has_rap && (ch->sample_num != prev_sample+1)) { - GF_ISOSample *found = ch->sample; - u32 samp_num = ch->sample_num; - ch->sample = NULL; - e = gf_isom_get_sample_for_movie_time(ch->owner->mov, ch->track, ch->sample_time + 1, &ivar, GF_ISOM_SEARCH_SYNC_BACKWARD, &ch->sample, &ch->sample_num); - /*if no sync point in the past, use the first non-sync for the given time*/ - if (!ch->sample || !ch->sample->data) { + if (e == GF_OK) { + + /*we are in forced seek mode: fetch all samples before the one matching the sample time*/ + if (ch->edit_sync_frame) { + ch->edit_sync_frame++; + if (ch->edit_sync_frame < ch->sample_num) { gf_isom_sample_del(&ch->sample); - ch->sample = found; - ch->sample_time = ch->sample->DTS; - ch->sample_num = samp_num; - } else { - gf_isom_sample_del(&found); - ch->edit_sync_frame = ch->sample_num; + ch->sample = gf_isom_get_sample(ch->owner->mov, ch->track, ch->edit_sync_frame, &ivar); ch->sample->DTS = ch->sample_time; ch->sample->CTS_Offset = 0; + } else { + ch->edit_sync_frame = 0; + if (ch->sample) ch->sample_time = ch->sample->DTS; } } else { - if (ch->sample) ch->sample_time = ch->sample->DTS; + /*if we get the same sample, figure out next interesting time (current sample + DTS gap to next sample should be a good bet)*/ + if (prev_sample == ch->sample_num) { + u32 time_diff = 2; + u32 sample_num = ch->sample_num ? ch->sample_num : 1; + GF_ISOSample *s1 = gf_isom_get_sample(ch->owner->mov, ch->track, sample_num, NULL); + GF_ISOSample *s2 = gf_isom_get_sample(ch->owner->mov, ch->track, sample_num+1, NULL); + + gf_isom_sample_del(&ch->sample); + + if (s2 && s1) { + time_diff = (u32) (s2->DTS - s1->DTS); + e = gf_isom_get_sample_for_movie_time(ch->owner->mov, ch->track, ch->sample_time + time_diff, &ivar, GF_ISOM_SEARCH_FORWARD, &ch->sample, &ch->sample_num); + } else if (s1 && !s2) { + e = GF_EOS; + } + gf_isom_sample_del(&s1); + gf_isom_sample_del(&s2); + + } + + /*we jumped to another segment - if RAP is needed look for closest rap in decoding order and + force seek mode*/ + if (ch->sample && !ch->sample->IsRAP && ch->has_rap && (ch->sample_num != prev_sample+1)) { + GF_ISOSample *found = ch->sample; + u32 samp_num = ch->sample_num; + ch->sample = NULL; + e = gf_isom_get_sample_for_movie_time(ch->owner->mov, ch->track, ch->sample_time + 1, &ivar, GF_ISOM_SEARCH_SYNC_BACKWARD, &ch->sample, &ch->sample_num); + assert (e == GF_OK); + /*if no sync point in the past, use the first non-sync for the given time*/ + if (!ch->sample || !ch->sample->data) { + gf_isom_sample_del(&ch->sample); + ch->sample = found; + ch->sample_time = ch->sample->DTS; + ch->sample_num = samp_num; + } else { + gf_isom_sample_del(&found); + ch->edit_sync_frame = ch->sample_num; + ch->sample->DTS = ch->sample_time; + ch->sample->CTS_Offset = 0; + } + } else { + if (ch->sample) ch->sample_time = ch->sample->DTS; + } } } - } else { ch->sample_num++; fetch_next: @@ -225,7 +273,8 @@ fetch_next: } else { GF_LOG(GF_LOG_DEBUG, GF_LOG_NETWORK, ("[IsoMedia] Track #%d fail to fetch sample %d / %d: %s\n", ch->track, ch->sample_num, gf_isom_get_sample_count(ch->owner->mov, ch->track), gf_error_to_string(gf_isom_last_error(ch->owner->mov)) )); } - if (ch->wait_for_segment_switch) check_segment_switch(ch->owner); + if (ch->wait_for_segment_switch) + check_segment_switch(ch->owner); return; } ch->last_state = GF_OK; diff --git a/modules/libplayer/libplayer.c b/modules/libplayer/libplayer.c index 67276d4..57dda91 100644 --- a/modules/libplayer/libplayer.c +++ b/modules/libplayer/libplayer.c @@ -44,7 +44,24 @@ typedef s32 off_t; #endif +// variable used to detect the number of libplayer instances created - up to 2 instances static int libplayer_id = 0; +//! unfortunately, saving data would not be a good solution for Mosaic Mode in ESG Application since the position changes everytime user +//! uses the navigation button +// value for input output windows, when save_data_instance1 equals to 1 <=> the second instance is already created with the correct size, +// save these values in in_instance1 and out_instance1 so that we do not need to recalculate every time +//~ static video_rect_t in_instance1, out_instance1; +//~ static save_data_instance1 = 0; + +// variable use to detect whether a dvb instance has already started for instance 1 +static int start_dvb = 0; + +enum +{ + PLAYER_FILE = 0, + PLAYER_DVB = 1 +}; + typedef struct { @@ -53,6 +70,7 @@ typedef struct u32 init; u32 state; u32 player_id; + u32 player_type; u32 width; u32 height; char *url; @@ -102,6 +120,8 @@ Bool LIBPLAYER_CanHandleURL(GF_InputService *plug, const char *url) u32 i; Bool ok = 0; char *cgi_par; + // case dvb + if (!strnicmp(url, "dvb://", 6)) return 1; if (!strnicmp(sExt, ".gz", 3)) sExt = strrchr(sExt, '.'); if (!strnicmp(url, "rtsp://", 7)) return 0; sExt++; @@ -118,6 +138,7 @@ Bool LIBPLAYER_CanHandleURL(GF_InputService *plug, const char *url) if (cgi_par) cgi_par[0] = '?'; if (ok) return 1; } + return 0; } @@ -169,6 +190,8 @@ GF_Err LIBPLAYER_ConnectService(GF_InputService *plug, GF_ClientService *serv, c read->height = 20; read->url = url; read->player_id = libplayer_id; + read->player_type = PLAYER_FILE; + #ifndef TEST_LIBPLAYER read->player = player_init(PLAYER_TYPE_DUMMY, PLAYER_AO_AUTO, PLAYER_VO_AUTO, PLAYER_MSG_INFO, read->player_id, on_libplayer_event); @@ -184,7 +207,84 @@ GF_Err LIBPLAYER_ConnectService(GF_InputService *plug, GF_ClientService *serv, c #ifndef TEST_LIBPLAYER mrl = NULL; + + // dvb if (!strnicmp(url, "dvb://", 6)) { + read->player_type = PLAYER_DVB; + mrl_resource_dvb_args_t *mrl_dvb_args; + mrl_dvb_args = calloc(1, sizeof(mrl_resource_dvb_args_t)); + char *frequency; + + // fetch frequency + if (frequency = strchr(url+6, '@')) { + char *enc, *pid; + mrl_dvb_args->frequency = atoi(frequency+1); + + /* + * video + */ + // video codec + if (enc = strstr(url+6, "mpeg2")) { + mrl_dvb_args->video_enc = PLAYER_VIDEO_MPEG2; + } + else if (enc = strstr(url+6, "h264")) { + mrl_dvb_args->video_enc = PLAYER_VIDEO_H264; + } + else { + GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[LibPlayerIN] Unknown video encoding\n")); + mrl_dvb_args->video_enc = PLAYER_VIDEO_UNKNOWN; + } + + // video PID + if (mrl_dvb_args->video_enc != PLAYER_VIDEO_UNKNOWN) { + pid = strchr(enc, ':'); + mrl_dvb_args->video_pid = atoi(pid+1); + } + + /* + * audio + */ + // audio codec + if (enc = strstr(url+6, "mp2")) { + mrl_dvb_args->audio_enc = PLAYER_AUDIO_MP2; + } + else if (enc = strstr(url+6, "ac3")) { + mrl_dvb_args->audio_enc = PLAYER_AUDIO_AC3; + } + else { + GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[LibPlayerIN] Unknown audio encoding\n")); + mrl_dvb_args->audio_enc = PLAYER_AUDIO_UNKNOWN; + } + + // audio PID + if (mrl_dvb_args->audio_enc != PLAYER_AUDIO_UNKNOWN) { + pid = strchr(enc, ':'); + mrl_dvb_args->audio_pid = atoi(pid+1); + } + + if (mrl_dvb_args->video_enc == PLAYER_VIDEO_UNKNOWN && mrl_dvb_args->audio_enc == PLAYER_AUDIO_UNKNOWN) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[LibPlayerIN] Unknown video and audio encoding\n")); + free(mrl_dvb_args); + return GF_BAD_PARAM; + } + } + else { + GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[LibPlayerIN] Unknown frequency\n")); + free(mrl_dvb_args); + return GF_BAD_PARAM; + } + + // player instance 1 has not starter yet in dvb case <=> player instance 1 is not created yet + if (start_dvb == 0) { + mrl = mrl_new(read->player, MRL_RESOURCE_DVB, mrl_dvb_args); + GF_LOG(GF_LOG_DEBUG, GF_LOG_MODULE, ("[LibPlayerIN] MRL created for DVB\n")); + + // player has already started, zapping case, make sure the player is not recreated + } else { + GF_LOG(GF_LOG_DEBUG, GF_LOG_MODULE, ("[LibPlayerIN] Starting DVB PES filtering\n")); + player_dvb_pes_filter_start(read->player, mrl_dvb_args->video_enc, mrl_dvb_args->video_pid, mrl_dvb_args->audio_enc, mrl_dvb_args->audio_pid); + } + } else if (!strnicmp(url, "file://", 7) || !strstr(url, "://")) { mrl_resource_local_args_t *mrl_args; @@ -195,17 +295,19 @@ GF_Err LIBPLAYER_ConnectService(GF_InputService *plug, GF_ClientService *serv, c mrl_args->location = strdup(url); } mrl = mrl_new (read->player, MRL_RESOURCE_FILE, mrl_args); - } - else { + } - if (!mrl) { - GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[LibPlayerIN] Failed to create MRL for url %s\n", url)); - gf_term_on_connect(serv, NULL, GF_URL_ERROR); - return GF_OK; + // only for DVB case to make sure player is only set once + if (start_dvb == 0) { + if (!mrl) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[LibPlayerIN] Failed to create MRL for url %s\n", url)); + gf_term_on_connect(serv, NULL, GF_URL_ERROR); + return GF_OK; + } + + player_mrl_set(read->player, mrl); } - - player_mrl_set(read->player, mrl); #endif read->state = 0; @@ -242,20 +344,29 @@ GF_Err LIBPLAYER_CloseService(GF_InputService *plug) LibPlayerIn *read = (LibPlayerIn *) plug->priv; #ifndef TEST_LIBPLAYER + // only disconnect if + if (read->player_type == PLAYER_FILE) { player_playback_stop(read->player); printf("[LibPlayerIN]player_playback_stop for instance %d\n", read->player_id); player_uninit(read->player); printf("[LibPlayerIN]player_uninit for instance %d\n", read->player_id); read->player = NULL; libplayer_id--; - - -#endif + read->state = 0; - gf_term_on_disconnect(read->service, NULL, GF_OK); - GF_LOG(GF_LOG_DEBUG, GF_LOG_MODULE, ("[LibPlayerIn] Closing libplayer instance %d\n", read->player_id)); + gf_term_on_disconnect(read->service, NULL, GF_OK); + GF_LOG(GF_LOG_DEBUG, GF_LOG_MODULE, ("[LibPlayerIn] Closing libplayer instance %d\n", read->player_id)); + + + + // channel zapping dvb case, don't disconnect service + } else { + GF_LOG(GF_LOG_DEBUG, GF_LOG_MODULE, ("[LibPlayerIn] Don't close service libplayer instance %d, use this instance for channel zapping\n", read->player_id)); + } + return GF_OK; +#endif } /*Dummy input just send a file name, no multitrack to handle so we don't need to check sub_url nor expected type*/ @@ -290,19 +401,34 @@ GF_Err LIBPLAYER_ServiceCommand(GF_InputService *plug, GF_NetworkCommand *com) case GF_NET_CHAN_PLAY: if (read->state==0) { #ifndef TEST_LIBPLAYER - player_playback_start(read->player); + if ((read->player_id == 0) && (read->player_type == PLAYER_DVB) && (start_dvb == 1)) { + GF_LOG(GF_LOG_DEBUG, GF_LOG_MODULE, ("[LibPlayerIN] Instance %d has already started, zapping mode\n", read->player_id)); + + } else { + player_playback_start(read->player); + if ((read->player_id == 0) && (read->player_type == PLAYER_DVB)) { + start_dvb = 1; + } + + read->state = 1; + GF_LOG(GF_LOG_DEBUG, GF_LOG_MODULE, ("[LibPlayerIN] Starting playback for instance %d\n", read->player_id)); + } #endif - read->state = 1; - GF_LOG(GF_LOG_DEBUG, GF_LOG_MODULE, ("[LibPlayerIN] Starting playback for instance %d\n", read->player_id)); } + return GF_OK; case GF_NET_CHAN_STOP: if (read->state==1) { #ifndef TEST_LIBPLAYER - player_playback_pause(read->player); + // channel zapping, don't stop channel + if ((read->player_id == 0) && (read->player_type == PLAYER_DVB) && (start_dvb = 1)) { + GF_LOG(GF_LOG_DEBUG, GF_LOG_MODULE, ("[LibPlayerIN] Instance %d is in zapping mode, don't stop channel\n", read->player_id)); + } else { + player_playback_stop(read->player); + read->state = 0; + GF_LOG(GF_LOG_DEBUG, GF_LOG_MODULE, ("[LibPlayerIN] Stopping playback for instance %d\n", read->player_id)); + } #endif - read->state = 0; - GF_LOG(GF_LOG_DEBUG, GF_LOG_MODULE, ("[LibPlayerIN] Stopping playback for instance %d\n", read->player_id)); } return GF_OK; case GF_NET_CHAN_CONFIG: return GF_OK; @@ -341,6 +467,7 @@ GF_Err LIBPLAYER_DisconnectChannel(GF_InputService *plug, LPNETCHANNEL channel) Bool LIBPLAYER_CanHandleURLInService(GF_InputService *plug, const char *url) { return 0; + //return 1; } static GF_Err LIBPLAYER_AttachStream(GF_BaseDecoder *dec, GF_ESD *esd) @@ -408,11 +535,24 @@ static GF_Err LIBPLAYER_Control(GF_PrivateMediaDecoder *dec, Bool mute, GF_Windo if (!read) return GF_OK; GF_LOG(GF_LOG_DEBUG, GF_LOG_MODULE, ("[LibPlayerDEC] Control instance %d\n",read->player_id)); - + //! unfortunately, saving data would not be a good solution for Mosaic Mode in ESG Application since the position changes everytime user + //! uses the navigation button + //~ if (read->player_id == 1 && save_data_instance1 == 1) { + //~ printf("in here for save data instance\n"); + //~ player_video_io_windows_set(read->player, &in_instance1, &out_instance1); + //~ + //~ return GF_OK; + //~ + //~ } else { width = mrl_get_property(read->player, NULL, MRL_PROPERTY_VIDEO_WIDTH); height = mrl_get_property(read->player, NULL, MRL_PROPERTY_VIDEO_HEIGHT); + //~ } + + if((width != read->width) || (height != read->height)) { + printf("in here for video size changed\t"); + printf("width %d read->width %d height %d read->height %d\n", width, read->width, height, read->height); GF_LOG(GF_LOG_DEBUG, GF_LOG_MODULE, ("[LibPlayerDEC] video size changed to width %d - height %d\n", width, height)); if (width && height) { read->width = width; @@ -421,15 +561,26 @@ static GF_Err LIBPLAYER_Control(GF_PrivateMediaDecoder *dec, Bool mute, GF_Windo return GF_BUFFER_TOO_SMALL; } - in.x = src->x; - in.y = src->y; - in.w = src->w; - in.h = src->h; - out.x = dst->x; - out.y = dst->y; - out.w = dst->w; - out.h = dst->h; - player_video_io_windows_set(read->player, &in, &out); + in.x = src->x; + in.y = src->y; + in.w = src->w; + in.h = src->h; + out.x = dst->x; + out.y = dst->y; + out.w = dst->w; + out.h = dst->h; + player_video_io_windows_set(read->player, &in, &out); + + //! unfortunately, saving data would not be a good solution for Mosaic Mode in ESG Application since the position changes everytime user + //! uses the navigation button + //~ if (read->player_id == 1) { + //~ in_instance1 = in; + //~ // //~ out_instance1.w = out.w; + //~ // //~ out_instance1.h = out.h; + //~ out_instance1 = out; + //~ save_data_instance1 = 1; + //~ } + //~ #endif diff --git a/modules/mp3_in/mp3_in.c b/modules/mp3_in/mp3_in.c index 2565a43..6ae3255 100644 --- a/modules/mp3_in/mp3_in.c +++ b/modules/mp3_in/mp3_in.c @@ -354,6 +354,9 @@ void mp3_download_file(GF_InputService *plug, char *url) if (!read->dnload) { read->needs_connection = 0; gf_term_on_connect(read->service, NULL, GF_NOT_SUPPORTED); + } else { + /*start our download (threaded)*/ + gf_dm_sess_process(read->dnload); } /*service confirm is done once fetched*/ } diff --git a/modules/mpd_in/mpd_in.c b/modules/mpd_in/mpd_in.c index 6e03e95..c7734f1 100644 --- a/modules/mpd_in/mpd_in.c +++ b/modules/mpd_in/mpd_in.c @@ -32,11 +32,13 @@ #include #include +/*set to 1 if you want MPD to use SegmentTemplate if possible instead of SegmentList*/ +#define M3U8_TO_MPD_USE_TEMPLATE 0 /*! * All the possible Mime-types for MPD files */ -static const char * MPD_MIME_TYPES[] = { "video/vnd.3gpp.mpd", "audio/vnd.3gpp.mpd", NULL }; +static const char * MPD_MIME_TYPES[] = { "application/dash+xml", "video/vnd.3gpp.mpd", "audio/vnd.3gpp.mpd", NULL }; /*! * All the possible Mime-types for M3U8 files @@ -55,21 +57,29 @@ typedef struct { char *cache; char *url; + u64 start_range, end_range; } segment_cache_entry; +/*this structure Group is the implementation of the adaptationSet element of the MPD.*/ typedef struct __mpd_group { - GF_List *representations; - u32 group_id; + /*pointer to adaptation set*/ + GF_MPD_AdaptationSet *adaptation_set; + /*pointer to active period*/ + GF_MPD_Period *period; + + /*active representation index in adaptation_set->representations*/ + u32 active_rep_index; + Bool selected; Bool done; Bool force_switch_bandwidth; u32 nb_bw_check; - /*pointer toactive period*/ - GF_MPD_Period *period; - /*active representation index in period->representations*/ - u32 active_rep_index; u32 active_bitrate, max_bitrate, min_bitrate; + + u32 nb_segments_in_rep; + Double segment_duration; + /*local file playback, do not delete them*/ Bool local_files; /*next segment to download for this group*/ @@ -77,20 +87,28 @@ typedef struct __mpd_group /*next file (cached) to delete at next GF_NET_SERVICE_QUERY_NEXT for this group*/ char * urlToDeleteNext; - volatile u32 max_cached, nb_cached; + volatile u32 max_cached_segments, nb_cached_segments; segment_cache_entry *cached; GF_DownloadSession *segment_dnload; const char *segment_local_url; + /*usually 0-0 (no range) but can be non-zero when playing local MPD/DASH sessions*/ + u64 local_url_start_range, local_url_end_range; u32 nb_segments_done; Bool segment_must_be_streamed; /* Service really managing the segments */ - GF_InputService *service; + GF_InputService *input_module; char *service_mime; - Bool service_connected; + Bool service_connected, service_descriptor_fetched; + + struct __mpd_module *mpd_in; + + u32 force_representation_idx_plus_one; + + Bool force_segment_switch; } GF_MPD_Group; typedef struct __mpd_module { @@ -102,7 +120,7 @@ typedef struct __mpd_module { u32 option_max_cached; u32 auto_switch_count; - Bool keep_files; + Bool keep_files, disable_switching; /* MPD downloader*/ GF_DownloadSession *mpd_dnload; @@ -117,6 +135,9 @@ typedef struct __mpd_module { /* active period in MPD (only one currently supported) */ u32 active_period_index; + u32 request_period_switch; + + u64 start_time_in_active_period; /*list of groups in the active period*/ GF_List *groups; @@ -134,9 +155,23 @@ typedef struct __mpd_module { /* TODO - handle playback status for SPEED/SEEK through SIDX */ - Double playback_speed, playback_start_range, playback_end_range; + Double playback_speed, playback_start_range, previous_start_range; + Bool in_seek; } GF_MPD_In; +void MPD_ResetGroups(GF_MPD_In *mpdin); +GF_Err MPD_SetupPeriod(GF_MPD_In *mpdin); +void MPD_SeekGroupsDownloads(GF_MPD_In *mpdin); + + +static const char *MPD_GetMimeType(GF_MPD_SubRepresentation *subrep, GF_MPD_Representation *rep, GF_MPD_AdaptationSet *set) +{ + if (subrep && subrep->mime_type) return subrep->mime_type; + if (rep && rep->mime_type) return rep->mime_type; + if (set && set->mime_type) return set->mime_type; + return NULL; +} + static Bool MPD_CheckRootType(const char *local_url) { @@ -171,40 +206,41 @@ void MPD_NetIO_Segment(void *cbk, GF_NETIO_Parameter *param) } if ((param->msg_type == GF_NETIO_PARSE_HEADER) && !strcmp(param->name, "Content-Type")) { - if (!group->service_mime) group->service_mime = gf_strdup(param->value); - else if (strcmp(group->service_mime, param->value)) { - GF_MPD_Representation *rep = gf_list_get(group->period->representations, group->active_rep_index); - if (!rep->mime) rep->mime = gf_strdup(param->value); + if (!group->service_mime) { + group->service_mime = gf_strdup(param->value); + } else if (strcmp(group->service_mime, param->value)) { + GF_MPD_Representation *rep = gf_list_get(group->adaptation_set->representations, group->active_rep_index); + if (! MPD_GetMimeType(NULL, rep, group->adaptation_set) ) rep->mime_type = gf_strdup(param->value); rep->disabled = 1; group->force_switch_bandwidth = 1; - gf_dm_sess_abort(group->segment_dnload); + gf_dm_sess_abort(group->segment_dnload); return; } } e = param->error; - if (param->msg_type == GF_NETIO_PARSE_REPLY) { - if (! gf_dm_sess_can_be_cached_on_disk(group->segment_dnload)) { - GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, - ("[MPD_IN] Segment %s cannot be cached on disk, will use direct streaming\n", gf_dm_sess_get_resource_name(group->segment_dnload))); - group->segment_must_be_streamed = 1; - gf_dm_sess_abort(group->segment_dnload); - } else { - group->segment_must_be_streamed = 0; - } - } + if (param->msg_type == GF_NETIO_PARSE_REPLY) { + if (! gf_dm_sess_can_be_cached_on_disk(group->segment_dnload)) { + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, + ("[MPD_IN] Segment %s cannot be cached on disk, will use direct streaming\n", gf_dm_sess_get_resource_name(group->segment_dnload))); + group->segment_must_be_streamed = 1; + gf_dm_sess_abort(group->segment_dnload); + } else { + group->segment_must_be_streamed = 0; + } + } else if ((param->msg_type == GF_NETIO_DATA_EXCHANGE) || (param->msg_type == GF_NETIO_DATA_TRANSFERED)) { - if (gf_dm_sess_get_stats(group->segment_dnload, NULL, NULL, NULL, NULL, &download_rate, NULL) == GF_OK) { + if (!group->mpd_in->disable_switching && (gf_dm_sess_get_stats(group->segment_dnload, NULL, NULL, NULL, NULL, &download_rate, NULL) == GF_OK)) { if (download_rate) { download_rate *= 8; if (download_ratemin_bitrate) group->min_bitrate = download_rate; if (download_rate>group->max_bitrate) group->max_bitrate = download_rate; if (download_rate && (download_rate < group->active_bitrate)) { - fprintf(stdout, "Downloading from group %d at rate %d kbps but group bitrate is %d kbps\n", group->group_id, download_rate/1024, group->active_bitrate/1024); + fprintf(stdout, "Downloading from set %s at rate %d kbps but group bitrate is %d kbps\n", group->adaptation_set->id, download_rate/1024, group->active_bitrate/1024); group->nb_bw_check ++; if (group->nb_bw_check>2) { - fprintf(stdout, "Downloading from group %d at rate %d kbps but group bitrate is %d kbps - switching\n", group->group_id, download_rate/1024, group->active_bitrate/1024); + fprintf(stdout, "Downloading from group %s at rate %d kbps but group bitrate is %d kbps - switching\n", group->adaptation_set->id, download_rate/1024, group->active_bitrate/1024); group->force_switch_bandwidth = 1; gf_dm_sess_abort(group->segment_dnload); } @@ -248,15 +284,6 @@ static Bool MPD_isM3U8_mime(const char * mime) { return 0; } -static Bool MPD_segments_are_equals(GF_MPD_SegmentInfo * info1, GF_MPD_SegmentInfo * info2) { - assert( info1 ); - assert( info2 ); - return info1->byterange_end == info2->byterange_end && - info1->byterange_start == info2->byterange_start && - info1->use_byterange == info2->use_byterange && - (info1->url == info2->url || !strcmp(info1->url, info2->url)); -} - void MPD_NetIO(void *cbk, GF_NETIO_Parameter *param) { GF_Err e; @@ -270,19 +297,17 @@ void MPD_NetIO(void *cbk, GF_NETIO_Parameter *param) static GF_Err MPD_UpdatePlaylist(GF_MPD_In *mpdin) { GF_Err e; - u32 i, j, rep_idx, group_idx; + u32 group_idx, rep_idx, i, j; Bool seg_found = 0; GF_DOMParser *mpd_parser; GF_MPD_Period *period, *new_period; - GF_MPD_Representation *rep, *new_rep; - GF_List *segs; const char *local_url; char mime[128]; char * purl; Bool is_m3u8 = 0; - u32 oldUpdateTime = mpdin->mpd->min_update_time; + u32 oldUpdateTime = mpdin->mpd->minimum_update_period; /*reset update time - if any error occurs, we will no longer attempt to update the MPD*/ - mpdin->mpd->min_update_time = 0; + mpdin->mpd->minimum_update_period = 0; if (!mpdin->mpd_dnload) { GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[MPD_IN] Error - cannot update playlist: missing downloader\n")); @@ -314,7 +339,7 @@ static GF_Err MPD_UpdatePlaylist(GF_MPD_In *mpdin) /* Some servers, for instance http://tv.freebox.fr, serve m3u8 as text/plain */ if (MPD_isM3U8_mime(mime) || strstr(purl, ".m3u8")) { - gf_m3u8_to_mpd(mpdin->service, local_url, purl, NULL, mpdin->reload_count, mpdin->mimeTypeForM3U8Segments); + gf_m3u8_to_mpd(local_url, purl, NULL, mpdin->reload_count, mpdin->mimeTypeForM3U8Segments, mpdin->service, 0, M3U8_TO_MPD_USE_TEMPLATE); } else if (!MPD_is_MPD_mime(mime)) { GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[MPD_IN] mime '%s' should be m3u8 or mpd\n", mime)); gf_term_on_connect(mpdin->service, NULL, GF_BAD_PARAM); @@ -338,10 +363,9 @@ static GF_Err MPD_UpdatePlaylist(GF_MPD_In *mpdin) if (! memcmp( signature, mpdin->lastMPDSignature, sizeof(mpdin->lastMPDSignature))) { GF_LOG(GF_LOG_DEBUG, GF_LOG_MODULE, ("[MPD_IN] MPD file did not change\n")); mpdin->reload_count++; - mpdin->mpd->min_update_time = oldUpdateTime; + mpdin->mpd->minimum_update_period = oldUpdateTime; } else { GF_MPD *new_mpd; - GF_MPD_SegmentInfo *info1; mpdin->reload_count = 0; memccpy(mpdin->lastMPDSignature, signature, sizeof(char), sizeof(mpdin->lastMPDSignature)); @@ -372,101 +396,121 @@ static GF_Err MPD_UpdatePlaylist(GF_MPD_In *mpdin) return GF_NON_COMPLIANT_BITSTREAM; } - if (gf_list_count(period->representations) != gf_list_count(new_period->representations)) { - GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[MPD_IN] Error - cannot update playlist: missing representation\n")); + if (gf_list_count(period->adaptation_sets) != gf_list_count(new_period->adaptation_sets)) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[MPD_IN] Error - cannot update playlist: missing AdaptationSet\n")); gf_mpd_del(new_mpd); return GF_NON_COMPLIANT_BITSTREAM; } for (group_idx=0; group_idxgroups); group_idx++) { + GF_MPD_AdaptationSet *set, *new_set; GF_MPD_Group *group = gf_list_get(mpdin->groups, group_idx); if (!group->selected) continue; + set = group->adaptation_set; + new_set = gf_list_get(new_period->adaptation_sets, group_idx); - rep = gf_list_get(period->representations, group->active_rep_index); - info1 = gf_list_get(rep->segments, group->download_segment_index - 1); + if (gf_list_count(new_set->representations) != gf_list_count(group->adaptation_set->representations)) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[MPD_IN] Error - cannot update playlist: missing representation in adaptation set\n")); + gf_mpd_del(new_mpd); + return GF_NON_COMPLIANT_BITSTREAM; + } + + /*get all representations in both periods*/ + for (rep_idx = 0; rep_idx adaptation_set->representations); rep_idx++) { + GF_List *segments, *new_segments; + GF_MPD_Representation *rep = gf_list_get(group->adaptation_set->representations, rep_idx); + GF_MPD_Representation *new_rep = gf_list_get(new_set->representations, rep_idx); + + if (rep->segment_base || group->adaptation_set->segment_base || period->segment_base) { + if (!new_rep->segment_base && !new_set->segment_base && !new_period->segment_base) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[MPD_IN] Error - cannot update playlist: representation does not use segment base as previous version\n")); + gf_mpd_del(new_mpd); + return GF_NON_COMPLIANT_BITSTREAM; + } + /*what else should we check ??*/ - for (rep_idx = 0; rep_idxrepresentations); rep_idx++) { - rep = gf_list_get(period->representations, rep_idx); - new_rep = gf_list_get(new_period->representations, rep_idx); - if (!new_rep) { - GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[MPD_IN] Error - cannot update playlist: missing representation in period\n")); - gf_mpd_del(new_mpd); - return GF_NON_COMPLIANT_BITSTREAM; + /*OK, this rep is fine*/ } - /*merge init segment*/ - if (new_rep->init_url) { - seg_found = 0; - - if (!strcmp(new_rep->init_url, rep->init_url)) { - seg_found = 1; - } else { - for (j=0; jsegments); j++) { - GF_MPD_SegmentInfo *seg = gf_list_get(rep->segments, j); - if (!strcmp(new_rep->init_url, seg->url)) { - seg_found = 1; - break; - } - } - } - /*remove from new list and push to old one*/ - if (!seg_found) { - GF_MPD_SegmentInfo *new_seg; - GF_SAFEALLOC(new_seg, GF_MPD_SegmentInfo); - new_seg->url = gf_strdup(new_rep->init_url); - new_seg->use_byterange = new_rep->init_use_range; - new_seg->byterange_start = new_rep->init_byterange_start; - new_seg->byterange_end = new_rep->init_byterange_end; - gf_list_add(rep->segments, new_seg); - GF_LOG(GF_LOG_DEBUG, GF_LOG_MODULE, ("[MPD_IN] Representation #%d: Adding new segment from initialization segment %s\n", rep_idx+1, new_seg->url)); + + else if (rep->segment_template || group->adaptation_set->segment_template || period->segment_template) { + if (!new_rep->segment_template && !new_set->segment_template && !new_period->segment_template) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[MPD_IN] Error - cannot update playlist: representation does not use segment template as previous version\n")); + gf_mpd_del(new_mpd); + return GF_NON_COMPLIANT_BITSTREAM; } + /*what else should we check ??*/ + + /*OK, this rep is fine*/ } - /*merge segment list*/ - for (i=0; isegments); i++) { - GF_MPD_SegmentInfo *new_seg = gf_list_get(new_rep->segments, i); - assert( new_seg ); - assert( new_seg->url); - seg_found = 0; - for (j=0; jsegments); j++) { - GF_MPD_SegmentInfo *seg = gf_list_get(rep->segments, j); - assert( seg ); - assert( seg->url); - if (!strcmp(new_seg->url, seg->url)) { - seg_found = 1; - break; + else { + /*we're using segment list*/ + assert(rep->segment_list || group->adaptation_set->segment_list || period->segment_list); + + if (!new_rep->segment_list && !new_set->segment_list && !new_period->segment_list) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[MPD_IN] Error - cannot update playlist: representation does not use segment list as previous version\n")); + gf_mpd_del(new_mpd); + return GF_NON_COMPLIANT_BITSTREAM; + } + /*what else should we check ??*/ + + /*get the segment list*/ + segments = new_segments = NULL; + if (period->segment_list && period->segment_list->segment_URLs) segments = period->segment_list->segment_URLs; + if (set->segment_list && set->segment_list->segment_URLs) segments = set->segment_list->segment_URLs; + if (rep->segment_list && rep->segment_list->segment_URLs) segments = rep->segment_list->segment_URLs; + + if (new_period->segment_list && new_period->segment_list->segment_URLs) new_segments = new_period->segment_list->segment_URLs; + if (new_set->segment_list && new_set->segment_list->segment_URLs) new_segments = new_set->segment_list->segment_URLs; + if (new_rep->segment_list && new_rep->segment_list->segment_URLs) new_segments = new_rep->segment_list->segment_URLs; + + + for (i=0; imedia && new_seg->media && !strcmp(seg->media, new_seg->media)) { + found=1; + break; + } + if (seg->media_range && new_seg->media_range && (seg->media_range->start_range==new_seg->media_range->start_range) && (seg->media_range->end_range==new_seg->media_range->end_range) ) { + found=1; + break; + } + } + /*this is a new segment, merge it: we remove from new list and push to old one, before doing a final swap + this ensures that indexing in the segment_list is still correct after merging*/ + if (!found) { + gf_list_rem(new_segments, i); + i--; + gf_list_add(segments, new_seg); + GF_LOG(GF_LOG_DEBUG, GF_LOG_MODULE, ("[MPD_IN] Representation #%d: Adding new segment %s\n", rep_idx+1, new_seg->media)); } } - /*remove from new list and push to old one*/ - if (!seg_found) { - gf_list_rem(new_rep->segments, i); - gf_list_add(rep->segments, new_seg); - GF_LOG(GF_LOG_DEBUG, GF_LOG_MODULE, ("[MPD_IN] Representation #%d: Adding new segment %s\n", rep_idx+1, new_seg->url)); - i--; + + /*what else should we check ?*/ + + /*swap segment list content*/ + gf_list_swap(new_segments, segments); + + /*current representation is the active one in the group - update the number of segments*/ + if (group->active_rep_index==rep_idx) { + group->nb_segments_in_rep = gf_list_count(new_segments); } } - /*swap lists*/ - { - GF_MPD_SegmentInfo *info2; - segs = new_rep->segments; - new_rep->segments = rep->segments; - new_rep->disabled = rep->disabled; - if (!new_rep->mime) { - new_rep->mime = rep->mime; - rep->mime = NULL; - } - rep->segments = segs; - info2 = gf_list_get(rep->segments, group->download_segment_index); + + /*copy over a few things from former rep*/ + new_rep->disabled = rep->disabled; + if (!new_rep->mime_type) { + new_rep->mime_type = rep->mime_type; + rep->mime_type = NULL; } } /*update group/period to new period*/ + j = gf_list_find(group->period->adaptation_sets, group->adaptation_set); + group->adaptation_set = gf_list_get(new_period->adaptation_sets, j); group->period = new_period; - - /*and rebuild representations for this group*/ - gf_list_reset(group->representations); - for (i=0; irepresentations); i++) { - GF_MPD_Representation *rep = gf_list_get(new_period->representations, i); - if (rep->groupID==group->group_id) gf_list_add(group->representations, rep); - } } /*swap representations - we don't need to update download_segment_index as it still points to the right entry in the merged list*/ if (mpdin->mpd) @@ -484,17 +528,43 @@ static GF_Err MPD_ClientQuery(GF_InputService *ifce, GF_NetworkCommand *param) { u32 i; GF_MPD_Group *group = NULL; + GF_MPD_In *mpdin = (GF_MPD_In *) ifce->proxy_udta; if (!param || !ifce || !ifce->proxy_udta) return GF_BAD_PARAM; + if (param->command_type==GF_NET_SERVICE_QUERY_INIT_RANGE) { + param->url_query.next_url = NULL; + param->url_query.start_range = 0; + param->url_query.end_range = 0; + + mpdin->in_seek = 0; + + for (i=0; igroups); i++) { + group = gf_list_get(mpdin->groups, i); + if (group->selected && (group->input_module == ifce)) break; + group = NULL; + } + + if (!group) return GF_SERVICE_ERROR; + param->url_query.start_range = group->local_url_start_range; + param->url_query.end_range = group->local_url_end_range; + + return GF_OK; + } if (param->command_type==GF_NET_SERVICE_QUERY_NEXT) { u32 timer = gf_sys_clock(); - GF_MPD_In *mpdin = (GF_MPD_In *) ifce->proxy_udta; GF_LOG(GF_LOG_DEBUG, GF_LOG_MODULE, ("[MPD_IN] Received Service Query Next request from terminal\n")); gf_mx_p(mpdin->dl_mutex); + + param->url_query.discontinuity_type = 0; + if (mpdin->in_seek) { + mpdin->in_seek = 0; + param->url_query.discontinuity_type = 2; + } + for (i=0; igroups); i++) { group = gf_list_get(mpdin->groups, i); - if (group->selected && (group->service == ifce)) break; + if (group->selected && (group->input_module == ifce)) break; group = NULL; } @@ -502,17 +572,31 @@ static GF_Err MPD_ClientQuery(GF_InputService *ifce, GF_NetworkCommand *param) gf_mx_v(mpdin->dl_mutex); return GF_SERVICE_ERROR; } + group->force_segment_switch = 0; /* Wait until no file is scheduled to be downloaded */ - while (mpdin->mpd_is_running && group->nb_cached<2) { + while (mpdin->mpd_is_running && group->nb_cached_segments<2) { gf_mx_v(mpdin->dl_mutex); if (group->done) { + if (!mpdin->request_period_switch && (mpdin->active_period_indexmpd->periods))) { + GF_NetworkCommand com; + memset(&com, 0, sizeof(GF_NetworkCommand)); + com.command_type = GF_NET_BUFFER_QUERY; + while (mpdin->request_period_switch != 1) { + gf_term_on_command(mpdin->service, &com, GF_OK); + if (!com.buffer.occupancy) { + mpdin->request_period_switch = 1; + break; + } + gf_sleep(20); + } + } return GF_EOS; } gf_sleep(16); gf_mx_p(mpdin->dl_mutex); } - if (group->nb_cached<2) { + if (group->nb_cached_segments<2) { GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("[MPD_IN] No more file in cache, EOS\n")); gf_mx_v(mpdin->dl_mutex); return GF_EOS; @@ -522,7 +606,7 @@ static GF_Err MPD_ClientQuery(GF_InputService *ifce, GF_NetworkCommand *param) if (group->cached[0].cache) { if (group->urlToDeleteNext) { if (!group->local_files && !mpdin->keep_files) - gf_dm_delete_cached_file_entry_session(mpdin->mpd_dnload, group->urlToDeleteNext); + gf_dm_delete_cached_file_entry_session(group->segment_dnload, group->urlToDeleteNext); gf_free( group->urlToDeleteNext); group->urlToDeleteNext = NULL; @@ -535,17 +619,23 @@ static GF_Err MPD_ClientQuery(GF_InputService *ifce, GF_NetworkCommand *param) group->cached[0].url = NULL; group->cached[0].cache = NULL; } - memmove(&group->cached[0], &group->cached[1], sizeof(segment_cache_entry)*(group->nb_cached-1)); - memset(&(group->cached[group->nb_cached-1]), 0, sizeof(segment_cache_entry)); - group->nb_cached--; + memmove(&group->cached[0], &group->cached[1], sizeof(segment_cache_entry)*(group->nb_cached_segments-1)); + memset(&(group->cached[group->nb_cached_segments-1]), 0, sizeof(segment_cache_entry)); + group->nb_cached_segments--; param->url_query.next_url = group->cached[0].cache; + param->url_query.start_range = group->cached[0].start_range; + param->url_query.end_range = group->cached[0].end_range; gf_mx_v(mpdin->dl_mutex); { u32 timer2 = gf_sys_clock() - timer ; if (timer2 > 1000) { - GF_LOG(GF_LOG_WARNING, GF_LOG_MODULE, ("[MPD_IN] We were stuck waiting for download to end during too much time : %u ms !\n", timer2)); + GF_LOG(GF_LOG_DEBUG, GF_LOG_MODULE, ("[MPD_IN] We were stuck waiting for download to end during too much time : %u ms !\n", timer2)); } - GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("[MPD_IN] Switching segment playback to \n\tURL: %s in %u ms\n\tCache: %s\n\tElements in cache: %u/%u\n", group->cached[0].url, timer2, group->cached[0].cache, group->nb_cached, group->max_cached)); + if (group->cached[0].end_range) { + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("[MPD_IN] Switching segment playback to \n\tURL: %s in %u ms\n\tMedia Range: "LLD"-"LLD"\n\tElements in cache: %u/%u\n", group->cached[0].url, timer2, group->cached[0].start_range, group->cached[0].end_range, group->nb_cached_segments, group->max_cached_segments)); + } else { + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("[MPD_IN] Switching segment playback to \n\tURL: %s in %u ms\n\tCache: %s\n\tElements in cache: %u/%u\n", group->cached[0].url, timer2, group->cached[0].cache, group->nb_cached_segments, group->max_cached_segments)); + } } } else { GF_LOG(GF_LOG_DEBUG, GF_LOG_MODULE, ("[MPD_IN] Received Client Query request (%d) from terminal\n", param->command_type)); @@ -563,10 +653,10 @@ static GF_Err MPD_LoadMediaService(GF_MPD_In *mpdin, GF_MPD_Group *group, const if (sPlug) sPlug = strrchr(sPlug, '"'); if (sPlug) { sPlug += 2; - group->service = (GF_InputService *) gf_modules_load_interface_by_name(mpdin->service->term->user->modules, sPlug, GF_NET_CLIENT_INTERFACE); - if (group->service) { - group->service->proxy_udta = mpdin; - group->service->query_proxy = MPD_ClientQuery; + group->input_module = (GF_InputService *) gf_modules_load_interface_by_name(mpdin->service->term->user->modules, sPlug, GF_NET_CLIENT_INTERFACE); + if (group->input_module) { + group->input_module->proxy_udta = mpdin; + group->input_module->query_proxy = MPD_ClientQuery; return GF_OK; } } @@ -577,9 +667,9 @@ static GF_Err MPD_LoadMediaService(GF_MPD_In *mpdin, GF_MPD_Group *group, const if (!ifce) continue; if (ifce->CanHandleURL && ifce->CanHandleURL(ifce, init_segment_name)) { - group->service = ifce; - group->service->proxy_udta = mpdin; - group->service->query_proxy = MPD_ClientQuery; + group->input_module = ifce; + group->input_module->proxy_udta = mpdin; + group->input_module->query_proxy = MPD_ClientQuery; return GF_OK; } gf_modules_close_interface((GF_BaseInterface *) ifce); @@ -599,9 +689,11 @@ static GF_Err MPD_LoadMediaService(GF_MPD_In *mpdin, GF_MPD_Group *group, const */ GF_Err MPD_downloadWithRetry( GF_ClientService * service, GF_DownloadSession **sess, const char *url, gf_dm_user_io user_io, void *usr_cbk, u64 start_range, u64 end_range, Bool persistent) { + Bool had_sess = 0; GF_Err e; GF_LOG(GF_LOG_DEBUG, GF_LOG_MODULE, ("[MPD_IN] Downloading %s...\n", url)); + if (! *sess) { u32 flags = GF_NETIO_SESSION_NOT_THREADED; if (persistent) flags |= GF_NETIO_SESSION_PERSISTENT; @@ -612,6 +704,7 @@ GF_Err MPD_downloadWithRetry( GF_ClientService * service, GF_DownloadSession **s return GF_OUT_OF_MEM; } } else { + had_sess = 1; e = gf_dm_sess_setup_from_url(*sess, url); if (e) { GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[MPD_IN] Cannot resetup session for url %s: %s\n", url, gf_error_to_string(e) )); @@ -622,6 +715,13 @@ GF_Err MPD_downloadWithRetry( GF_ClientService * service, GF_DownloadSession **s if (end_range) { e = gf_dm_sess_set_range(*sess, start_range, end_range); if (e) { + if (had_sess) { + gf_term_download_del(*sess); + *sess = NULL; + return MPD_downloadWithRetry(service, sess, url, user_io, usr_cbk, start_range, end_range, persistent); + } + + GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[MPD_IN] Cannot setup byte-range download for %s: %s\n", url, gf_error_to_string(e) )); return e; } @@ -655,6 +755,106 @@ GF_Err MPD_downloadWithRetry( GF_ClientService * service, GF_DownloadSession **s } } +static void MPD_SetGroupRepresentation(GF_MPD_Group *group, GF_MPD_Representation *rep) +{ + u64 duration = 0; + u64 mediaDuration = 0; +#ifndef GPAC_DISABLE_LOG + u32 width=0, height=0, samplerate=0; + GF_MPD_Fractional *framerate=NULL; +#endif + u32 timescale = 1; + GF_MPD_AdaptationSet *set; + GF_MPD_Period *period; + u32 i = gf_list_find(group->adaptation_set->representations, rep); + assert((s32) i >= 0); + + group->active_rep_index = i; + group->active_bitrate = rep->bandwidth; + group->nb_segments_in_rep = 1; + + set = group->adaptation_set; + period = group->period; + +#ifndef GPAC_DISABLE_LOG + +#define GET_REP_ATTR(_a) _a = rep->_a; if (!_a) _a = set->_a; + + GET_REP_ATTR(width); + GET_REP_ATTR(height); + GET_REP_ATTR(samplerate); + GET_REP_ATTR(framerate); + + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("[MPDIn] Switched to representation bandwidth %d kbps\n", rep->bandwidth/1024)); + if (group->max_bitrate) GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("\tmax download bandwidth: %d kbps\n", group->max_bitrate/1024)); + if (width&&height) { + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("\tWidth %d Height %d", width, height)); + if (framerate) GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("framerate %d/%d", framerate->num, framerate->den)); + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("\n")); + } else if (samplerate) { + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("\tsamplerate %d\n", samplerate)); + } +#endif + + /*single segment*/ + if (rep->segment_base || set->segment_base || period->segment_base) { + return; + } + if (rep->segment_list || set->segment_list || period->segment_list) { + GF_List *segments = NULL; + if (period->segment_list) { + if (period->segment_list->duration) duration = period->segment_list->duration; + if (period->segment_list->timescale) timescale = period->segment_list->timescale; + if (period->segment_list->segment_URLs) segments = period->segment_list->segment_URLs; + } + if (set->segment_list) { + if (set->segment_list->duration) duration = set->segment_list->duration; + if (set->segment_list->timescale) timescale = set->segment_list->timescale; + if (set->segment_list->segment_URLs) segments = set->segment_list->segment_URLs; + } + if (rep->segment_list) { + if (rep->segment_list->duration) duration = rep->segment_list->duration; + if (rep->segment_list->timescale) timescale = rep->segment_list->timescale; + if (rep->segment_list->segment_URLs) segments = rep->segment_list->segment_URLs; + } + if (segments) + group->nb_segments_in_rep = gf_list_count(segments); + + if (!timescale) timescale=1; + group->segment_duration = (Double) duration; + group->segment_duration /= timescale; + return; + } + + if (period->segment_template) { + if (period->segment_template->duration) duration = period->segment_template->duration; + if (period->segment_template->timescale) timescale = period->segment_template->timescale; + } + if (set->segment_template) { + if (set->segment_template->duration) duration = set->segment_template->duration; + if (set->segment_template->timescale) timescale = set->segment_template->timescale; + } + if (rep->segment_template) { + if (rep->segment_template->duration) duration = rep->segment_template->duration; + if (rep->segment_template->timescale) timescale = rep->segment_template->timescale; + } + if (!timescale) timescale=1; + group->segment_duration = (Double) duration; + group->segment_duration /= timescale; + mediaDuration = period->duration; + if (!mediaDuration) mediaDuration = group->mpd_in->mpd->media_presentation_duration; + if (mediaDuration && duration) { + Double nb_seg = (Double) mediaDuration; + /*duration is given in ms*/ + nb_seg /= 1000; + nb_seg *= timescale; + nb_seg /= duration; + group->nb_segments_in_rep = (u32) ceil(nb_seg); + } else { + group->nb_segments_in_rep = 0; + } +} + static void MPD_SwitchGroupRepresentation(GF_MPD_In *mpd, GF_MPD_Group *group) { u32 i, bandwidth, min_bandwidth; @@ -666,24 +866,32 @@ static void MPD_SwitchGroupRepresentation(GF_MPD_In *mpd, GF_MPD_Group *group) GF_LOG(GF_LOG_DEBUG, GF_LOG_MODULE, ("[MPDIn] Checking representations between %d and %d kbps\n", group->min_bitrate/1024, group->max_bitrate/1024)); - for (i=0; irepresentations); i++) { - GF_MPD_Representation *rep = gf_list_get(group->representations, i); - if (rep->disabled) continue; - if ((rep->bandwidth > bandwidth) && (rep->bandwidth < group->max_bitrate )) { - rep_sel = rep; - bandwidth = rep->bandwidth; - } - if (rep->bandwidth < min_bandwidth) { - min_rep_sel = rep; - min_bandwidth = rep->bandwidth; + if (group->force_representation_idx_plus_one) { + rep_sel = gf_list_get(group->adaptation_set->representations, group->force_representation_idx_plus_one - 1); + group->force_representation_idx_plus_one = 0; + } + + if (!rep_sel) { + for (i=0; iadaptation_set->representations); i++) { + GF_MPD_Representation *rep = gf_list_get(group->adaptation_set->representations, i); + if (rep->disabled) continue; + if ((rep->bandwidth > bandwidth) && (rep->bandwidth < group->max_bitrate )) { + rep_sel = rep; + bandwidth = rep->bandwidth; + } + if (rep->bandwidth < min_bandwidth) { + min_rep_sel = rep; + min_bandwidth = rep->bandwidth; + } } } + if (!rep_sel) { rep_sel = min_rep_sel; min_bandwidth_selected = 1; } assert(rep_sel); - i = gf_list_find(group->period->representations, rep_sel); + i = gf_list_find(group->adaptation_set->representations, rep_sel); assert((s32) i >= 0); @@ -693,75 +901,394 @@ static void MPD_SwitchGroupRepresentation(GF_MPD_In *mpd, GF_MPD_Group *group) if (i != group->active_rep_index) { if (min_bandwidth_selected) { - GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[MPDIn] No representation found with bandwidth below %d kbps - using representation @ %d kbps\n", group->max_bitrate/1024, rep_sel->bandwidth/1024)); - } else { - GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[MPDIn] Switching to representation bandwidth %d kbps for download bandwidth %d kbps\n", rep_sel->bandwidth/1024, group->max_bitrate/1024)); + GF_LOG(GF_LOG_WARNING, GF_LOG_MODULE, ("[MPDIn] No representation found with bandwidth below %d kbps - using representation @ %d kbps\n", group->max_bitrate/1024, rep_sel->bandwidth/1024)); + } + MPD_SetGroupRepresentation(group, rep_sel); + } +} + + +u64 MPD_ResolveDuration(GF_MPD_Representation *rep, GF_MPD_AdaptationSet *set, GF_MPD_Period *period, u32 item_index) +{ + GF_MPD_SegmentTimeline *segment_timeline; + GF_MPD_MultipleSegmentBase *mbase_rep, *mbase_set, *mbase_period; + /*single media segment - duration is not known unless indicated in period*/ + if (rep->segment_base || set->segment_base || period->segment_base) { + return period ? period->duration : 0; + } + /*we have a segment template list or template*/ + mbase_rep = rep->segment_list ? (GF_MPD_MultipleSegmentBase *) rep->segment_list : (GF_MPD_MultipleSegmentBase *) rep->segment_template; + mbase_set = set->segment_list ? (GF_MPD_MultipleSegmentBase *)set->segment_list : (GF_MPD_MultipleSegmentBase *)set->segment_template; + mbase_period = period->segment_list ? (GF_MPD_MultipleSegmentBase *)period->segment_list : (GF_MPD_MultipleSegmentBase *)period->segment_template; + + segment_timeline = NULL; + if (mbase_period) segment_timeline = mbase_period->segment_timeline; + if (mbase_set) segment_timeline = mbase_set->segment_timeline; + if (mbase_rep) segment_timeline = mbase_rep->segment_timeline; + + if (segment_timeline) { + /*not supported yet*/ + assert(0); + return 0; + } + if (mbase_rep && mbase_rep->duration) return mbase_rep->duration; + if (mbase_set && mbase_set->duration) return mbase_set->duration; + if (mbase_period && mbase_period->duration) return mbase_period->duration; + return 0; +} + +typedef enum +{ + GF_MPD_RESOLVE_URL_MEDIA, + GF_MPD_RESOLVE_URL_INIT, + GF_MPD_RESOLVE_URL_INDEX, +} GF_MPDURLResolveType; + + +GF_Err MPD_ResolveURL(GF_MPD *mpd, GF_MPD_Representation *rep, GF_MPD_AdaptationSet *set, GF_MPD_Period *period, char *mpd_url, GF_MPDURLResolveType resolve_type, u32 item_index, char **out_url, u64 *out_range_start, u64 *out_range_end) +{ + GF_MPD_BaseURL *url_child; + u32 start_number = 1; + char *url; + char *url_to_solve, *solved_template, *first_sep, *media_url; + char *init_template, *index_template; + + *out_range_start = *out_range_end = 0; + *out_url = NULL; + + /*resolve base URLs from document base (download location) to representation (media)*/ + url = gf_strdup(mpd_url); + url_child = gf_list_get(mpd->base_URLs, 0); + if (url_child) { + char *t_url = gf_url_concatenate(url, url_child->URL); + gf_free(url); + url = t_url; + } + + url_child = gf_list_get(period->base_URLs, 0); + if (url_child) { + char *t_url = gf_url_concatenate(url, url_child->URL); + gf_free(url); + url = t_url; + } + + url_child = gf_list_get(set->base_URLs, 0); + if (url_child) { + char *t_url = gf_url_concatenate(url, url_child->URL); + gf_free(url); + url = t_url; + } + + url_child = gf_list_get(rep->base_URLs, 0); + if (url_child) { + char *t_url = gf_url_concatenate(url, url_child->URL); + gf_free(url); + url = t_url; + } + + /*single URL*/ + if (rep->segment_base || set->segment_base || period->segment_base) { + GF_MPD_URL *res_url; + if (item_index>0) return GF_EOS; + switch (resolve_type) { + case GF_MPD_RESOLVE_URL_MEDIA: + if (!url) return GF_NON_COMPLIANT_BITSTREAM; + *out_url = url; + return GF_OK; + case GF_MPD_RESOLVE_URL_INIT: + case GF_MPD_RESOLVE_URL_INDEX: + res_url = NULL; + if (resolve_type == GF_MPD_RESOLVE_URL_INDEX) { + if (period->segment_base) res_url = period->segment_base->representation_index; + if (set->segment_base) res_url = set->segment_base->representation_index; + if (rep->segment_base) res_url = rep->segment_base->representation_index; + } else { + if (period->segment_base) res_url = period->segment_base->initialization_segment; + if (set->segment_base) res_url = set->segment_base->initialization_segment; + if (rep->segment_base) res_url = rep->segment_base->initialization_segment; + } + /*no initialization segment / index*/ + if (!res_url) { + gf_free(url); + return GF_OK; + } + if (res_url->sourceURL) { + *out_url = gf_url_concatenate(url, res_url->sourceURL); + gf_free(url); + } else { + *out_url = url; + } + if (res_url->byte_range) { + *out_range_start = res_url->byte_range->start_range; + *out_range_end = res_url->byte_range->end_range; + } + return GF_OK; + default: + break; + } + gf_free(url); + return GF_BAD_PARAM; + } + + /*segmentList*/ + if (rep->segment_list || set->segment_list || period->segment_list) { + GF_MPD_URL *init_url, *index_url; + GF_MPD_SegmentURL *segment; + GF_List *segments = NULL; + u32 segment_count; + + init_url = index_url = NULL; + + /*apply inheritance of attributes, lowest level having preceedence*/ + if (period->segment_list) { + if (period->segment_list->initialization_segment) init_url = period->segment_list->initialization_segment; + if (period->segment_list->representation_index) index_url = period->segment_list->representation_index; + if (period->segment_list->segment_URLs) segments = period->segment_list->segment_URLs; + if (period->segment_list->start_number != (u32) -1) start_number = period->segment_list->start_number; + } + if (set->segment_list) { + if (set->segment_list->initialization_segment) init_url = set->segment_list->initialization_segment; + if (set->segment_list->representation_index) index_url = set->segment_list->representation_index; + if (set->segment_list->segment_URLs) segments = set->segment_list->segment_URLs; + if (set->segment_list->start_number != (u32) -1) start_number = set->segment_list->start_number; + } + if (rep->segment_list) { + if (rep->segment_list->initialization_segment) init_url = rep->segment_list->initialization_segment; + if (rep->segment_list->representation_index) index_url = rep->segment_list->representation_index; + if (rep->segment_list->segment_URLs) segments = rep->segment_list->segment_URLs; + if (rep->segment_list->start_number != (u32) -1) start_number = rep->segment_list->start_number; + } + + + segment_count = gf_list_count(segments); + + switch (resolve_type) { + case GF_MPD_RESOLVE_URL_INIT: + + if (init_url) { + if (init_url->sourceURL) { + *out_url = gf_url_concatenate(url, init_url->sourceURL); + gf_free(url); + } else { + *out_url = url; + } + if (init_url->byte_range) { + *out_range_start = init_url->byte_range->start_range; + *out_range_end = init_url->byte_range->end_range; + } + } else { + gf_free(url); + } + return GF_OK; + case GF_MPD_RESOLVE_URL_MEDIA: + if (!url) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[MPD_IN] Media URL is not set in segment list\n")); + return GF_SERVICE_ERROR; + } + if (item_index >= segment_count) { + gf_free(url); + return GF_EOS; + } + *out_url = url; + segment = gf_list_get(segments, item_index); + if (segment->media) { + *out_url = gf_url_concatenate(url, segment->media); + gf_free(url); + } + if (segment->media_range) { + *out_range_start = segment->media_range->start_range; + *out_range_end = segment->media_range->end_range; + } + return GF_OK; + case GF_MPD_RESOLVE_URL_INDEX: + if (item_index >= segment_count) { + gf_free(url); + return GF_EOS; + } + *out_url = url; + segment = gf_list_get(segments, item_index); + if (segment->index) { + *out_url = gf_url_concatenate(url, segment->index); + gf_free(url); + } + if (segment->index_range) { + *out_range_start = segment->index_range->start_range; + *out_range_end = segment->index_range->end_range; + } + return GF_OK; + default: + break; + } + gf_free(url); + return GF_BAD_PARAM; + } + + /*segmentTemplate*/ + init_template = index_template = NULL; + + /*apply inheritance of attributes, lowest level having preceedence*/ + if (period->segment_template) { + if (period->segment_template->initialization) init_template = period->segment_template->initialization; + if (period->segment_template->index) index_template = period->segment_template->index; + if (period->segment_template->media) media_url = period->segment_template->media; + if (period->segment_template->start_number != (u32) -1) start_number = period->segment_template->start_number; + } + if (set->segment_template) { + if (set->segment_template->initialization) init_template = set->segment_template->initialization; + if (set->segment_template->index) index_template = set->segment_template->index; + if (set->segment_template->media) media_url = set->segment_template->media; + if (set->segment_template->start_number != (u32) -1) start_number = set->segment_template->start_number; + } + if (rep->segment_template) { + if (rep->segment_template->initialization) init_template = rep->segment_template->initialization; + if (rep->segment_template->index) index_template = rep->segment_template->index; + if (rep->segment_template->media) media_url = rep->segment_template->media; + if (rep->segment_template->start_number != (u32) -1) start_number = rep->segment_template->start_number; + } + url_to_solve = NULL; + switch (resolve_type) { + case GF_MPD_RESOLVE_URL_INIT: + url_to_solve = init_template; + break; + case GF_MPD_RESOLVE_URL_MEDIA: + url_to_solve = media_url; + break; + case GF_MPD_RESOLVE_URL_INDEX: + url_to_solve = index_template; + break; + default: + gf_free(url); + return GF_BAD_PARAM; + } + if (!url_to_solve) { + gf_free(url); + return GF_OK; + } + /*let's solve the template*/ + solved_template = gf_malloc(sizeof(char)*strlen(url_to_solve)*2); + solved_template[0] = 0; + strcpy(solved_template, url_to_solve); + first_sep = strchr(solved_template, '$'); + if (first_sep) first_sep[0] = 0; + + first_sep = strchr(url_to_solve, '$'); + while (first_sep) { + char szFormat[100]; + char *format_tag; + char *second_sep = strchr(first_sep+1, '$'); + if (!second_sep) { + gf_free(url); + gf_free(solved_template); + return GF_NON_COMPLIANT_BITSTREAM; + } + second_sep[0] = 0; + format_tag = strchr(first_sep+1, '%'); + if (format_tag) format_tag[0] = 0; + /* identifier is $$ -> replace by $*/ + if (!strlen(first_sep+1)) { + strcat(solved_template, "$"); + } + else if (!strcmp(first_sep+1, "RepresentationID")) { + strcat(solved_template, rep->id); + } + else if (!strcmp(first_sep+1, "Number")) { + if (format_tag) { + char szPrintFormat[20]; + strcpy(szPrintFormat, "%"); + strcat(szPrintFormat, format_tag+1); + strcat(szPrintFormat, "d"); + sprintf(szFormat, szPrintFormat, start_number + item_index); + } else { + sprintf(szFormat, "%d", start_number + item_index); + } + strcat(solved_template, szFormat); } - group->active_rep_index = i; - group->active_bitrate = rep_sel->bandwidth; + else if (!strcmp(first_sep+1, "Bandwidth")) { + if (format_tag) { + char szPrintFormat[20]; + strcpy(szPrintFormat, "%"); + strcat(szPrintFormat, format_tag+1); + strcat(szPrintFormat, "d"); + sprintf(szFormat, format_tag+1, rep->bandwidth); + } else { + sprintf(szFormat, "%d", rep->bandwidth); + } + strcat(solved_template, szFormat); + } + else if (!strcmp(first_sep+1, "Time")) { + assert(0); + /*uses segment timeline, not supported yet*/ + } + if (format_tag) format_tag[0] = '%'; + second_sep[0] = '$'; + /*look for next keyword - copy over remaining text if any*/ + first_sep = strchr(second_sep+1, '$'); + if (first_sep) first_sep[0] = 0; + if (strlen(second_sep+1)) + strcat(solved_template, second_sep+1); + if (first_sep) first_sep[0] = '$'; } + *out_url = gf_url_concatenate(url, solved_template); + gf_free(url); + gf_free(solved_template); + return GF_OK; } static GF_Err MPD_DownloadInitSegment(GF_MPD_In *mpdin, GF_MPD_Group *group) { GF_Err e; char *base_init_url; - char * url_to_dl; GF_MPD_Representation *rep; u64 start_range, end_range; /* This variable is 0 if there is a initURL, the index of first segment downloaded otherwise */ - u32 firstSegment = 0; + u32 nb_segment_read = 0; if (!mpdin || !group) return GF_BAD_PARAM; gf_mx_p(mpdin->dl_mutex); - assert( group->period && group->period->representations ); - rep = gf_list_get(group->period->representations, group->active_rep_index); + assert( group->adaptation_set && group->adaptation_set->representations ); + rep = gf_list_get(group->adaptation_set->representations, group->active_rep_index); if (!rep) { gf_mx_v(mpdin->dl_mutex); GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[MPD_IN] Unable to find any representation, aborting.\n")); return GF_IO_ERR; } start_range = end_range = 0; - if (!rep->init_url) { - GF_MPD_SegmentInfo * seg = gf_list_get(rep->segments, 0); - /* No init URL provided, we have to download the first segment then */ - if (!seg->url) { - mpdin->mpd_stop_request = 1; - gf_mx_v(mpdin->dl_mutex); - return GF_BAD_PARAM; - } - firstSegment = 1; - url_to_dl = seg->url; - if (seg->use_byterange) { - start_range = seg->byterange_start; - end_range = seg->byterange_end; - } - } else { - url_to_dl = rep->init_url; - if (rep->init_use_range) { - start_range = rep->init_byterange_start; - end_range = rep->init_byterange_end; - } - } - if (rep->default_base_url) { - base_init_url = gf_url_concatenate(rep->default_base_url, url_to_dl); - } else { - base_init_url = gf_strdup(url_to_dl); - } + + e = MPD_ResolveURL(mpdin->mpd, rep, group->adaptation_set, group->period, mpdin->url, GF_MPD_RESOLVE_URL_INIT, 0, &base_init_url, &start_range, &end_range); + if (e) { + gf_mx_v(mpdin->dl_mutex); + GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[MPD_IN] Unable to resolve initialization URL: %s\n", gf_error_to_string(e) )); + return e; + } + /*no error and no init segment, go for media segment*/ + if (!base_init_url) { + e = MPD_ResolveURL(mpdin->mpd, rep, group->adaptation_set, group->period, mpdin->url, GF_MPD_RESOLVE_URL_MEDIA, group->download_segment_index, &base_init_url, &start_range, &end_range); + if (e) { + gf_mx_v(mpdin->dl_mutex); + GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[MPD_IN] Unable to resolve media URL: %s\n", gf_error_to_string(e) )); + return e; + } + nb_segment_read = 1; + } if (!strstr(base_init_url, "://") || !strnicmp(base_init_url, "file://", 7)) { - assert(!group->nb_cached); + assert(!group->nb_cached_segments); group->cached[0].cache = gf_strdup(base_init_url); group->cached[0].url = gf_strdup(base_init_url); - group->nb_cached = 1; + group->nb_cached_segments = 1; /*do not erase local files*/ group->local_files = 1; - group->download_segment_index = firstSegment; + group->download_segment_index += nb_segment_read; group->segment_local_url = group->cached[0].cache; + group->local_url_start_range = start_range; + group->local_url_end_range = end_range; GF_LOG(GF_LOG_DEBUG, GF_LOG_MODULE, ("[MPD_IN] Setup initialization segment %s \n", group->segment_local_url)); - if (!group->service) e = MPD_LoadMediaService(mpdin, group, rep->mime, group->segment_local_url); + if (!group->input_module) { + const char *mime_type = MPD_GetMimeType(NULL, rep, group->adaptation_set); + e = MPD_LoadMediaService(mpdin, group, mime_type, group->segment_local_url); + } gf_mx_v(mpdin->dl_mutex); gf_free(base_init_url); return GF_OK; @@ -782,19 +1309,16 @@ static GF_Err MPD_DownloadInitSegment(GF_MPD_In *mpdin, GF_MPD_Group *group) if (e == GF_URL_ERROR && !base_init_url) { /* We have a 404 and started with segments */ /* It is possible that the first segment has been deleted while we made the first request... * so we try with the next segment on some M3U8 servers */ - GF_MPD_SegmentInfo * seg = gf_list_get(rep->segments, 1); - gf_free(base_init_url); - if (!seg || !seg->url) { + + gf_free(base_init_url); + + e = MPD_ResolveURL(mpdin->mpd, rep, group->adaptation_set, group->period, mpdin->url, GF_MPD_RESOLVE_URL_MEDIA, group->download_segment_index + 1, &base_init_url, &start_range, &end_range); + if (!e) { gf_mx_v(mpdin->dl_mutex); return e; } - if (rep->default_base_url) { - base_init_url = gf_url_concatenate(rep->default_base_url, seg->url); - } else { - base_init_url = gf_strdup(seg->url); - } GF_LOG(GF_LOG_WARNING, GF_LOG_MODULE, ("Download of first segment failed... retrying with second one : %s\n", base_init_url)); - firstSegment = 2; + nb_segment_read = 2; /*use persistent connection for segment downloads*/ e = MPD_downloadWithRetry(mpdin->service, &(group->segment_dnload), base_init_url, MPD_NetIO_Segment, group, 0, 0, 1); } /* end of 404 */ @@ -806,42 +1330,52 @@ static GF_Err MPD_DownloadInitSegment(GF_MPD_In *mpdin, GF_MPD_Group *group) return e; } else { char mime[128]; - u32 count = gf_list_count(rep->segments) + 1; - if (count < group->max_cached) { + const char *mime_type; + u32 count = group->nb_segments_in_rep + 1; + if (count < group->max_cached_segments) { if (count < 1) { GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[MPD_IN] 0 representations, aborting\n")); gf_free(base_init_url); gf_mx_v(mpdin->dl_mutex); return GF_BAD_PARAM; } - GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("[MPD_IN] Resizing to %u max_cached elements instead of %u.\n", count, group->max_cached)); + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("[MPD_IN] Resizing to %u max_cached_segments elements instead of %u.\n", count, group->max_cached_segments)); /* OK, we have a problem, it may ends download */ - group->max_cached = count; + group->max_cached_segments = count; } e = gf_dm_sess_process(group->segment_dnload); /* Mime-Type check */ strncpy(mime, gf_dm_sess_mime_type(group->segment_dnload), sizeof(mime)); strlwr(mime); - if (mime && group->service == NULL) { + if (mime && group->input_module == NULL) { GF_Err e; GF_LOG(GF_LOG_DEBUG, GF_LOG_MODULE, ("[MPD_IN] Searching an input plugin for mime type : %s...\n", mime)); gf_free( mpdin->mimeTypeForM3U8Segments); mpdin->mimeTypeForM3U8Segments = gf_strdup( mime ); - gf_free( rep->mime); - rep->mime = gf_strdup( mime ); + if (rep->mime_type) gf_free( rep->mime_type); + rep->mime_type = gf_strdup( mime ); e = MPD_LoadMediaService(mpdin, group, mime, base_init_url); if (e != GF_OK) { gf_mx_v(mpdin->dl_mutex); return e; } } - if (!mime || (stricmp(mime, rep->mime))) { - GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[MPD_IN] Mime '%s' is not correct for '%s', it should be '%s'\n", mime, base_init_url, rep->mime)); - mpdin->mpd_stop_request = 0; - gf_mx_v(mpdin->dl_mutex); - gf_free(base_init_url); - base_init_url = NULL; - return GF_BAD_PARAM; + mime_type = MPD_GetMimeType(NULL, rep, group->adaptation_set); + if (!mime || (stricmp(mime, mime_type))) { + Bool valid = 0; + char *stype1, *stype2; + stype1 = strchr(mime_type, '/'); + stype2 = mime ? strchr(mime, '/') : NULL; + if (stype1 && stype2 && !strcmp(stype1, stype2)) valid = 1; + + if (!valid) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[MPD_IN] Mime '%s' is not correct for '%s', it should be '%s'\n", mime, base_init_url, mime_type)); + mpdin->mpd_stop_request = 0; + gf_mx_v(mpdin->dl_mutex); + gf_free(base_init_url); + base_init_url = NULL; + return GF_BAD_PARAM; + } } if (group->segment_must_be_streamed ) { group->segment_local_url = gf_dm_sess_get_resource_name(group->segment_dnload); @@ -857,11 +1391,11 @@ static GF_Err MPD_DownloadInitSegment(GF_MPD_In *mpdin, GF_MPD_Group *group) gf_free(base_init_url); return GF_BAD_PARAM; } else { - assert(!group->nb_cached); + assert(!group->nb_cached_segments); group->cached[0].cache = gf_strdup(group->segment_local_url); group->cached[0].url = gf_strdup(gf_dm_sess_get_resource_name(group->segment_dnload)); - group->nb_cached = 1; - group->download_segment_index = firstSegment; + group->nb_cached_segments = 1; + group->download_segment_index += nb_segment_read; GF_LOG(GF_LOG_DEBUG, GF_LOG_MODULE, ("[MPD_IN] Adding initialization segment %s to cache: %s\n", group->segment_local_url, group->cached[0].url )); gf_mx_v(mpdin->dl_mutex); gf_free(base_init_url); @@ -872,26 +1406,25 @@ static GF_Err MPD_DownloadInitSegment(GF_MPD_In *mpdin, GF_MPD_Group *group) static void MPDIn_skip_disabled_rep(GF_MPD_Group *group, GF_MPD_Representation *rep) { - s32 rep_idx = gf_list_find(group->representations, rep); + s32 rep_idx = gf_list_find(group->adaptation_set->representations, rep); while (1) { rep_idx++; - if (rep_idx==gf_list_count(group->representations)) rep_idx = 0; - rep = gf_list_get(group->representations, rep_idx); + if (rep_idx==gf_list_count(group->adaptation_set->representations)) rep_idx = 0; + rep = gf_list_get(group->adaptation_set->representations, rep_idx); if (!rep->disabled) break; } assert(rep && !rep->disabled); - group->active_rep_index = gf_list_find(group->period->representations, rep); - group->active_bitrate = rep->bandwidth; + MPD_SetGroupRepresentation(group, rep); GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("[MPD_IN] Switching to representation %d - BW %d\n", group->active_rep_index, group->active_bitrate )); } + static u32 download_segments(void *par) { GF_Err e; GF_MPD_In *mpdin = (GF_MPD_In*) par; GF_MPD_Period *period; GF_MPD_Representation *rep; - GF_MPD_SegmentInfo *seg; u32 i, group_count, ret = 0; Bool go_on = 1; char *new_base_seg_url; @@ -906,6 +1439,7 @@ static u32 download_segments(void *par) mpdin->mpd_is_running = MPD_STATE_CONNECTING; gf_mx_v(mpdin->dl_mutex); +restart_period: e = GF_OK; period = gf_list_get(mpdin->mpd->periods, mpdin->active_period_index); group_count = gf_list_count(mpdin->groups); @@ -932,17 +1466,18 @@ static u32 download_segments(void *par) for (i=0; igroups, i); GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("[MPD_IN] Connecting initial service... %s\n", group->segment_local_url)); - if (! group->service) { + if (! group->input_module) { e = GF_SERVICE_ERROR; gf_term_on_connect(mpdin->service, NULL, e); ret = 1; goto exit; } - e = group->service->ConnectService(group->service, mpdin->service, group->segment_local_url); + e = group->input_module->ConnectService(group->input_module, mpdin->service, group->segment_local_url); if (e) { ret = 1; goto exit; } + group->service_connected = 1; GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("[MPD_IN] Connecting initial service DONE\n", group->segment_local_url)); } @@ -956,29 +1491,44 @@ static u32 download_segments(void *par) /*wait until next segment is needed*/ while (!mpdin->mpd_stop_request) { u32 timer = gf_sys_clock() - mpdin->last_update_time; - Bool shouldParsePlaylist = mpdin->mpd->min_update_time && (timer > mpdin->mpd->min_update_time); + Bool shouldParsePlaylist = mpdin->mpd->minimum_update_period && (timer > mpdin->mpd->minimum_update_period); if (shouldParsePlaylist) { - GF_LOG(GF_LOG_DEBUG, GF_LOG_MODULE, ("[MPD_IN] Next segment in cache, but it is time to update the playlist (%u ms/%u)\n", timer, mpdin->mpd->min_update_time)); + GF_LOG(GF_LOG_DEBUG, GF_LOG_MODULE, ("[MPD_IN] Next segment in cache, but it is time to update the playlist (%u ms/%u)\n", timer, mpdin->mpd->minimum_update_period)); e = MPD_UpdatePlaylist(mpdin); group_count = gf_list_count(mpdin->groups); if (e) { - GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[MPD_IN] Error updating MDP %s\n", gf_error_to_string(e))); + GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[MPD_IN] Error updating MPD %s\n", gf_error_to_string(e))); } } else { + Bool all_groups_done = 1; Bool cache_full = 1; gf_mx_p(mpdin->dl_mutex); for (i=0; igroups, i); if (!group->selected || group->done) continue; - if (group->nb_cachedmax_cached) { + all_groups_done = 0; + if (group->nb_cached_segmentsmax_cached_segments) { cache_full = 0; break; } } gf_mx_v(mpdin->dl_mutex); if (!cache_full) break; - + + if (mpdin->request_period_switch==2) all_groups_done = 1; + + if (all_groups_done && mpdin->request_period_switch) { + MPD_ResetGroups(mpdin); + if (mpdin->request_period_switch == 1) + mpdin->active_period_index++; + + MPD_SetupPeriod(mpdin); + mpdin->request_period_switch = 0; + + goto restart_period; + } + gf_sleep(16); } } @@ -994,37 +1544,39 @@ static u32 download_segments(void *par) /*for each selected groups*/ for (i=0; igroups, i); if (! group->selected) continue; if (group->done) continue; - if (group->nb_cached>=group->max_cached) { + if (group->nb_cached_segments>=group->max_cached_segments) { continue; } - rep = gf_list_get(period->representations, group->active_rep_index); + rep = gf_list_get(group->adaptation_set->representations, group->active_rep_index); /* if the index of the segment to be downloaded is greater or equal to the last segment (as seen in the playlist), we need to check if a new playlist is ready */ - if (group->download_segment_index>=gf_list_count(rep->segments)) { + if (group->download_segment_index>=group->nb_segments_in_rep) { u32 timer = gf_sys_clock() - mpdin->last_update_time; /* update of the playlist, only if indicated */ - if (mpdin->mpd->min_update_time && timer > mpdin->mpd->min_update_time) { + if (mpdin->mpd->minimum_update_period && timer > mpdin->mpd->minimum_update_period) { GF_LOG(GF_LOG_DEBUG, GF_LOG_MODULE, ("[MPD_IN] Last segment in current playlist downloaded, checking updates after %u ms\n", timer)); e = MPD_UpdatePlaylist(mpdin); if (e) { - GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[MPD_IN] Error updating MDP %s\n", gf_error_to_string(e))); + GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[MPD_IN] Error updating MPD %s\n", gf_error_to_string(e))); } group_count = gf_list_count(mpdin->groups); period = gf_list_get(mpdin->mpd->periods, mpdin->active_period_index); - rep = gf_list_get(period->representations, group->active_rep_index); + rep = gf_list_get(group->adaptation_set->representations, group->active_rep_index); } else { gf_sleep(16); } /* Now that the playlist is up to date, we can check again */ - if (group->download_segment_index >= gf_list_count(rep->segments)) { - if (mpdin->mpd->min_update_time) { + if (group->download_segment_index >= group->nb_segments_in_rep) { + if (mpdin->mpd->minimum_update_period) { /* if there is a specified update period, we redo the whole process */ continue; } else { @@ -1036,41 +1588,39 @@ static u32 download_segments(void *par) } } gf_mx_p(mpdin->dl_mutex); + /* At this stage, there are some segments left to be downloaded */ - seg = gf_list_get(rep->segments, group->download_segment_index); + e = MPD_ResolveURL(mpdin->mpd, rep, group->adaptation_set, group->period, mpdin->url, GF_MPD_RESOLVE_URL_MEDIA, group->download_segment_index, &new_base_seg_url, &start_range, &end_range); gf_mx_v(mpdin->dl_mutex); - - if (!seg->url) { - if (rep->init_url) - new_base_seg_url = gf_url_concatenate(rep->default_base_url, rep->init_url); - else - new_base_seg_url = gf_strdup(rep->default_base_url); - } else if (rep->default_base_url) { - new_base_seg_url = gf_url_concatenate(rep->default_base_url, seg->url); - } else { - new_base_seg_url = gf_strdup(seg->url); + if (e) { + /*do something!!*/ + break; } - if (seg->use_byterange) { - GF_LOG(GF_LOG_DEBUG, GF_LOG_MODULE, ("[MPD_IN] Downloading new segment: %s (range: %d-%d)\n", new_base_seg_url, seg->byterange_start, seg->byterange_end)); + use_byterange = (start_range || end_range) ? 1 : 0; + + if (use_byterange) { + GF_LOG(GF_LOG_DEBUG, GF_LOG_MODULE, ("[MPD_IN] Downloading new segment: %s (range: "LLD"-"LLD")\n", new_base_seg_url, start_range, end_range)); } /*local file*/ if (!strstr(new_base_seg_url, "://") || !strnicmp(new_base_seg_url, "file://", 7)) { - /*byte-range request from file are ignored*/ - if (seg->use_byterange) { - group->done = 1; - break; - } resource_name = local_file_name = (char *) new_base_seg_url; e = GF_OK; /*do not erase local files*/ group->local_files = 1; + if (group->force_switch_bandwidth && !mpdin->auto_switch_count) { + MPD_SwitchGroupRepresentation(mpdin, group); + /*restart*/ + i--; + continue; + } + } else { group->max_bitrate = 0; group->min_bitrate = (u32)-1; /*use persistent connection for segment downloads*/ - if (seg->use_byterange) { - e = MPD_downloadWithRetry(mpdin->service, &(group->segment_dnload), new_base_seg_url, MPD_NetIO_Segment, group, seg->byterange_start, seg->byterange_end, 1); + if (use_byterange) { + e = MPD_downloadWithRetry(mpdin->service, &(group->segment_dnload), new_base_seg_url, MPD_NetIO_Segment, group, start_range, end_range, 1); } else { e = MPD_downloadWithRetry(mpdin->service, &(group->segment_dnload), new_base_seg_url, MPD_NetIO_Segment, group, 0, 0, 1); } @@ -1097,28 +1647,42 @@ static u32 download_segments(void *par) { u32 total_size, bytes_per_sec; + u64 duration; Double bitrate; gf_dm_sess_get_stats(group->segment_dnload, NULL, NULL, &total_size, NULL, &bytes_per_sec, NULL); bitrate = 8*total_size; bitrate *= 1000; - bitrate /= rep->default_segment_duration; - bitrate /= 1024; - fprintf(stdout, "Downloaded segment bitrate %d kbps with bandwith %d kbps\n", (u32) bitrate, 8*bytes_per_sec/1024); + duration = MPD_ResolveDuration(rep, group->adaptation_set, group->period, 0); + if (!duration) { + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("Downloaded segment (bandwidth %d) with bandwith %d kbps\n", 8*rep->bandwidth/1024, 8*bytes_per_sec/1024)); + } else { + bitrate /= duration; + bitrate /= 1024; + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("Downloaded segment (bandwidth %d) bitrate %d with bandwith %d kbps\n", 8*rep->bandwidth/1024, (u32) bitrate, 8*bytes_per_sec/1024)); + } /*TODO switch quality*/ if (!mpdin->auto_switch_count) { - MPD_SwitchGroupRepresentation(mpdin, group); +// MPD_SwitchGroupRepresentation(mpdin, group); } } } if (e == GF_OK || group->segment_must_be_streamed) { gf_mx_p(mpdin->dl_mutex); - assert(group->nb_cachedmax_cached); - group->cached[group->nb_cached].cache = gf_strdup(local_file_name); - group->cached[group->nb_cached].url = gf_strdup( resource_name ); - GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("[MPD_IN] Added file to cache\n\tURL: %s\n\tCache: %s\n\tElements in cache: %u/%u\n", group->cached[group->nb_cached].url, group->cached[group->nb_cached].cache, group->nb_cached+1, group->max_cached)); - group->nb_cached++; + assert(group->nb_cached_segmentsmax_cached_segments); + group->cached[group->nb_cached_segments].cache = gf_strdup(local_file_name); + group->cached[group->nb_cached_segments].url = gf_strdup( resource_name ); + group->cached[group->nb_cached_segments].start_range = 0; + group->cached[group->nb_cached_segments].end_range = 0; + if (group->local_files && use_byterange) { + group->cached[group->nb_cached_segments].start_range = start_range; + group->cached[group->nb_cached_segments].end_range = end_range; + } + if (!group->local_files) { + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("[MPD_IN] Added file to cache\n\tURL: %s\n\tCache: %s\n\tElements in cache: %u/%u\n", group->cached[group->nb_cached_segments].url, group->cached[group->nb_cached_segments].cache, group->nb_cached_segments+1, group->max_cached_segments)); + } + group->nb_cached_segments++; group->download_segment_index++; if (mpdin->auto_switch_count) { group->nb_segments_done++; @@ -1137,9 +1701,6 @@ static u32 download_segments(void *par) break; } } - - if (!go_on) break; - } exit: @@ -1192,7 +1753,7 @@ Bool MPD_CanHandleURL(GF_InputService *plug, const char *url) GF_InputService *MPD_GetInputServiceForChannel(GF_MPD_In *mpdin, LPNETCHANNEL channel) { GF_Channel *ch; - if (mpdin->group_zero_selected) return mpdin->group_zero_selected->service; + if (mpdin->group_zero_selected) return mpdin->group_zero_selected->input_module; ch = (GF_Channel *) channel; assert(ch && ch->odm && ch->odm->OD); @@ -1204,7 +1765,7 @@ GF_MPD_Group *MPD_GetGroupForInputService(GF_MPD_In *mpdin, GF_InputService *ifc u32 i; for (i=0; igroups); i++) { GF_MPD_Group *group = gf_list_get(mpdin->groups, i); - if (group->service==ifce) return group; + if (group->input_module==ifce) return group; } return NULL; } @@ -1245,13 +1806,17 @@ static u32 MPD_GetPeriodIndexFromTime(GF_MPD_In *mpdin, u32 time) static void MPD_DownloadStop(GF_MPD_In *mpdin) { u32 i; + assert( mpdin ); + if (mpdin->groups){ for (i=0; igroups); i++) { GF_MPD_Group *group = gf_list_get(mpdin->groups, i); + assert( group ); if (group->selected && group->segment_dnload) { gf_dm_sess_abort(group->segment_dnload); group->done = 1; } } + } /* stop the download thread */ gf_mx_p(mpdin->dl_mutex); if (mpdin->mpd_is_running != MPD_STATE_STOPPED) { @@ -1273,16 +1838,116 @@ static void MPD_DownloadStop(GF_MPD_In *mpdin) } } +Bool MPD_SeekPeriods(GF_MPD_In *mpdin) +{ + Double start_time; + u32 i, period_idx; + + gf_mx_p(mpdin->dl_mutex); + start_time = 0; + period_idx = 0; + for (i=0; i<=gf_list_count(mpdin->mpd->periods); i++) { + GF_MPD_Period *period = gf_list_get(mpdin->mpd->periods, i); + Double dur = period->duration; + dur /= 1000; + if (mpdin->playback_start_range >= start_time) { + if ((i+1==gf_list_count(mpdin->mpd->periods)) || (mpdin->playback_start_range < start_time + dur) ) { + period_idx = i; + break; + } + } + start_time += dur; + } + if (period_idx != mpdin->active_period_index) { + mpdin->playback_start_range -= start_time; + mpdin->active_period_index = period_idx; + mpdin->request_period_switch = 2; + } + gf_mx_v(mpdin->dl_mutex); + + return mpdin->request_period_switch ? 1 : 0; +} + +void MPD_SeekGroup(GF_MPD_In *mpdin, GF_MPD_Group *group) +{ + Double seg_start; + u32 first_downloaded, last_downloaded, segment_idx; + + group->force_segment_switch = 0; + + /*figure out where to seek*/ + segment_idx = 0; + seg_start = 0.0; + while (1) { + if ((mpdin->playback_start_range >= seg_start) && (mpdin->playback_start_range < seg_start + group->segment_duration)) + break; + seg_start += group->segment_duration; + segment_idx++; + } + /*todo - seek to given duration*/ + mpdin->playback_start_range -= seg_start; + + first_downloaded = last_downloaded = group->download_segment_index; + if (group->download_segment_index +1 >= group->nb_cached_segments) { + first_downloaded = group->download_segment_index + 1 - group->nb_cached_segments; + } + /*we are seeking in our download range, just go on*/ + if ((segment_idx >= first_downloaded) && (segment_idx<=last_downloaded)) return; + + group->force_segment_switch = 1; + group->download_segment_index = segment_idx; + + if (group->segment_dnload) + gf_dm_sess_abort(group->segment_dnload); + + if (group->urlToDeleteNext) { + if (!mpdin->keep_files && !group->local_files) + gf_dm_delete_cached_file_entry_session(group->segment_dnload, group->urlToDeleteNext); + + gf_free(group->urlToDeleteNext); + group->urlToDeleteNext = NULL; + } + if (group->segment_dnload) { + gf_term_download_del(group->segment_dnload); + group->segment_dnload = NULL; + } + while (group->nb_cached_segments) { + group->nb_cached_segments --; + if (!mpdin->keep_files && !group->local_files) + gf_delete_file(group->cached[group->nb_cached_segments].cache); + + gf_free(group->cached[group->nb_cached_segments].cache); + gf_free(group->cached[group->nb_cached_segments].url); + memset(&group->cached[group->nb_cached_segments], 0, sizeof(segment_cache_entry)); + } +} + +void MPD_SeekGroupsDownloads(GF_MPD_In *mpdin) +{ + u32 i; + + gf_mx_p(mpdin->dl_mutex); + for (i=0; igroups); i++) { + GF_MPD_Group *group = gf_list_get(mpdin->groups, i); + MPD_SeekGroup(mpdin, group); + } + gf_mx_v(mpdin->dl_mutex); +} + + void MPD_ResetGroups(GF_MPD_In *mpdin) { + mpdin->service->subservice_disconnect = 1; + gf_term_on_disconnect(mpdin->service, NULL, GF_OK); + + mpdin->service->subservice_disconnect = 2; while (gf_list_count(mpdin->groups)) { GF_MPD_Group *group = gf_list_last(mpdin->groups); gf_list_rem_last(mpdin->groups); - gf_list_del(group->representations); if (group->urlToDeleteNext) { if (!mpdin->keep_files && !group->local_files) - gf_dm_delete_cached_file_entry_session(mpdin->mpd_dnload, group->urlToDeleteNext); + gf_dm_delete_cached_file_entry_session(group->segment_dnload, group->urlToDeleteNext); gf_free(group->urlToDeleteNext); group->urlToDeleteNext = NULL; @@ -1291,21 +1956,33 @@ void MPD_ResetGroups(GF_MPD_In *mpdin) gf_term_download_del(group->segment_dnload); group->segment_dnload = NULL; } - while (group->nb_cached) { - group->nb_cached --; + while (group->nb_cached_segments) { + group->nb_cached_segments --; if (!mpdin->keep_files && !group->local_files) - gf_delete_file(group->cached[group->nb_cached].cache); + gf_delete_file(group->cached[group->nb_cached_segments].cache); - gf_free(group->cached[group->nb_cached].cache); - gf_free(group->cached[group->nb_cached].url); + gf_free(group->cached[group->nb_cached_segments].cache); + gf_free(group->cached[group->nb_cached_segments].url); } gf_free(group->cached); + + if (group->input_module) { + if (group->service_connected) { + group->input_module->CloseService(group->input_module); + group->service_connected = 0; + } + gf_modules_close_interface((GF_BaseInterface *) group->input_module); + group->input_module = NULL; + } + gf_free(group); } gf_list_del(mpdin->groups); mpdin->groups = NULL; + mpdin->service->subservice_disconnect = 0; } +/* create groups (implemntation of adaptations set) */ GF_Err MPD_SetupGroups(GF_MPD_In *mpdin) { GF_Err e; @@ -1319,16 +1996,14 @@ GF_Err MPD_SetupGroups(GF_MPD_In *mpdin) period = gf_list_get(mpdin->mpd->periods, mpdin->active_period_index); if (!period) return GF_BAD_PARAM; - count = gf_list_count(period->representations); + count = gf_list_count(period->adaptation_sets); for (i=0; irepresentations, i); + GF_MPD_AdaptationSet *set = gf_list_get(period->adaptation_sets, i); for (j=0; jgroups); j++) { GF_MPD_Group *group = gf_list_get(mpdin->groups, j); - if (group->group_id==rep->groupID) { + if (group->adaptation_set==set) { found = 1; - e = gf_list_add(group->representations, rep); - if (e) return e; break; } } @@ -1336,57 +2011,32 @@ GF_Err MPD_SetupGroups(GF_MPD_In *mpdin) GF_MPD_Group *group; GF_SAFEALLOC(group, GF_MPD_Group); if (!group) return GF_OUT_OF_MEM; - group->representations = gf_list_new(); - if (!group->representations) { - gf_free(group); - return GF_OUT_OF_MEM; - } - group->group_id = rep->groupID; + group->mpd_in = mpdin; + group->adaptation_set = set; + group->period = period; group->period = period; - group->max_cached = mpdin->option_max_cached; - group->cached = gf_malloc(sizeof(segment_cache_entry)*group->max_cached); - memset(group->cached, 0, sizeof(segment_cache_entry)*group->max_cached); + group->max_cached_segments = mpdin->option_max_cached; + group->cached = gf_malloc(sizeof(segment_cache_entry)*group->max_cached_segments); + memset(group->cached, 0, sizeof(segment_cache_entry)*group->max_cached_segments); if (!group->cached) { - gf_list_del(group->representations); gf_free(group); return GF_OUT_OF_MEM; } e = gf_list_add(mpdin->groups, group); if (e) { gf_free(group->cached); - gf_list_del(group->representations); gf_free(group); return e; } - - e = gf_list_add(group->representations, rep); - if (e) return e; - } } return GF_OK; } -static GF_Err MPD_SegmentsProcessStart(GF_MPD_In *mpdin, u32 time) +GF_Err MPD_SetupPeriod(GF_MPD_In *mpdin) { - GF_Err e; + GF_Err e; u32 rep_i, group_i; - GF_MPD_Period *period; - e = GF_BAD_PARAM; - -#if 0 - MPD_DownloadStop(mpdin); -#endif - - MPD_ResetGroups(mpdin); - - /* Get the right period from the given time */ - mpdin->active_period_index = MPD_GetPeriodIndexFromTime(mpdin, time); - period = gf_list_get(mpdin->mpd->periods, mpdin->active_period_index); - if (!period || !gf_list_count(period->representations) ) { - GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[MPD_IN] Error - cannot start: not enough periods or representations in MPD\n")); - goto exit; - } /*setup all groups*/ MPD_SetupGroups(mpdin); @@ -1395,53 +2045,88 @@ static GF_Err MPD_SegmentsProcessStart(GF_MPD_In *mpdin, u32 time) for (group_i=0; group_igroups); group_i++) { GF_MPD_Representation *rep_sel; u32 active_rep; + const char *mime_type; GF_MPD_Group *group = gf_list_get(mpdin->groups, group_i); - if (group->group_id==0) { + if (group->adaptation_set->group==0) { mpdin->group_zero_selected = group; } else if (mpdin->group_zero_selected) { + /* if this group is not the group 0 and we have found the group 0, + we can safely ignore this group. */ break; } /* Select the appropriate representation in the given period */ active_rep = 0; - for (rep_i = 0; rep_i < gf_list_count(group->representations); rep_i++) { - GF_MPD_Representation *rep = gf_list_get(group->representations, rep_i); - rep_sel = gf_list_get(group->representations, active_rep); + for (rep_i = 0; rep_i < gf_list_count(group->adaptation_set->representations); rep_i++) { + GF_MPD_Representation *rep = gf_list_get(group->adaptation_set->representations, rep_i); + rep_sel = gf_list_get(group->adaptation_set->representations, active_rep); + + if (rep_i && ( !rep->codecs || !rep_sel->codecs || strcmp(rep->codecs, rep_sel->codecs) ) ) continue; + /*by default tune to best quality and/or full bandwith*/ - if (rep->qualityRanking > rep_sel->qualityRanking) { + if (rep->quality_ranking > rep_sel->quality_ranking) { active_rep = rep_i; - } else if (rep->bandwidth > rep_sel->bandwidth) { + } else if (rep->bandwidth < rep_sel->bandwidth) { active_rep = rep_i; } + } - rep_sel = gf_list_get(group->representations, active_rep); - group->active_rep_index = gf_list_find(group->period->representations, rep_sel); - group->active_bitrate = rep_sel->bandwidth; + rep_sel = gf_list_get(group->adaptation_set->representations, active_rep); + MPD_SetGroupRepresentation(group, rep_sel); + + if (mpdin->playback_start_range>=0) + MPD_SeekGroup(mpdin, group); + mime_type = MPD_GetMimeType(NULL, rep_sel, group->adaptation_set); + + if (!mime_type) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[MPD_IN] Error - cannot start: missing mime\n")); + return GF_NON_COMPLIANT_BITSTREAM; + } /* TODO: Generate segment names if urltemplates are used */ - if (!gf_list_count(rep_sel->segments) || !rep_sel->mime) { - if (!rep_sel->mime) { - GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[MPD_IN] Error - cannot start: missing mime\n")); - } else { - GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[MPD_IN] Error - cannot start: missing segments\n")); - } - goto exit; + if (!rep_sel->segment_base && !rep_sel->segment_list && !rep_sel->segment_template + && !group->adaptation_set->segment_base && !group->adaptation_set->segment_list && !group->adaptation_set->segment_template + && !group->period->segment_base && !group->period->segment_list && !group->period->segment_template + + ) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[MPD_IN] Error - cannot start: missing segments\n")); + return GF_NON_COMPLIANT_BITSTREAM; } - group->service = NULL; - if (strcmp(M3U8_UNKOWN_MIME_TYPE, rep_sel->mime)) { - e = MPD_LoadMediaService(mpdin, group, rep_sel->mime, NULL); - if (e != GF_OK) - goto exit; + group->input_module = NULL; + if (strcmp(M3U8_UNKOWN_MIME_TYPE, mime_type)) { + e = MPD_LoadMediaService(mpdin, group, mime_type, NULL); + if (e != GF_OK) return e; } else { - GF_LOG(GF_LOG_DEBUG, GF_LOG_MODULE, ("[MPD_IN] Ignoring mime type %s, wait for first file...\n", rep_sel->mime)); + GF_LOG(GF_LOG_DEBUG, GF_LOG_MODULE, ("[MPD_IN] Ignoring mime type %s, wait for first file...\n", mime_type)); } group->selected = 1; } + /*and seek if needed*/ + return GF_OK; +} + +static GF_Err MPD_SegmentsProcessStart(GF_MPD_In *mpdin, u32 time) +{ + GF_Err e = GF_BAD_PARAM; + GF_MPD_Period *period; + + MPD_ResetGroups(mpdin); + + /* Get the right period from the given time */ + mpdin->active_period_index = MPD_GetPeriodIndexFromTime(mpdin, time); + period = gf_list_get(mpdin->mpd->periods, mpdin->active_period_index); + if (!period || !gf_list_count(period->adaptation_sets) ) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[MPD_IN] Error - cannot start: not enough periods or representations in MPD\n")); + goto exit; + } + + e = MPD_SetupPeriod(mpdin); + if (e) goto exit; gf_th_run(mpdin->mpd_thread, download_segments, mpdin); return GF_OK; @@ -1488,9 +2173,15 @@ GF_Err MPD_ConnectService(GF_InputService *plug, GF_ClientService *serv, const c opt = gf_modules_get_option((GF_BaseInterface *)plug, "DASH", "KeepFiles"); if (!opt) gf_modules_set_option((GF_BaseInterface *)plug, "DASH", "KeepFiles", "no"); if (opt && !strcmp(opt, "yes")) mpdin->keep_files = 1; + + opt = gf_modules_get_option((GF_BaseInterface *)plug, "DASH", "DisableSwitching"); + if (opt && !strcmp(opt, "yes")) mpdin->disable_switching = 1; if (mpdin->mpd_dnload) gf_term_download_del(mpdin->mpd_dnload); mpdin->mpd_dnload = NULL; + mpdin->in_seek = 0; + mpdin->previous_start_range = -1; + if (!strnicmp(url, "file://", 7)) { local_url = url + 7; @@ -1557,10 +2248,10 @@ GF_Err MPD_ConnectService(GF_InputService *plug, GF_ClientService *serv, const c if (sep) sep[0]=0; strcat(local_path, ".mpd"); - gf_m3u8_to_mpd(mpdin->service, local_url, url, local_path, mpdin->reload_count, mpdin->mimeTypeForM3U8Segments); + gf_m3u8_to_mpd(local_url, url, local_path, mpdin->reload_count, mpdin->mimeTypeForM3U8Segments, mpdin->service, 0, M3U8_TO_MPD_USE_TEMPLATE); local_url = local_path; } else { - gf_m3u8_to_mpd(mpdin->service, local_url, url, NULL, mpdin->reload_count, mpdin->mimeTypeForM3U8Segments); + gf_m3u8_to_mpd(local_url, url, NULL, mpdin->reload_count, mpdin->mimeTypeForM3U8Segments, mpdin->service, 0, M3U8_TO_MPD_USE_TEMPLATE); } } @@ -1576,7 +2267,7 @@ GF_Err MPD_ConnectService(GF_InputService *plug, GF_ClientService *serv, const c mpd_parser = gf_xml_dom_new(); e = gf_xml_dom_parse(mpd_parser, local_url, NULL, NULL); if (e != GF_OK) { - GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[MPD_IN] Error - cannot connect service: MPD parsing problem %s\n", gf_error_to_string(e))); + GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[MPD_IN] Error - cannot connect service: MPD parsing problem %s\n", gf_xml_dom_get_error(mpd_parser) )); gf_xml_dom_del(mpd_parser); gf_term_on_connect(mpdin->service, NULL, e); gf_term_download_del(mpdin->mpd_dnload); @@ -1612,8 +2303,8 @@ static GF_Descriptor *MPD_GetServiceDesc(GF_InputService *plug, u32 expect_type, GF_MPD_In *mpdin = (GF_MPD_In*) plug->priv; GF_LOG(GF_LOG_DEBUG, GF_LOG_MODULE, ("[MPD_IN] Received Service Description request from terminal for %s\n", sub_url)); if (mpdin->group_zero_selected) { - if (mpdin->group_zero_selected->service) { - return mpdin->group_zero_selected->service->GetServiceDescriptor(mpdin->group_zero_selected->service, expect_type, sub_url); + if (mpdin->group_zero_selected->input_module) { + return mpdin->group_zero_selected->input_module->GetServiceDescriptor(mpdin->group_zero_selected->input_module, expect_type, sub_url); } else { GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[MPD_IN] Error - cannot provide service description: no segment service hanlder created\n")); return NULL; @@ -1623,9 +2314,9 @@ static GF_Descriptor *MPD_GetServiceDesc(GF_InputService *plug, u32 expect_type, for (i=0; igroups); i++) { GF_MPD_Group *group = gf_list_get(mpdin->groups, i); if (!group->selected) continue; - if (group->service_connected) continue; - group->service_connected = 1; - return group->service->GetServiceDescriptor(group->service, expect_type, sub_url); + if (group->service_descriptor_fetched) continue; + group->service_descriptor_fetched = 1; + return group->input_module->GetServiceDescriptor(group->input_module, expect_type, sub_url); } return NULL; } @@ -1633,6 +2324,7 @@ static GF_Descriptor *MPD_GetServiceDesc(GF_InputService *plug, u32 expect_type, void MPD_Stop(GF_MPD_In *mpdin) { + assert( mpdin ); GF_LOG(GF_LOG_DEBUG, GF_LOG_MODULE, ("[MPD_IN] Stopping service %p\n", mpdin->service)); MPD_DownloadStop(mpdin); MPD_ResetGroups(mpdin); @@ -1648,22 +2340,13 @@ void MPD_Stop(GF_MPD_In *mpdin) GF_Err MPD_CloseService(GF_InputService *plug) { - u32 i; GF_MPD_In *mpdin = (GF_MPD_In*) plug->priv; + assert( mpdin ); GF_LOG(GF_LOG_DEBUG, GF_LOG_MODULE, ("[MPD_IN] Received Close Service (%p) request from terminal\n", mpdin->service)); - for (i=0; igroups); i++) { - GF_MPD_Group *group = gf_list_get(mpdin->groups, i); - - if (group->service && group->service_connected) { - group->service->CloseService(group->service); - group->service_connected = 0; - group->service = NULL; - } - } MPD_Stop(mpdin); - MPD_ResetGroups(mpdin); - gf_term_on_disconnect(mpdin->service, NULL, GF_OK); + + gf_term_on_disconnect(mpdin->service, NULL, GF_OK); return GF_OK; } @@ -1676,7 +2359,7 @@ GF_Err MPD_ServiceCommand(GF_InputService *plug, GF_NetworkCommand *com) GF_MPD_Group *group = NULL; if (!plug || !plug->priv || !com ) return GF_SERVICE_ERROR; - if (mpdin->group_zero_selected) segment_ifce = mpdin->group_zero_selected->service; + if (mpdin->group_zero_selected) segment_ifce = mpdin->group_zero_selected->input_module; switch (com->command_type) { case GF_NET_SERVICE_INFO: @@ -1690,12 +2373,13 @@ GF_Err MPD_ServiceCommand(GF_InputService *plug, GF_NetworkCommand *com) } if (e!= GF_OK || !com->info.name || 2 > strlen(com->info.name)) { - com->info.name = mpdin->mpd->title; + GF_MPD_ProgramInfo *info = gf_list_get(mpdin->mpd->program_infos, 0); + if (info) { + com->info.name = info->title; + com->info.comment = info->source; + } if (!com->info.name && mpdin->group_zero_selected) - com->info.name = mpdin->group_zero_selected->cached[0].url; - - if (mpdin->mpd->source) - com->info.comment = mpdin->mpd->source; + com->info.name = mpdin->group_zero_selected->cached[0].url; } return GF_OK; } @@ -1706,6 +2390,47 @@ GF_Err MPD_ServiceCommand(GF_InputService *plug, GF_NetworkCommand *com) return segment_ifce->ServiceCommand(segment_ifce, com); } return GF_NOT_SUPPORTED; + + case GF_NET_SERVICE_QUALITY_SWITCH: + { + u32 i; + + for (i=0; igroups); i++) { + Bool do_switch = 0; + GF_MPD_Group *group = gf_list_get(mpdin->groups, i); + u32 current_idx = group->active_rep_index; + if (! group->selected) continue; + + if (group->force_representation_idx_plus_one) current_idx = group->force_representation_idx_plus_one - 1; + if (com->switch_quality.up) { + if (current_idx + 1 < gf_list_count(group->adaptation_set->representations)) { + group->force_representation_idx_plus_one = 1 + current_idx+1; + do_switch = 1; + } + } else { + if (current_idx) { + group->force_representation_idx_plus_one = 1 + current_idx - 1; + do_switch = 1; + } + } + if (do_switch) { + gf_mx_p(mpdin->dl_mutex); + group->force_switch_bandwidth = 1; + /*in local playback just switch at the end of the current segment*/ + while (group->local_files && (group->nb_cached_segments>1)) { + group->nb_cached_segments--; + gf_free(group->cached[group->nb_cached_segments].url); + group->cached[group->nb_cached_segments].url = NULL; + group->cached[group->nb_cached_segments].start_range = 0; + group->cached[group->nb_cached_segments].end_range = 0; + assert(group->download_segment_index>1); + group->download_segment_index--; + } + gf_mx_v(mpdin->dl_mutex); + } + } + } + return GF_OK; } /*not supported*/ if (!com->base.on_channel) return GF_NOT_SUPPORTED; @@ -1745,7 +2470,14 @@ GF_Err MPD_ServiceCommand(GF_InputService *plug, GF_NetworkCommand *com) Note: the duration of the initial segment will be 0 anyway (in MP4).*/ { Double duration; - duration = (Double)mpdin->mpd->duration; + duration = (Double)mpdin->mpd->media_presentation_duration; + if (!duration) { + u32 i; + for (i=0; impd->periods); i++) { + GF_MPD_Period *period = gf_list_get(mpdin->mpd->periods, i); + duration += (Double)period->duration; + } + } duration /= 1000; com->duration.duration = duration; return GF_OK; @@ -1753,11 +2485,38 @@ GF_Err MPD_ServiceCommand(GF_InputService *plug, GF_NetworkCommand *com) break; case GF_NET_CHAN_PLAY: GF_LOG(GF_LOG_DEBUG, GF_LOG_MODULE, ("[MPD_IN] Received Play command from terminal on channel %p on Service (%p)\n", com->base.on_channel, mpdin->service)); - mpdin->playback_speed = com->play.speed; - mpdin->playback_start_range = com->play.start_range; - mpdin->playback_end_range = com->play.end_range; + + if (!com->play.dash_segment_switch && ! mpdin->in_seek) { + Bool skip_seek; + + mpdin->in_seek = 1; + + /*if start range request is the same as previous one, don't process it + - this happens at period switch when new objects are declared*/ + skip_seek = (mpdin->previous_start_range==com->play.start_range) ? 1 : 0; + mpdin->previous_start_range = com->play.start_range; + + mpdin->playback_speed = com->play.speed; + mpdin->playback_start_range = com->play.start_range; + + /*first check if we seek to another period*/ + if (! MPD_SeekPeriods(mpdin)) { + /*if no, seek in group*/ + MPD_SeekGroupsDownloads(mpdin); + } + } + /*For MPEG-2 TS or formats not using Init Seg: since objects are declared and started once the first + segment is playing, we will stay in playback_start_range!=-1 until next segment (because we won't have a query_next), + which will prevent seeking until then ... we force a reset of playback_start_range to allow seeking asap*/ + else if (mpdin->in_seek && (com->play.start_range==0)) { +// mpdin->in_seek = 0; + } group->done = 0; - return segment_ifce->ServiceCommand(segment_ifce, com); + com->play.dash_segment_switch = group->force_segment_switch; + /*don't forward commands, we are killing the service anyway ...*/ + if (mpdin->request_period_switch) return GF_OK; + + return segment_ifce->ServiceCommand(segment_ifce, com); case GF_NET_CHAN_STOP: GF_LOG(GF_LOG_DEBUG, GF_LOG_MODULE, ("[MPD_IN] Received Stop command from terminal on channel %p on Service (%p)\n", com->base.on_channel, mpdin->service)); @@ -1855,19 +2614,24 @@ GF_Err MPD_ChannelReleaseSLP(GF_InputService *plug, LPNETCHANNEL channel) Bool MPD_CanHandleURLInService(GF_InputService *plug, const char *url) { - /*JLF: commented out, this shall not happen*/ -#if 0 + /** + * May arrive when using pid:// URLs into a TS stream + */ GF_MPD_In *mpdin = (GF_MPD_In*) plug->priv; GF_LOG(GF_LOG_DEBUG, GF_LOG_MODULE, ("[MPD_IN] Received Can Handle URL In Service (%p) request from terminal for %s\n", mpdin->service, url)); if (!plug || !plug->priv) return GF_SERVICE_ERROR; if (mpdin->url && !strcmp(mpdin->url, url)) { return 1; } else { + GF_InputService *segment_ifce = NULL; + GF_MPD_Group *group = NULL; + if (mpdin->group_zero_selected) + segment_ifce = mpdin->group_zero_selected->input_module; + if (segment_ifce && segment_ifce->CanHandleURLInService){ + return segment_ifce->CanHandleURLInService(plug, url); + } return 0; } -#endif - - return 0; } GF_EXPORT @@ -1914,16 +2678,24 @@ void ShutdownInterface(GF_BaseInterface *bi) GF_InputService *ifcn = (GF_InputService*)bi; if (ifcn->InterfaceType==GF_NET_CLIENT_INTERFACE) { GF_MPD_In *mpdin = (GF_MPD_In*)ifcn->priv; + assert( mpdin ); if (mpdin->mpd) gf_mpd_del(mpdin->mpd); mpdin->mpd = NULL; if (mpdin->url) gf_free(mpdin->url); mpdin->url = NULL; - gf_th_del(mpdin->mpd_thread); - gf_mx_del(mpdin->dl_mutex); - gf_free(mpdin->mimeTypeForM3U8Segments); + if (mpdin->mpd_thread) + gf_th_del(mpdin->mpd_thread); + mpdin->mpd_thread = NULL; + if (mpdin->dl_mutex) + gf_mx_del(mpdin->dl_mutex); + mpdin->dl_mutex = NULL; + if (mpdin->mimeTypeForM3U8Segments) + gf_free(mpdin->mimeTypeForM3U8Segments); + mpdin->mimeTypeForM3U8Segments = NULL; gf_free(mpdin); + ifcn->priv = NULL; gf_free(bi); } } diff --git a/modules/mpegts_in/mpegts_in.c b/modules/mpegts_in/mpegts_in.c index 6d84d6b..bb512c6 100644 --- a/modules/mpegts_in/mpegts_in.c +++ b/modules/mpegts_in/mpegts_in.c @@ -192,9 +192,11 @@ static GF_ObjectDescriptor *MP2TS_GetOD(M2TSIn *m2ts, GF_M2TS_PES *stream, char case GF_M2TS_AUDIO_LATM_AAC: case GF_M2TS_AUDIO_AAC: if (!dsi) { - /*discard regulate until we fetch the AAC config*/ - m2ts->ts->file_regulate = 0; - /*turn on parsing*/ + /*turn on parsing to get AAC config + NB: we removed "no file regulation" since we may get broken files where PMT declares an AAC stream but no AAC PID is in the MUX + (filtered out). In this case, "no regulation" will make the entire TS to be read as fast as possible + */ + m2ts->ts->file_regulate = 2; gf_m2ts_set_pes_framing(stream, GF_M2TS_PES_FRAMING_DEFAULT); gf_odf_desc_del((GF_Descriptor *)esd); return NULL; @@ -264,7 +266,9 @@ static void MP2TS_SetupProgram(M2TSIn *m2ts, GF_M2TS_Program *prog, Bool regener if (!found) return; } #endif - if (m2ts->ts->file || m2ts->ts->dnload) m2ts->ts->file_regulate = no_declare ? 0 : 1; + + /*TS is a file, start regulation regardless of how the TS is access (with or without fragment URI)*/ + if (m2ts->ts->file || m2ts->ts->dnload) m2ts->ts->file_regulate = 1; for (i=0; istreams, i); @@ -273,13 +277,13 @@ static void MP2TS_SetupProgram(M2TSIn *m2ts, GF_M2TS_Program *prog, Bool regener if (!es->user) gf_m2ts_set_pes_framing((GF_M2TS_PES *)es, GF_M2TS_PES_FRAMING_SKIP); - if (!prog->pmt_iod && !no_declare) { + if (!prog->pmt_iod && !no_declare) { MP2TS_DeclareStream(m2ts, (GF_M2TS_PES *)es, NULL, 0); } /*if IOD, streams not declared through OD framework are refered to by pid:// scheme, and will be declared upon request by the terminal through GetServiceDesc*/ } - + /*force scene regeneration*/ if (!prog->pmt_iod && regenerate_scene) gf_term_add_media(m2ts->service, NULL, 0); @@ -465,15 +469,27 @@ static void M2TS_OnEvent(GF_M2TS_Demuxer *ts, u32 evt_type, void *param) evt.forwarded_event.param = ts; gf_term_on_service_event(m2ts->service, &evt); break; + case GF_M2TS_EVT_DSMCC_FOUND: + evt.type = GF_EVENT_FORWARDED; + evt.forwarded_event.forward_type = GF_EVT_FORWARDED_MPEG2; + evt.forwarded_event.service_event_type = evt_type; + evt.forwarded_event.param = param; + gf_term_on_service_event(m2ts->service, &evt); + break; case GF_M2TS_EVT_PMT_FOUND: if (gf_list_count(m2ts->ts->programs) == 1) { gf_term_on_connect(m2ts->service, NULL, GF_OK); m2ts->is_connected = 1; - } - + } /*do not declare if single program was requested for playback*/ MP2TS_SetupProgram(m2ts, param, m2ts->request_all_pids, m2ts->request_all_pids ? 0 : 1); - M2TS_FlushRequested(m2ts); + M2TS_FlushRequested(m2ts); + /* Send the TS to the a user if needed. Useful to check the number of received programs*/ + evt.type = GF_EVENT_FORWARDED; + evt.forwarded_event.forward_type = GF_M2TS_EVT_PMT_FOUND; + evt.forwarded_event.service_event_type = evt_type; + evt.forwarded_event.param = param; + gf_term_on_service_event(m2ts->service, &evt); break; case GF_M2TS_EVT_PMT_REPEAT: // case GF_M2TS_EVT_PMT_UPDATE: @@ -539,7 +555,7 @@ static void M2TS_OnEvent(GF_M2TS_Demuxer *ts, u32 evt_type, void *param) u32 stb = gf_sys_clock(); if (m2ts->regulation_pcr_pid==0) { - /*we pick the first PCR PID for file regulation - we don't need to make sure this is the PCR of a program being plyaed as we + /*we pick the first PCR PID for file regulation - we don't need to make sure this is the PCR of a program being played as we only check buffer levels, not DTS/PTS of the streams in the regulation step*/ m2ts->regulation_pcr_pid = ((GF_M2TS_PES_PCK *) param)->stream->pid; } else if (m2ts->regulation_pcr_pid != ((GF_M2TS_PES_PCK *) param)->stream->pid) { @@ -558,7 +574,7 @@ static void M2TS_OnEvent(GF_M2TS_Demuxer *ts, u32 evt_type, void *param) u64 pcr_diff = (pcr - ts->pcr_last); pcr_diff /= 27000; if (pcr_diff>1000) { - GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[M2TS In] PCR diff too big: "LLU" ms - PCR "LLU" - previous PCR "LLU" - error in TS ?\n", pcr_diff, ((GF_M2TS_PES_PCK *) param)->PTS, ts->pcr_last)); + GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[M2TS In] PCR diff too big: "LLU" ms - PCR "LLU" - previous PCR "LLU" - error in TS ?\n", pcr_diff, ((GF_M2TS_PES_PCK *) param)->PTS, ts->pcr_last)); diff = 100; } else { diff = (u32) pcr_diff - (stb - ts->stb_at_last_pcr); @@ -660,7 +676,7 @@ void m2ts_net_io(void *cbk, GF_NETIO_Parameter *param) { GF_Err e; M2TSIn *m2ts = (M2TSIn *) cbk; - assert( m2ts ); + assert( m2ts ); /*handle service message*/ gf_term_download_update_stats(m2ts->ts->dnload); @@ -668,33 +684,35 @@ void m2ts_net_io(void *cbk, GF_NETIO_Parameter *param) e = GF_EOS; } else if (param->msg_type==GF_NETIO_DATA_EXCHANGE) { e = GF_OK; - assert( m2ts->ts); - if (param->size > 0){ - /*process chunk*/ - assert(param->data); - if (m2ts->network_buffer_size < param->size){ - m2ts->network_buffer = gf_realloc(m2ts->network_buffer, sizeof(char) * param->size); - m2ts->network_buffer_size = param->size; - } - assert( m2ts->network_buffer ); - memcpy(m2ts->network_buffer, param->data, param->size); - gf_m2ts_process_data(m2ts->ts, m2ts->network_buffer, param->size); - } + assert( m2ts->ts); + if (param->size > 0){ + /*process chunk*/ + assert(param->data); + if (m2ts->network_buffer_size < param->size){ + m2ts->network_buffer = gf_realloc(m2ts->network_buffer, sizeof(char) * param->size); + m2ts->network_buffer_size = param->size; + } + assert( m2ts->network_buffer ); + memcpy(m2ts->network_buffer, param->data, param->size); + gf_m2ts_process_data(m2ts->ts, m2ts->network_buffer, param->size); + } /*if asked to regulate, wait until we get a play request*/ - if (m2ts->ts->run_state && !m2ts->ts->nb_playing && m2ts->ts->file_regulate) { - while (m2ts->ts->run_state && !m2ts->ts->nb_playing && m2ts->ts->file_regulate) { + if (m2ts->ts->run_state && !m2ts->ts->nb_playing && (m2ts->ts->file_regulate==1)) { + while (m2ts->ts->run_state && !m2ts->ts->nb_playing && (m2ts->ts->file_regulate==1) ) { gf_sleep(50); continue; } } else { gf_sleep(1); } +#if 1 //see commit 3642: crashes when reload quickly with http if (!m2ts->ts->run_state) { if (m2ts->ts->dnload) gf_term_download_del( m2ts->ts->dnload ); m2ts->ts->dnload = NULL; } +#endif } else { e = param->error; @@ -712,12 +730,12 @@ void m2ts_net_io(void *cbk, GF_NETIO_Parameter *param) if (!m2ts->ts_setup) { m2ts->ts_setup = 1; } - GF_LOG( GF_LOG_ERROR, GF_LOG_CONTAINER,("[MPEGTSIn] : Error while getting data : %s\n", gf_error_to_string(e))); + GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER,("[MPEGTSIn] : Error while getting data : %s\n", gf_error_to_string(e))); gf_term_on_connect(m2ts->service, NULL, e); } } -static const char *M2TS_QueryNextFile(void *udta) +static GF_Err M2TS_QueryNextFile(void *udta, Bool query_init, const char **out_url, u64 *out_start_range, u64 *out_end_range) { GF_NetworkCommand param; GF_Err query_ret; @@ -725,18 +743,25 @@ static const char *M2TS_QueryNextFile(void *udta) assert(m2ts->owner); assert( m2ts->owner->query_proxy); - param.command_type = GF_NET_SERVICE_QUERY_NEXT; + if (out_url) *out_url = NULL; + if (out_start_range) *out_start_range = 0; + if (out_end_range) *out_end_range = 0; + + param.command_type = query_init ? GF_NET_SERVICE_QUERY_INIT_RANGE : GF_NET_SERVICE_QUERY_NEXT; param.url_query.next_url = NULL; query_ret = m2ts->owner->query_proxy(m2ts->owner, ¶m); - if ((query_ret==GF_OK) && param.url_query.next_url){ - GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[M2TS In] Switching to next segment %s\n", param.url_query.next_url)); - return param.url_query.next_url; - } else if (query_ret==GF_OK){ + + + if ((query_ret==GF_OK) && !query_init && !param.url_query.next_url){ GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[M2TS In] Cannot query next file: no file provided but no error raised\n")); + } else if (query_ret) { + GF_LOG((query_ret<0) ? GF_LOG_ERROR : GF_LOG_INFO, GF_LOG_CONTAINER, ("[M2TS In] Cannot query next file: error: %s\n", gf_error_to_string(query_ret))); } else { - GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[M2TS In] Cannot query next file: error: %s\n", gf_error_to_string(query_ret))); - } - return NULL; + if (out_url) *out_url = param.url_query.next_url; + if (out_start_range) *out_start_range = param.url_query.start_range; + if (out_end_range) *out_end_range = param.url_query.end_range; + } + return query_ret; } @@ -753,12 +778,17 @@ static GF_Err M2TS_ConnectService(GF_InputService *plug, GF_ClientService *serv, opt = gf_modules_get_option((GF_BaseInterface *)m2ts->owner, "HybRadio", "Activated"); if (opt && !strcmp(opt, "true")) { m2ts->hybrid_on = 1; - } + } m2ts->service = serv; if (m2ts->owner->query_proxy) { m2ts->ts->query_next = M2TS_QueryNextFile; - m2ts->ts->udta_query = m2ts; + m2ts->ts->query_udta = m2ts; + } + + opt = gf_modules_get_option((GF_BaseInterface *)m2ts->owner, "DSMCC", "Activated"); + if (opt && !strcmp(opt, "true")) { + gf_m2ts_demux_dmscc_init(m2ts->ts); } if (!strnicmp(url, "http://", 7)) { @@ -854,6 +884,7 @@ static GF_Descriptor *M2TS_GetServiceDesc(GF_InputService *plug, u32 expect_type if (prog->pmt_iod) { m2ts->request_all_pids = 0; gf_odf_desc_copy((GF_Descriptor *)prog->pmt_iod, &desc); + ((GF_InitialObjectDescriptor *)desc)->service_ifce = m2ts->owner; return desc; } } @@ -996,11 +1027,15 @@ static GF_Err M2TS_ServiceCommand(GF_InputService *plug, GF_NetworkCommand *com) /*mark pcr as not initialized*/ if (pes->program->pcr_pid==pes->pid) pes->program->first_dts=0; gf_m2ts_set_pes_framing(pes, GF_M2TS_PES_FRAMING_DEFAULT); - GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[M2TSIn] Setting default reframing for PID %d\n", pes->pid)); + GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[M2TSIn] Setting default reframing for PID %d\n", pes->pid)); /*this is a multplex, only trigger the play command for the first stream activated*/ if (!ts->nb_playing) { ts->start_range = (u32) (com->play.start_range*1000); ts->end_range = (com->play.end_range>0) ? (u32) (com->play.end_range*1000) : 0; + + if (ts->query_next && ts->file) + ts->segment_switch = 1; + /*start demuxer*/ if (ts->run_state!=1) { return TSDemux_DemuxPlay(ts); @@ -1016,8 +1051,6 @@ static GF_Err M2TS_ServiceCommand(GF_InputService *plug, GF_NetworkCommand *com) } return GF_STREAM_NOT_FOUND; } - - gf_m2ts_set_pes_framing(pes, GF_M2TS_PES_FRAMING_SKIP); /* In case of EOS, we may receive a stop command after no one is playing */ if (ts->nb_playing) ts->nb_playing--; @@ -1027,9 +1060,11 @@ static GF_Err M2TS_ServiceCommand(GF_InputService *plug, GF_NetworkCommand *com) while (ts->run_state!=2) gf_sleep(2); if (gf_list_count(m2ts->ts->requested_progs)) { ts->file_regulate = 0; + gf_m2ts_set_pes_framing(pes, GF_M2TS_PES_FRAMING_SKIP); return TSDemux_DemuxPlay(ts); } } + gf_m2ts_set_pes_framing(pes, GF_M2TS_PES_FRAMING_SKIP); return GF_OK; case GF_NET_CHAN_CONFIG: pes = M2TS_GetChannel(m2ts, com->base.on_channel); diff --git a/modules/opencv_is/Makefile b/modules/opencv_is/Makefile index 883c178..b0b2e5b 100644 --- a/modules/opencv_is/Makefile +++ b/modules/opencv_is/Makefile @@ -15,11 +15,11 @@ LDFLAGS+=-pg endif #common obj -OBJS= opencv.o +OBJS= opencv_is.o SRCS := $(OBJS:.o=.c) -LDFLAGS+=-lcv -lcvaux -lhighgui -lcxcore +EXTRALIBS+=-lcv -lcvaux -lhighgui -lcxcore LIB=gm_opencv.$(DYN_LIB_SUFFIX) ifeq ($(CONFIG_WIN32),yes) diff --git a/modules/opensvc_dec/Makefile b/modules/opensvc_dec/Makefile index dec8bf2..fa9e923 100644 --- a/modules/opensvc_dec/Makefile +++ b/modules/opensvc_dec/Makefile @@ -2,7 +2,7 @@ include ../../config.mak vpath %.c $(SRC_PATH)/modules/opensvc_dec -CFLAGS= $(OPTFLAGS) -I"$(SRC_PATH)/include" +CFLAGS=$(OPTFLAGS) -I"$(SRC_PATH)/include" $(OSVC_CFLAGS) ifeq ($(DEBUGBUILD), yes) CFLAGS+=-g @@ -14,7 +14,7 @@ CFLAGS+=-pg LDFLAGS+=-pg endif -LDFLAGS+=-lOpenSVCDec +EXTRALIBS+=-L../../bin/gcc -lgpac $(OSVC_LDFLAGS) #common obj OBJS= opensvc_dec.o @@ -28,11 +28,7 @@ all: $(LIB) $(LIB): $(OBJS) - $(CC) $(SHFLAGS) $(LDFLAGS) -o ../../bin/gcc/$@ $(OBJS) $(EXTRALIBS) -L../../bin/gcc -lgpac -ifeq ($(STATICBUILD),yes) - $(CC) $(SHFLAGS) $(LDFLAGS) -o ../../bin/gcc/gm_opensvc_dec-static.$(DYN_LIB_SUFFIX) $(OBJS) $(EXTRALIBS) -L../../bin/gcc -lgpac_static -endif - + $(CC) $(SHFLAGS) $(LDFLAGS) -o ../../bin/gcc/$@ $(OBJS) $(EXTRALIBS) %.o: %.c $(CC) $(CFLAGS) -c -o $@ $< diff --git a/modules/opensvc_dec/opensvc_dec.c b/modules/opensvc_dec/opensvc_dec.c index 20639f7..02a12e7 100644 --- a/modules/opensvc_dec/opensvc_dec.c +++ b/modules/opensvc_dec/opensvc_dec.c @@ -33,32 +33,8 @@ # pragma comment(lib, "OpenSVCDecoder") #endif -typedef struct{ - int Width; - int Height; - unsigned char* pY[1]; - unsigned char* pU[1]; - unsigned char* pV[1]; -} OPENSVCFRAME; - - -enum { - SVC_STATUS_ERROR = -1, - SVC_STATUS_OK = 0, // no error and no frame ready - SVC_IMAGE_READY = 1, // no error and image ready - SVC_GHOST_IMAGE = 2 // no image for chosen layer but image could be ready for other layers -}; - -int SVCDecoder_init(void **PlayerStruct); -int SVCDecoder_close(void *PlayerStruct); -//TODO -int decodeNAL(void *PlayerStruct, unsigned char* nal, int nal_length, OPENSVCFRAME *Frame, int *LayerCommand); -/*ID vient du NAL type 14 et 20*/ -//TODO -void SetCommandLayer(int *Command, int DqIdMax, int CurrDqId, int *TemporalCom, int TemporalId); -void ParseAuPlayers(void *PlayerStruct, const unsigned char *buf, int buf_size, int nal_length_size, int is_avc); -int GetDqIdMax(const unsigned char *buf, int buf_size, int nal_length_size, int *DqidTable, int is_avc); - +#include +#include typedef struct { @@ -205,15 +181,11 @@ static GF_Err OSVC_SetCapabilities(GF_BaseDecoder *ifcg, GF_CodecCapability capa switch (capability.CapCode) { case GF_CODEC_MEDIA_SWITCH_QUALITY: if (capability.cap.valueInt) { - if (ctx->layer<32) { - ctx->layer += 8; - ctx->CurrDqId = ctx->layer; - } + // set layer up (command=1) + UpdateLayer( ctx->DqIdTable, &ctx->CurrDqId, &ctx->TemporalCom, &ctx->TemporalId, ctx->MaxDqId, 1 ); } else { - if (ctx->layer>=8) { - ctx->layer -= 8; - ctx->CurrDqId = ctx->layer; - } + // set layer down (command=0) + UpdateLayer( ctx->DqIdTable, &ctx->CurrDqId, &ctx->TemporalCom, &ctx->TemporalId, ctx->MaxDqId, 0 ); } return GF_OK; } diff --git a/modules/osd/osd.c b/modules/osd/osd.c index 40c2cfc..74913f8 100644 --- a/modules/osd/osd.c +++ b/modules/osd/osd.c @@ -179,8 +179,9 @@ Bool osd_load_scene(GF_OSD *osd) } -Bool osd_on_event_play(GF_OSD*osd , GF_Event *event, Bool consumed_by_compositor) +Bool osd_on_event_play(void *udta, GF_Event *event, Bool consumed_by_compositor) { + GF_OSD* osd = (GF_OSD*)udta; switch (event->type) { case GF_EVENT_SCENE_SIZE: gf_sg_set_scene_size_info(osd->odm->subscene->graph, event->size.width, event->size.height, 1); @@ -239,8 +240,8 @@ static Bool osd_process(GF_TermExt *termext, u32 action, void *param) case GF_TERM_EXT_PROCESS: /*flush all events until current time if reached*/ - if (gf_sys_get_rti(osd->refresh_time_ms, &osd->rti, 0)) { - sprintf(osd->statBuffer, "CPU %02d - FPS %02.2f - MEM %d KB", osd->rti.process_cpu_usage, gf_sc_get_fps(osd->term->compositor, 0), osd->rti.process_memory/1000); + if ((osd->visible->whichChoice==0) && gf_sys_get_rti(osd->refresh_time_ms, &osd->rti, 0)) { + sprintf(osd->statBuffer, "CPU %02d - FPS %02.2f - MEM "LLU" KB", osd->rti.process_cpu_usage, gf_sc_get_fps(osd->term->compositor, 0), osd->rti.process_memory/1000); gf_node_dirty_set((GF_Node *) osd->text, GF_SG_NODE_DIRTY, 1); } break; diff --git a/modules/platinum/GPACFileMediaServer.cpp b/modules/platinum/GPACFileMediaServer.cpp index 8b55958..cdc337d 100644 --- a/modules/platinum/GPACFileMediaServer.cpp +++ b/modules/platinum/GPACFileMediaServer.cpp @@ -466,7 +466,7 @@ void GPAC_FileMediaServer::ShareVirtualResource(const char *res_uri, const char if (sep) { sep = strchr(sep+3, '/'); if (sep) uri = sep; - else uri="/"; + else uri = (char*)"/"; } the_uri = ""; while (1) { diff --git a/modules/platinum/GPACPlatinum.cpp b/modules/platinum/GPACPlatinum.cpp index 6431d7e..e1286a2 100644 --- a/modules/platinum/GPACPlatinum.cpp +++ b/modules/platinum/GPACPlatinum.cpp @@ -64,10 +64,12 @@ GF_UPnP::~GF_UPnP() #endif } +#ifdef GPAC_HAS_SPIDERMONKEY void GF_UPnP::LockJavascript(Bool do_lock) { gf_sg_lock_javascript(m_pJSCtx, do_lock); } +#endif void GF_UPnP::OnStop(const char *src_url) { @@ -218,8 +220,8 @@ void GF_UPnP::onTimeChanged(s32 renderer_idx, Double time) JS_CallFunctionValue(m_pJSCtx, m_pObj, funval, 2, argv, &rval); } LockJavascript(0); - } #endif + } } void GF_UPnP::onDurationChanged(s32 renderer_idx, Double dur) @@ -237,8 +239,8 @@ void GF_UPnP::onDurationChanged(s32 renderer_idx, Double dur) JS_CallFunctionValue(m_pJSCtx, m_pObj, funval, 2, argv, &rval); } LockJavascript(0); - } #endif + } } @@ -547,6 +549,7 @@ static JSBool upnpdevice_getProperty(JSContext *c, JSObject *obj, SMJS_PROP_GETT return JS_TRUE; } +#ifdef GPAC_UNUSED_FUNC static JSBool upnp_device_subscribe(JSContext *c, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { PLT_Service* service; @@ -564,6 +567,8 @@ static JSBool upnp_device_subscribe(JSContext *c, JSObject *obj, uintN argc, jsv SMJS_FREE(c, service_uuid); return JS_TRUE; } +#endif + static JSBool SMJS_FUNCTION(upnp_device_find_service) { char *service_uuid; @@ -835,7 +840,6 @@ static JSBool SMJS_FUNCTION(upnp_renderer_open) NPT_String objID = item; // look back for the PLT_MediaItem in the results - PLT_MediaObject* track = NULL; NPT_List::Iterator item = server->m_BrowseResults->GetFirstItem(); while (item) { if ((*item)->m_ObjectID == objID) { @@ -970,9 +974,9 @@ static JSBool SMJS_FUNCTION(upnp_server_browse) _dir = _filter = NULL; dir = _dir = SMJS_CHARS(c, argv[0]); - if (!dir) dir = "0"; + if (!dir) dir = (char*)"0"; filter = _filter = SMJS_CHARS(c, argv[1]); - if (!filter) filter = "*"; + if (!filter) filter = (char*)"*"; PLT_Service* service; if (NPT_SUCCEEDED(server->m_device->FindServiceByType("urn:schemas-upnp-org:service:ContentDirectory:1", service))) { @@ -1324,6 +1328,7 @@ static JSBool SMJS_FUNCTION(upnp_device_start) return JS_TRUE; } +#ifdef GPAC_UNUSED_FUNC static JSBool SMJS_FUNCTION(upnp_device_stop) { SMJS_OBJ @@ -1335,7 +1340,7 @@ static JSBool SMJS_FUNCTION(upnp_device_stop) return JS_TRUE; } - +#endif static GPAC_GenericDevice *upnp_create_generic_device(GF_UPnP *upnp, JSObject*global, const char *id, const char *name) { @@ -1574,15 +1579,20 @@ static Bool upnp_process(GF_TermExt *termext, u32 action, void *param) u32 now; now = gf_sys_clock() - upnp->upnp_init_time; if (now - upnp->last_time > 200) { - u32 i, count; + u32 i, count, arg_set; jsval argv[1], rval; upnp->LockJavascript(1); - argv[0] = DOUBLE_TO_JSVAL( JS_NewDouble(upnp->m_pJSCtx, (Double)now / 1000.0) ); + arg_set = 0; count = gf_list_count(upnp->m_Devices); for (i=0; im_Devices, i); - if (device->run_proc) + if (device->run_proc) { + if (!arg_set) { + argv[0] = DOUBLE_TO_JSVAL( JS_NewDouble(upnp->m_pJSCtx, (Double)now / 1000.0) ); + arg_set = 1; + } JS_CallFunctionValue(upnp->m_pJSCtx, device->obj, device->run_proc, 1, argv, &rval); + } } upnp->LockJavascript(0); upnp->last_time = now; diff --git a/modules/platinum/GenericDevice.cpp b/modules/platinum/GenericDevice.cpp index 58ac692..bb32dcc 100644 --- a/modules/platinum/GenericDevice.cpp +++ b/modules/platinum/GenericDevice.cpp @@ -212,13 +212,14 @@ static JSBool SMJS_FUNCTION(upnp_service_set_action_listener) i=0; while ((argl = (GPAC_ActionArgListener *)gf_list_enum(service->m_ArgListeners, &i))) { if (argl->arg == desc) break; + argl = NULL; } if (!argl) { argl = new GPAC_ActionArgListener(); argl->arg = desc; - argl->action = action; gf_list_add(service->m_ArgListeners, argl); } + argl->action = action; if (argl->on_event) gf_js_remove_root(c, &argl->on_event, GF_JSGC_VAL); if (JSVAL_IS_NULL(argv[1])) { gf_list_del_item(service->m_ArgListeners, argl); @@ -294,7 +295,6 @@ static JSBool SMJS_FUNCTION(upnp_service_has_action) static JSBool SMJS_FUNCTION(upnp_service_call_action) { - u32 i=1; GPAC_ActionUDTA *act_udta = NULL; char *action_name = NULL; SMJS_OBJ @@ -303,13 +303,13 @@ static JSBool SMJS_FUNCTION(upnp_service_call_action) if (!service || !argc || !JSVAL_IS_STRING(argv[0]) ) return JS_FALSE; action_name = SMJS_CHARS(c, argv[0]); - PLT_ActionDesc* action_desc = service->m_service->FindActionDesc(action_name); + PLT_ActionDesc* action_desc = service->m_service->FindActionDesc(action_name); SMJS_FREE(c, action_name); if (action_desc == NULL) return JS_FALSE; - PLT_ActionReference action; + PLT_ActionReference action; - NPT_CHECK_SEVERE( + NPT_CHECK_SEVERE( service->m_device->m_pUPnP->m_pGenericController->m_CtrlPoint->CreateAction( service->m_device->m_device, service->m_service->GetServiceType(), @@ -335,7 +335,7 @@ static JSBool SMJS_FUNCTION(upnp_service_call_action) JS_GetElement(c, list, (jsint) i+1, &an_arg); - param_val = ""; + param_val = (char*)""; if (JSVAL_IS_STRING(an_arg)) { param_val = _param_val = SMJS_CHARS(c, an_arg); } else if (JSVAL_IS_BOOLEAN(an_arg)) { @@ -506,7 +506,6 @@ static JSBool SMJS_FUNCTION(upnp_action_get_argument_value) static JSBool SMJS_FUNCTION(upnp_action_get_error_code) { NPT_String res; - char *arg_name = NULL; SMJS_OBJ PLT_Action *action = (PLT_Action *) JS_GetPrivate(c, obj); if (!action ) return JS_FALSE; @@ -518,7 +517,6 @@ static JSBool SMJS_FUNCTION(upnp_action_get_error) { NPT_String res; unsigned int code; - char *arg_name = NULL; SMJS_OBJ PLT_Action *action = (PLT_Action *) JS_GetPrivate(c, obj); if (!action ) return JS_FALSE; @@ -847,7 +845,6 @@ static JSBool SMJS_FUNCTION(upnp_action_get_argument) static JSBool SMJS_FUNCTION(upnp_action_send_reply) { - u32 i=1; SMJS_OBJ SMJS_ARGS GPAC_GenericDevice *device = (GPAC_GenericDevice *)JS_GetPrivate(c, obj); @@ -870,7 +867,7 @@ static JSBool SMJS_FUNCTION(upnp_action_send_reply) JS_GetElement(c, list, (jsint) i+1, &an_arg); - param_val = ""; + param_val = (char*)""; if (JSVAL_IS_STRING(an_arg)) { param_val = _param_val = SMJS_CHARS(c, an_arg); } else if (JSVAL_IS_BOOLEAN(an_arg)) { @@ -916,10 +913,14 @@ GPAC_GenericDevice::OnAction(PLT_ActionReference& action, { NPT_COMPILER_UNUSED(context); +#ifdef GPAC_HAS_SPIDERMONKEY gf_mx_p(m_pMutex); +#endif PLT_ActionDesc &act_desc = action->GetActionDesc(); NPT_String name = act_desc.GetName(); +#ifdef GPAC_HAS_SPIDERMONKEY assert(!m_pSema); +#endif GF_LOG(GF_LOG_INFO, GF_LOG_NETWORK, ("[UPnP] Action %s called (thread %d)\n", (char *) name, gf_th_id() )); #ifdef GPAC_HAS_SPIDERMONKEY diff --git a/modules/redirect_av/redirect_av.c b/modules/redirect_av/redirect_av.c index 0c7f15c..b89a490 100644 --- a/modules/redirect_av/redirect_av.c +++ b/modules/redirect_av/redirect_av.c @@ -165,7 +165,7 @@ static Bool audio_encoding_thread_run(void *param) while (avr->is_running) { u32 readen; if (U32_ABS(avr->audioCurrentTime, myTime) > 25000) { - //GF_LOG( GF_LOG_ERROR, GF_LOG_MODULE, ("[AVRedirect] Drift in audio : audioCurrentTime = %u, myTime=%u, resync...\n", avr->audioCurrentTime, myTime)); + //GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[AVRedirect] Drift in audio : audioCurrentTime = %u, myTime=%u, resync...\n", avr->audioCurrentTime, myTime)); //myTime = lastCurrentTime = avr->audioCurrentTime; //frameCountSinceReset = 0; sendPts = 1; @@ -188,7 +188,7 @@ static Bool audio_encoding_thread_run(void *param) ts_encode_audio_frame(avr->ts_implementation, outBuff, encoded, sendPts ? myTime : AV_NOPTS_VALUE ); frameCountSinceReset++; #ifdef DUMP_MP3 - fwrite( outBuff, sizeof(char), encoded, mp3); + gf_fwrite( outBuff, sizeof(char), encoded, mp3); #endif /* DUMP_MP3 */ //if (ctx->codec->id == CODEC_ID_MP3) { /* It seems the MP3 codec only fetches 50% of data... */ @@ -271,7 +271,7 @@ static Bool video_encoding_thread_run(void *param) #ifdef AVR_DUMP_RAW_AVI if ( AVI_write_frame ( avr->avi_out, avr->frame, avr->size, 1 ) <0 ) { - GF_LOG ( GF_LOG_ERROR, GF_LOG_MODULE, ( "[AVRedirect] Error writing video frame\n" ) ); + GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ( "[AVRedirect] Error writing video frame\n" ) ); } #endif /* AVR_DUMP_RAW_AVI */ gf_mx_v(avr->frameMutex); @@ -285,7 +285,7 @@ static Bool video_encoding_thread_run(void *param) //ctx->coded_frame->pts = currentFrameTimeProcessed; if ( written < 0 ) { - GF_LOG ( GF_LOG_ERROR, GF_LOG_MODULE, ( "[AVRedirect] Error while encoding video frame =%d\n", written ) ); + GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ( "[AVRedirect] Error while encoding video frame =%d\n", written ) ); } else if ( written > 0 ) { @@ -329,7 +329,7 @@ static Bool start_if_needed(GF_AVRedirect *avr) { if ( !avr->avi_out ) { gf_mx_v(avr->frameMutex); - GF_LOG ( GF_LOG_ERROR, GF_LOG_MODULE, ( "[AVRedirect] Error opening output AVI file\n" ) ); + GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ( "[AVRedirect] Error opening output AVI file\n" ) ); return 4; } #endif /* AVR_DUMP_RAW_AVI */ @@ -345,14 +345,14 @@ static Bool start_if_needed(GF_AVRedirect *avr) { if ( !avr->audioCodec) { gf_mx_v(avr->frameMutex); - GF_LOG ( GF_LOG_ERROR, GF_LOG_MODULE, ( "[AVRedirect] Cannot find audio codec.\n" ) ); + GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ( "[AVRedirect] Cannot find audio codec.\n" ) ); return 1; } #endif if ( !avr->videoCodec ) { - GF_LOG ( GF_LOG_ERROR, GF_LOG_MODULE, ( "[AVRedirect] Cannot find video codec.\n" ) ); + GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ( "[AVRedirect] Cannot find video codec.\n" ) ); return 2; } @@ -378,7 +378,7 @@ static Bool start_if_needed(GF_AVRedirect *avr) { memset ( avr->videoOutbuf, 0, avr->videoOutbufSize ); } #ifdef AVR_DUMP_RAW_AVI - GF_LOG ( GF_LOG_INFO, GF_LOG_MODULE, ( "[AVRedirect] Open output AVI file %s\n", "dump.avi" ) ); + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ( "[AVRedirect] Open output AVI file %s\n", "dump.avi" ) ); #endif /* AVR_DUMP_RAW_AVI */ /* Setting up the TS mux */ { @@ -400,7 +400,7 @@ static Bool start_if_needed(GF_AVRedirect *avr) { /* if ( e ) { - GF_LOG ( GF_LOG_ERROR, GF_LOG_MODULE, ( "[AVRedirect] Error while initializing UDP address %s\n", gf_error_to_string ( e ) ) ); + GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ( "[AVRedirect] Error while initializing UDP address %s\n", gf_error_to_string ( e ) ) ); return e; } */ @@ -466,7 +466,7 @@ static void avr_on_video_frame ( void *udta, u32 time ) e = gf_sc_get_screen_buffer ( avr->term->compositor, &fb, 0 ); if ( e ) { - GF_LOG ( GF_LOG_ERROR, GF_LOG_MODULE, ( "[AVRedirect] Error grabing frame buffer %s\n", gf_error_to_string ( e ) ) ); + GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ( "[AVRedirect] Error grabing frame buffer %s\n", gf_error_to_string ( e ) ) ); return; } /*convert frame*/ @@ -486,7 +486,7 @@ static void avr_on_video_frame ( void *udta, u32 time ) avr->frameTime = time; gf_mx_v(avr->frameMutex); gf_sc_release_screen_buffer ( avr->term->compositor, &fb ); - GF_LOG ( GF_LOG_DEBUG, GF_LOG_MODULE, ( "[AVRedirect] Writing video frame\n" ) ); + GF_LOG(GF_LOG_DEBUG, GF_LOG_MODULE, ( "[AVRedirect] Writing video frame\n" ) ); } static void avr_on_video_reconfig ( void *udta, u32 width, u32 height ) @@ -501,7 +501,7 @@ static void avr_on_video_reconfig ( void *udta, u32 width, u32 height ) comp[0] = comp[1] = comp[2] = comp[3] = comp[4] = 0; AVI_set_video ( avr->avi_out, width, height, 30, comp ); #endif /* AVR_DUMP_RAW_AVI */ - GF_LOG ( GF_LOG_INFO, GF_LOG_MODULE, ( "[AVRedirect] Video reconfig width %d height %d\n", width, height ) ); + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ( "[AVRedirect] Video reconfig width %d height %d\n", width, height ) ); gf_mx_p(avr->frameMutex); if ( avr->frame ) gf_free ( avr->frame ); avr->size = 3*width*height; @@ -558,7 +558,7 @@ static void avr_close ( GF_AVRedirect *avr ) if ( !avr->is_open ) return; avr->is_open = 0; #ifdef AVR_DUMP_RAW_AVI - GF_LOG ( GF_LOG_INFO, GF_LOG_MODULE, ( "[AVRedirect] Closing output AVI file\n" ) ); + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ( "[AVRedirect] Closing output AVI file\n" ) ); AVI_close ( avr->avi_out ); avr->avi_out = NULL; #endif /* AVR_DUMP_RAW_AVI */ @@ -660,7 +660,7 @@ static Bool avr_process ( GF_TermExt *termext, u32 action, void *param ) } else { - GF_LOG ( GF_LOG_ERROR, GF_LOG_MODULE, ( "[AVRedirect] Not a known Video codec : %s, using MPEG-2 video instead, %s\n", opt, possibleCodecs ) ); + GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ( "[AVRedirect] Not a known Video codec : %s, using MPEG-2 video instead, %s\n", opt, possibleCodecs ) ); avr->videoCodec = avcodec_find_encoder ( CODEC_ID_MPEG2VIDEO ); } } @@ -672,7 +672,7 @@ static Bool avr_process ( GF_TermExt *termext, u32 action, void *param ) if (!opt) { gf_modules_set_option ( ( GF_BaseInterface* ) termext, moduleName, AVR_UDP_ADDRESS_OPTION, DEFAULT_UDP_OUT_ADDRESS); avr->udp_address = DEFAULT_UDP_OUT_ADDRESS; - GF_LOG ( GF_LOG_ERROR, GF_LOG_MODULE, ( "[AVRedirect] %s not set, using %s\n", AVR_UDP_ADDRESS_OPTION, DEFAULT_UDP_OUT_ADDRESS ) ); + GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ( "[AVRedirect] %s not set, using %s\n", AVR_UDP_ADDRESS_OPTION, DEFAULT_UDP_OUT_ADDRESS ) ); } else avr->udp_address = opt; opt = gf_modules_get_option ( ( GF_BaseInterface* ) termext, moduleName, AVR_UDP_PORT_OPTION); @@ -687,7 +687,7 @@ static Bool avr_process ( GF_TermExt *termext, u32 action, void *param ) } if (!opt) { gf_modules_set_option ( ( GF_BaseInterface* ) termext, moduleName, AVR_UDP_PORT_OPTION, DEFAULT_UDP_OUT_PORT_STR); - GF_LOG ( GF_LOG_ERROR, GF_LOG_MODULE, ( "[AVRedirect] %s is not set or valid, using %s\n", AVR_UDP_PORT_OPTION, DEFAULT_UDP_OUT_PORT_STR ) ); + GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ( "[AVRedirect] %s is not set or valid, using %s\n", AVR_UDP_PORT_OPTION, DEFAULT_UDP_OUT_PORT_STR ) ); avr->udp_port = DEFAULT_UDP_OUT_PORT; } */ @@ -695,7 +695,7 @@ static Bool avr_process ( GF_TermExt *termext, u32 action, void *param ) if (!opt) { gf_modules_set_option ( ( GF_BaseInterface* ) termext, moduleName, AVR_DESTINATION, AVR_DEFAULT_DESTINATION); avr->destination = AVR_DEFAULT_DESTINATION; - GF_LOG ( GF_LOG_ERROR, GF_LOG_MODULE, ( "[AVRedirect] %s not set, using %s\n", AVR_DESTINATION, AVR_DEFAULT_DESTINATION ) ); + GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ( "[AVRedirect] %s not set, using %s\n", AVR_DESTINATION, AVR_DEFAULT_DESTINATION ) ); } else avr->destination = opt; avr->audio_listen.udta = avr; diff --git a/modules/rtp_in/rtp_session.c b/modules/rtp_in/rtp_session.c index f688019..2f67cf9 100644 --- a/modules/rtp_in/rtp_session.c +++ b/modules/rtp_in/rtp_session.c @@ -361,7 +361,7 @@ void RP_RemoveStream(RTPClient *rtp, RTPStream *ch) void RP_ResetSession(RTSPSession *sess, GF_Err e) { GF_RTSPCommand *com; - u32 first = 1; + //u32 first = 1; //destroy command list while (gf_list_count(sess->rtsp_commands)) { @@ -370,7 +370,7 @@ void RP_ResetSession(RTSPSession *sess, GF_Err e) //this destroys stacks if any // RP_SendFailure(sess, com, first ? e : GF_OK); gf_rtsp_command_del(com); - first = 0; + //first = 0; } /*reset session state*/ gf_rtsp_session_reset(sess->session, 1); diff --git a/modules/rtp_in/sdp_fetch.c b/modules/rtp_in/sdp_fetch.c index 6551d6f..ec61e42 100644 --- a/modules/rtp_in/sdp_fetch.c +++ b/modules/rtp_in/sdp_fetch.c @@ -148,7 +148,6 @@ void SDP_NetIO(void *cbk, GF_NETIO_Parameter *param) void RP_FetchSDP(RTPClient *rtp, char *url, RTPStream *stream, char *original_url) { - u32 flags = 0; SDPFetch *sdp; /*if local URL get file*/ if (strstr(url, "data:application/sdp")) { @@ -174,8 +173,13 @@ void RP_FetchSDP(RTPClient *rtp, char *url, RTPStream *stream, char *original_ur rtp->dnload = NULL; rtp->sdp_temp = sdp; - rtp->dnload = gf_term_download_new(rtp->service, url, flags, SDP_NetIO, rtp); - if (!rtp->dnload) gf_term_on_connect(rtp->service, NULL, GF_NOT_SUPPORTED); + rtp->dnload = gf_term_download_new(rtp->service, url, 0, SDP_NetIO, rtp); + if (!rtp->dnload) { + gf_term_on_connect(rtp->service, NULL, GF_NOT_SUPPORTED); + } else { + /*start our download (threaded)*/ + gf_dm_sess_process(rtp->dnload); + } /*service confirm is done once fetched*/ } diff --git a/modules/rtp_in/sdp_load.c b/modules/rtp_in/sdp_load.c index 7d691d4..6cb2a5c 100644 --- a/modules/rtp_in/sdp_load.c +++ b/modules/rtp_in/sdp_load.c @@ -352,10 +352,8 @@ void RP_LoadSDP(RTPClient *rtp, char *sdp_text, u32 sdp_len, RTPStream *stream) void MigrateSDP_NetIO(void *cbk, GF_NETIO_Parameter *param) { - GF_Err e; RTPClient *rtp = (RTPClient *)cbk; - e = param->error; switch (param->msg_type) { case GF_NETIO_GET_METHOD: param->name = "POST"; @@ -513,7 +511,7 @@ void RP_SaveSessionState(RTPClient *rtp) FILE *f = gf_f64_open(opt, "wt"); if (f) { sdp_buf = rtp->session_state_data + strlen("data:application/sdp,"); - fwrite(sdp_buf, 1, strlen(sdp_buf), f); + gf_fwrite(sdp_buf, 1, strlen(sdp_buf), f); fclose(f); } else { e = GF_IO_ERR; diff --git a/modules/rvc_dec/Makefile b/modules/rvc_dec/Makefile index 008d85f..b28709b 100644 --- a/modules/rvc_dec/Makefile +++ b/modules/rvc_dec/Makefile @@ -14,7 +14,7 @@ CFLAGS+=-pg LDFLAGS+=-pg endif -LDFLAGS+=-lRVCDecoder +EXTRALIBS+=-lRVCDecoder #common obj OBJS= rvc_dec.o diff --git a/modules/rvc_dec/rvc_dec.c b/modules/rvc_dec/rvc_dec.c index cb7c8ce..3649c02 100644 --- a/modules/rvc_dec/rvc_dec.c +++ b/modules/rvc_dec/rvc_dec.c @@ -98,7 +98,7 @@ static GF_Err RVCD_AttachStream(GF_BaseDecoder *ifcg, GF_ESD *esd) FILE *f; u32 size; sprintf(opt, "Predefined_%d", esd->decoderConfig->predefined_rvc_config); - path = gf_modules_get_option((GF_BaseInterface *)ifcg, "RVCDecoder", (const char *)opt); + path = (char *) gf_modules_get_option((GF_BaseInterface *)ifcg, "RVCDecoder", (const char *)opt); if (!opt) return GF_NOT_SUPPORTED; f = fopen(path, "rt"); if (!f) return GF_NOT_SUPPORTED; diff --git a/modules/saf_in/saf_in.c b/modules/saf_in/saf_in.c index ce0e082..bfbccc9 100644 --- a/modules/saf_in/saf_in.c +++ b/modules/saf_in/saf_in.c @@ -332,6 +332,9 @@ static void SAF_DownloadFile(GF_InputService *plug, char *url) if (!read->dnload) { read->needs_connection = 0; gf_term_on_connect(read->service, NULL, GF_NOT_SUPPORTED); + } else { + /*start our download (threaded)*/ + gf_dm_sess_process(read->dnload); } /*service confirm is done once fetched*/ } @@ -426,7 +429,7 @@ static GF_Err SAF_CloseService(GF_InputService *plug) if (read->th) { if (read->run_state == 1) { read->run_state=0; - while (read->run_state!=2) gf_sleep(0); + while (read->run_state!=2) gf_sleep(2); } gf_th_del(read->th); read->th = NULL; diff --git a/modules/sdl_out/video.c b/modules/sdl_out/video.c index 0d4da20..826ea1d 100644 --- a/modules/sdl_out/video.c +++ b/modules/sdl_out/video.c @@ -384,8 +384,9 @@ static void sdl_translate_key(u32 SDLkey, GF_EventKey *evt) default: if ((SDLkey>=0x30) && (SDLkey<=0x39)) evt->key_code = GF_KEY_0 + SDLkey-0x30; - else if ((SDLkey>=0x41) && (SDLkey<=0x5A)) evt->key_code = GF_KEY_A + SDLkey-0x51; - else + else if ((SDLkey>=0x41) && (SDLkey<=0x5A)) evt->key_code = GF_KEY_A + SDLkey-0x41; + else if ((SDLkey>=0x61) && (SDLkey<=0x7A)) evt->key_code = GF_KEY_A + SDLkey-0x61; + else { evt->key_code = GF_KEY_UNIDENTIFIED; } @@ -474,6 +475,7 @@ GF_Err SDLVid_ResizeWindow(GF_VideoOutput *dr, u32 width, u32 height) assert(ctx->screen); ctx->width = width; ctx->height = height; + memset(&evt, 0, sizeof(GF_Event)); evt.type = GF_EVENT_VIDEO_SETUP; dr->on_event(dr->evt_cbk_hdl, &evt); } else { @@ -832,6 +834,7 @@ GF_Err SDLVid_SetFullScreen(GF_VideoOutput *dr, u32 bFullScreenOn, u32 *screen_w /*GL has changed*/ if (ctx->output_3d_type==1) { GF_Event evt; + memset(&evt, 0, sizeof(GF_Event)); evt.type = GF_EVENT_VIDEO_SETUP; dr->on_event(dr->evt_cbk_hdl, &evt); } @@ -1122,6 +1125,39 @@ static void copy_yuv(u8 *pYD, u8 *pVD, u8 *pUD, u32 pixel_format , u32 pitch_y, memcpy(pYD, pY, sizeof(unsigned char)*src_width*src_height); memcpy(pVD, pV, sizeof(unsigned char)*src_width*src_height/4); memcpy(pUD, pU, sizeof(unsigned char)*src_width*src_height/4); + } else if (src_pf==GF_PIXEL_YUY2){ + u32 i, j; + unsigned char *dst_y, *dst_u, *dst_v; + + pY = src + src_stride * src_wnd->y + src_wnd->x; + pU = src + src_stride * src_wnd->y + src_wnd->x + 1; + pV = src + src_stride * src_wnd->y + src_wnd->x + 3; + + + dst_y = pYD; + dst_v = pVD; + dst_u = pUD; + for (i=0; ih; i++) { + for (j=0; jw; j+=2) { + *dst_y = * pY; + *(dst_y+1) = * (pY+2); + dst_y += 2; + pY += 4; + if (i%2) continue; + + *dst_u = (*pU + *(pU + src_stride)) / 2; + *dst_v = (*pV + *(pV + src_stride)) / 2; + dst_u++; + dst_v++; + pU += 4; + pV += 4; + } + if (i%2) { + pU += src_stride; + pV += src_stride; + } + } + } else { u32 i; unsigned char *dst, *src, *dst2, *src2, *dst3, *src3; diff --git a/modules/soft_raster/ftgrays.c b/modules/soft_raster/ftgrays.c index b49f2c3..5e769f0 100644 --- a/modules/soft_raster/ftgrays.c +++ b/modules/soft_raster/ftgrays.c @@ -364,7 +364,7 @@ static GFINLINE void gray_render_line(TRaster *raster, TPos to_x, TPos to_y, i } /* vertical line - avoid calling gray_render_scanline */ incr = 1; - if ( dx == 0 ) { + if (dx == 0 ) { TCoord ex = TRUNC( raster->x ); TCoord two_fx = (TCoord)( ( raster->x - SUBPIXELS( ex ) ) << 1 ); TPos area; @@ -458,14 +458,12 @@ End: static int EVG_Outline_Decompose(EVG_Outline *outline, TRaster *user) { - EVG_Vector v_last; EVG_Vector v_start; EVG_Vector* point; EVG_Vector* limit; char* tags; int n; /* index of contour in outline */ int first; /* index of first point in contour */ - char tag; /* current point's state */ #ifdef INLINE_POINT_CONVERSION TPos _x, _y; #endif @@ -475,13 +473,9 @@ static int EVG_Outline_Decompose(EVG_Outline *outline, TRaster *user) int last; /* index of last point in contour */ last = outline->contours[n]; limit = outline->points + last; - v_start = outline->points[first]; - v_last = outline->points[last]; - point = outline->points + first; tags = (char*) outline->tags + first; - tag = tags[0]; gray_move_to(&v_start, user); while ( point < limit ) { point++; diff --git a/modules/soft_raster/rast_soft.h b/modules/soft_raster/rast_soft.h index 1fa4dae..34d128f 100644 --- a/modules/soft_raster/rast_soft.h +++ b/modules/soft_raster/rast_soft.h @@ -126,8 +126,6 @@ struct _evg_surface /*color buffer for variable stencils - size of width*/ u32 *stencil_pix_run; - /*aliasing flag (on/off only)*/ - u8 AALevel; /*default texture filter level*/ u32 texture_filter; diff --git a/modules/soft_raster/raster_565.c b/modules/soft_raster/raster_565.c index 836cb6f..7e8f996 100644 --- a/modules/soft_raster/raster_565.c +++ b/modules/soft_raster/raster_565.c @@ -95,12 +95,10 @@ void evg_565_fill_const(s32 y, s32 count, EVG_Span *spans, EVGSurface *surf) register s32 i; u32 len; s32 x; - u8 aa_lev = surf->AALevel; col_no_a = col&0x00FFFFFF; for (i=0; ipitch_x; len = spans[i].len; if (spans[i].coverage != 0xFF) { @@ -122,12 +120,10 @@ void evg_565_fill_const_a(s32 y, s32 count, EVG_Span *spans, EVGSurface *surf) u32 col = surf->fill_col; register u32 a, fin, col_no_a; register s32 i; - u8 aa_lev = surf->AALevel; a = (col>>24)&0xFF; col_no_a = col&0x00FFFFFF; for (i=0; ipitch_x), surf->pitch_x, spans[i].len); @@ -142,10 +138,8 @@ void evg_565_fill_var(s32 y, s32 count, EVG_Span *spans, EVGSurface *surf) register s32 i, x; register u32 len; register u32 *col; - u8 aa_lev = surf->AALevel; for (i=0; isten->fill_run(surf->sten, surf, spans[i].x, y, len); diff --git a/modules/soft_raster/raster_argb.c b/modules/soft_raster/raster_argb.c index a0b37e3..f3ab03d 100644 --- a/modules/soft_raster/raster_argb.c +++ b/modules/soft_raster/raster_argb.c @@ -99,7 +99,6 @@ void evg_bgra_fill_const(s32 y, s32 count, EVG_Span *spans, EVGSurface *surf) s32 i, x; u32 len; u8 col_a, col_r, col_g, col_b; - u8 aa_lev = surf->AALevel; col_a = GF_COL_A(col); col_r = GF_COL_R(col); @@ -107,7 +106,6 @@ void evg_bgra_fill_const(s32 y, s32 count, EVG_Span *spans, EVGSurface *surf) col_b = GF_COL_B(col); col_no_a = col & 0x00FFFFFF; for (i=0; ipitch_x; len = spans[i].len; @@ -132,13 +130,11 @@ void evg_bgra_fill_const_a(s32 y, s32 count, EVG_Span *spans, EVGSurface *surf) u8 *dst = surf->pixels + y * surf->pitch_y; u32 col = surf->fill_col; u32 a, fin, col_no_a; - u8 aa_lev = surf->AALevel; s32 i; a = (col>>24)&0xFF; col_no_a = col & 0x00FFFFFF; for (i=0; ipitch_x*spans[i].x, surf->pitch_x, spans[i].len); @@ -153,10 +149,8 @@ void evg_bgra_fill_var(s32 y, s32 count, EVG_Span *spans, EVGSurface *surf) s32 i, x; u32 len; u32 *col, _col; - u8 aa_lev = surf->AALevel; for (i=0; isten->fill_run(surf->sten, surf, spans[i].x, y, len); @@ -275,7 +269,6 @@ void evg_bgrx_fill_const(s32 y, s32 count, EVG_Span *spans, EVGSurface *surf) u8 *dst = surf->pixels + y * surf->pitch_y; s32 i, x; u32 len; - u8 aa_lev = surf->AALevel; col_no_a = col & 0x00FFFFFF; col_r = GF_COL_R(col); @@ -283,7 +276,6 @@ void evg_bgrx_fill_const(s32 y, s32 count, EVG_Span *spans, EVGSurface *surf) col_b = GF_COL_B(col); for (i=0; ipitch_x; len = spans[i].len; @@ -307,13 +299,11 @@ void evg_bgrx_fill_const_a(s32 y, s32 count, EVG_Span *spans, EVGSurface *surf) u8 *dst = surf->pixels + y * surf->pitch_y; u32 col = surf->fill_col; u32 a, fin, col_no_a; - u8 aa_lev = surf->AALevel; s32 i; a = (col>>24)&0xFF; col_no_a = col & 0x00FFFFFF; for (i=0; ipitch_x*spans[i].x, surf->pitch_x, spans[i].len); @@ -328,10 +318,8 @@ void evg_bgrx_fill_var(s32 y, s32 count, EVG_Span *spans, EVGSurface *surf) s32 i, x; u32 len; u32 *col; - u8 aa_lev = surf->AALevel; for (i=0; isten->fill_run(surf->sten, surf, spans[i].x, y, len); @@ -403,7 +391,6 @@ void evg_rgbx_fill_const(s32 y, s32 count, EVG_Span *spans, EVGSurface *surf) u8 r, g, b; s32 i, x; u32 len; - u8 aa_lev = surf->AALevel; col_no_a = col & 0x00FFFFFF; r = GF_COL_R(col); @@ -412,7 +399,6 @@ void evg_rgbx_fill_const(s32 y, s32 count, EVG_Span *spans, EVGSurface *surf) for (i=0; ipitch_x; len = spans[i].len; @@ -436,13 +422,11 @@ void evg_rgbx_fill_const_a(s32 y, s32 count, EVG_Span *spans, EVGSurface *surf) u8 *dst = surf->pixels + y * surf->pitch_y; u32 col = surf->fill_col; u32 a, fin, col_no_a; - u8 aa_lev = surf->AALevel; s32 i; a = (col>>24)&0xFF; col_no_a = col & 0x00FFFFFF; for (i=0; ipitch_x*spans[i].x, surf->pitch_x, spans[i].len); @@ -457,10 +441,8 @@ void evg_rgbx_fill_var(s32 y, s32 count, EVG_Span *spans, EVGSurface *surf) s32 i, x; u32 len; u32 *col, _col; - u8 aa_lev = surf->AALevel; for (i=0; isten->fill_run(surf->sten, surf, spans[i].x, y, len); @@ -585,7 +567,6 @@ void evg_rgba_fill_const(s32 y, s32 count, EVG_Span *spans, EVGSurface *surf) char *p; s32 i; u32 len; - u8 aa_lev = surf->AALevel; a = GF_COL_A(col); r = GF_COL_R(col); @@ -594,7 +575,6 @@ void evg_rgba_fill_const(s32 y, s32 count, EVG_Span *spans, EVGSurface *surf) col_no_a = col & 0x00FFFFFF; for (i=0; ipitch_x; len = spans[i].len; @@ -618,14 +598,12 @@ void evg_rgba_fill_const_a(s32 y, s32 count, EVG_Span *spans, EVGSurface *surf) { u8 *dst = surf->pixels + y * surf->pitch_y; u32 a, fin, col_no_a; - u8 aa_lev = surf->AALevel; s32 i; a = GF_COL_A(surf->fill_col); col_no_a = surf->fill_col & 0x00FFFFFF; for (i=0; ipitch_x, surf->pitch_x, spans[i].len); @@ -641,10 +619,8 @@ void evg_rgba_fill_var(s32 y, s32 count, EVG_Span *spans, EVGSurface *surf) s32 i; u32 len; u32 *col; - u8 aa_lev = surf->AALevel; for (i=0; ipitch_x; len = spans[i].len; spanalpha = spans[i].coverage; diff --git a/modules/soft_raster/raster_rgb.c b/modules/soft_raster/raster_rgb.c index c151980..34fe846 100644 --- a/modules/soft_raster/raster_rgb.c +++ b/modules/soft_raster/raster_rgb.c @@ -81,7 +81,6 @@ void evg_rgb_fill_const(s32 y, s32 count, EVG_Span *spans, EVGSurface *surf) char *p; s32 i; u32 len, r, g, b; - u8 aa_lev = surf->AALevel; r = GF_COL_R(col); g = GF_COL_G(col); @@ -89,7 +88,6 @@ void evg_rgb_fill_const(s32 y, s32 count, EVG_Span *spans, EVGSurface *surf) col_no_a = col & 0x00FFFFFF; for (i=0; ipitch_x; @@ -112,14 +110,11 @@ void evg_rgb_fill_const_a(s32 y, s32 count, EVG_Span *spans, EVGSurface *surf) { char *dst = surf->pixels + y * surf->pitch_y; u32 col = surf->fill_col; - u32 a, fin, col_no_a; + u32 a, fin; s32 i; - u8 aa_lev = surf->AALevel; a = (col>>24)&0xFF; - col_no_a = col & 0x00FFFFFF; for (i=0; ipitch_x * spans[i].x, surf->pitch_x, spans[i].len); @@ -133,13 +128,10 @@ void evg_rgb_fill_var(s32 y, s32 count, EVG_Span *spans, EVGSurface *surf) u8 spanalpha, col_a; s32 i; s32 x; - u32 len, bpp; + u32 len; u32 *col; - u8 aa_lev = surf->AALevel; - bpp = surf->BPP; for (i=0; isten->fill_run(surf->sten, surf, spans[i].x, y, len); @@ -241,7 +233,6 @@ void evg_bgr_fill_const(s32 y, s32 count, EVG_Span *spans, EVGSurface *surf) char *p; s32 i; u32 len, r, g, b; - u8 aa_lev = surf->AALevel; r = GF_COL_R(col); g = GF_COL_G(col); @@ -249,7 +240,6 @@ void evg_bgr_fill_const(s32 y, s32 count, EVG_Span *spans, EVGSurface *surf) col_no_a = col & 0x00FFFFFF; for (i=0; ipitch_x; @@ -274,12 +264,10 @@ void evg_bgr_fill_const_a(s32 y, s32 count, EVG_Span *spans, EVGSurface *surf) u32 col = surf->fill_col; u32 a, fin, col_no_a; s32 i; - u8 aa_lev = surf->AALevel; a = (col>>24)&0xFF; col_no_a = col & 0x00FFFFFF; for (i=0; ipitch_x * spans[i].x, surf->pitch_x, spans[i].len); @@ -294,10 +282,8 @@ void evg_bgr_fill_var(s32 y, s32 count, EVG_Span *spans, EVGSurface *surf) s32 i, x; u32 len; u32 *col, _col; - u8 aa_lev = surf->AALevel; for (i=0; iAALevel; col_no_a = surf->fill_col; - for (i=0; iraster_fill_run_alpha(surf->raster_cbk, spans[i].x, y, spans[i].len, col_no_a, a); @@ -379,12 +362,9 @@ void evg_user_fill_const_a(s32 y, s32 count, EVG_Span *spans, EVGSurface *surf) { u32 col, a, col_no_a; s32 i; - u8 aa_lev = surf->AALevel; - a = (surf->fill_col>>24)&0xFF; col_no_a = surf->fill_col | 0xFF000000; for (i=0; iraster_fill_run_alpha(surf->raster_cbk, spans[i].x, y, spans[i].len, col_no_a, col); } @@ -394,13 +374,10 @@ void evg_user_fill_var(s32 y, s32 count, EVG_Span *spans, EVGSurface *surf) { u8 spanalpha, col_a, a; s32 i; - u32 x, len, bpp; + u32 x, len; u32 *col; - u8 aa_lev = surf->AALevel; - bpp = surf->BPP; for (i=0; iAALevel = 1;/*don't draw pixels with 0 alpha...*/ surf->texture_filter = GF_TEXTURE_FILTER_HIGH_QUALITY; break; case GF_RASTER_MID: - surf->AALevel = 90; surf->texture_filter = GF_TEXTURE_FILTER_HIGH_QUALITY; break; case GF_RASTER_HIGH_SPEED: default: - surf->AALevel = 254; surf->texture_filter = GF_TEXTURE_FILTER_HIGH_SPEED; break; } diff --git a/modules/svg_in/svg_in.c b/modules/svg_in/svg_in.c index cfa5c3b..c810e25 100644 --- a/modules/svg_in/svg_in.c +++ b/modules/svg_in/svg_in.c @@ -205,7 +205,7 @@ static GF_Err SVG_ProcessData(GF_SceneDecoder *plug, const char *inBuffer, u32 i size = gf_bs_read_u32(bs); nb_bytes = 6; } -// fwrite( inBuffer + pos + nb_bytes + 1, 1, size - 1, f ); +// gf_fwrite( inBuffer + pos + nb_bytes + 1, 1, size - 1, f ); dims_hdr = gf_bs_read_u8(bs); prev = buf2[pos + nb_bytes + size]; @@ -230,13 +230,14 @@ static GF_Err SVG_ProcessData(GF_SceneDecoder *plug, const char *inBuffer, u32 i } exit: - if ((svgin->scene->graph_attached!=1) && (gf_sg_get_root_node(svgin->loader.scene_graph)!=NULL) ) { + if ((e>=GF_OK) && (svgin->scene->graph_attached!=1) && (gf_sg_get_root_node(svgin->loader.scene_graph)!=NULL) ) { gf_scene_attach_to_compositor(svgin->scene); } /*prepare for next playback*/ - if (e==GF_EOS) { + if (e) { gf_sm_load_done(&svgin->loader); svgin->loader.fileName = NULL; + e = GF_EOS; } return e; } diff --git a/modules/timedtext/timedtext_in.c b/modules/timedtext/timedtext_in.c index f16519e..d4a8c5c 100644 --- a/modules/timedtext/timedtext_in.c +++ b/modules/timedtext/timedtext_in.c @@ -188,6 +188,9 @@ void TTIn_download_file(GF_InputService *plug, const char *url) if (!tti->dnload) { tti->needs_connection = 0; gf_term_on_connect(tti->service, NULL, GF_NOT_SUPPORTED); + } else { + /*start our download (threaded)*/ + gf_dm_sess_process(tti->dnload); } /*service confirm is done once fetched*/ } diff --git a/modules/ui_rec/ui_rec.c b/modules/ui_rec/ui_rec.c index 4ddbd58..b34dd7c 100644 --- a/modules/ui_rec/ui_rec.c +++ b/modules/ui_rec/ui_rec.c @@ -152,7 +152,7 @@ static Bool uir_process(GF_TermExt *termext, u32 action, void *param) opt = gf_modules_get_option((GF_BaseInterface*)termext, "UIRecord", "Mode"); if (!opt) return 0; uifile = gf_modules_get_option((GF_BaseInterface*)termext, "UIRecord", "File"); - if (!opt) return 0; + if (!uifile) return 0; if (!strcmp(opt, "Play")) { uir->uif = gf_f64_open(uifile, "rb"); @@ -166,7 +166,7 @@ static Bool uir_process(GF_TermExt *termext, u32 action, void *param) uir_load_event(uir); } else if (!strcmp(opt, "Record")) { - uir->uif = fopen(uifile, "wb"); + uir->uif = gf_f64_open(uifile, "wb"); if (!uir->uif) return 0; uir->bs = gf_bs_from_file(uir->uif, GF_BITSTREAM_WRITE); diff --git a/modules/validator/README.TXT b/modules/validator/README.TXT new file mode 100644 index 0000000..8fb86ac --- /dev/null +++ b/modules/validator/README.TXT @@ -0,0 +1,41 @@ +The validator allows recording user interactions and taking snapshots when playing back interactive content; and replaying the content simulating the interactions, generating and comparing snapshots. To activate it you need to modify the GPAC configuration as follows: + +Add a section: +[Validator] +Mode=Play + +Mode can be: Play, Record, or Disable + +The validator will try to load a single file to be tested or a playlist. +Single files are indicated with: +XVS=C:\Users\Cyril Concolato\code\tsi_svn\trunk\gpac_public\regression_tests\svg\file.xvs +XVS means XML Validation Sequence. An example of XVS is: + + + + + + + + + +Playlist of XVS are givin with: +XVL=C:\Users\Cyril Concolato\code\tsi_svn\trunk\gpac_public\regression_tests\svg\svg1.1f2.xvl +XVL means XML Validation List. An example of XVL is: + + + + + + + + +When recording and playing back, the GPAC player rendering is switched to 5 FPS without antialiasing. + +When recording, you can trigger some actions as follows: +CTRL+Insert: takes a snapshot and records it as a PNG. In replay mode, a PNG will be taken at the same scene time and the PNG will be compared. +CTRL+Fin: Quit +CTRL+F1: Takes snapshot at next frame change. +Page Down: Ends current XVS and switches to next in the XVL + +Note: after recording, upon closing the player, the Validator mode is automatically switched to Play to avoid losing recorded interactions. \ No newline at end of file diff --git a/modules/validator/validator.c b/modules/validator/validator.c new file mode 100644 index 0000000..7863caf --- /dev/null +++ b/modules/validator/validator.c @@ -0,0 +1,1013 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Authors: Cyril Concolato + * Copyright (c) 2010 Telecom ParisTech + * All rights reserved + * + * This file is part of GPAC / Test Suite Validator Recorder sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include +#include +#include + +typedef struct __validation_module +{ + GF_Terminal *term; + + Bool is_recording; + char *prev_fps; + char *prev_alias; + + /* Clock used to synchronize events in recording and playback*/ + GF_Clock *ck; + + /* Next event to process */ + Bool next_event_snapshot; + GF_Event next_event; + u32 xvs_event_index; + u32 next_time; + Bool evt_loaded; + + GF_VideoListener video_listener; + + /* XML Validation List (the list of files to be tested) */ + char *xvl_filename; + GF_DOMParser *xvl_parser; + GF_XMLNode *xvl_node; + GF_XMLNode *xvs_node_in_xvl; + u32 xvl_node_index; + + /* Pointer to the current validation script file being tested */ + char *xvs_filename; + GF_DOMParser *xvs_parser; + GF_XMLNode *xvs_node; + Bool xvs_result; + + /* test sequence */ + char *test_base; + char *test_filename; + + Bool snapshot_next_frame; + u32 snapshot_number; + + GF_TermEventFilter evt_filter; +} GF_Validator; + +static void validator_xvs_close(GF_Validator *validator); +static Bool validator_xvs_next(GF_Validator *validator, Bool reverse); + +static void validator_xvs_add_snapshot_node(GF_Validator *validator, const char *filename, u32 scene_time) +{ + GF_XMLNode *snap_node; + GF_XMLAttribute *att; + GF_SAFEALLOC(snap_node, GF_XMLNode); + snap_node->name = gf_strdup("snapshot"); + snap_node->attributes = gf_list_new(); + GF_SAFEALLOC(att, GF_XMLAttribute); + att->name = gf_strdup("time"); + att->value = gf_malloc(100); + sprintf(att->value, "%d", scene_time); + gf_list_add(snap_node->attributes, att); + GF_SAFEALLOC(att, GF_XMLAttribute); + att->name = gf_strdup("image"); + att->value = gf_strdup(filename); + gf_list_add(snap_node->attributes, att); + gf_list_add(validator->xvs_node->content, snap_node); + + /* adding an extra text node for line break in serialization */ + GF_SAFEALLOC(snap_node, GF_XMLNode); + snap_node->type = GF_XML_TEXT_TYPE; + snap_node->name = gf_strdup("\n"); + gf_list_add(validator->xvs_node->content, snap_node); +} + +static char *validator_get_snapshot_name(char *test_filename, Bool is_reference, u32 number) +{ + char *dot; + char dumpname[GF_MAX_PATH]; + dot = strrchr(test_filename, '.'); + dot[0] = 0; + sprintf(dumpname, "%s-%s-%03d.png", test_filename, (is_reference?"reference":"newest"), number); + dot[0] = '.'; + return gf_strdup(dumpname); +} + +static char *validator_create_snapshot(GF_Validator *validator) +{ + GF_Err e; + GF_VideoSurface fb; + GF_Terminal *term = validator->term; + char *dumpname; + + dumpname = validator_get_snapshot_name(validator->test_filename, validator->is_recording, validator->snapshot_number); + + e = gf_term_get_screen_buffer(term, &fb); + if (e) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[Validator] Error dumping screen buffer %s\n", gf_error_to_string(e))); + } else { + u32 dst_size = fb.width*fb.height*3; + char *dst=gf_malloc(sizeof(char)*dst_size); + + e = gf_img_png_enc(fb.video_buffer, fb.width, fb.height, fb.pitch_y, fb.pixel_format, dst, &dst_size); + if (e) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[Validator] Error encoding PNG %s\n", gf_error_to_string(e))); + } else { + FILE *png = gf_f64_open(dumpname, "wb"); + if (!png) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[Validator] Error writing file %s\n", dumpname)); + } else { + gf_fwrite(dst, dst_size, 1, png); + fclose(png); + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("[Validator] Writing file %s\n", dumpname)); + } + } + if (dst) gf_free(dst); + gf_term_release_screen_buffer(term, &fb); + } + validator->snapshot_number++; + return dumpname; +} + +static Bool validator_compare_snapshots(GF_Validator *validator) +{ + char *ref_name, *new_name; + u32 ref_width, ref_height, ref_pixel_format, ref_data_size; + u32 new_width, new_height, new_pixel_format, new_data_size; + char *ref_data, *new_data; + Bool result = 0; + GF_Err e; + u32 i; + + u32 snap_number = validator->snapshot_number - 1; + ref_name = validator_get_snapshot_name(validator->test_filename, 1, snap_number); + new_name = validator_get_snapshot_name(validator->test_filename, 0, snap_number); + + e = gf_img_file_dec(ref_name, NULL, &ref_width, &ref_height, &ref_pixel_format, &ref_data, &ref_data_size); + if (e) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[Validator] Cannot decode PNG file %s\n", ref_name)); + goto end; + } + e = gf_img_file_dec(new_name, NULL, &new_width, &new_height, &new_pixel_format, &new_data, &new_data_size); + if (e) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[Validator] Cannot decode PNG file %s\n", new_name)); + goto end; + } + if (ref_width != new_width) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[Validator] Snapshots have different widths: %d vs %d\n", ref_width, new_width)); + goto end; + } + if (ref_height != new_height) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[Validator] Snapshots have different heights: %d vs %d\n", ref_height, new_height)); + goto end; + } + if (ref_pixel_format != new_pixel_format) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[Validator] Snapshots have different pixel formats: %d vs %d\n", ref_pixel_format, new_pixel_format)); + goto end; + } + + for (i = 0; isnapshot_next_frame) { + char *snap_name = validator_create_snapshot(validator); + validator_xvs_add_snapshot_node(validator, snap_name, gf_clock_time(validator->ck)); + gf_free(snap_name); + validator->snapshot_next_frame = 0; + } +} + +static void validator_on_video_reconfig(void *udta, u32 width, u32 height) +{ +} + +Bool validator_on_event_play(GF_Validator *validator, GF_Event *event, Bool consumed_by_compositor) +{ + switch (event->type) { + case GF_EVENT_CONNECT: + if (event->connect.is_connected) { + gf_sc_add_video_listener(validator->term->compositor, &validator->video_listener); + validator->ck = validator->term->root_scene->scene_codec ? + validator->term->root_scene->scene_codec->ck : + validator->term->root_scene->dyn_ck; + } + break; + case GF_EVENT_CLICK: + case GF_EVENT_MOUSEUP: + case GF_EVENT_MOUSEDOWN: + case GF_EVENT_MOUSEOVER: + case GF_EVENT_MOUSEOUT: + case GF_EVENT_MOUSEMOVE: + case GF_EVENT_MOUSEWHEEL: + case GF_EVENT_KEYDOWN: + case GF_EVENT_TEXTINPUT: + return 1; + case GF_EVENT_KEYUP: + if ((event->key.key_code == GF_KEY_END)&&(event->key.flags & GF_KEY_MOD_CTRL)) { + GF_Event evt; + evt.type = GF_EVENT_QUIT; + validator->term->compositor->video_out->on_event(validator->term->compositor->video_out->evt_cbk_hdl, &evt); + } + return 1; + } + return 0; +} + +static void validator_xvs_add_event_dom(GF_Validator *validator, GF_Event *event) +{ + GF_XMLNode *evt_node; + GF_XMLAttribute *att; + + GF_SAFEALLOC(evt_node, GF_XMLNode); + + switch (event->type) { + case GF_EVENT_CLICK: + case GF_EVENT_MOUSEUP: + case GF_EVENT_MOUSEDOWN: + case GF_EVENT_MOUSEOVER: + case GF_EVENT_MOUSEOUT: + case GF_EVENT_MOUSEMOVE: + case GF_EVENT_MOUSEWHEEL: + case GF_EVENT_KEYUP: + case GF_EVENT_KEYDOWN: + case GF_EVENT_TEXTINPUT: + evt_node->name = gf_strdup(gf_dom_event_get_name(event->type)); + break; + } + + if (!evt_node->name) { + gf_free(evt_node); + return; + } + + evt_node->attributes = gf_list_new(); + + GF_SAFEALLOC(att, GF_XMLAttribute); + att->name = gf_strdup("time"); + att->value = gf_malloc(100); + sprintf(att->value, "%f", gf_scene_get_time(validator->term->root_scene)*1000); + gf_list_add(evt_node->attributes, att); + + switch (event->type) { + case GF_EVENT_CLICK: + case GF_EVENT_MOUSEUP: + case GF_EVENT_MOUSEDOWN: + case GF_EVENT_MOUSEOVER: + case GF_EVENT_MOUSEOUT: + case GF_EVENT_MOUSEMOVE: + case GF_EVENT_MOUSEWHEEL: + if (event->type == GF_EVENT_MOUSEDOWN || event->type == GF_EVENT_MOUSEUP) { + GF_SAFEALLOC(att, GF_XMLAttribute); + att->name = gf_strdup("button"); + switch (event->mouse.button) { + case 0: + att->value = gf_strdup("Left"); + break; + case 1: + att->value = gf_strdup("Middle"); + break; + case 2: + att->value = gf_strdup("Right"); + break; + } + gf_list_add(evt_node->attributes, att); + } + GF_SAFEALLOC(att, GF_XMLAttribute); + att->name = gf_strdup("x"); + att->value = gf_malloc(100); + sprintf(att->value, "%d", event->mouse.x); + gf_list_add(evt_node->attributes, att); + GF_SAFEALLOC(att, GF_XMLAttribute); + att->name = gf_strdup("y"); + att->value = gf_malloc(100); + sprintf(att->value, "%d", event->mouse.y); + gf_list_add(evt_node->attributes, att); + if (event->type == GF_EVENT_MOUSEWHEEL) { + GF_SAFEALLOC(att, GF_XMLAttribute); + att->name = gf_strdup("wheel_pos"); + att->value = gf_malloc(100); + sprintf(att->value, "%f", event->mouse.wheel_pos); + gf_list_add(evt_node->attributes, att); + } + if (event->mouse.key_states & GF_KEY_MOD_SHIFT) { + GF_SAFEALLOC(att, GF_XMLAttribute); + att->name = gf_strdup("shift"); + att->value = gf_strdup("true"); + gf_list_add(evt_node->attributes, att); + } + if (event->mouse.key_states & GF_KEY_MOD_CTRL) { + GF_SAFEALLOC(att, GF_XMLAttribute); + att->name = gf_strdup("ctrl"); + att->value = gf_strdup("true"); + gf_list_add(evt_node->attributes, att); + } + if (event->mouse.key_states & GF_KEY_MOD_ALT) { + GF_SAFEALLOC(att, GF_XMLAttribute); + att->name = gf_strdup("alt"); + att->value = gf_strdup("true"); + gf_list_add(evt_node->attributes, att); + } + break; + /*Key Events*/ + case GF_EVENT_KEYUP: + case GF_EVENT_KEYDOWN: + case GF_EVENT_LONGKEYPRESS: + GF_SAFEALLOC(att, GF_XMLAttribute); + att->name = gf_strdup("key_identifier"); + att->value = gf_strdup(gf_dom_get_key_name(event->key.key_code)); + gf_list_add(evt_node->attributes, att); + if (event->key.flags & GF_KEY_MOD_SHIFT) { + GF_SAFEALLOC(att, GF_XMLAttribute); + att->name = gf_strdup("shift"); + att->value = gf_strdup("true"); + gf_list_add(evt_node->attributes, att); + } + if (event->key.flags & GF_KEY_MOD_CTRL) { + GF_SAFEALLOC(att, GF_XMLAttribute); + att->name = gf_strdup("ctrl"); + att->value = gf_strdup("true"); + gf_list_add(evt_node->attributes, att); + } + if (event->key.flags & GF_KEY_MOD_ALT) { + GF_SAFEALLOC(att, GF_XMLAttribute); + att->name = gf_strdup("alt"); + att->value = gf_strdup("true"); + gf_list_add(evt_node->attributes, att); + } + break; + case GF_EVENT_TEXTINPUT: + GF_SAFEALLOC(att, GF_XMLAttribute); + att->name = gf_strdup("unicode-char"); + att->value = gf_malloc(100); + sprintf(att->value, "%d", event->character.unicode_char); + gf_list_add(evt_node->attributes, att); + break; + } + gf_list_add(validator->xvs_node->content, evt_node); + /* adding an extra text node for line break in serialization */ + GF_SAFEALLOC(evt_node, GF_XMLNode); + evt_node->type = GF_XML_TEXT_TYPE; + evt_node->name = gf_strdup("\n"); + gf_list_add(validator->xvs_node->content, evt_node); +} + +Bool validator_on_event_record(GF_Validator *validator, GF_Event *event, Bool consumed_by_compositor) +{ + Bool rec_event = 1; + switch (event->type) { + case GF_EVENT_CONNECT: + if (event->connect.is_connected) { + gf_sc_add_video_listener(validator->term->compositor, &validator->video_listener); + validator->ck = validator->term->root_scene->scene_codec ? validator->term->root_scene->scene_codec->ck : validator->term->root_scene->dyn_ck; + } + break; + case GF_EVENT_KEYDOWN: + if (event->key.key_code == GF_KEY_INSERT) { + rec_event = 0; + } else if (event->key.key_code == GF_KEY_PAGEDOWN) { + rec_event = 0; + } else if (event->key.key_code == GF_KEY_PAGEUP) { + rec_event = 0; + } else if (event->key.key_code == GF_KEY_END) { + rec_event = 0; + } else if (event->key.key_code == GF_KEY_CONTROL) { + rec_event = 0; + } else if (event->key.flags & GF_KEY_MOD_CTRL) { + rec_event = 0; + } + break; + case GF_EVENT_KEYUP: + if (event->key.flags & GF_KEY_MOD_CTRL) { + rec_event = 0; + if (event->key.key_code == GF_KEY_INSERT) { + char *snap_name = validator_create_snapshot(validator); + validator_xvs_add_snapshot_node(validator, snap_name, gf_clock_time(validator->ck)); + gf_free(snap_name); + } else if (event->key.key_code == GF_KEY_END) { + GF_Event evt; + evt.type = GF_EVENT_QUIT; + validator->term->compositor->video_out->on_event(validator->term->compositor->video_out->evt_cbk_hdl, &evt); + } else if (event->key.key_code == GF_KEY_F1) { + validator->snapshot_next_frame = 1; + } + } else if (event->key.key_code == GF_KEY_PAGEDOWN) { + rec_event = 0; + validator_xvs_close(validator); + gf_term_disconnect(validator->term); + gf_sc_remove_video_listener(validator->term->compositor, &validator->video_listener); + validator_xvs_next(validator, 0); + } else if (event->key.key_code == GF_KEY_PAGEUP) { + rec_event = 0; + validator_xvs_close(validator); + gf_term_disconnect(validator->term); + gf_sc_remove_video_listener(validator->term->compositor, &validator->video_listener); + validator_xvs_next(validator, 1); + } else if (event->key.key_code == GF_KEY_CONTROL) { + rec_event = 0; + } + break; + } + if (rec_event) { + validator_xvs_add_event_dom(validator, event); + } + return 0; +} + +static void validator_xvl_open(GF_Validator *validator) +{ + GF_Err e; + u32 att_index; + GF_XMLAttribute *att; + validator->xvl_parser = gf_xml_dom_new(); + e = gf_xml_dom_parse(validator->xvl_parser, validator->xvl_filename, NULL, NULL); + if (e != GF_OK) { + gf_xml_dom_del(validator->xvl_parser); + validator->xvl_parser = NULL; + return; + } + validator->xvl_node = gf_xml_dom_get_root(validator->xvl_parser); + if (!validator->xvl_node) { + gf_xml_dom_del(validator->xvl_parser); + validator->xvl_parser = NULL; + return; + } + att_index = 0; + while (1) { + att = gf_list_get(validator->xvl_node->attributes, att_index); + if (!att) break; + if (!strcmp(att->name, "content-base")) { + validator->test_base = gf_strdup(att->value); + } + att_index++; + } +} + +static void validator_xvl_close(GF_Validator *validator) +{ + if (validator->xvl_parser) { + /* writing the validation results */ + if (!validator->is_recording) { + FILE *xvl_fp; + char *xvl_content; + char result_filename[GF_MAX_PATH]; + char *dot; + xvl_content = gf_xml_dom_serialize(validator->xvl_node, 0); + dot = strrchr(validator->xvl_filename, '.'); + dot[0] = 0; + sprintf(result_filename, "%s-result.xml", validator->xvl_filename); + dot[0] = '.'; + xvl_fp = gf_f64_open(result_filename, "wt"); + gf_fwrite(xvl_content, strlen(xvl_content), 1, xvl_fp); + fclose(xvl_fp); + gf_free(xvl_content); + } + gf_xml_dom_del(validator->xvl_parser); + validator->xvl_parser = NULL; + validator->xvl_filename = NULL; + } +} + +static void validator_xvl_get_next_xvs(GF_Validator *validator, Bool reverse) +{ + u32 xvl_att_index; + validator->xvs_node = NULL; + validator->xvs_filename = NULL; + validator->test_filename = NULL; + while (1) { + validator->xvs_node_in_xvl = gf_list_get(validator->xvl_node->content, validator->xvl_node_index); + if (!validator->xvs_node_in_xvl) { + return; + } + if (validator->xvs_node_in_xvl->type != GF_XML_NODE_TYPE) { + if (!reverse) validator->xvl_node_index++; + else validator->xvl_node_index--; + continue; + } + xvl_att_index = 0; + while(1) { + GF_XMLAttribute *att = gf_list_get(validator->xvs_node_in_xvl->attributes, xvl_att_index); + if (!att) break; + if (!strcmp(att->name, "scenario")) { + validator->xvs_filename = att->value; + } else if (!strcmp(att->name, "content")) { + validator->test_filename = att->value; + } + xvl_att_index++; + } + if (!reverse) validator->xvl_node_index++; + else validator->xvl_node_index--; + break; + } +} + +static Bool validator_xvs_open(GF_Validator *validator) +{ + GF_Err e; + GF_LOG(GF_LOG_DEBUG, GF_LOG_MODULE, ("[Validator] Opening Validation Script: %s\n", validator->xvs_filename)); + validator->snapshot_number = 0; + validator->xvs_parser = gf_xml_dom_new(); + e = gf_xml_dom_parse(validator->xvs_parser, validator->xvs_filename, NULL, NULL); + if (e != GF_OK) { + if (validator->is_recording) { + GF_SAFEALLOC(validator->xvs_node, GF_XMLNode); + validator->xvs_node->name = gf_strdup("TestValidationScript"); + validator->xvs_node->attributes = gf_list_new(); + validator->xvs_node->content = gf_list_new(); + } else { + gf_xml_dom_del(validator->xvs_parser); + validator->xvs_parser = NULL; + return 0; + } + } else { + validator->xvs_node = gf_xml_dom_get_root(validator->xvs_parser); + } + /* Get the file name from the XVS if not found in the XVL */ + if (!validator->test_filename) { + GF_XMLAttribute *att; + GF_XMLAttribute *att_file; + u32 att_index = 0; + att_file = NULL; + while (1) { + att = gf_list_get(validator->xvs_node->attributes, att_index); + if (!att) { + break; + } else if (!strcmp(att->name, "file")) { + att_file = att; + } + att_index++; + } + if (!att_file) { + gf_xml_dom_del(validator->xvs_parser); + validator->xvs_parser = NULL; + validator->xvs_node = NULL; + return 0; + } else { + char *sep; + sep = strrchr(att_file->value, GF_PATH_SEPARATOR); + if (!sep) { + validator->test_filename = att_file->value; + } else { + sep[0] = 0; + validator->test_base = gf_strdup(att_file->value); + sep[0] = GF_PATH_SEPARATOR; + validator->test_filename = sep+1; + } + } + } + if (validator->is_recording) { + GF_XMLNode *node; + /* Removing prerecorded interactions */ + while (gf_list_count(validator->xvs_node->content)) { + GF_XMLNode *child = (GF_XMLNode *)gf_list_last(validator->xvs_node->content); + gf_list_rem_last(validator->xvs_node->content); + gf_xml_dom_node_del(child); + } + /* adding an extra text node for line break in serialization */ + GF_SAFEALLOC(node, GF_XMLNode); + node->type = GF_XML_TEXT_TYPE; + node->name = gf_strdup("\n"); + gf_list_add(validator->xvs_node->content, node); + } else { + validator->xvs_result = 1; + } + return 1; +} + +static void validator_xvs_close(GF_Validator *validator) +{ + if (validator->xvs_parser) { + if (validator->is_recording) { + FILE *xvs_fp; + char *xvs_content; + char filename[100]; + GF_XMLAttribute *att; + GF_XMLAttribute *att_file = NULL; + u32 att_index = 0; + while (1) { + att = gf_list_get(validator->xvs_node->attributes, att_index); + if (!att) { + break; + } else if (!strcmp(att->name, "file")) { + att_file = att; + } + att_index++; + } + + if (!att_file) { + GF_SAFEALLOC(att, GF_XMLAttribute); + att->name = gf_strdup("file"); + gf_list_add(validator->xvs_node->attributes, att); + } else { + att = att_file; + if (att->value) gf_free(att->value); + } + sprintf(filename, "%s%c%s", validator->test_base, GF_PATH_SEPARATOR, validator->test_filename); + att->value = gf_strdup(filename); + xvs_content = gf_xml_dom_serialize(validator->xvs_node, 0); + xvs_fp = gf_f64_open(validator->xvs_filename, "wt"); + gf_fwrite(xvs_content, strlen(xvs_content), 1, xvs_fp); + fclose(xvs_fp); + gf_free(xvs_content); + } else { + GF_LOG(GF_LOG_DEBUG, GF_LOG_MODULE, ("[Validator] XVS Result : %s\n", (validator->xvs_result?"Success":"Failure"))); + if (validator->xvs_node_in_xvl) { + GF_XMLAttribute *att; + GF_XMLAttribute *att_result = NULL; + u32 att_index = 0; + while (1) { + att = gf_list_get(validator->xvs_node_in_xvl->attributes, att_index); + if (!att) { + break; + } else if (!strcmp(att->name, "result")) { + att_result = att; + } + att_index++; + } + if (!att_result) { + GF_SAFEALLOC(att_result, GF_XMLAttribute); + att_result->name = gf_strdup("result"); + gf_list_add(validator->xvs_node_in_xvl->attributes, att_result); + } + if (att_result->value) gf_free(att_result->value); + att_result->value = gf_strdup(validator->xvs_result ? "pass" : "fail"); + } + } + gf_xml_dom_del(validator->xvs_parser); + validator->xvs_parser = NULL; + } + validator->xvs_node = NULL; + validator->xvs_node_in_xvl = NULL; + validator->xvs_filename = NULL; + validator->test_filename = NULL; + validator->ck = NULL; + validator->xvs_event_index = 0; + validator->snapshot_number = 0; +} + +static void validator_test_open(GF_Validator *validator) +{ + char filename[100]; + if (validator->test_base) + sprintf(filename, "%s%c%s", validator->test_base, GF_PATH_SEPARATOR, validator->test_filename); + else + sprintf(filename, "%s", validator->test_filename); + gf_sc_add_video_listener(validator->term->compositor, &validator->video_listener); + if (validator->is_recording) + validator->snapshot_next_frame = 1; + gf_term_connect(validator->term, filename); + validator->ck = validator->term->root_scene->scene_codec ? + validator->term->root_scene->scene_codec->ck : + validator->term->root_scene->dyn_ck; +} + +static Bool validator_xvs_next(GF_Validator *validator, Bool reverse) +{ + if (validator->xvl_node) { + validator_xvl_get_next_xvs(validator, reverse); + if (validator->xvs_filename) { + validator_xvs_open(validator); + if (!validator->xvs_node) { + return 0; + } + if (validator->test_filename) { + validator_test_open(validator); + } else { + validator_xvs_close(validator); + return 0; + } + } else { + return 0; + } + return 1; + } else { + return 0; + } +} + +static Bool validator_load_event(GF_Validator *validator) +{ + GF_XMLNode *event_node; + GF_XMLAttribute *att; + u32 att_index; + + memset(&validator->next_event, 0, sizeof(GF_Event)); + validator->evt_loaded = 0; + validator->next_event_snapshot = 0; + + if (!validator->xvs_node) return 0; + + while (1) { + event_node = gf_list_get(validator->xvs_node->content, validator->xvs_event_index); + if (!event_node) { + return 0; + } else if (event_node->type == GF_XML_NODE_TYPE) { + validator->xvs_event_index++; + break; + } else { + validator->xvs_event_index++; + } + } + + if (!strcmp(event_node->name, "snapshot")) { + validator->next_event_snapshot = 1; + } else { + validator->next_event.type = gf_dom_event_type_by_name(event_node->name); + if (validator->next_event.type == GF_EVENT_UNKNOWN) { + return 1; + } + } + + att_index = 0; + while (1) { + att = gf_list_get(event_node->attributes, att_index); + if (!att) break; + if (!strcmp(att->name, "time")) { + validator->next_time = atoi(att->value); + } else if (!strcmp(att->name, "button")) { + if (!strcmp(att->value, "Left")) { + validator->next_event.mouse.button = 0; + } else if (!strcmp(att->value, "Middle")) { + validator->next_event.mouse.button = 1; + } else if (!strcmp(att->value, "Right")) { + validator->next_event.mouse.button = 2; + } + } else if (!strcmp(att->name, "x")) { + validator->next_event.mouse.x = atoi(att->value); + } else if (!strcmp(att->name, "y")) { + validator->next_event.mouse.y = atoi(att->value); + } else if (!strcmp(att->name, "wheel_pos")) { + validator->next_event.mouse.wheel_pos = FLT2FIX(atof(att->value)); + } else if (!strcmp(att->name, "shift") && !strcmp(att->value, "true")) { + validator->next_event.mouse.key_states |= GF_KEY_MOD_SHIFT; + } else if (!strcmp(att->name, "alt") && !strcmp(att->value, "true")) { + validator->next_event.mouse.key_states |= GF_KEY_MOD_ALT; + } else if (!strcmp(att->name, "ctrl") && !strcmp(att->value, "true")) { + validator->next_event.mouse.key_states |= GF_KEY_MOD_CTRL; + } else if (!strcmp(att->name, "key_identifier")) { + validator->next_event.key.key_code = gf_dom_get_key_type(att->value); + } else if (!strcmp(att->name, "unicode-char")) { + validator->next_event.character.unicode_char = atoi(att->value); + } + att_index++; + } + validator->evt_loaded = 1; + return 1; +} + +static Bool validator_process(GF_TermExt *termext, u32 action, void *param) +{ + const char *opt; + GF_Validator *validator = termext->udta; + + switch (action) { + + /* Upon starting of the terminal, we parse (possibly an XVL file), an XVS file, and start the first test sequence */ + case GF_TERM_EXT_START: + validator->term = (GF_Terminal *) param; + + /* if the validator is loaded, we switch off anti-aliasing for image comparison and we put a low framerate, + but we store the previous value to restore it upon termination of the validator */ + opt = (char *)gf_modules_get_option((GF_BaseInterface*)termext, "Compositor", "FrameRate"); + if (opt) validator->prev_fps = gf_strdup(opt); + opt = (char *)gf_modules_get_option((GF_BaseInterface*)termext, "Compositor", "AntiAlias"); + if (opt) validator->prev_alias = gf_strdup(opt); + + /* Check if the validator should be loaded and in which mode */ + opt = gf_modules_get_option((GF_BaseInterface*)termext, "Validator", "Mode"); + if (!opt) { + GF_LOG(GF_LOG_DEBUG, GF_LOG_MODULE, ("Validator missing configuration, stopping.\n")); + return 0; + } else if (!strcmp(opt, "Play")) { + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("Validator starting in playback mode.\n")); + validator->is_recording = 0; + } else if (!strcmp(opt, "Record")) { + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("Validator starting in recording mode.\n")); + validator->is_recording = 1; + } else if (!strcmp(opt, "Disable")) { + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("Validator is disabled.\n")); + return 0; + } else { + GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("Validator configuration using wrong mode, stopping.\n")); + return 0; + } + + /* initializes the validator and starts */ + validator->xvs_filename = NULL; + validator->xvl_filename = (char *)gf_modules_get_option((GF_BaseInterface*)termext, "Validator", "XVL"); + if (!validator->xvl_filename) { + validator->xvs_filename = (char *)gf_modules_get_option((GF_BaseInterface*)termext, "Validator", "XVS"); + if (!validator->xvs_filename) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("Validator configuration without input, stopping.\n")); + return 0; + } else { + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("Validator using scenario file: %s\n", validator->xvs_filename)); + } + } else { + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("Validator using scenario playlist: %s\n", validator->xvl_filename)); + } + + /* since we changed parameters of the compositor, we need to trigger a reconfiguration */ + gf_modules_set_option((GF_BaseInterface*)termext, "Compositor", "FrameRate", "5.0"); + gf_modules_set_option((GF_BaseInterface*)termext, "Compositor", "AntiAlias", "None"); + gf_term_set_option(validator->term, GF_OPT_RELOAD_CONFIG, 1); + + validator->evt_filter.udta = validator; + if (!validator->is_recording) { + validator->evt_filter.on_event = validator_on_event_play; + termext->caps |= GF_TERM_EXTENSION_NOT_THREADED; + } else { + validator->evt_filter.on_event = validator_on_event_record; + } + gf_term_add_event_filter(validator->term, &validator->evt_filter); + validator->video_listener.udta = validator; + validator->video_listener.on_video_frame = validator_on_video_frame; + validator->video_listener.on_video_reconfig = validator_on_video_reconfig; + + /* TODO: if start returns 0, the module is not loaded, so the above init (filter registration) is not removed, + should probably return 1 all the time, to make sure stop is called */ + if (validator->xvl_filename) { + validator_xvl_open(validator); + if (!validator->xvl_node) { + return 0; + } + validator_xvs_next(validator, 0); + if (!validator->xvs_node) { + return 0; + } + } else if (validator->xvs_filename) { + validator_xvs_open(validator); + if (!validator->xvs_node) { + return 0; + } + if (validator->test_filename) { + validator_test_open(validator); + } else { + validator_xvs_close(validator); + return 0; + } + } else { + return 0; + } + if (!validator->is_recording) { + validator_load_event(validator); + } + return 1; + + /* when the terminal stops, we close the XVS parser and XVL parser if any, restore the config, + and free all validator data (the validator will be destroyed when the module is unloaded) + Note: we don't need to disconnect the terminal since it's already stopping */ + case GF_TERM_EXT_STOP: + gf_term_remove_event_filter(validator->term, &validator->evt_filter); + validator_xvs_close(validator); + validator_xvl_close(validator); + validator->term = NULL; + if (validator->test_base) { + gf_free(validator->test_base); + validator->test_base = NULL; + } + /*auto-disable the recording by default*/ + if (validator->is_recording) { + gf_modules_set_option((GF_BaseInterface*)termext, "Validator", "Mode", "Play"); + } else { + gf_modules_set_option((GF_BaseInterface*)termext, "Validator", "Mode", "Disable"); + } + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("Stopping validator\n")); + if (validator->prev_fps) { + gf_modules_set_option((GF_BaseInterface*)termext, "Compositor", "FrameRate", validator->prev_fps); + gf_free(validator->prev_fps); + validator->prev_fps = NULL; + } + if (validator->prev_alias) { + gf_modules_set_option((GF_BaseInterface*)termext, "Compositor", "AntiAlias", validator->prev_alias); + gf_free(validator->prev_alias); + validator->prev_alias = NULL; + } + break; + + /* When called in the main loop of the terminal, we don't do anything in the recording mode. + In the playing/validating mode, we need to check if an event needs to be dispatched or if snapshots need to be made, + until there is no more event, in which case we trigger either the load of the next XVS or the quit */ + case GF_TERM_EXT_PROCESS: + /* if the time is right, dispatch the event and load the next one */ + while (!validator->is_recording && validator->evt_loaded && validator->ck && (validator->next_time <= gf_clock_time(validator->ck) )) { + Bool has_more_events; + u32 diff = gf_clock_time(validator->ck) - validator->next_time; + //GF_LOG(GF_LOG_ERROR, GF_LOG_MODULE, ("[Validator] Time diff: evt_time=%d clock_time = %d, diff=%d\n", validator->next_time, gf_clock_time(validator->ck), diff)); + if (validator->next_event_snapshot) { + Bool res; + char *snap_name = validator_create_snapshot(validator); + gf_free(snap_name); + res = validator_compare_snapshots(validator); + validator->xvs_result &= res; + validator->next_event_snapshot = 0; + } else { + validator->term->compositor->video_out->on_event(validator->term->compositor->video_out->evt_cbk_hdl, &validator->next_event); + } + has_more_events = validator_load_event(validator); + if (!has_more_events) { + validator_xvs_close(validator); + gf_term_disconnect(validator->term); + gf_sc_remove_video_listener(validator->term->compositor, &validator->video_listener); + validator_xvs_next(validator, 0); + if (!validator->xvs_node) { + GF_Event evt; + evt.type = GF_EVENT_QUIT; + validator->term->compositor->video_out->on_event(validator->term->compositor->video_out->evt_cbk_hdl, &evt); + } else { + if (!validator->is_recording) { + validator_load_event(validator); + } + } + } + } + break; + } + return 0; +} + + +GF_TermExt *validator_new() +{ + GF_TermExt *dr; + GF_Validator *validator; + dr = (GF_TermExt*)gf_malloc(sizeof(GF_TermExt)); + memset(dr, 0, sizeof(GF_TermExt)); + GF_REGISTER_MODULE_INTERFACE(dr, GF_TERM_EXT_INTERFACE, "GPAC Test Validator", "gpac distribution"); + + GF_SAFEALLOC(validator, GF_Validator); + dr->process = validator_process; + dr->udta = validator; + return dr; +} + + +void validator_delete(GF_BaseInterface *ifce) +{ + GF_TermExt *dr = (GF_TermExt *) ifce; + GF_Validator *validator = dr->udta; + if (validator->prev_fps) gf_free(validator->prev_fps); + if (validator->prev_alias) gf_free(validator->prev_alias); + gf_free(validator); + gf_free(dr); +} + +GF_EXPORT +const u32 *QueryInterfaces() +{ + static u32 si [] = { + GF_TERM_EXT_INTERFACE, + 0 + }; + return si; +} + +GF_EXPORT +GF_BaseInterface *LoadInterface(u32 InterfaceType) +{ + if (InterfaceType == GF_TERM_EXT_INTERFACE) return (GF_BaseInterface *)validator_new(); + return NULL; +} + +GF_EXPORT +void ShutdownInterface(GF_BaseInterface *ifce) +{ + switch (ifce->InterfaceType) { + case GF_TERM_EXT_INTERFACE: + validator_delete(ifce); + break; + } +} diff --git a/modules/x11_out/Makefile b/modules/x11_out/Makefile index 7c111ce..0c1e0d3 100644 --- a/modules/x11_out/Makefile +++ b/modules/x11_out/Makefile @@ -25,26 +25,26 @@ endif ifeq ($(X11_LIB_PATH), ) else -LDFLAGS+=-L$(X11_LIB_PATH) +EXTRALIBS+=-L$(X11_LIB_PATH) endif ifeq ($(USE_X11_XV), yes) CFLAGS+=-DGPAC_HAS_X11_XV -LDFLAGS+=-lXv +EXTRALIBS+=-lXv endif ifeq ($(USE_X11_SHM), yes) CFLAGS+=-DGPAC_HAS_X11_SHM -LDFLAGS+=-lXext +EXTRALIBS+=-lXext endif ifeq ($(HAS_OPENGL), yes) ifeq ($(GPAC_USE_TINYGL), yes) else CFLAGS+=$(OGL_INCLS) -LDFLAGS+=$(OGL_LIBS) +EXTRALIBS+=$(OGL_LIBS) ifeq ($(CONFIG_DARWIN),yes) -LDFLAGS+=-lGL -lGLU +EXTRALIBS+=-lGL -lGLU endif endif endif @@ -64,9 +64,9 @@ all: $(LIB) $(LIB): $(OBJS) - $(CC) $(SHFLAGS) $(LDFLAGS) -lX11 -L../../bin/gcc -lgpac -o ../../bin/gcc/$@ $(OBJS) + $(CC) $(SHFLAGS) $(LDFLAGS) -lX11 -L../../bin/gcc -lgpac -o ../../bin/gcc/$@ $(OBJS) $(EXTRALIBS) ifeq ($(STATICBUILD),yes) - $(CC) $(SHFLAGS) $(LDFLAGS) -o ../../bin/gcc/gm_x11_out-static.$(DYN_LIB_SUFFIX) $(OBJS) -lX11 -L../../bin/gcc -lgpac_static + $(CC) $(SHFLAGS) $(LDFLAGS) -o ../../bin/gcc/gm_x11_out-static.$(DYN_LIB_SUFFIX) $(OBJS) -lX11 -L../../bin/gcc -lgpac_static $(EXTRALIBS) endif #static-bin: diff --git a/modules/x11_out/x11_out.c b/modules/x11_out/x11_out.c index a254662..b9ef396 100644 --- a/modules/x11_out/x11_out.c +++ b/modules/x11_out/x11_out.c @@ -680,6 +680,7 @@ static GF_Err X11_SetupGL(GF_VideoOutput *vout) if ( ! glXMakeCurrent(xWin->display, xWin->fullscreen ? xWin->full_wnd : xWin->wnd, xWin->glx_context) ) return GF_IO_ERR; XSync(xWin->display, False); + memset(&evt, 0, sizeof(GF_Event)); evt.type = GF_EVENT_VIDEO_SETUP; vout->on_event (vout->evt_cbk_hdl,&evt); xWin->is_init = 1; @@ -970,7 +971,9 @@ GF_Err X11_SetFullScreen (struct _video_out * vout, u32 bFullScreenOn, u32 * scr XUnmapWindow (xWindow->display, xWindow->full_wnd); XMapWindow (xWindow->display, xWindow->wnd); XUngrabKeyboard(xWindow->display, CurrentTime); - if (xWindow->par_wnd) XSetInputFocus(xWindow->display, xWindow->wnd, RevertToNone, CurrentTime); + /*looks like this makes osmozilla crash*/ + //if (xWindow->par_wnd) XSetInputFocus(xWindow->display, xWindow->wnd, RevertToNone, CurrentTime); + /*backbuffer resize will be done right after this is called */ } #ifdef GPAC_HAS_OPENGL diff --git a/modules/xvid_dec/Makefile b/modules/xvid_dec/Makefile index da27339..d105a12 100644 --- a/modules/xvid_dec/Makefile +++ b/modules/xvid_dec/Makefile @@ -20,7 +20,7 @@ OBJS=xvid_dec.o #local xvid lib ifeq ($(CONFIG_XVID), local) CFLAGS+= -I"$(LOCAL_INC_PATH)/xvid" -LDFLAGS+= -L../../extra_lib/lib/gcc +EXTRALIBS+= -L../../extra_lib/lib/gcc endif EXTRALIBS+= -lxvidcore -lpthread diff --git a/regression_tests/bifs/bifs-linking-anchor-mp4-next.bt b/regression_tests/bifs/bifs-linking-anchor-mp4-next.bt index 11c3eb6..a733e10 100644 --- a/regression_tests/bifs/bifs-linking-anchor-mp4-next.bt +++ b/regression_tests/bifs/bifs-linking-anchor-mp4-next.bt @@ -31,7 +31,7 @@ OrderedGroup { title "Anchor Test" } Anchor { - url ["bifs-linking-anchor-mp4-prev.mp4"] + url ["bifs-linking-anchor-mp4-prev.bt"] children [ Shape { appearance Appearance { diff --git a/regression_tests/bifs/bifs-linking-inline-direct.bt b/regression_tests/bifs/bifs-linking-inline-direct.bt index 4965245..a1d3866 100644 --- a/regression_tests/bifs/bifs-linking-inline-direct.bt +++ b/regression_tests/bifs/bifs-linking-inline-direct.bt @@ -41,7 +41,7 @@ OrderedGroup { scale 1.5 1 children [ Inline { - url ["bifs-linking-inline-direct-inline.mp4"] + url ["bifs-linking-inline-direct-inline.bt"] } ] } diff --git a/src/Makefile b/src/Makefile index 248abcb..06c8800 100644 --- a/src/Makefile +++ b/src/Makefile @@ -36,7 +36,7 @@ LIBGPAC_SCENE=scenegraph/base_scenegraph.o scenegraph/mpeg4_animators.o scenegra LIBGPAC_MCRYPT=mcrypt/cbc.o mcrypt/cfb.o mcrypt/ctr.o mcrypt/des.o mcrypt/ecb.o mcrypt/g_crypt.o mcrypt/ncfb.o mcrypt/nofb.o mcrypt/ofb.o mcrypt/rijndael-128.o mcrypt/rijndael-192.o mcrypt/rijndael-256.o mcrypt/stream.o mcrypt/tripledes.o mcrypt/sha1.o ## libgpac objects gathering: src/media tools -LIBGPAC_MEDIATOOLS=media_tools/av_parsers.o media_tools/avilib.o media_tools/dvb.o media_tools/filestreamer.o media_tools/gpac_ogg.o media_tools/img.o media_tools/ismacryp.o media_tools/isom_hinter.o media_tools/isom_tools.o media_tools/media_export.o media_tools/media_import.o media_tools/mpeg2_ps.o media_tools/text_import.o media_tools/saf.o media_tools/mpegts.o media_tools/dvb_mpe.o media_tools/reedsolomon.o media_tools/vobsub.o media_tools/m2ts_mux.o media_tools/m3u8.o media_tools/mpd.o media_tools/carousel.o +LIBGPAC_MEDIATOOLS=media_tools/ait.o media_tools/av_parsers.o media_tools/avilib.o media_tools/dsmcc.o media_tools/dvb.o media_tools/filestreamer.o media_tools/gpac_ogg.o media_tools/img.o media_tools/ismacryp.o media_tools/isom_hinter.o media_tools/isom_tools.o media_tools/media_export.o media_tools/media_import.o media_tools/mpeg2_ps.o media_tools/text_import.o media_tools/saf.o media_tools/mpegts.o media_tools/dvb_mpe.o media_tools/reedsolomon.o media_tools/vobsub.o media_tools/m2ts_mux.o media_tools/m3u8.o media_tools/mpd.o ## libgpac objects gathering: src/scene_manager LIBGPAC_SCENEMANAGER=scene_manager/loader_bt.o scene_manager/loader_isom.o scene_manager/loader_qt.o scene_manager/loader_xmt.o scene_manager/scene_dump.o scene_manager/scene_manager.o scene_manager/scene_stats.o scene_manager/swf_parse.o scene_manager/swf_bifs.o scene_manager/text_to_bifs.o scene_manager/scene_engine.o scene_manager/encode_isom.o scene_manager/loader_svg.o @@ -175,6 +175,7 @@ endif ifeq ($(CONFIG_SUNOS), yes) LD_SONAME="-Wl,-h,$(LIB)" +LINKLIBS+= -lrt endif SRCS := $(OBJS:.o=.c) diff --git a/src/bifs/bifs_node_tables.c b/src/bifs/bifs_node_tables.c index 0c9829f..220014b 100644 --- a/src/bifs/bifs_node_tables.c +++ b/src/bifs/bifs_node_tables.c @@ -24,7 +24,7 @@ /* - DO NOT MOFIFY - File generated on GMT Wed Jul 20 05:50:21 2011 + DO NOT MOFIFY - File generated on GMT Tue Nov 08 09:10:57 2011 BY MPEG4Gen for GPAC Version 0.4.6-DEV */ diff --git a/src/bifs/com_dec.c b/src/bifs/com_dec.c index 3a9044d..21d8f19 100644 --- a/src/bifs/com_dec.c +++ b/src/bifs/com_dec.c @@ -179,7 +179,7 @@ static GF_Err BD_XReplace(GF_BifsDecoder * codec, GF_BitStream *bs) memcpy(&sffield, &targetField, sizeof(GF_FieldInfo)); sffield.fieldType = sftype; sffield.far_ptr = slot_ptr; - gf_bifs_dec_sf_field(codec, bs, target, &sffield); + gf_bifs_dec_sf_field(codec, bs, target, &sffield, 0); } } gf_bifs_check_field_change(target, &targetField); @@ -227,7 +227,7 @@ static GF_Err BD_XReplace(GF_BifsDecoder * codec, GF_BitStream *bs) list = list->next; } } else { - e = gf_bifs_dec_field(codec, bs, target, &targetField); + e = gf_bifs_dec_field(codec, bs, target, &targetField, 0); if (e) return e; } if (previous) @@ -245,7 +245,7 @@ static GF_Err BD_XReplace(GF_BifsDecoder * codec, GF_BitStream *bs) if (fromField.fieldType == targetField.fieldType) gf_sg_vrml_field_clone(targetField.far_ptr, fromField.far_ptr, targetField.fieldType, codec->current_graph); } else { - e = gf_bifs_dec_field(codec, bs, target, &targetField); + e = gf_bifs_dec_field(codec, bs, target, &targetField, 0); } break; } @@ -332,7 +332,7 @@ static GF_Err BD_DecMultipleIndexReplace(GF_BifsDecoder * codec, GF_BitStream *b e = gf_sg_vrml_mf_get_item(field.far_ptr, field.fieldType, & sffield.far_ptr, pos); if (e) return e; - e = gf_bifs_dec_sf_field(codec, bs, node, &sffield); + e = gf_bifs_dec_sf_field(codec, bs, node, &sffield, 0); if (e) break; count--; } @@ -641,7 +641,7 @@ static GF_Err BD_DecIndexInsert(GF_BifsDecoder * codec, GF_BitStream *bs) e = gf_sg_vrml_mf_insert(field.far_ptr, field.fieldType, & sffield.far_ptr, pos); } if (e) return e; - e = gf_bifs_dec_sf_field(codec, bs, def, &sffield); + e = gf_bifs_dec_sf_field(codec, bs, def, &sffield, 0); if (!e) gf_bifs_check_field_change(def, &field); } return e; @@ -806,7 +806,7 @@ static GF_Err BD_DecFieldReplace(GF_BifsDecoder * codec, GF_BitStream *bs) /*parse the field*/ codec->is_com_dec = 1; - e = gf_bifs_dec_field(codec, bs, node, &field); + e = gf_bifs_dec_field(codec, bs, node, &field, 0); codec->is_com_dec = 0; /*remove prev nodes*/ if (field.fieldType == GF_SG_VRML_SFNODE) { @@ -883,7 +883,7 @@ static GF_Err BD_DecIndexValueReplace(GF_BifsDecoder * codec, GF_BitStream *bs) e = gf_sg_vrml_mf_get_item(field.far_ptr, field.fieldType, & sffield.far_ptr, pos); if (e) return e; - e = gf_bifs_dec_sf_field(codec, bs, node, &sffield); + e = gf_bifs_dec_sf_field(codec, bs, node, &sffield, 0); if (!e) gf_bifs_check_field_change(node, &field); } @@ -981,7 +981,7 @@ static GF_Err BD_DecReplace(GF_BifsDecoder * codec, GF_BitStream *bs) GF_Err gf_bifs_dec_proto_list(GF_BifsDecoder * codec, GF_BitStream *bs, GF_List *proto_list) { u8 flag, field_type, event_type, useQuant, useAnim, f; - u32 i, NbRoutes, ID, numProtos, numFields, count, qpsftype, QP_Type, NumBits, Anim_Type; + u32 i, NbRoutes, ID, numProtos, numFields, count, qpsftype, QP_Type, NumBits; GF_Node *node; char name[1000]; GF_ProtoFieldInterface *proto_field; @@ -1045,7 +1045,7 @@ GF_Err gf_bifs_dec_proto_list(GF_BifsDecoder * codec, GF_BitStream *bs, GF_List case GF_SG_EVENT_FIELD: /*parse default value except nodes ...*/ if (gf_sg_vrml_is_sf_field(field_type)) { - e = gf_bifs_dec_sf_field(codec, bs, NULL, &field); + e = gf_bifs_dec_sf_field(codec, bs, NULL, &field, 0); } else { f = 0; if (codec->info->config.UsePredictiveMFField) { @@ -1121,6 +1121,9 @@ GF_Err gf_bifs_dec_proto_list(GF_BifsDecoder * codec, GF_BitStream *bs, GF_List e = gf_node_register(node, NULL); if (e) goto exit; + //Ivica patch - Flush immediately because of proto instantiation + gf_bifs_flush_command_list(codec); + gf_sg_proto_add_node_code(proto, node); flag = gf_bs_read_int(bs, 1); } @@ -1186,12 +1189,12 @@ GF_Err gf_bifs_dec_proto_list(GF_BifsDecoder * codec, GF_BitStream *bs, GF_List qp_min_value = gf_sg_vrml_field_pointer_new(qpsftype); field.name = "QPMinValue"; field.far_ptr = qp_min_value; - gf_bifs_dec_sf_field(codec, bs, NULL, &field); + gf_bifs_dec_sf_field(codec, bs, NULL, &field, 0); qp_max_value = gf_sg_vrml_field_pointer_new(qpsftype); field.name = "QPMaxValue"; field.far_ptr = qp_max_value; - gf_bifs_dec_sf_field(codec, bs, NULL, &field); + gf_bifs_dec_sf_field(codec, bs, NULL, &field, 0); } /*and store*/ @@ -1206,9 +1209,7 @@ GF_Err gf_bifs_dec_proto_list(GF_BifsDecoder * codec, GF_BitStream *bs, GF_List if (useAnim && ( (field.eventType == GF_SG_EVENT_IN) || (field.eventType == GF_SG_EVENT_EXPOSED_FIELD) )) { flag = gf_bs_read_int(bs, 1); if (flag) { - Anim_Type = gf_bs_read_int(bs, 4); - } else { - Anim_Type = 0; + /*Anim_Type = */gf_bs_read_int(bs, 4); } } } diff --git a/src/bifs/com_enc.c b/src/bifs/com_enc.c index cb4a5a0..71ba742 100644 --- a/src/bifs/com_enc.c +++ b/src/bifs/com_enc.c @@ -34,9 +34,14 @@ GF_Err BE_EncProtoList(GF_BifsEncoder *codec, GF_List *protoList, GF_BitStream * void gf_bifs_enc_name(GF_BifsEncoder *codec, GF_BitStream *bs, char *name) { u32 i = 0; - while (name[i]) { - gf_bs_write_int(bs, name[i], 8); - i++; + if (!name) { + GF_LOG(GF_LOG_WARNING, GF_LOG_CODING, ("[BIFS] Coding IDs using names but no name is specified\n")); + i = 1; + } else { + while (name[i]) { + gf_bs_write_int(bs, name[i], 8); + i++; + } } gf_bs_write_int(bs, 0, 8); GF_LOG(GF_LOG_DEBUG, GF_LOG_CODING, ("[BIFS] DEF\t\t%d\t\t%s\n", 8*i, name)); diff --git a/src/bifs/conditional.c b/src/bifs/conditional.c index 2de3b9a..2016cf6 100644 --- a/src/bifs/conditional.c +++ b/src/bifs/conditional.c @@ -55,19 +55,18 @@ void Conditional_BufferReplaced(GF_BifsDecoder *codec, GF_Node *n) static void Conditional_execute(M_Conditional *node) { - GF_Err e; char *buffer; u32 len; GF_BitStream *bs; GF_BifsDecoder *codec; GF_Proto *prevproto; - GF_SceneGraph *prev_graph, *cur_graph; + GF_SceneGraph *prev_graph; ConditionalStack *priv = (ConditionalStack*)gf_node_get_private((GF_Node*)node); if (!priv) return; /*set the codec working graph to the node one (to handle conditional in protos)*/ prev_graph = priv->codec->current_graph; - cur_graph = priv->codec->current_graph = gf_node_get_graph((GF_Node*)node); + priv->codec->current_graph = gf_node_get_graph((GF_Node*)node); assert(priv->codec->current_graph); priv->codec->info = priv->info; @@ -94,7 +93,7 @@ static void Conditional_execute(M_Conditional *node) /*and a conditional may destroy the entire scene!*/ cur_graph->graph_has_been_reset = 0; #endif - e = gf_bifs_dec_command(codec, bs); + gf_bifs_dec_command(codec, bs); gf_bs_del(bs); #ifdef GF_SELF_REPLACE_ENABLE if (cur_graph->graph_has_been_reset) { @@ -104,7 +103,7 @@ static void Conditional_execute(M_Conditional *node) if (node->buffer.buffer) { gf_free(buffer); } else { - node->buffer.buffer = buffer; + node->buffer.buffer = (u8*)buffer; node->buffer.bufferSize = len; } //set isActive - to clarify in the specs diff --git a/src/bifs/field_decode.c b/src/bifs/field_decode.c index 1653e99..b39422e 100644 --- a/src/bifs/field_decode.c +++ b/src/bifs/field_decode.c @@ -70,7 +70,7 @@ Fixed BD_ReadSFFloat(GF_BifsDecoder * codec, GF_BitStream *bs) } -GF_Err gf_bifs_dec_sf_field(GF_BifsDecoder * codec, GF_BitStream *bs, GF_Node *node, GF_FieldInfo *field) +GF_Err gf_bifs_dec_sf_field(GF_BifsDecoder * codec, GF_BitStream *bs, GF_Node *node, GF_FieldInfo *field, Bool is_mem_com) { GF_Err e; GF_Node *new_node; @@ -125,8 +125,15 @@ GF_Err gf_bifs_dec_sf_field(GF_BifsDecoder * codec, GF_BitStream *bs, GF_Node *n if (node && (node->sgprivate->tag==TAG_MPEG4_CacheTexture) && (field->fieldIndex<=2)) { M_CacheTexture *ct = (M_CacheTexture *) node; ct->data_len = length; + if (ct->data) gf_free(ct->data); ct->data = gf_malloc(sizeof(char)*length); - gf_bs_read_data(bs, ct->data, length); + gf_bs_read_data(bs, (char*)ct->data, length); + } else if (node && (node->sgprivate->tag==TAG_MPEG4_BitWrapper) ) { + M_BitWrapper *bw = (M_BitWrapper*) node; + if (bw->buffer.buffer) gf_free(bw->buffer.buffer); + bw->buffer_len = length; + bw->buffer.buffer = gf_malloc(sizeof(char)*length); + gf_bs_read_data(bs, (char*)bw->buffer.buffer, length); } else { if ( ((SFString *)field->far_ptr)->buffer ) gf_free( ((SFString *)field->far_ptr)->buffer); ((SFString *)field->far_ptr)->buffer = (char *)gf_malloc(sizeof(char)*(length+1)); @@ -231,7 +238,7 @@ GF_Err gf_bifs_dec_sf_field(GF_BifsDecoder * codec, GF_BitStream *bs, GF_Node *n //for nodes the field ptr is a ptr to the field, which is a node ptr ;) new_node = gf_bifs_dec_node(codec, bs, field->NDTtype); if (new_node) { - e = gf_node_register(new_node, node); + e = gf_node_register(new_node, is_mem_com ? NULL : node); if (e) return e; } //it may happen that new_node is NULL (this is valid for a proto declaration) @@ -264,7 +271,7 @@ GF_Err gf_bifs_dec_sf_field(GF_BifsDecoder * codec, GF_BitStream *bs, GF_Node *n return codec->LastError; } -GF_Err BD_DecMFFieldList(GF_BifsDecoder * codec, GF_BitStream *bs, GF_Node *node, GF_FieldInfo *field) +GF_Err BD_DecMFFieldList(GF_BifsDecoder * codec, GF_BitStream *bs, GF_Node *node, GF_FieldInfo *field, Bool is_mem_com) { GF_Node *new_node; GF_Err e; @@ -288,12 +295,12 @@ GF_Err BD_DecMFFieldList(GF_BifsDecoder * codec, GF_BitStream *bs, GF_Node *node e = GF_OK;; if (field->fieldType != GF_SG_VRML_MFNODE) { e = gf_sg_vrml_mf_append(field->far_ptr, field->fieldType, & sffield.far_ptr); - e = gf_bifs_dec_sf_field(codec, bs, node, &sffield); + e = gf_bifs_dec_sf_field(codec, bs, node, &sffield, 0); } else { new_node = gf_bifs_dec_node(codec, bs, field->NDTtype); //append if (new_node) { - e = gf_node_register(new_node, node); + e = gf_node_register(new_node, is_mem_com ? NULL : node); if (e) return e; //regular coding @@ -353,7 +360,7 @@ GF_Err BD_DecMFFieldList(GF_BifsDecoder * codec, GF_BitStream *bs, GF_Node *node return GF_OK; } -GF_Err BD_DecMFFieldVec(GF_BifsDecoder * codec, GF_BitStream *bs, GF_Node *node, GF_FieldInfo *field) +GF_Err BD_DecMFFieldVec(GF_BifsDecoder * codec, GF_BitStream *bs, GF_Node *node, GF_FieldInfo *field, Bool is_mem_com) { GF_Err e; u32 NbBits, nbFields; @@ -387,14 +394,14 @@ GF_Err BD_DecMFFieldVec(GF_BifsDecoder * codec, GF_BitStream *bs, GF_Node *node, for (i=0;ifar_ptr, field->fieldType, & sffield.far_ptr, i); if (e) return e; - e = gf_bifs_dec_sf_field(codec, bs, node, &sffield); + e = gf_bifs_dec_sf_field(codec, bs, node, &sffield, 0); } } else { last = NULL; for (i=0;iNDTtype); if (new_node) { - e = gf_node_register(new_node, node); + e = gf_node_register(new_node, is_mem_com ? NULL : node); if (e) return e; if (node) { @@ -463,7 +470,7 @@ void gf_bifs_check_field_change(GF_Node *node, GF_FieldInfo *field) } -GF_Err gf_bifs_dec_field(GF_BifsDecoder * codec, GF_BitStream *bs, GF_Node *node, GF_FieldInfo *field) +GF_Err gf_bifs_dec_field(GF_BifsDecoder * codec, GF_BitStream *bs, GF_Node *node, GF_FieldInfo *field, Bool is_mem_com) { GF_Err e; u8 flag; @@ -474,7 +481,7 @@ GF_Err gf_bifs_dec_field(GF_BifsDecoder * codec, GF_BitStream *bs, GF_Node *node // if (field->fieldType == GF_SG_VRML_UNKNOWN) return GF_NON_COMPLIANT_BITSTREAM; if (gf_sg_vrml_is_sf_field(field->fieldType)) { - e = gf_bifs_dec_sf_field(codec, bs, node, field); + e = gf_bifs_dec_sf_field(codec, bs, node, field, is_mem_com); if (e) return e; } else { /*clean up the eventIn field if not done*/ @@ -512,9 +519,9 @@ GF_Err gf_bifs_dec_field(GF_BifsDecoder * codec, GF_BitStream *bs, GF_Node *node /*List description - alloc is dynamic*/ flag = gf_bs_read_int(bs, 1); if (flag) { - e = BD_DecMFFieldList(codec, bs, node, field); + e = BD_DecMFFieldList(codec, bs, node, field, is_mem_com); } else { - e = BD_DecMFFieldVec(codec, bs, node, field); + e = BD_DecMFFieldVec(codec, bs, node, field, is_mem_com); } if (e) return e; } @@ -576,7 +583,7 @@ GF_Err gf_bifs_dec_node_list(GF_BifsDecoder * codec, GF_BitStream *bs, GF_Node * if (e) return e; e = gf_node_get_field(node, field_all, &field); if (e) return e; - e = gf_bifs_dec_field(codec, bs, node, &field); + e = gf_bifs_dec_field(codec, bs, node, &field, 0); if (e) return e; flag = gf_bs_read_int(bs, 1); @@ -614,7 +621,7 @@ GF_Err gf_bifs_dec_node_mask(GF_BifsDecoder * codec, GF_BitStream *bs, GF_Node * else { e = gf_node_get_field(node, i, &field); if (e) return e; - e = gf_bifs_dec_field(codec, bs, node, &field); + e = gf_bifs_dec_field(codec, bs, node, &field, 0); } if (e) return e; } @@ -628,7 +635,7 @@ GF_Err gf_bifs_dec_node_mask(GF_BifsDecoder * codec, GF_BitStream *bs, GF_Node * gf_bifs_get_field_index(node, i, GF_SG_FIELD_CODING_DEF, &index); e = gf_node_get_field(node, index, &field); if (e) return e; - e = gf_bifs_dec_field(codec, bs, node, &field); + e = gf_bifs_dec_field(codec, bs, node, &field, 0); if (e) return e; if (is_proto) gf_sg_proto_mark_field_loaded(node, &field); @@ -683,7 +690,6 @@ static void UpdateTimeNode(GF_BifsDecoder * codec, GF_Node *node) GF_Node *gf_bifs_dec_node(GF_BifsDecoder * codec, GF_BitStream *bs, u32 NDT_Tag) { u32 nodeID, NDTBits, node_type, node_tag, ProtoID, BVersion; - u8 node_flag; Bool skip_init, reset_qp14; GF_Node *new_node; GF_Err e; @@ -703,7 +709,6 @@ GF_Node *gf_bifs_dec_node(GF_BifsDecoder * codec, GF_BitStream *bs, u32 NDT_Tag) BVersion = GF_BIFS_V1; - node_flag = 0; /*this is a USE statement*/ if (gf_bs_read_int(bs, 1)) { diff --git a/src/bifs/field_encode.c b/src/bifs/field_encode.c index 1d3c095..56e4f92 100644 --- a/src/bifs/field_encode.c +++ b/src/bifs/field_encode.c @@ -107,10 +107,14 @@ GF_Err gf_bifs_enc_sf_field(GF_BifsEncoder *codec, GF_BitStream *bs, GF_Node *no size -= read; } } else { - u32 i; + u32 i, len, val; char *str = (char *) ((SFString*)field->far_ptr)->buffer; - u32 len = str ? strlen(str) : 0; - u32 val = gf_get_bit_size(len); + if (node && (node->sgprivate->tag==TAG_MPEG4_BitWrapper) ) { + len = ((M_BitWrapper*)node)->buffer_len; + } else { + len = str ? strlen(str) : 0; + } + val = gf_get_bit_size(len); GF_BIFS_WRITE_INT(codec, bs, val, 5, "nbBits", NULL); GF_BIFS_WRITE_INT(codec, bs, len, val, "length", NULL); for (i=0; inew_node, NULL); } else { field.far_ptr = inf->field_ptr = gf_sg_vrml_field_pointer_new(inf->fieldType); - e = gf_bifs_dec_sf_field(codec, bs, node, &field); + e = gf_bifs_dec_sf_field(codec, bs, node, &field, 1); if (e) goto err; } count--; @@ -126,7 +126,7 @@ static GF_Err BM_ParseMultipleReplace(GF_BifsDecoder *codec, GF_BitStream *bs, G } else { field.far_ptr = inf->field_ptr = gf_sg_vrml_field_pointer_new(inf->fieldType); } - e = gf_bifs_dec_field(codec, bs, node, &field); + e = gf_bifs_dec_field(codec, bs, node, &field, 1); if (e) goto exit; } } else { @@ -148,7 +148,7 @@ static GF_Err BM_ParseMultipleReplace(GF_BifsDecoder *codec, GF_BitStream *bs, G } else { field.far_ptr = inf->field_ptr = gf_sg_vrml_field_pointer_new(inf->fieldType); } - e = gf_bifs_dec_field(codec, bs, node, &field); + e = gf_bifs_dec_field(codec, bs, node, &field, 1); if (e) goto exit; flag = gf_bs_read_int(bs, 1); } @@ -342,7 +342,7 @@ static GF_Err BM_XReplace(GF_BifsDecoder *codec, GF_BitStream *bs, GF_List *com_ } else { decfield.far_ptr = inf->field_ptr = gf_sg_vrml_field_pointer_new(inf->fieldType); } - e = gf_bifs_dec_sf_field(codec, bs, target, &decfield); + e = gf_bifs_dec_sf_field(codec, bs, target, &decfield, 1); if (e) return e; gf_list_add(com_list, com); @@ -511,7 +511,7 @@ GF_Err BM_ParseIndexInsert(GF_BifsDecoder *codec, GF_BitStream *bs, GF_List *com inf->fieldIndex = field_ind; inf->fieldType = sffield.fieldType; sffield.far_ptr = inf->field_ptr = gf_sg_vrml_field_pointer_new(sffield.fieldType); - codec->LastError = gf_bifs_dec_sf_field(codec, bs, def, &sffield); + codec->LastError = gf_bifs_dec_sf_field(codec, bs, def, &sffield, 1); gf_list_add(com_list, com); } return codec->LastError; @@ -723,7 +723,7 @@ GF_Err BM_ParseFieldReplace(GF_BifsDecoder *codec, GF_BitStream *bs, GF_List *co field.far_ptr = inf->field_ptr = gf_sg_vrml_field_pointer_new(field.fieldType); } /*parse the field*/ - codec->LastError = gf_bifs_dec_field(codec, bs, node, &field); + codec->LastError = gf_bifs_dec_field(codec, bs, node, &field, 1); gf_list_add(com_list, com); return codec->LastError; @@ -783,7 +783,7 @@ GF_Err BM_ParseIndexValueReplace(GF_BifsDecoder *codec, GF_BitStream *bs, GF_Lis sffield.fieldType = gf_sg_vrml_get_sf_type(field.fieldType); inf->fieldType = sffield.fieldType; sffield.far_ptr = inf->field_ptr = gf_sg_vrml_field_pointer_new(sffield.fieldType); - codec->LastError = gf_bifs_dec_sf_field(codec, bs, node, &sffield); + codec->LastError = gf_bifs_dec_sf_field(codec, bs, node, &sffield, 1); } gf_list_add(com_list, com); return codec->LastError; @@ -794,12 +794,9 @@ GF_Err BM_ParseRouteReplace(GF_BifsDecoder *codec, GF_BitStream *bs, GF_List *co GF_Err e; GF_Command *com; u32 RouteID, numBits, ind, node_id, fromID, toID; - GF_Route *r; GF_Node *OutNode, *InNode; RouteID = 1+gf_bs_read_int(bs, codec->info->config.RouteIDBits); - - r = gf_sg_route_find(codec->current_graph, RouteID); /*origin*/ node_id = 1 + gf_bs_read_int(bs, codec->info->config.NodeIDBits); diff --git a/src/bifs/script_dec.c b/src/bifs/script_dec.c index e86426a..3f5be24 100644 --- a/src/bifs/script_dec.c +++ b/src/bifs/script_dec.c @@ -128,7 +128,7 @@ GF_Err ParseScriptField(ScriptParser *parser) if (gf_bs_read_int(parser->bs, 1)) { e = gf_sg_script_field_get_info(field, &info); if (e) return e; - gf_bifs_dec_field(parser->codec, parser->bs, parser->script, &info); + gf_bifs_dec_field(parser->codec, parser->bs, parser->script, &info, 0); } } @@ -159,7 +159,6 @@ GF_Err SFScript_Parse(GF_BifsDecoder *codec, SFScript *script_field, GF_BitStrea u32 i, count, nbBits; char *ptr; ScriptParser parser; - Bool has_fields = 0; e = GF_OK; if (gf_node_get_tag(n) != TAG_MPEG4_Script) return GF_NON_COMPLIANT_BITSTREAM; @@ -180,7 +179,6 @@ GF_Err SFScript_Parse(GF_BifsDecoder *codec, SFScript *script_field, GF_BitStrea while (!gf_bs_read_int(bs, 1)){ e = ParseScriptField(&parser); if (e) goto exit; - else has_fields = 1; } } else { nbBits = gf_bs_read_int(bs, 4); @@ -188,7 +186,6 @@ GF_Err SFScript_Parse(GF_BifsDecoder *codec, SFScript *script_field, GF_BitStrea for (i=0; iis_open) return GF_BAD_PARAM; /*get media object*/ - ai->stream = gf_mo_register(ai->owner, url, 0, 0); + ai->stream = gf_mo_register(ai->owner, url, lock_timeline, 0); /*bad URL*/ if (!ai->stream) return GF_NOT_SUPPORTED; diff --git a/src/compositor/audio_render.c b/src/compositor/audio_render.c index 02b047d..cc9704b 100644 --- a/src/compositor/audio_render.c +++ b/src/compositor/audio_render.c @@ -362,7 +362,7 @@ u32 gf_ar_proc(void *p) /*THIS IS NEEDED FOR SYMBIAN - if no yield here, the audio module always grabs the main mixer mutex and it takes forever before it can be grabed by another thread, for instance when reconfiguring scene*/ - gf_sleep(0); + gf_sleep(1); gf_mixer_lock(ar->mixer, 1); if (ar->Frozen || gf_mixer_empty(ar->mixer) ) { diff --git a/src/compositor/camera.c b/src/compositor/camera.c index 5a4a7ba..d92ea3e 100644 --- a/src/compositor/camera.c +++ b/src/compositor/camera.c @@ -401,6 +401,7 @@ void camera_reset_viewpoint(GF_Camera *cam, Bool animate) if (!animate || (cam->had_viewpoint==2) ) { camera_set_vectors(cam, cam->vp_position, cam->vp_orientation, cam->vp_fov); cam->last_pos = cam->vp_position; + cam->anim_len = 0; return; } #ifndef FORCE_CAMERA_3D diff --git a/src/compositor/compositor.c b/src/compositor/compositor.c index fea5db4..7bdfb70 100644 --- a/src/compositor/compositor.c +++ b/src/compositor/compositor.c @@ -44,6 +44,8 @@ void gf_sc_next_frame_state(GF_Compositor *compositor, u32 state) { GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Compositor] Forcing frame redraw state: %d\n", state)); compositor->frame_draw_type = state; + if (state==GF_SC_DRAW_FLUSH) + compositor->skip_flush = 2; } @@ -474,7 +476,7 @@ GF_Compositor *gf_sc_new(GF_User *user, Bool self_threaded, GF_Terminal *term) /*wait until init is done*/ while (tmp->video_th_state < GF_COMPOSITOR_THREAD_RUN) { - gf_sleep(0); + gf_sleep(1); } /*init failure*/ if (tmp->video_th_state == GF_COMPOSITOR_THREAD_INIT_FAILED) { @@ -1001,6 +1003,14 @@ void gf_sc_reload_config(GF_Compositor *compositor) /*changing drivers needs exclusive access*/ gf_sc_lock(compositor, 1); + + sOpt = gf_cfg_get_key(compositor->user->config, "Compositor", "FrameRate"); + if (!sOpt) { + sOpt = "30.0"; + gf_cfg_set_key(compositor->user->config, "Compositor", "FrameRate", "30.0"); + } + gf_sc_set_fps(compositor, atof(sOpt)); + sOpt = gf_cfg_get_key(compositor->user->config, "Compositor", "ForceSceneSize"); if (sOpt && ! stricmp(sOpt, "yes")) { compositor->override_size_flags = 1; @@ -1244,7 +1254,7 @@ void gf_sc_reload_config(GF_Compositor *compositor) #ifdef GF_SR_USE_DEPTH sOpt = gf_cfg_get_key(compositor->user->config, "Compositor", "AutoStereoCalibration"); - compositor->auto_calibration = (!sOpt || !strcmp(sOpt, "yes")) ? 1 : 0; + compositor->auto_calibration = (sOpt && !strcmp(sOpt, "yes")) ? 1 : 0; sOpt = gf_cfg_get_key(compositor->user->config, "Compositor", "DisplayDepth"); compositor->display_depth = sOpt ? (!strcmp(sOpt, "auto") ? -1 : atoi(sOpt)) : 0; @@ -1632,10 +1642,10 @@ GF_Err gf_sc_get_screen_buffer(GF_Compositor *compositor, GF_VideoSurface *frame GF_Err gf_sc_get_offscreen_buffer(GF_Compositor *compositor, GF_VideoSurface *framebuffer, u32 view_idx, u32 depth_dump_mode) { - GF_Err e; if (!compositor || !framebuffer) return GF_BAD_PARAM; #ifndef GPAC_DISABLE_3D if (compositor->visual->type_3d && compositor->visual->nb_views && (compositor->visual->autostereo_type>GF_3D_STEREO_SIDE)) { + GF_Err e; gf_mx_p(compositor->mx); e = compositor_3d_get_offscreen_buffer(compositor, framebuffer, view_idx, depth_dump_mode); if (e != GF_OK) gf_mx_v(compositor->mx); @@ -1736,8 +1746,8 @@ static void gf_sc_setup_root_visual(GF_Compositor *compositor, GF_Node *top_node /*move to perspective 3D when simulating depth*/ #ifdef GF_SR_USE_DEPTH if (compositor->display_depth) { - compositor->visual->type_3d = 2; - compositor->visual->camera.is_3D = 1; + compositor->visual->type_3d = 0; + compositor->visual->camera.is_3D = 0; } else #endif { @@ -1837,7 +1847,7 @@ static void gf_sc_recompute_ar(GF_Compositor *compositor, GF_Node *top_node) #ifndef GPAC_DISABLE_LOG u32 time=0; - if ((gf_log_get_level() >= GF_LOG_DEBUG) && (gf_log_get_tools() & GF_LOG_RTI)) { + if (gf_log_tool_level_on(GF_LOG_RTI, GF_LOG_DEBUG)) { time = gf_sys_clock(); } #endif @@ -1856,7 +1866,7 @@ static void gf_sc_recompute_ar(GF_Compositor *compositor, GF_Node *top_node) gf_sc_next_frame_state(compositor, GF_SC_DRAW_NONE); #ifndef GPAC_DISABLE_LOG - if ((gf_log_get_level() >= GF_LOG_DEBUG) && (gf_log_get_tools() & GF_LOG_RTI)) { + if (gf_log_tool_level_on(GF_LOG_RTI, GF_LOG_DEBUG)) { compositor->visual_config_time = gf_sys_clock() - time; } #endif @@ -1885,13 +1895,18 @@ static void gf_sc_draw_scene(GF_Compositor *compositor) flags = compositor->traverse_state->immediate_draw; - if (! visual_draw_frame(compositor->visual, top_node, compositor->traverse_state, 1)) - compositor->skip_flush = 1; - - /*if using OpenGL, flush even if no changes as the display may be dirty (as seen on android, likely other devices)*/ + if (! visual_draw_frame(compositor->visual, top_node, compositor->traverse_state, 1)) { + /*android backend uses opengl without telling it to us, we need an ugly hack here ...*/ #ifdef GPAC_ANDROID - compositor->skip_flush = 0; + compositor->skip_flush = 0; +#else + if (compositor->skip_flush==2) { + compositor->skip_flush = 0; + } else { + compositor->skip_flush = 1; + } #endif + } compositor->traverse_state->immediate_draw = flags; @@ -2155,7 +2170,7 @@ void gf_sc_simulation_tick(GF_Compositor *compositor) if(compositor->user->init_flags & GF_TERM_INIT_HIDE) compositor->skip_flush = 1; - if (!compositor->skip_flush) { + if (compositor->skip_flush!=1) { rc.x = rc.y = 0; rc.w = compositor->display_width; rc.h = compositor->display_height; @@ -2245,7 +2260,7 @@ void gf_sc_simulation_tick(GF_Compositor *compositor) /*TO CHECK - THERE WAS A BUG HERE WITH TRISCOPE@SHIX*/ if (end_time > compositor->frame_duration) { - gf_sleep(0); + gf_sleep(1); return; } @@ -2275,7 +2290,7 @@ void gf_sc_visual_unregister(GF_Compositor *compositor, GF_VisualManager *visual gf_list_del_item(compositor->visuals, visual); } -void gf_sc_traverse_subscene(GF_Compositor *compositor, GF_Node *inline_parent, GF_SceneGraph *subscene, void *rs) +void gf_sc_traverse_subscene_ex(GF_Compositor *compositor, GF_Node *inline_parent, GF_SceneGraph *subscene, void *rs) { Fixed min_hsize, vp_scale; Bool use_pm, prev_pm, prev_coord; @@ -2516,6 +2531,17 @@ static Bool gf_sc_handle_event_intern(GF_Compositor *compositor, GF_Event *event #endif } +void gf_sc_traverse_subscene(GF_Compositor *compositor, GF_Node *inline_parent, GF_SceneGraph *subscene, void *rs) +{ + u32 i=0; + GF_SceneGraph *subsg; + + gf_sc_traverse_subscene_ex(compositor, inline_parent, subscene, rs); + + while ( (subsg = gf_scene_enum_extra_scene(subscene, &i))) + gf_sc_traverse_subscene_ex(compositor, inline_parent, subsg, rs); + +} static Bool gf_sc_on_event_ex(GF_Compositor *compositor , GF_Event *event, Bool from_user) { @@ -2525,6 +2551,8 @@ static Bool gf_sc_on_event_ex(GF_Compositor *compositor , GF_Event *event, Bool if (compositor->msg_type & GF_SR_IN_RECONFIG) { if (event->type==GF_EVENT_VIDEO_SETUP) { compositor->reset_graphics = 2; + if (event->setup.back_buffer) + compositor->recompute_ar = 1; } return 0; } @@ -2549,6 +2577,8 @@ static Bool gf_sc_on_event_ex(GF_Compositor *compositor , GF_Event *event, Bool { Bool locked = gf_mx_try_lock(compositor->mx); compositor->reset_graphics = 2; + if (event->setup.back_buffer) + compositor->recompute_ar = 1; if (locked) gf_mx_v(compositor->mx); } break; @@ -2578,6 +2608,8 @@ static Bool gf_sc_on_event_ex(GF_Compositor *compositor , GF_Event *event, Bool case GF_EVENT_KEYDOWN: case GF_EVENT_KEYUP: + { + Bool ret; switch (event->key.key_code) { case GF_KEY_SHIFT: if (event->type==GF_EVENT_KEYDOWN) { @@ -2602,14 +2634,16 @@ static Bool gf_sc_on_event_ex(GF_Compositor *compositor , GF_Event *event, Bool break; } - + + ret = 0; event->key.flags |= compositor->key_states; /*key sensor*/ if (compositor->term && (compositor->interaction_level & GF_INTERACT_INPUT_SENSOR) ) { - gf_term_keyboard_input(compositor->term, event->key.key_code, event->key.hw_code, (event->type==GF_EVENT_KEYUP) ? 1 : 0); - } - - return gf_sc_handle_event_intern(compositor, event, from_user); + ret = gf_term_keyboard_input(compositor->term, event->key.key_code, event->key.hw_code, (event->type==GF_EVENT_KEYUP) ? 1 : 0); + } + ret += gf_sc_handle_event_intern(compositor, event, from_user); + return ret; + } case GF_EVENT_TEXTINPUT: if (compositor->term && (compositor->interaction_level & GF_INTERACT_INPUT_SENSOR) ) diff --git a/src/compositor/compositor_2d.c b/src/compositor/compositor_2d.c index 4d7e662..2263628 100644 --- a/src/compositor/compositor_2d.c +++ b/src/compositor/compositor_2d.c @@ -282,6 +282,8 @@ GF_Err compositor_2d_get_video_access(GF_VisualManager *visual) } compositor->hw_locked = 0; visual->is_attached = 0; + /*if using BlitTexture, return OK to still be able to blit images*/ + if (compositor->video_out->BlitTexture) e = GF_OK; return e; } @@ -314,8 +316,8 @@ Bool compositor_texture_rectangles(GF_VisualManager *visual, GF_TextureHandler * if (disable_blit) *disable_blit = 0; if (has_scale) *has_scale = 0; - /*this should never happen but we check for float rounding safety*/ if (final.width<=0 || final.height <=0) return 0; + if (txh->width==0 || txh->height==0) return 0; w_scale = final.width / txh->width; @@ -456,7 +458,7 @@ static Bool compositor_2d_draw_bitmap_ex(GF_VisualManager *visual, GF_TextureHan } if (!compositor_texture_rectangles(visual, txh, clip, unclip, &src_wnd, &dst_wnd, &use_blit, &has_scale)) return 1; - + /*can we use hardware blitter ?*/ hw_caps = visual->compositor->video_out->hw_caps; overlay_type = 0; @@ -483,8 +485,8 @@ static Bool compositor_2d_draw_bitmap_ex(GF_VisualManager *visual, GF_TextureHan case GF_PIXEL_BGR_24: case GF_PIXEL_RGBS: case GF_PIXEL_RGBD: - case GF_PIXEL_RGB_555: - case GF_PIXEL_RGB_565: +// case GF_PIXEL_RGB_555: +// case GF_PIXEL_RGB_565: if (hw_caps & GF_VIDEO_HW_HAS_RGB) use_soft_stretch = 0; break; @@ -635,7 +637,7 @@ static Bool compositor_2d_draw_bitmap_ex(GF_VisualManager *visual, GF_TextureHan visual->compositor->video_memory = 2; } /*force a reconfigure of video output*/ - else { + else if (visual->compositor->video_memory!=2) { GF_LOG(GF_LOG_INFO, GF_LOG_COMPOSE, ("[Compositor2D] Reconfiguring video output to use video memory\n")); visual->compositor->request_video_memory = 1; visual->compositor->root_visual_setup = 0; @@ -696,6 +698,8 @@ Bool compositor_2d_draw_bitmap(GF_VisualManager *visual, GF_TraverseState *tr_st if (!alpha) return 1; switch (ctx->aspect.fill_texture->pixelformat) { + case GF_PIXEL_ALPHAGREY: + case GF_PIXEL_GREYSCALE: case GF_PIXEL_RGB_24: case GF_PIXEL_BGR_24: case GF_PIXEL_RGB_555: @@ -707,9 +711,10 @@ Bool compositor_2d_draw_bitmap(GF_VisualManager *visual, GF_TraverseState *tr_st case GF_PIXEL_YVYU: case GF_PIXEL_YUY2: case GF_PIXEL_I420: + case GF_PIXEL_NV21: case GF_PIXEL_YUVA: case GF_PIXEL_RGBS: - case GF_PIXEL_RGBAS: + case GF_PIXEL_RGBAS: break; case GF_PIXEL_YUVD: case GF_PIXEL_RGBD: diff --git a/src/compositor/compositor_node_init.c b/src/compositor/compositor_node_init.c index 74a389a..d1ab396 100644 --- a/src/compositor/compositor_node_init.c +++ b/src/compositor/compositor_node_init.c @@ -28,12 +28,18 @@ #include + void compositor_init_afx_node(GF_Compositor *compositor, GF_Node *node, MFURL *url) { GF_MediaObject *mo = gf_mo_register(node, url, 0, 0); if (!mo) { - GF_LOG(GF_LOG_WARNING, GF_LOG_COMPOSE, ("[Compositor] AFX Decoder not found for node %s - node will not be rendered\n", gf_node_get_class_name(node))); + GF_LOG(GF_LOG_WARNING, GF_LOG_COMPOSE, ("[Compositor] AFX Decoder not found for node %s - node may not be completely/correctly rendered\n", gf_node_get_class_name(node))); + } +#ifndef GPAC_DISABLE_VRML + if (gf_node_get_tag(node)==TAG_MPEG4_BitWrapper) { + compositor_init_bitwrapper(compositor, node); } +#endif } @@ -396,6 +402,9 @@ void gf_sc_on_node_init(GF_Compositor *compositor, GF_Node *node) case TAG_MPEG4_SBVCAnimation: compositor_init_afx_node(compositor, node, & ((M_SBVCAnimation *)node)->url); break; + case TAG_MPEG4_BitWrapper: + compositor_init_afx_node(compositor, node, & ((M_BitWrapper *)node)->url); + break; default: GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Compositor] node %s will not be rendered\n", gf_node_get_class_name(node))); @@ -452,6 +461,10 @@ void gf_sc_invalidate(GF_Compositor *compositor, GF_Node *byObj) #endif /*GPAC_DISABLE_VRML*/ +#ifndef GPAC_DISABLE_SVG + case TAG_SVG_video: compositor_svg_video_modified(compositor, byObj); break; +#endif /*GPAC_DISABLE_SVG*/ + default: /*for all nodes, invalidate parent graph - note we do that for sensors as well to force recomputing sensor list cached at grouping node level*/ diff --git a/src/compositor/drawable.c b/src/compositor/drawable.c index f63ee46..cfb1340 100644 --- a/src/compositor/drawable.c +++ b/src/compositor/drawable.c @@ -803,9 +803,12 @@ void drawable_compute_line_scale(GF_TraverseState *tr_state, DrawAspect2D *asp) asp->line_scale = MAX(gf_divfix(tr_state->visual->compositor->scale_x, rc.width), gf_divfix(tr_state->visual->compositor->scale_y, rc.height)); } +//#define REMOVE_UNUSED_CTX void drawable_finalize_sort_ex(DrawableContext *ctx, GF_TraverseState *tr_state, GF_Rect *orig_bounds, Bool skip_focus) { - Bool can_remove; +#ifdef REMVE_UNUSED_CTX + Bool can_remove = 0; +#endif Fixed pw; GF_Rect unclip, store_orig_bounds; @@ -867,14 +870,19 @@ void drawable_finalize_sort_ex(DrawableContext *ctx, GF_TraverseState *tr_state, ctx->bi->clip.width = 0; } - +#ifdef REMVE_UNUSED_CTX can_remove = drawable_finalize_end(ctx, tr_state); +#else + drawable_finalize_end(ctx, tr_state); +#endif if (ctx->drawable && !skip_focus) drawable_check_focus_highlight(ctx->drawable->node, tr_state, &store_orig_bounds); /*remove if this is the last context*/ -// if (can_remove && (tr_state->visual->cur_context == ctx)) -// tr_state->visual->cur_context->drawable = NULL; +#ifdef REMVE_UNUSED_CTX + if (can_remove && (tr_state->visual->cur_context == ctx)) + tr_state->visual->cur_context->drawable = NULL; +#endif } void drawable_finalize_sort(struct _drawable_context *ctx, GF_TraverseState *tr_state, GF_Rect *orig_bounds) diff --git a/src/compositor/events.c b/src/compositor/events.c index bb4c25c..5059bfd 100644 --- a/src/compositor/events.c +++ b/src/compositor/events.c @@ -69,16 +69,19 @@ static Bool exec_text_selection(GF_Compositor *compositor, GF_Event *event) static void flush_text_node_edit(GF_Compositor *compositor, Bool final_flush) { Bool signal; - u8 *txt; + char *txt; u32 len; if (!compositor->edited_text) return; + /* if this is the final editing and there is text, + we need to remove the caret from the text selection buffer */ if (final_flush && compositor->sel_buffer_len) { memmove(&compositor->sel_buffer[compositor->caret_pos], &compositor->sel_buffer[compositor->caret_pos+1], sizeof(u16)*(compositor->sel_buffer_len-compositor->caret_pos)); compositor->sel_buffer_len--; compositor->sel_buffer[compositor->sel_buffer_len] = 0; } + /* Recomputes the edited text */ if (*compositor->edited_text) { gf_free(*compositor->edited_text); *compositor->edited_text = NULL; @@ -101,8 +104,11 @@ static void flush_text_node_edit(GF_Compositor *compositor, Bool final_flush) gf_sc_next_frame_state(compositor, GF_SC_DRAW_FRAME); /*notify compositor that text has been edited, in order to update composite textures*/ //compositor->text_edit_changed = 1; + gf_node_set_private(compositor->focus_highlight->node, NULL); + /* if this is the final flush, we free the selection buffer and edited text buffer + and signal a text content change in the focus node */ if (final_flush) { GF_FieldInfo info; if (compositor->sel_buffer) gf_free(compositor->sel_buffer); @@ -782,6 +788,15 @@ TODO clean: figure out whether we use a mouse or a touch device - if touch devic evt.detail = event->key.key_code; evt.key_hw_code = event->key.hw_code; target = compositor->focus_node; + + /*dirty hack to simulate browserback*/ + if (event->key.key_code==GF_KEY_BACKSPACE && (event->key.flags & GF_KEY_MOD_CTRL)) { + event->key.key_code = GF_KEY_BROWSERBACK; + } + + if ((event->key.key_code>=GF_KEY_BROWSERBACK) && (event->key.key_code<=GF_KEY_BROWSERSTOP)) { + target = NULL; + } if (!target) target = gf_sg_get_root_node(compositor->scene); ret += gf_dom_event_fire(target, &evt); @@ -800,7 +815,7 @@ TODO clean: figure out whether we use a mouse or a touch device - if touch devic case '\r': case '\n': case '\t': - case '\b': + //case '\b': break; default: memset(&evt, 0, sizeof(GF_DOM_Event)); @@ -827,7 +842,7 @@ TODO clean: figure out whether we use a mouse or a touch device - if touch devic Bool gf_sc_exec_event_vrml(GF_Compositor *compositor, GF_Event *ev) { u32 res = 0; - GF_SensorHandler *hs, *hs_grabbed; + GF_SensorHandler *hs; GF_List *tmp; u32 i, count, stype; @@ -866,8 +881,6 @@ Bool gf_sc_exec_event_vrml(GF_Compositor *compositor, GF_Event *ev) compositor->prev_hit_appear = compositor->hit_appear; } - - hs_grabbed = NULL; /*if we have a hit node at the compositor level, use "touch" as default cursor - this avoid resetting the cursor when the picked node is a DOM node in a composite texture*/ stype = (compositor->hit_node!=NULL) ? GF_CURSOR_TOUCH : GF_CURSOR_NORMAL; @@ -877,15 +890,25 @@ Bool gf_sc_exec_event_vrml(GF_Compositor *compositor, GF_Event *ev) for (i=0; isensors, i); - res += hs->OnUserEvent(hs, 1, 0, ev, compositor); /*try to remove this sensor from the previous sensor list*/ gf_list_del_item(compositor->previous_sensors, hs); - stype = gf_node_get_tag(hs->sensor); - keynav = gf_scene_get_keynav(gf_node_get_graph(hs->sensor), hs->sensor); if (keynav) gf_sc_change_key_navigator(compositor, keynav); + + /*call the sensor LAST, as this may triger a destroy of the scene the sensor is in + this is only true for anchors, as other other sensors output events are queued as routes untill next pass*/ + res += hs->OnUserEvent(hs, 1, 0, ev, compositor); + if ((stype == TAG_MPEG4_Anchor) +#ifndef GPAC_DISABLE_X3D + || (stype == TAG_X3D_Anchor) +#endif + ) { + /*subscene with active sensor has been deleted, we cannot continue process the sensors stack*/ + if (count != gf_list_count(compositor->sensors)) + break; + } } compositor->grabbed_sensor = 0; /*check if we have grabbed sensors*/ @@ -1702,8 +1725,7 @@ static GF_Node *browse_parent_for_focus(GF_Compositor *compositor, GF_Node *elt, } if (prev_focus) { - u32 i, count; - count = gf_node_list_get_count(child); + u32 i; /*!! this may happen when walking up PROTO nodes !!*/ for (i=idx; i>0; i--) { n = gf_node_list_get_child(child, i-1); @@ -1926,7 +1948,7 @@ static Bool forward_event(GF_Compositor *compositor, GF_Event *ev, Bool consumed Bool gf_sc_exec_event(GF_Compositor *compositor, GF_Event *evt) { - s32 x, y; + s32 x=0, y=0; Bool switch_coords=0; Bool ret=0; if (evt->type<=GF_EVENT_MOUSEWHEEL) { @@ -1940,9 +1962,13 @@ Bool gf_sc_exec_event(GF_Compositor *compositor, GF_Event *evt) } /*process regular events except if navigation is grabbed*/ - if ( (compositor->navigation_state<2) && (compositor->interaction_level & GF_INTERACT_NORMAL) && gf_sc_execute_event(compositor, compositor->traverse_state, evt, NULL)) { - compositor->navigation_state = 0; - ret = 1; + if ( (compositor->navigation_state<2) && (compositor->interaction_level & GF_INTERACT_NORMAL)) { + Bool res; + res = gf_sc_execute_event(compositor, compositor->traverse_state, evt, NULL); + if (res) { + compositor->navigation_state = 0; + ret = 1; + } } if (switch_coords) { evt->mouse.x = x; diff --git a/src/compositor/font_engine.c b/src/compositor/font_engine.c index 8dc1072..26c12e6 100644 --- a/src/compositor/font_engine.c +++ b/src/compositor/font_engine.c @@ -356,6 +356,8 @@ static GF_Glyph *gf_font_get_glyph(GF_FontManager *fm, GF_Font *font, u32 name) glyph->width = 0; glyph->ID = name; glyph->utf_name=name; + } else if (name==(u32) '\t') { + return NULL; } else { /*load glyph*/ if (font->load_glyph) { @@ -498,7 +500,7 @@ void gf_font_manager_refresh_span_bounds(GF_TextSpan *span) { u32 i; Fixed descent, ascent, bline; - Fixed min_x, min_y, width, max_y; + Fixed min_x, min_y, max_y; if (!span->nb_glyphs) { span->bounds.width = span->bounds.height = 0; @@ -517,8 +519,6 @@ void gf_font_manager_refresh_span_bounds(GF_TextSpan *span) bline = span->font->baseline * span->font_scale; - width = (span->flags & GF_TEXT_SPAN_HORIZONTAL) ? 0 : span->font->max_advance_h*span->font_scale; - min_x = span->dx ? FIX_MAX : span->off_x; min_y = span->dy ? FIX_MAX : span->off_y - descent; max_y = span->dy ? -FIX_MAX : span->off_y + ascent; diff --git a/src/compositor/gl_inc.h b/src/compositor/gl_inc.h index d0aa84b..177e385 100644 --- a/src/compositor/gl_inc.h +++ b/src/compositor/gl_inc.h @@ -281,20 +281,10 @@ extern void (*glXGetProcAddress(const GLubyte *procname))( void ); GLDECL(void, glActiveTexture, (GLenum texture) ) GLDECL(void, glClientActiveTexture, (GLenum texture) ) -#define GL_ARRAY_BUFFER 0x8892 -#define GL_STREAM_DRAW 0x88E0 -#define GL_STATIC_DRAW 0x88E4 -#define GL_DYNAMIC_DRAW 0x88E8 -GLDECL(void, glGenBuffers, (GLsizei , GLuint *) ) -GLDECL(void, glDeleteBuffers, (GLsizei , GLuint *) ) -GLDECL(void, glBindBuffer, (GLenum, GLuint ) ) -GLDECL(void, glBufferData, (GLenum, int, void *, GLenum) ) -GLDECL(void, glBufferSubData, (GLenum, int, int, void *) ) +GLDECL(void, glBlendEquation, (GLint mode) ) #endif //GL_VERSION_1_3 - - #ifndef GL_VERSION_1_4 #ifdef LOAD_GL_FUNCS @@ -308,13 +298,33 @@ GLDECL(void, glBufferSubData, (GLenum, int, int, void *) ) #define GL_BLEND_EQUATION_RGB GL_BLEND_EQUATION -GLDECL(void, glBlendEquation, (GLint mode) ) GLDECL(void, glPointParameterf, (GLenum , GLfloat) ) GLDECL(void, glPointParameterfv, (GLenum, const GLfloat *) ) #endif + +#ifndef GL_VERSION_1_5 + +#ifdef LOAD_GL_FUNCS +#define LOAD_GL_1_5 +#endif + +#define GL_ARRAY_BUFFER 0x8892 +#define GL_STREAM_DRAW 0x88E0 +#define GL_STATIC_DRAW 0x88E4 +#define GL_DYNAMIC_DRAW 0x88E8 + +GLDECL(void, glGenBuffers, (GLsizei , GLuint *) ) +GLDECL(void, glDeleteBuffers, (GLsizei , GLuint *) ) +GLDECL(void, glBindBuffer, (GLenum, GLuint ) ) +GLDECL(void, glBufferData, (GLenum, int, void *, GLenum) ) +GLDECL(void, glBufferSubData, (GLenum, int, int, void *) ) + +#endif //GL_VERSION_1_5 + + #ifndef GL_VERSION_2_0 #ifdef LOAD_GL_FUNCS diff --git a/src/compositor/hc_flash_shape.c b/src/compositor/hc_flash_shape.c index 09e0a1d..a8e764e 100644 --- a/src/compositor/hc_flash_shape.c +++ b/src/compositor/hc_flash_shape.c @@ -89,7 +89,7 @@ static void build_shape(FSStack *st, GF_Node *node) u32 wi, li, fi, ci, command, i, has_ci; FSItem *fill_item, *line_item; Fixed w; - SFVec2f cur, pt, ct1, ct2, *pts; + SFVec2f cur, pt, ct1={0,0}, ct2, *pts; GF_Rect rc; u32 line_col, fill_col; Bool need_line, need_fill; diff --git a/src/compositor/mesh.c b/src/compositor/mesh.c index 3b978e0..df401c7 100644 --- a/src/compositor/mesh.c +++ b/src/compositor/mesh.c @@ -1032,10 +1032,9 @@ void mesh_new_ifs_intern(GF_Mesh *mesh, GF_Node *__coord, MFInt32 *coordIndex, GF_Node *__texCoords, MFInt32 *texCoordIndex, Fixed creaseAngle) { - u32 i, n, count, c_count, col_count, nor_count; + u32 i, n, count, c_count, nor_count; u32 index; - u32 first_idx, last_idx; - Bool move_to, smooth_normals; + Bool smooth_normals; SFVec2f tx; u32 s_axis, t_axis; SFVec3f pt, nor, bounds, center; @@ -1133,14 +1132,12 @@ void mesh_new_ifs_intern(GF_Mesh *mesh, GF_Node *__coord, MFInt32 *coordIndex, if (!colorIndex->vals) colorIndex = coordIndex; } } - col_count = colorIndex->count ? colorIndex->count : c_count; if (has_normal && !normalPerVertex) { index = normalIndex->count ? normalIndex->vals[0] : 0; if (index < nor_count) nor = normal->vector.vals[index]; } - move_to = 1; count = coordIndex->count; has_coord = count ? 1 : 0; if (!has_coord) count = c_count; @@ -1151,9 +1148,7 @@ void mesh_new_ifs_intern(GF_Mesh *mesh, GF_Node *__coord, MFInt32 *coordIndex, if (!has_coord) { face_count = 1; } else { - u32 pt_p_face; face_count = 0; - pt_p_face = 0; for (i=0; ivals[i] == -1) face_count++; } @@ -1178,10 +1173,8 @@ void mesh_new_ifs_intern(GF_Mesh *mesh, GF_Node *__coord, MFInt32 *coordIndex, } cur_face = 0; - first_idx = last_idx = 0; for (i=0; ivals[i] == -1) { - move_to = 1; n++; if (has_color && !colorPerVertex) { GET_IDX(n, colorIndex); @@ -1982,17 +1975,20 @@ static void mesh_extrude_path_intern(GF_Mesh *mesh, GF_Path *path, MFVec3f *thes n = SCPbegin.yaxis; gf_vec_norm(&n); - MESH_SET_NORMAL(vx, n); convexity = gf_polygone2d_get_convexity(path->points, path->n_points); switch (convexity) { case GF_POLYGON_CONVEX_CCW: case GF_POLYGON_COMPLEX_CCW: - gf_vec_rev(vx.normal); + break; + default: gf_vec_rev(n); break; } + MESH_SET_NORMAL(vx, n); + + if (smooth_normals) { faces_info[begin_face].nor = n; assert(gf_vec_len(n)); diff --git a/src/compositor/mpeg4_audio.c b/src/compositor/mpeg4_audio.c index 086c4eb..c83e933 100644 --- a/src/compositor/mpeg4_audio.c +++ b/src/compositor/mpeg4_audio.c @@ -38,7 +38,7 @@ typedef struct static void audioclip_activate(AudioClipStack *st, M_AudioClip *ac) { - if (gf_sc_audio_open(&st->input, &ac->url, 0, -1) != GF_OK) { + if (gf_sc_audio_open(&st->input, &ac->url, 0, -1, 0) != GF_OK) { st->failure = 1; return; } @@ -149,7 +149,7 @@ void compositor_audioclip_modified(GF_Node *node) if (st->input.is_open && st->input.is_open) { if (gf_sc_audio_check_url(&st->input, &ac->url)) { gf_sc_audio_stop(&st->input); - gf_sc_audio_open(&st->input, &ac->url, 0, -1); + gf_sc_audio_open(&st->input, &ac->url, 0, -1, 0); /*force unregister to resetup audio cfg*/ gf_sc_audio_unregister(&st->input); gf_sc_invalidate(st->input.compositor, NULL); @@ -181,7 +181,7 @@ typedef struct static void audiosource_activate(AudioSourceStack *st, M_AudioSource *as) { - if (gf_sc_audio_open(&st->input, &as->url, 0, -1) != GF_OK) + if (gf_sc_audio_open(&st->input, &as->url, 0, -1, 0) != GF_OK) return; st->is_active = 1; gf_mo_set_speed(st->input.stream, st->input.speed); @@ -281,7 +281,7 @@ void compositor_audiosource_modified(GF_Node *node) gf_sc_audio_unregister(&st->input); gf_sc_invalidate(st->input.compositor, NULL); - if (st->is_active) gf_sc_audio_open(&st->input, &as->url, 0, -1); + if (st->is_active) gf_sc_audio_open(&st->input, &as->url, 0, -1, 0); } //update state if we're active diff --git a/src/compositor/mpeg4_bitmap.c b/src/compositor/mpeg4_bitmap.c index 0dde951..c2eee07 100644 --- a/src/compositor/mpeg4_bitmap.c +++ b/src/compositor/mpeg4_bitmap.c @@ -53,7 +53,11 @@ static void Bitmap_BuildGraph(GF_Node *node, BitmapStack *st, GF_TraverseState * if (! ((M_Appearance *)tr_state->appear)->texture) return; txh = gf_sc_texture_get_handler( ((M_Appearance *)tr_state->appear)->texture ); /*bitmap not ready*/ - if (!txh || !txh->tx_io || !txh->width || !txh->height) { + if (!txh || !txh->width || !txh->height +#ifndef GPAC_DISABLE_3D + || (tr_state->visual->type_3d && !txh->tx_io) +#endif + ) { if (notify_changes) gf_node_dirty_set(node, 0, 1); return; } @@ -123,12 +127,10 @@ static void draw_bitmap_3d(GF_Node *node, GF_TraverseState *tr_state) static void draw_bitmap_2d(GF_Node *node, GF_TraverseState *tr_state) { GF_ColorKey *key, keyColor; - GF_Compositor *compositor; DrawableContext *ctx = tr_state->ctx; BitmapStack *st = (BitmapStack *) gf_node_get_private(node); - compositor = tr_state->visual->compositor; /*bitmaps are NEVER rotated (forbidden in spec). In case a rotation was done we still display (reset the skew components)*/ ctx->transform.m[1] = ctx->transform.m[3] = 0; @@ -215,6 +217,7 @@ static void TraverseBitmap(GF_Node *node, void *rs, Bool is_destroy) memset(&rc, 0, sizeof(rc)); Bitmap_BuildGraph(node, st, tr_state, &rc, 1); + if (!rc.width || !rc.height) return; ctx = drawable_init_context_mpeg4(st->graph, tr_state); if (!ctx || !ctx->aspect.fill_texture ) { diff --git a/src/compositor/mpeg4_form.c b/src/compositor/mpeg4_form.c index d669440..4c2b8cf 100644 --- a/src/compositor/mpeg4_form.c +++ b/src/compositor/mpeg4_form.c @@ -201,7 +201,7 @@ static void form_apply(FormStack *st, const char *constraint, u32 *group_idx, u3 static void TraverseForm(GF_Node *n, void *rs, Bool is_destroy) { #if FORM_CLIPS - GF_Rect clip, prev_clipper; + GF_Rect prev_clipper; Bool had_clip; GF_IRect prev_clip; #endif @@ -338,7 +338,7 @@ static void TraverseForm(GF_Node *n, void *rs, Bool is_destroy) /*update clipper*/ if (tr_state->traversing_mode==TRAVERSE_SORT) { prev_clip = tr_state->visual->top_clipper; - clip = compositor_2d_update_clipper(tr_state, st->clip, &had_clip, &prev_clipper, 0); + compositor_2d_update_clipper(tr_state, st->clip, &had_clip, &prev_clipper, 0); if (tr_state->has_clip) { tr_state->visual->top_clipper = gf_rect_pixelize(&tr_state->clipper); gf_irect_intersect(&tr_state->visual->top_clipper, &prev_clip); diff --git a/src/compositor/mpeg4_geometry_2d.c b/src/compositor/mpeg4_geometry_2d.c index 2850f3b..501d21f 100644 --- a/src/compositor/mpeg4_geometry_2d.c +++ b/src/compositor/mpeg4_geometry_2d.c @@ -34,6 +34,7 @@ Bool compositor_get_2d_plane_intersection(GF_Ray *ray, SFVec3f *res) if (!ray->dir.x && !ray->dir.y) { res->x = ray->orig.x; res->y = ray->orig.y; + res->z = 0; return 1; } p.normal.x = p.normal.y = 0; p.normal.z = FIX_ONE; @@ -689,4 +690,29 @@ void compositor_init_pointset2d(GF_Compositor *compositor, GF_Node *node) gf_node_set_callback_function(node, TraversePointSet2D); } +static void TraverseBitWrapper(GF_Node *node, void *rs, Bool is_destroy) +{ + GF_TraverseState *tr_state; + M_BitWrapper *bitWrap; + + if (is_destroy) { + gf_node_set_private(node, NULL); + return; + } + + tr_state = (GF_TraverseState *)rs; + // Traverse the node here + bitWrap = (M_BitWrapper *)node; + gf_node_traverse(bitWrap->node, tr_state); +} + +void compositor_init_bitwrapper(GF_Compositor *compositor, GF_Node *node) +{ + M_BitWrapper *bit; + bit = (M_BitWrapper *)node; + gf_node_set_private(node, gf_node_get_private(bit->node)); + gf_node_set_callback_function(node, TraverseBitWrapper); +} + + #endif /*GPAC_DISABLE_VRML*/ diff --git a/src/compositor/mpeg4_geometry_ifs2d.c b/src/compositor/mpeg4_geometry_ifs2d.c index 95d1760..b79e8e8 100644 --- a/src/compositor/mpeg4_geometry_ifs2d.c +++ b/src/compositor/mpeg4_geometry_ifs2d.c @@ -78,7 +78,7 @@ static void ifs2d_check_changes(GF_Node *node, Drawable *stack, GF_TraverseState static void IFS2D_Draw(GF_Node *node, GF_TraverseState *tr_state) { u32 i, count, ci_count; - u32 start_pts, j, ind_col, num_col; + u32 j, ind_col, num_col; SFVec2f center, end; SFColor col_cen; GF_STENCIL grad; @@ -184,7 +184,6 @@ static void IFS2D_Draw(GF_Node *node, GF_TraverseState *tr_state) start = pts[ifs2D->coordIndex.vals[i]]; center = start; gf_path_add_move_to(path, start.x, start.y); - start_pts = i; num_col = 1; i+=1; while (ifs2D->coordIndex.vals[i] != -1) { diff --git a/src/compositor/mpeg4_grouping_2d.c b/src/compositor/mpeg4_grouping_2d.c index 4b507ae..ec81c5d 100644 --- a/src/compositor/mpeg4_grouping_2d.c +++ b/src/compositor/mpeg4_grouping_2d.c @@ -36,7 +36,7 @@ typedef struct static void TraverseSwitch(GF_Node *node, void *rs, Bool is_destroy) { GF_ChildNodeItem *l; - u32 i, count; + u32 i; Bool prev_switch; GF_ChildNodeItem *children; s32 whichChoice; @@ -67,8 +67,6 @@ static void TraverseSwitch(GF_Node *node, void *rs, Bool is_destroy) } if (tr_state->traversing_mode!=TRAVERSE_GET_BOUNDS) { - count = gf_node_list_get_count(children); - prev_switch = tr_state->switched_off; /*check changes in choice field*/ if ((gf_node_dirty_get(node) & GF_SG_NODE_DIRTY) || (st->last_switch != whichChoice) ) { diff --git a/src/compositor/mpeg4_layer_2d.c b/src/compositor/mpeg4_layer_2d.c index 02efc8f..d140cd5 100644 --- a/src/compositor/mpeg4_layer_2d.c +++ b/src/compositor/mpeg4_layer_2d.c @@ -112,8 +112,8 @@ static void TraverseLayer2D(GF_Node *node, void *rs, Bool is_destroy) GF_TraverseState *tr_state = (GF_TraverseState *) rs; if (is_destroy) { - gf_list_del(st->backs); - gf_list_del(st->views); + BindableStackDelete(st->backs); + BindableStackDelete(st->views); group_2d_destroy(node, (GroupingNode2D*)st); gf_free(st); return; diff --git a/src/compositor/mpeg4_layout.c b/src/compositor/mpeg4_layout.c index 7b864e9..7cb9f22 100644 --- a/src/compositor/mpeg4_layout.c +++ b/src/compositor/mpeg4_layout.c @@ -622,7 +622,7 @@ static void TraverseLayout(GF_Node *node, void *rs, Bool is_destroy) GF_IRect prev_clip; Bool mode_bckup, had_clip; ParentNode2D *parent_bck; - GF_Rect clip, prev_clipper; + GF_Rect prev_clipper; M_Layout *l = (M_Layout *)node; LayoutStack *st = (LayoutStack *) gf_node_get_private(node); GF_TraverseState *tr_state = (GF_TraverseState *)rs; @@ -673,7 +673,7 @@ static void TraverseLayout(GF_Node *node, void *rs, Bool is_destroy) /*setup clipping*/ prev_clip = tr_state->visual->top_clipper; if (tr_state->traversing_mode==TRAVERSE_SORT) { - clip = compositor_2d_update_clipper(tr_state, st->clip, &had_clip, &prev_clipper, 0); + compositor_2d_update_clipper(tr_state, st->clip, &had_clip, &prev_clipper, 0); if (tr_state->has_clip) { tr_state->visual->top_clipper = gf_rect_pixelize(&tr_state->clipper); gf_irect_intersect(&tr_state->visual->top_clipper, &prev_clip); diff --git a/src/compositor/mpeg4_text.c b/src/compositor/mpeg4_text.c index 7d29cf2..d211f85 100644 --- a/src/compositor/mpeg4_text.c +++ b/src/compositor/mpeg4_text.c @@ -200,7 +200,7 @@ static void build_text_split(TextStack *st, M_Text *txt, GF_TraverseState *tr_st static void build_text(TextStack *st, M_Text *txt, GF_TraverseState *tr_state) { u32 i, j, int_major, k, styles, count; - Fixed fontSize, start_x, start_y, line_spacing, tot_width, tot_height, max_scale, space, maxExtent; + Fixed fontSize, start_x, start_y, line_spacing, tot_width, tot_height, max_scale, maxExtent; u32 size, trim_size; GF_Font *font; Bool horizontal; @@ -229,7 +229,6 @@ static void build_text(TextStack *st, M_Text *txt, GF_TraverseState *tr_state) /*NOTA: we could use integer maths here but we have a risk of overflow with large fonts, so use fixed maths*/ st->ascent = gf_muldiv(fontSize, INT2FIX(font->ascent), INT2FIX(font->em_size)); st->descent = -gf_muldiv(fontSize, INT2FIX(font->descent), INT2FIX(font->em_size)); - space = gf_muldiv(fontSize, INT2FIX(font->line_spacing), INT2FIX(font->em_size)) ; line_spacing = gf_mulfix(FSSPACE, fontSize); maxExtent = txt->maxExtent; diff --git a/src/compositor/mpeg4_textures.c b/src/compositor/mpeg4_textures.c index 62d6fe5..3ae06d0 100644 --- a/src/compositor/mpeg4_textures.c +++ b/src/compositor/mpeg4_textures.c @@ -278,40 +278,52 @@ static void imagetexture_update(GF_TextureHandler *txh) M_CacheTexture *ct = (M_CacheTexture *) txh->owner; /*decode cacheTexture data */ - if (ct->data && !txh->data) { + if ((ct->data || ct->image.buffer) && !txh->data) { u32 out_size; GF_Err e; - switch (ct->objectTypeIndication) { - case GPAC_OTI_IMAGE_JPEG: - out_size = 0; - e = gf_img_jpeg_dec(ct->data, ct->data_len, &txh->width, &txh->height, &txh->pixelformat, NULL, &out_size, 3); - if (e==GF_BUFFER_TOO_SMALL) { - u32 BPP; - txh->data = gf_malloc(sizeof(char) * out_size); - if (txh->pixelformat==GF_PIXEL_GREYSCALE) BPP = 1; - else BPP = 3; - - e = gf_img_jpeg_dec(ct->data, ct->data_len, &txh->width, &txh->height, &txh->pixelformat, txh->data, &out_size, BPP); - if (e==GF_OK) { - txh->needs_refresh = 1; - txh->stride = out_size / txh->height; - } + + /*BT/XMT playback*/ + if (ct->image.buffer) { + e = gf_img_file_dec(ct->image.buffer, &ct->objectTypeIndication, &txh->width, &txh->height, &txh->pixelformat, &txh->data, &out_size); + if (e==GF_OK) { + txh->needs_refresh = 1; + txh->stride = out_size / txh->height; } - break; - case GPAC_OTI_IMAGE_PNG: - out_size = 0; - e = gf_img_png_dec(ct->data, ct->data_len, &txh->width, &txh->height, &txh->pixelformat, NULL, &out_size); - if (e==GF_BUFFER_TOO_SMALL) { - txh->data = gf_malloc(sizeof(char) * out_size); - e = gf_img_png_dec(ct->data, ct->data_len, &txh->width, &txh->height, &txh->pixelformat, txh->data, &out_size); - if (e==GF_OK) { - txh->needs_refresh = 1; - txh->stride = out_size / txh->height; + } + /*BIFS decoded playback*/ + else { + switch (ct->objectTypeIndication) { + case GPAC_OTI_IMAGE_JPEG: + out_size = 0; + e = gf_img_jpeg_dec(ct->data, ct->data_len, &txh->width, &txh->height, &txh->pixelformat, NULL, &out_size, 3); + if (e==GF_BUFFER_TOO_SMALL) { + u32 BPP; + txh->data = gf_malloc(sizeof(char) * out_size); + if (txh->pixelformat==GF_PIXEL_GREYSCALE) BPP = 1; + else BPP = 3; + + e = gf_img_jpeg_dec(ct->data, ct->data_len, &txh->width, &txh->height, &txh->pixelformat, txh->data, &out_size, BPP); + if (e==GF_OK) { + txh->needs_refresh = 1; + txh->stride = out_size / txh->height; + } } + break; + case GPAC_OTI_IMAGE_PNG: + out_size = 0; + e = gf_img_png_dec(ct->data, ct->data_len, &txh->width, &txh->height, &txh->pixelformat, NULL, &out_size); + if (e==GF_BUFFER_TOO_SMALL) { + txh->data = gf_malloc(sizeof(char) * out_size); + e = gf_img_png_dec(ct->data, ct->data_len, &txh->width, &txh->height, &txh->pixelformat, txh->data, &out_size); + if (e==GF_OK) { + txh->needs_refresh = 1; + txh->stride = out_size / txh->height; + } + } + break; } - break; } - + /*cacheURL is specified, store the image*/ if (ct->cacheURL.buffer) { u32 i; @@ -341,7 +353,7 @@ static void imagetexture_update(GF_TextureHandler *txh) strcat(szExtractName, ct->cacheURL.buffer); cached_texture = gf_f64_open(szExtractName, "wb"); if (cached_texture) { - fwrite(ct->data, 1, ct->data_len, cached_texture); + gf_fwrite(ct->data, 1, ct->data_len, cached_texture); fclose(cached_texture); } @@ -366,8 +378,8 @@ static void imagetexture_update(GF_TextureHandler *txh) } /*done with image, destroy buffer*/ - gf_free(ct->data); - ct->data =NULL; + if (ct->data) gf_free(ct->data); + ct->data = NULL; ct->data_len = 0; } } diff --git a/src/compositor/mpeg4_viewport.c b/src/compositor/mpeg4_viewport.c index 5ba9bc8..8e53985 100644 --- a/src/compositor/mpeg4_viewport.c +++ b/src/compositor/mpeg4_viewport.c @@ -132,7 +132,7 @@ static void viewport_set_bind(GF_Node *node, GF_Route *route) static void TraverseViewport(GF_Node *node, void *rs, Bool is_destroy) { - Fixed ar, sx, sy, w, h, tx, ty; + Fixed sx, sy, w, h, tx, ty; #ifndef GPAC_DISABLE_3D GF_Matrix mx; #endif @@ -200,7 +200,6 @@ static void TraverseViewport(GF_Node *node, void *rs, Bool is_destroy) sx = (vp->size.x>=0) ? vp->size.x : w; sy = (vp->size.y>=0) ? vp->size.y : h; rc = gf_rect_center(sx, sy); - ar = gf_divfix(h, w); rc_bckup = rc; switch (vp->fit) { diff --git a/src/compositor/navigate.c b/src/compositor/navigate.c index d4cf4b1..7490bc0 100644 --- a/src/compositor/navigate.c +++ b/src/compositor/navigate.c @@ -280,13 +280,13 @@ static Bool compositor_handle_navigation_3d(GF_Compositor *compositor, GF_Event is_pixel_metrics = compositor->traverse_state->pixel_metrics; } - if (!cam->navigate_mode) return 0; keys = compositor->key_states; + if (!cam->navigate_mode && !(keys & GF_KEY_MOD_ALT) ) return 0; x = y = 0; /*renorm between -1, 1*/ if (ev->type<=GF_EVENT_MOUSEWHEEL) { - x = gf_divfix( INT2FIX(ev->mouse.x + (s32) compositor->visual->width/2), INT2FIX(compositor->visual->width)); - y = gf_divfix( INT2FIX(ev->mouse.y + (s32) compositor->visual->height/2), INT2FIX(compositor->visual->height)); + x = gf_divfix( INT2FIX(ev->mouse.x - (s32) compositor->visual->width/2), INT2FIX(compositor->visual->width)); + y = gf_divfix( INT2FIX(ev->mouse.y - (s32) compositor->visual->height/2), INT2FIX(compositor->visual->height)); } dx = (x - compositor->grab_x); @@ -296,13 +296,13 @@ static Bool compositor_handle_navigation_3d(GF_Compositor *compositor, GF_Event key_trans = is_pixel_metrics ? INT2FIX(10) : cam->avatar_size.x; */ trans_scale = cam->width/20; - key_trans = cam->avatar_size.x; + key_trans = cam->avatar_size.x/2; if (cam->world_bbox.is_set && (key_trans*5 > cam->world_bbox.radius)) { - key_trans = cam->world_bbox.radius / 10; + key_trans = cam->world_bbox.radius / 100; } key_pan = FIX_ONE/25; - key_exam = FIX_ONE/10; + key_exam = FIX_ONE/20; key_inv = 1; if (keys & GF_KEY_MOD_SHIFT) { @@ -467,7 +467,7 @@ static Bool compositor_handle_navigation_3d(GF_Compositor *compositor, GF_Event case GF_KEY_RIGHT: if (keys & GF_KEY_MOD_ALT) { if ( (keys & GF_KEY_MOD_SHIFT) && (compositor->visual->nb_views > 1) ) { - compositor->view_distance_offset += key_inv * INT2FIX((is_pixel_metrics ? 5 : 1)); + compositor->view_distance_offset += key_inv * (is_pixel_metrics ? INT2FIX(1) : FLT2FIX(0.1)); cam->flags |= CAM_IS_DIRTY; fprintf(stdout, "AutoStereo view distance %f\n", FIX2FLT(compositor->view_distance_offset + compositor->video_out->view_distance)/100); gf_sc_invalidate(compositor, NULL); @@ -570,7 +570,7 @@ static Bool compositor_handle_navigation_2d(GF_VisualManager *visual, GF_Event * if (visual->type_3d) navigation_mode = visual->camera.navigate_mode; #endif - if (!navigation_mode /*&& !(keys & GF_KEY_MOD_ALT) */) return 0; + if (!navigation_mode && !(keys & GF_KEY_MOD_ALT) ) return 0; x = y = 0; @@ -580,7 +580,11 @@ static Bool compositor_handle_navigation_2d(GF_VisualManager *visual, GF_Event * y = INT2FIX(ev->mouse.y); } dx = x - visual->compositor->grab_x; - dy = visual->compositor->grab_y - y; + if (visual->center_coords) { + dy = visual->compositor->grab_y - y; + } else { + dy = y - visual->compositor->grab_y; + } if (!is_pixel_metrics) { dx /= visual->width; dy /= visual->height; } key_inv = 1; diff --git a/src/compositor/nodes_stacks.h b/src/compositor/nodes_stacks.h index 3141941..6968d69 100644 --- a/src/compositor/nodes_stacks.h +++ b/src/compositor/nodes_stacks.h @@ -156,6 +156,8 @@ void compositor_init_transformmatrix2d(GF_Compositor *compositor, GF_Node *node) void compositor_init_indexed_line_set2d(GF_Compositor *compositor, GF_Node *node); void compositor_init_indexed_face_set2d(GF_Compositor *compositor, GF_Node *node); +void compositor_init_bitwrapper(GF_Compositor *compositor, GF_Node *node); + void compositor_init_viewport(GF_Compositor *compositor, GF_Node *node); void tr_mx2d_get_matrix(GF_Node *n, GF_Matrix2D *mat); @@ -321,6 +323,8 @@ void svg_pause_animation(GF_Node *n, Bool pause); void svg_pause_audio(GF_Node *n, Bool pause); void svg_pause_video(GF_Node *n, Bool pause); +void compositor_svg_video_modified(GF_Compositor *compositor, GF_Node *node); + #endif diff --git a/src/compositor/svg_geometry.c b/src/compositor/svg_geometry.c index 2d0a666..0d309b0 100644 --- a/src/compositor/svg_geometry.c +++ b/src/compositor/svg_geometry.c @@ -278,6 +278,7 @@ void svg_drawable_pick(GF_Node *node, Drawable *drawable, GF_TraverseState *tr_s memcpy(&backup_props, tr_state->svg_props, sizeof(SVGPropertiesPointers)); gf_svg_apply_inheritance(&all_atts, tr_state->svg_props); + if (compositor_svg_is_display_off(tr_state->svg_props)) return; compositor_svg_apply_local_transformation(tr_state, &all_atts, &backup_matrix, &mx_3d); diff --git a/src/compositor/svg_grouping.c b/src/compositor/svg_grouping.c index ffd6e7d..5d0e1b7 100644 --- a/src/compositor/svg_grouping.c +++ b/src/compositor/svg_grouping.c @@ -90,7 +90,7 @@ static void svg_recompute_viewport_transformation(GF_Node *node, SVGsvgStack *st gf_mx2d_init(mx); - if (stack->root_svg) { + if (stack->root_svg && !tr_state->parent_is_use) { const char *frag_uri = gf_scene_get_fragment_uri(node); if (frag_uri) { /*SVGView*/ @@ -994,7 +994,7 @@ static void svg_traverse_resource(GF_Node *node, void *rs, Bool is_destroy, Bool GF_Matrix2D translate; SVGPropertiesPointers backup_props; u32 backup_flags, dirty; - Bool is_fragment; + Bool is_fragment, parent_is_use; GF_Node *used_node; GF_TraverseState *tr_state = (GF_TraverseState *)rs; SVGAllAttributes all_atts; @@ -1075,6 +1075,8 @@ static void svg_traverse_resource(GF_Node *node, void *rs, Bool is_destroy, Bool prev_opacity = tr_state->parent_use_opacity; tr_state->parent_use_opacity = all_atts.opacity; + parent_is_use = tr_state->parent_is_use; + tr_state->parent_is_use = is_foreign_object ? 0 : 1; if (tr_state->traversing_mode == TRAVERSE_GET_BOUNDS) { compositor_svg_apply_local_transformation(tr_state, &all_atts, &backup_matrix, &mx_3d); @@ -1117,6 +1119,7 @@ static void svg_traverse_resource(GF_Node *node, void *rs, Bool is_destroy, Bool gf_list_rem_last(tr_state->use_stack); tr_state->vp_size = prev_vp; + tr_state->parent_is_use = parent_is_use; tr_state->parent_use_opacity = prev_opacity; end: @@ -1308,11 +1311,16 @@ static void svg_traverse_animation(GF_Node *node, void *rs, Bool is_destroy) gf_mx2d_apply_rect(&tr_state->transform, &rc); prev_clip = tr_state->visual->top_clipper; clip = gf_rect_pixelize(&rc); -// gf_irect_intersect(&tr_state->visual->top_clipper, &clip); + gf_irect_intersect(&tr_state->visual->top_clipper, &clip); if (!stack->inline_sg && stack->resource) { stack->inline_sg = gf_mo_get_scenegraph(stack->resource); } + /*if we have the focus, move it to the subtree*/ + if (tr_state->visual->compositor->focus_node==node) { + GF_Node *subroot = gf_sg_get_root_node(stack->inline_sg); + if (subroot) tr_state->visual->compositor->focus_node = subroot; + } if (stack->inline_sg) { gf_sc_traverse_subscene(tr_state->visual->compositor, node, stack->inline_sg, tr_state); diff --git a/src/compositor/svg_media.c b/src/compositor/svg_media.c index 59f2732..648c764 100644 --- a/src/compositor/svg_media.c +++ b/src/compositor/svg_media.c @@ -80,7 +80,6 @@ static Bool svg_video_get_transform_behavior(GF_TraverseState *tr_state, SVGAllA static void SVG_Draw_bitmap(GF_TraverseState *tr_state) { DrawableContext *ctx = tr_state->ctx; - if (!tr_state->visual->DrawBitmap(tr_state->visual, tr_state, ctx, NULL)) { visual_2d_texture_path(tr_state->visual, ctx->drawable->path, ctx, tr_state); } @@ -287,7 +286,7 @@ static void svg_traverse_bitmap(GF_Node *node, void *rs, Bool is_destroy) stack->audio = NULL; } stack->audio_dirty = 1; - + if (stack->txurl.count) svg_play_texture(stack, &all_atts); gf_node_dirty_clear(node, GF_SG_SVG_XLINK_HREF_DIRTY); } @@ -298,6 +297,7 @@ static void svg_traverse_bitmap(GF_Node *node, void *rs, Bool is_destroy) gf_node_dirty_clear(node, 0); SVG_Build_Bitmap_Graph((SVG_video_stack*)gf_node_get_private(node), tr_state); } + } if (tr_state->traversing_mode == TRAVERSE_GET_BOUNDS) { @@ -437,11 +437,9 @@ static void SVG_Update_video(GF_TextureHandler *txh) SVG_video_stack *stack = (SVG_video_stack *) gf_node_get_private(txh->owner); if (!txh->is_open) { - u32 tag; SVG_InitialVisibility init_vis; if (stack->first_frame_fetched) return; - tag = gf_node_get_tag(txh->owner); init_vis = SVG_INITIALVISIBILTY_WHENSTARTED; if (gf_node_get_attribute_by_tag(txh->owner, TAG_SVG_ATT_initialVisibility, 0, 0, &init_vis_info) == GF_OK) { @@ -473,17 +471,20 @@ static void SVG_Update_video(GF_TextureHandler *txh) } if (!stack->audio && stack->audio_dirty) { - stack->audio_dirty = 0; - if (gf_mo_has_audio(stack->txh.stream)) { - GF_FieldInfo att_vid, att_aud; - stack->audio = gf_node_new(gf_node_get_graph(stack->txh.owner), TAG_SVG_audio); - gf_node_register(stack->audio, NULL); - if (gf_node_get_attribute_by_tag(stack->txh.owner, TAG_XLINK_ATT_href, 0, 0, &att_vid)==GF_OK) { - gf_node_get_attribute_by_tag(stack->audio, TAG_XLINK_ATT_href, 1, 0, &att_aud); - gf_svg_attributes_copy(&att_aud, &att_vid, 0); + u32 res = gf_mo_has_audio(stack->txh.stream); + if (res != 2) { + stack->audio_dirty = 0; + if (res) { + GF_FieldInfo att_vid, att_aud; + stack->audio = gf_node_new(gf_node_get_graph(stack->txh.owner), TAG_SVG_audio); + gf_node_register(stack->audio, NULL); + if (gf_node_get_attribute_by_tag(stack->txh.owner, TAG_XLINK_ATT_href, 0, 0, &att_vid)==GF_OK) { + gf_node_get_attribute_by_tag(stack->audio, TAG_XLINK_ATT_href, 1, 0, &att_aud); + gf_svg_attributes_copy(&att_aud, &att_vid, 0); + } + /*BYPASS SMIL TIMING MODULE!!*/ + compositor_init_svg_audio(stack->txh.compositor, stack->audio, 1); } - /*BYPASS SMIL TIMING MODULE!!*/ - compositor_init_svg_audio(stack->txh.compositor, stack->audio, 1); } } @@ -562,6 +563,30 @@ void svg_pause_video(GF_Node *n, Bool pause) else gf_mo_resume(st->txh.stream); } +void compositor_svg_video_modified(GF_Compositor *compositor, GF_Node *node) +{ + /*if href has been modified, stop the video (and associated audio if any) right away - we cannot wait for next traversal to + process this as the video could be in a hidden subtree not traversed*/ + if (gf_node_dirty_get(node) & GF_SG_SVG_XLINK_HREF_DIRTY) { + SVG_video_stack *st = gf_node_get_private(node); + /*WARNING - stack may be NULL at this point when inserting the video from script*/ + if (st && st->txh.is_open) { + if (st->audio) { + svg_audio_smil_evaluate_ex(NULL, 0, SMIL_TIMING_EVAL_REMOVE, st->audio, st->txh.owner); + gf_node_unregister(st->audio, NULL); + st->audio = NULL; + } + /*reset cached URL to avoid reopening the resource in the smil timing callback*/ + gf_sg_vrml_mf_reset(&st->txurl, GF_SG_VRML_MFURL); + gf_sc_texture_stop(&st->txh); + } + } + gf_node_dirty_set(node, 0, 0); + /*and force a redraw of next frame*/ + gf_sc_next_frame_state(compositor, GF_SC_DRAW_FRAME); +} + + /*********************/ /* SVG audio element */ /*********************/ @@ -587,11 +612,15 @@ static void svg_audio_smil_evaluate_ex(SMIL_Timing_RTI *rti, Fixed normalized_sc if (!stack->is_active && !stack->is_error) { if (stack->aurl.count) { SVGAllAttributes atts; + Bool lock_timeline = 0; gf_svg_flatten_attributes((SVG_Element*) (video ? video : audio), &atts); + if (atts.syncBehavior) lock_timeline = (*atts.syncBehavior == SMIL_SYNCBEHAVIOR_LOCKED) ? 1 : 0; + if (gf_sc_audio_open(&stack->input, &stack->aurl, atts.clipBegin ? (*atts.clipBegin) : 0.0, - atts.clipEnd ? (*atts.clipEnd) : -1.0) == GF_OK) + atts.clipEnd ? (*atts.clipEnd) : -1.0, + lock_timeline) == GF_OK) { gf_mo_set_speed(stack->input.stream, FIX_ONE); stack->is_active = 1; @@ -666,6 +695,7 @@ static void svg_traverse_audio_ex(GF_Node *node, void *rs, Bool is_destroy, SVGP if (gf_node_dirty_get(node) & GF_SG_SVG_XLINK_HREF_DIRTY) { SVGAllAttributes atts; + Bool lock_timeline = 0; if (stack->is_active) gf_sc_audio_stop(&stack->input); @@ -675,10 +705,12 @@ static void svg_traverse_audio_ex(GF_Node *node, void *rs, Bool is_destroy, SVGP gf_term_get_mfurl_from_xlink(node, &(stack->aurl)); gf_svg_flatten_attributes((SVG_Element*) node, &atts); + if (atts.syncBehavior) lock_timeline = (*atts.syncBehavior == SMIL_SYNCBEHAVIOR_LOCKED) ? 1 : 0; if (stack->aurl.count && (gf_sc_audio_open(&stack->input, &stack->aurl, atts.clipBegin ? (*atts.clipBegin) : 0.0, - atts.clipEnd ? (*atts.clipEnd) : -1.0) == GF_OK) + atts.clipEnd ? (*atts.clipEnd) : -1.0, + lock_timeline) == GF_OK) ) { gf_mo_set_speed(stack->input.stream, FIX_ONE); diff --git a/src/compositor/svg_paint_servers.c b/src/compositor/svg_paint_servers.c index 832031f..63a1164 100644 --- a/src/compositor/svg_paint_servers.c +++ b/src/compositor/svg_paint_servers.c @@ -671,6 +671,21 @@ static void SVG_RG_ComputeMatrix(GF_TextureHandler *txh, GF_Rect *bounds, GF_Mat focal.y = center.y; } + /* clamping fx/fy according to: + http://www.w3.org/TR/SVG11/pservers.html#RadialGradients + If the point defined by ‘fxÂ’ and ‘fyÂ’ lies outside the circle defined by ‘cxÂ’, ‘cyÂ’ and ‘rÂ’, + then the user agent shall set the focal point to the intersection of the line from (‘cxÂ’, ‘cyÂ’) + to (‘fxÂ’, ‘fyÂ’) with the circle defined by ‘cxÂ’, ‘cyÂ’ and ‘rÂ’.*/ + { + Fixed norm = gf_v2d_distance(&focal, ¢er); + if (norm > radius) { + Fixed xdelta = gf_muldiv(radius, focal.x-center.x, norm); + Fixed ydelta = gf_muldiv(radius, focal.y-center.y, norm); + focal.x = center.x + xdelta; + focal.y = center.y + ydelta; + } + } + if (bounds && (!all_atts.gradientUnits || (*(SVG_GradientUnit*)all_atts.gradientUnits==SVG_GRADIENTUNITS_OBJECT)) ) { /*move to local coord system - cf SVG spec*/ gf_mx2d_add_scale(mat, bounds->width, bounds->height); diff --git a/src/compositor/texturing.c b/src/compositor/texturing.c index 5ac7696..8f5cfc4 100644 --- a/src/compositor/texturing.c +++ b/src/compositor/texturing.c @@ -187,6 +187,7 @@ void gf_sc_texture_update_frame(GF_TextureHandler *txh, Bool disable_resync) } if (gf_mo_is_private_media(txh->stream)) { setup_texture_object(txh, 1); + gf_node_dirty_set(txh->owner, 0, 0); } } txh->data = gf_mo_fetch_data(txh->stream, !disable_resync, &txh->stream_finished, &ts, &size); diff --git a/src/compositor/texturing_gl.c b/src/compositor/texturing_gl.c index 300bb44..49b036e 100644 --- a/src/compositor/texturing_gl.c +++ b/src/compositor/texturing_gl.c @@ -310,6 +310,7 @@ static Bool tx_setup_format(GF_TextureHandler *txh) break; #endif case GF_PIXEL_YV12: + case GF_PIXEL_NV21: #ifndef GPAC_USE_OGL_ES if (!compositor->disable_yuvgl && compositor->gl_caps.yuv_texture && !(txh->tx_io->flags & TX_MUST_SCALE) ) { txh->tx_io->gl_format = compositor->gl_caps.yuv_texture; @@ -486,6 +487,7 @@ assert(txh->data ); txh->tx_io->flags |= TX_IS_FLIPPED; return 1; case GF_PIXEL_YV12: + case GF_PIXEL_NV21: if (txh->tx_io->gl_format == compositor->gl_caps.yuv_texture) { txh->tx_io->conv_format = GF_PIXEL_YVYU; txh->tx_io->flags |= TX_NEEDS_HW_LOAD; @@ -536,6 +538,7 @@ assert(txh->data ); switch (txh->pixelformat) { case GF_PIXEL_YUY2: case GF_PIXEL_YV12: + case GF_PIXEL_NV21: txh->tx_io->conv_format = dst.pixel_format = GF_PIXEL_RGB_24; /*stretch and flip*/ gf_stretch_bits(&dst, &src, NULL, NULL, 0xFF, 1, NULL, NULL); @@ -920,8 +923,7 @@ static Bool gf_sc_texture_enable_matte_texture(GF_Node *n) char * action ; int tmp; u8 texture[4]; - MFFloat coefficients ; - GF_Compositor *compositor; + MFFloat coefficients; #endif M_MatteTexture *matte = (M_MatteTexture *)n; @@ -937,7 +939,6 @@ static Bool gf_sc_texture_enable_matte_texture(GF_Node *n) return 1; #else - compositor = b_surf->compositor; if (glActiveTexture==NULL) { tx_bind(b_surf); return 1; diff --git a/src/compositor/visual_manager_2d.c b/src/compositor/visual_manager_2d.c index b1e0302..156082d 100644 --- a/src/compositor/visual_manager_2d.c +++ b/src/compositor/visual_manager_2d.c @@ -242,7 +242,6 @@ GF_Err visual_2d_init_draw(GF_VisualManager *visual, GF_TraverseState *tr_state) M_Background2D *bck; #endif u32 draw_mode; - u32 time; /*reset display list*/ visual->cur_context = visual->context; @@ -271,7 +270,6 @@ GF_Err visual_2d_init_draw(GF_VisualManager *visual, GF_TraverseState *tr_state) } tr_state->invalidate_all = 0; - time = gf_sys_clock(); /*reset prev nodes if any (previous traverse was indirect)*/ rem = count = 0; prev = NULL; @@ -441,7 +439,10 @@ void ra_refresh(GF_RectArray *ra) static Bool register_context_rect(GF_RectArray *ra, DrawableContext *ctx, u32 ctx_idx, DrawableContext **first_opaque) { u32 i; - Bool is_transparent, needs_redraw; + Bool needs_redraw; +#ifdef TRACK_OPAQUE_REGIONS + Bool is_transparent = 1; +#endif GF_IRect *rc = &ctx->bi->clip; assert(rc->width && rc->height); @@ -449,10 +450,10 @@ static Bool register_context_rect(GF_RectArray *ra, DrawableContext *ctx, u32 ct needs_redraw = (ctx->flags & CTX_REDRAW_MASK) ? 1 : 0; /*node is transparent*/ - is_transparent = 1; if ((ctx->flags & CTX_NO_ANTIALIAS) && !(ctx->flags & CTX_IS_TRANSPARENT) ) { +#ifdef TRACK_OPAQUE_REGIONS is_transparent = 0; - +#endif if ((*first_opaque==NULL) && needs_redraw) *first_opaque = ctx; } @@ -651,8 +652,6 @@ Bool visual_2d_terminate_draw(GF_VisualManager *visual, GF_TraverseState *tr_sta it = it->next; } } - /*no visual */ - //if (!visual->compositor->output_width && !visual->compositor->output_height) goto exit; if (redraw_all) { ra_clear(&visual->to_redraw); @@ -722,12 +721,12 @@ Bool visual_2d_terminate_draw(GF_VisualManager *visual, GF_TraverseState *tr_sta skip_background: #ifndef GPAC_DISABLE_LOG - if ((gf_log_get_level() >= GF_LOG_DEBUG) && (gf_log_get_tools() & GF_LOG_COMPOSE)) { - GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Visual2D] Redraw %d / %d nodes (all: %s)", num_changed, num_nodes, redraw_all ? "yes" : "no")); + if (gf_log_tool_level_on(GF_LOG_COMPOSE, GF_LOG_INFO)) { + GF_LOG(GF_LOG_INFO, GF_LOG_COMPOSE, ("[Visual2D] Redraw %d / %d nodes (all: %s - %d dirty rects\n)", num_changed, num_nodes, redraw_all ? "yes" : "no", visual->to_redraw.count)); if (visual->to_redraw.count>1) GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("\n")); for (i=0; ito_redraw.count; i++) { - GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("\tDirtyRect #%d: %d:%d@%dx%d\n", i+1, visual->to_redraw.list[i].x, visual->to_redraw.list[i].y, visual->to_redraw.list[i].width, visual->to_redraw.list[i].height)); + GF_LOG(GF_LOG_INFO, GF_LOG_COMPOSE, ("\tDirtyRect #%d: %d:%d@%dx%d\n", i+1, visual->to_redraw.list[i].x, visual->to_redraw.list[i].y, visual->to_redraw.list[i].width, visual->to_redraw.list[i].height)); assert(visual->to_redraw.list[i].width); } } @@ -776,7 +775,6 @@ exit: visual_2d_release_raster(visual); visual_clean_contexts(visual); visual->num_nodes_prev_frame = visual->num_nodes_current_frame; - GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Visual2D] Redraw done - %schanged\n", has_changed ? "" : "un")); return has_changed; } @@ -796,6 +794,7 @@ Bool visual_2d_draw_frame(GF_VisualManager *visual, GF_Node *root, GF_TraverseSt e = visual_2d_init_draw(visual, tr_state); if (e) { gf_mx2d_copy(tr_state->transform, backup); + GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Visual2D] Cannot init draw phase: %s\n", gf_error_to_string(e))); return 0; } diff --git a/src/compositor/visual_manager_2d_draw.c b/src/compositor/visual_manager_2d_draw.c index 9bc86d3..e5840ea 100644 --- a/src/compositor/visual_manager_2d_draw.c +++ b/src/compositor/visual_manager_2d_draw.c @@ -98,7 +98,7 @@ static void visual_2d_fill_path(GF_VisualManager *visual, DrawableContext *ctx, /*background & direct drawing : use ctx clip*/ if ((ctx->flags & CTX_IS_BACKGROUND) || tr_state->immediate_draw) { if (ctx->bi->clip.width && ctx->bi->clip.height) { - GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Visual2D] Redrawing node %s (direct draw)\n", gf_node_get_log_name(ctx->drawable->node) )); + GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Visual2D] Redrawing node %s[%s] (direct draw)\n", gf_node_get_log_name(ctx->drawable->node), gf_node_get_class_name(ctx->drawable->node) )); if (stencil) { raster->surface_set_clipper(visual->raster_surface, &ctx->bi->clip); @@ -121,7 +121,7 @@ static void visual_2d_fill_path(GF_VisualManager *visual, DrawableContext *ctx, clip = ctx->bi->clip; gf_irect_intersect(&clip, &visual->to_redraw.list[i]); if (clip.width && clip.height) { - GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Visual2D] Redrawing node %s (indirect draw @ dirty rect idx %d)\n", gf_node_get_log_name(ctx->drawable->node), i)); + GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Visual2D] Redrawing node %s[%s] (indirect draw @ dirty rect idx %d)\n", gf_node_get_log_name(ctx->drawable->node), gf_node_get_class_name(ctx->drawable->node), i)); if (stencil) { raster->surface_set_clipper(visual->raster_surface, &clip); raster->surface_fill(visual->raster_surface, stencil); @@ -275,7 +275,10 @@ void visual_2d_texture_path_text(GF_VisualManager *visual, DrawableContext *txt_ GF_Rect orig_rc; u8 alpha, r, g, b; GF_ColorMatrix cmat; - GF_Raster2D *raster = visual->compositor->rasterizer; + GF_Raster2D *raster; + + if (!visual->is_attached) return; + raster = visual->compositor->rasterizer; stencil = gf_sc_texture_get_stencil(txh); if (!stencil) return; @@ -337,8 +340,10 @@ void visual_2d_texture_path_extended(GF_VisualManager *visual, GF_Path *path, GF u32 tx_tile; GF_STENCIL tx_raster; GF_Matrix2D mx_texture; - GF_Rect rc, orig_rc; - GF_Raster2D *raster = visual->compositor->rasterizer; + GF_Rect orig_rc; + GF_Raster2D *raster; + if (!visual->is_attached) return; + raster = visual->compositor->rasterizer; if (!txh) txh = ctx->aspect.fill_texture; if (!txh) return; @@ -394,10 +399,6 @@ void visual_2d_texture_path_extended(GF_VisualManager *visual, GF_Path *path, GF gf_path_get_bounds(path, &orig_rc); } - /*get active texture window in pixels*/ - rc.width = INT2FIX(txh->width); - rc.height = INT2FIX(txh->height); - /*get scaling ratio so that active texture view is stretched to original bounds (std 2D shape texture mapping in MPEG4)*/ sS = orig_rc.width / txh->width; sT = orig_rc.height / txh->height; @@ -461,7 +462,7 @@ void visual_2d_texture_path(GF_VisualManager *visual, GF_Path *path, struct _dra #ifdef SKIP_DRAW return; #endif - if (!visual->raster_surface || (ctx->flags & CTX_PATH_FILLED) || !ctx->aspect.fill_texture || visual->compositor->is_hidden) return; + if (!visual->is_attached || (ctx->flags & CTX_PATH_FILLED) || !ctx->aspect.fill_texture || visual->compositor->is_hidden) return; /*this is ambiguous in the spec, what if the material is filled and the texture is transparent ? let's draw, it's nicer */ @@ -485,8 +486,7 @@ void visual_2d_draw_path_extended(GF_VisualManager *visual, GF_Path *path, Drawa #ifdef SKIP_DRAW return; #endif - - assert(visual->raster_surface); + if (!visual->is_attached) return; if ((ctx->flags & CTX_PATH_FILLED) && (ctx->flags & CTX_PATH_STROKE) ) { if (visual->compositor->draw_bvol) draw_clipper(visual, ctx); @@ -579,7 +579,7 @@ void visual_2d_fill_rect(GF_VisualManager *visual, DrawableContext *ctx, GF_Rect return; #endif - if (!visual->raster_surface) return; + if (!visual->is_attached) return; if (!color && !strike_color) return; if ((ctx->flags & CTX_PATH_FILLED) && (ctx->flags & CTX_PATH_STROKE) ) { @@ -643,7 +643,7 @@ void visual_2d_fill_irect(GF_VisualManager *visual, GF_IRect *rc, u32 fill, u32 #endif if (!rc) return; - if (!visual->raster_surface) return; + if (!visual->is_attached) return; if (!fill && !strike ) return; /*no aa*/ diff --git a/src/compositor/visual_manager_3d.c b/src/compositor/visual_manager_3d.c index 9f537ef..a458758 100644 --- a/src/compositor/visual_manager_3d.c +++ b/src/compositor/visual_manager_3d.c @@ -173,6 +173,7 @@ void visual_3d_viewpoint_change(GF_TraverseState *tr_state, GF_Node *vp, Bool an Fixed dist; SFVec3f d; + /*update znear&zfar*/ tr_state->camera->z_near = tr_state->camera->avatar_size.x ; @@ -665,7 +666,13 @@ void visual_3d_register_context(GF_TraverseState *tr_state, GF_Node *geometry) } gf_node_traverse(geometry, tr_state); - + + /*clear appearance flag once drawn in 3D - not doing so would prevent + dirty flag propagation in the tree, whcih would typically prevent redrawing + layers/offscreen groups when texture changes*/ + if (tr_state->appear) + gf_node_dirty_clear(tr_state->appear, 0); + /*back to SORT*/ tr_state->traversing_mode = TRAVERSE_SORT; @@ -773,8 +780,15 @@ void visual_3d_flush_contexts(GF_VisualManager *visual, GF_TraverseState *tr_sta tr_state->depth_offset = ctx->depth_offset; #endif gf_node_traverse(ctx->geometry, tr_state); - tr_state->appear = NULL; - + + /*clear appearance flag once drawn in 3D - not doing so would prevent + dirty flag propagation in the tree, whcih would typically prevent redrawing + layers/offscreen groups when texture changes*/ + if (tr_state->appear) { + gf_node_dirty_clear(tr_state->appear, 0); + tr_state->appear = NULL; + } + /*reset directional lights*/ tr_state->local_light_on = 0; for (i=gf_list_count(ctx->directional_lights); i>0; i--) { @@ -890,7 +904,7 @@ static void reset_collide_cursor(GF_Compositor *compositor) void visual_3d_check_collisions(GF_TraverseState *tr_state, GF_ChildNodeItem *node_list) { - SFVec3f n, pos, dir; + SFVec3f n, dir; Bool go; Fixed diff, pos_diff; @@ -917,7 +931,6 @@ void visual_3d_check_collisions(GF_TraverseState *tr_state, GF_ChildNodeItem *no gf_vec_diff(dir, tr_state->camera->position, tr_state->camera->last_pos); pos_diff = gf_vec_len(dir); gf_vec_norm(&dir); - pos = tr_state->camera->position; diff = 0; go = 1; @@ -1322,8 +1335,7 @@ void visual_3d_vrml_drawable_pick(GF_Node *n, GF_TraverseState *tr_state, GF_Mes if (compositor_is_composite_texture(tr_state->appear)) { compositor->hit_appear = tr_state->appear; - } else - { + } else { compositor->hit_appear = NULL; } compositor->hit_node = n; @@ -1400,7 +1412,7 @@ void visual_3d_vrml_drawable_collide(GF_Node *node, GF_TraverseState *tr_state) tr_state->camera->collide_point = collide_pt; #ifndef GPAC_DISABLE_LOG - if ((gf_log_get_level() >= GF_LOG_DEBUG) && (gf_log_get_tools() & GF_LOG_COMPOSE)) { + if (gf_log_tool_level_on(GF_LOG_COMPOSE, GF_LOG_DEBUG)) { gf_vec_diff(v1, pos, collide_pt); gf_vec_norm(&v1); GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Collision] found at %g %g %g (WC) - dist (%g) - local normal %g %g %g\n", diff --git a/src/compositor/visual_manager_3d_gl.c b/src/compositor/visual_manager_3d_gl.c index 1269908..e4d50be 100644 --- a/src/compositor/visual_manager_3d_gl.c +++ b/src/compositor/visual_manager_3d_gl.c @@ -61,10 +61,6 @@ #undef GL_MAX_CLIP_PLANES #endif -#ifdef GPAC_ANDROID -#define GPAC_USE_TINYGL -#endif - #ifdef GPAC_USE_OGL_ES #define GL_CLAMP GL_CLAMP_TO_EDGE #endif @@ -77,11 +73,7 @@ GLDECL_STATIC(glActiveTexture); GLDECL_STATIC(glClientActiveTexture); -GLDECL_STATIC(glGenBuffers); -GLDECL_STATIC(glDeleteBuffers); -GLDECL_STATIC(glBindBuffer); -GLDECL_STATIC(glBufferData); -GLDECL_STATIC(glBufferSubData); +GLDECL_STATIC(glBlendEquation); #endif //LOAD_GL_1_3 @@ -92,6 +84,15 @@ GLDECL_STATIC(glPointParameterfv); #endif //LOAD_GL_1_4 + +#ifdef LOAD_GL_1_5 +GLDECL_STATIC(glGenBuffers); +GLDECL_STATIC(glDeleteBuffers); +GLDECL_STATIC(glBindBuffer); +GLDECL_STATIC(glBufferData); +GLDECL_STATIC(glBufferSubData); +#endif //LOAD_GL_1_5 + #ifdef LOAD_GL_2_0 GLDECL_STATIC(glCreateProgram); @@ -132,7 +133,6 @@ GLDECL_STATIC(glUniformMatrix2x4fv); GLDECL_STATIC(glUniformMatrix4x2fv); GLDECL_STATIC(glUniformMatrix3x4fv); GLDECL_STATIC(glUniformMatrix4x3fv); -GLDECL_STATIC(glBlendEquation); #endif //LOAD_GL_2_0 @@ -196,13 +196,7 @@ void gf_sc_load_opengl_extensions(GF_Compositor *compositor, Bool has_gl_context GET_GLFUN(glActiveTexture); GET_GLFUN(glClientActiveTexture); } - if (compositor->gl_caps.vbo) { - GET_GLFUN(glGenBuffers); - GET_GLFUN(glDeleteBuffers); - GET_GLFUN(glBindBuffer); - GET_GLFUN(glBufferData); - GET_GLFUN(glBufferSubData); - } + GET_GLFUN(glBlendEquation); #endif #ifdef LOAD_GL_1_4 @@ -210,7 +204,16 @@ void gf_sc_load_opengl_extensions(GF_Compositor *compositor, Bool has_gl_context GET_GLFUN(glPointParameterf); GET_GLFUN(glPointParameterfv); } - GET_GLFUN(glBlendEquation); +#endif + +#ifdef LOAD_GL_1_5 + if (compositor->gl_caps.vbo) { + GET_GLFUN(glGenBuffers); + GET_GLFUN(glDeleteBuffers); + GET_GLFUN(glBindBuffer); + GET_GLFUN(glBufferData); + GET_GLFUN(glBufferSubData); + } #endif @@ -2353,10 +2356,10 @@ void visual_3d_point_sprite(GF_VisualManager *visual, Drawable *stack, GF_Textur z = p[3]; z = z / 255; glColor4f(r, g, b, 1.0); - glVertex3f(x, y, -z*60); - x += inc; + glVertex3f(FIX2FLT(x), FIX2FLT(y), FIX2FLT(-z)*60); + x += FLT2FIX(inc); } - y -= inc; + y -= FLT2FIX(inc); } glEnd(); @@ -2365,7 +2368,7 @@ void visual_3d_point_sprite(GF_VisualManager *visual, Drawable *stack, GF_Textur } if (visual->compositor->depth_gl_type==GF_SC_DEPTH_GL_STRIPS) { - delta = visual->compositor->depth_gl_strips_filter; + delta = FIX2FLT(visual->compositor->depth_gl_strips_filter); if (!delta) first_pass = 2; else first_pass = 1; @@ -2409,7 +2412,7 @@ restart: glEnd(); in_strip = 0; } - x += inc; + x += FLT2FIX(inc); continue; } } else if (first_pass==0) { @@ -2419,7 +2422,7 @@ restart: glEnd(); in_strip = 0; } - x += inc; + x += FLT2FIX(inc); continue; } } @@ -2439,7 +2442,7 @@ restart: } glColor3f(r, g, b); - glVertex3f(x, y, z1*scale); + glVertex3f(FIX2FLT(x), FIX2FLT(y), FIX2FLT(z1)*scale); r = p2[0]; r /= 255; @@ -2449,15 +2452,15 @@ restart: b /= 255; glColor3f(r, g, b); - glVertex3f(x, y-inc, z2*scale); + glVertex3f(FIX2FLT(x), FIX2FLT(y)-inc, FIX2FLT(z2)*scale); - x += inc; + x += FLT2FIX(inc); } if (in_strip) { glEnd(); in_strip = 0; } - y -= inc; + y -= FLT2FIX(inc); } if (first_pass==1) { @@ -2485,7 +2488,7 @@ restart: for (w=0; wwidth; w++) { mesh_set_vertex(stack->mesh, x, y, 0, 0, 0, -FIX_ONE, INT2FIX(w) / (txh->width-1), INT2FIX(txh->height - h -1) / (txh->height-1) ); - x += inc; + x += FLT2FIX(inc); /*set triangle*/ if (h && w) { @@ -2494,7 +2497,7 @@ restart: mesh_set_triangle(stack->mesh, first_idx, txh->width + first_idx, txh->width + first_idx +1); } } - y -= inc; + y -= FLT2FIX(inc); } /*force recompute of Z*/ txh->needs_refresh = 1; diff --git a/src/export.cpp b/src/export.cpp index ba7efe3..87b1536 100644 --- a/src/export.cpp +++ b/src/export.cpp @@ -43,6 +43,9 @@ #pragma comment (linker, EXPORT_SYMBOL(gf_sys_init) ) #pragma comment (linker, EXPORT_SYMBOL(gf_sys_close) ) #pragma comment (linker, EXPORT_SYMBOL(gf_sleep) ) +#pragma comment (linker, EXPORT_SYMBOL(gf_mkdir) ) +#pragma comment (linker, EXPORT_SYMBOL(gf_rmdir) ) +#pragma comment (linker, EXPORT_SYMBOL(gf_cleanup_dir) ) #pragma comment (linker, EXPORT_SYMBOL(gf_sys_clock) ) #pragma comment (linker, EXPORT_SYMBOL(gf_sys_get_rti) ) #pragma comment (linker, EXPORT_SYMBOL(gf_sys_get_battery_state) ) @@ -53,22 +56,27 @@ #pragma comment (linker, EXPORT_SYMBOL(gf_rand) ) #pragma comment (linker, EXPORT_SYMBOL(gf_get_user_name) ) #pragma comment (linker, EXPORT_SYMBOL(gf_enum_directory) ) -#pragma comment (linker, EXPORT_SYMBOL(gf_log_get_tools) ) -#pragma comment (linker, EXPORT_SYMBOL(gf_log_get_level) ) -#pragma comment (linker, EXPORT_SYMBOL(gf_log_set_level) ) -#pragma comment (linker, EXPORT_SYMBOL(gf_log_set_tools) ) + +#pragma comment (linker, EXPORT_SYMBOL(gf_log_modify_tools_levels) ) +#pragma comment (linker, EXPORT_SYMBOL(gf_log_set_tools_levels) ) +#pragma comment (linker, EXPORT_SYMBOL(gf_log_set_tool_level) ) #pragma comment (linker, EXPORT_SYMBOL(gf_log_set_strict_error) ) #pragma comment (linker, EXPORT_SYMBOL(gf_log_set_callback) ) -#pragma comment (linker, EXPORT_SYMBOL(gf_log_parse_level) ) -#pragma comment (linker, EXPORT_SYMBOL(gf_log_parse_tools) ) +#pragma comment (linker, EXPORT_SYMBOL(gf_log_get_tools_levels) ) + +#ifndef GPAC_DISABLE_LOG +#pragma comment (linker, EXPORT_SYMBOL(gf_log_tool_level_on) ) #pragma comment (linker, EXPORT_SYMBOL(gf_log) ) #pragma comment (linker, EXPORT_SYMBOL(gf_log_lt) ) +#endif + #pragma comment (linker, EXPORT_SYMBOL(gf_set_progress) ) #pragma comment (linker, EXPORT_SYMBOL(gf_set_progress_callback) ) #pragma comment (linker, EXPORT_SYMBOL(gf_delete_file) ) #pragma comment (linker, EXPORT_SYMBOL(gf_move_file) ) #pragma comment (linker, EXPORT_SYMBOL(gf_temp_file_new) ) #pragma comment (linker, EXPORT_SYMBOL(gf_file_modification_time) ) +#pragma comment (linker, EXPORT_SYMBOL(gf_fwrite) ) #pragma comment (linker, EXPORT_SYMBOL(gf_f64_open) ) #pragma comment (linker, EXPORT_SYMBOL(gf_f64_seek) ) #pragma comment (linker, EXPORT_SYMBOL(gf_f64_tell) ) @@ -123,6 +131,7 @@ #pragma comment (linker, EXPORT_SYMBOL(gf_list_reset) ) #pragma comment (linker, EXPORT_SYMBOL(gf_list_last) ) #pragma comment (linker, EXPORT_SYMBOL(gf_list_rem_last) ) +#pragma comment (linker, EXPORT_SYMBOL(gf_list_swap) ) /* Bitstream */ #pragma comment (linker, EXPORT_SYMBOL(gf_bs_new) ) @@ -324,6 +333,8 @@ #pragma comment (linker, EXPORT_SYMBOL(gf_xml_dom_get_line) ) #pragma comment (linker, EXPORT_SYMBOL(gf_xml_dom_serialize) ) #pragma comment (linker, EXPORT_SYMBOL(gf_xml_dom_node_del) ) +#pragma comment (linker, EXPORT_SYMBOL(gf_xml_dom_parse_string) ) + #ifndef GPAC_DISABLE_SVG #pragma comment (linker, EXPORT_SYMBOL(gf_dom_get_key_name) ) @@ -350,6 +361,7 @@ #pragma comment (linker, EXPORT_SYMBOL(gf_angle_diff) ) #pragma comment (linker, EXPORT_SYMBOL(gf_v2d_len) ) #pragma comment (linker, EXPORT_SYMBOL(gf_v2d_from_polar) ) +#pragma comment (linker, EXPORT_SYMBOL(gf_v2d_distance) ) #pragma comment (linker, EXPORT_SYMBOL(gf_rect_union) ) #pragma comment (linker, EXPORT_SYMBOL(gf_rect_center) ) #pragma comment (linker, EXPORT_SYMBOL(gf_rect_overlaps) ) @@ -547,6 +559,7 @@ #pragma comment (linker, EXPORT_SYMBOL(gf_isom_delete) ) #pragma comment (linker, EXPORT_SYMBOL(gf_isom_get_mode) ) #pragma comment (linker, EXPORT_SYMBOL(gf_isom_get_file_size) ) +#pragma comment (linker, EXPORT_SYMBOL(gf_isom_moov_first) ) #pragma comment (linker, EXPORT_SYMBOL(gf_isom_get_timed_meta_data_info) ) #pragma comment (linker, EXPORT_SYMBOL(gf_isom_box_new) ) #pragma comment (linker, EXPORT_SYMBOL(gf_isom_box_del) ) @@ -577,7 +590,7 @@ #pragma comment (linker, EXPORT_SYMBOL(gf_isom_is_self_contained) ) #pragma comment (linker, EXPORT_SYMBOL(gf_isom_get_media_duration) ) #pragma comment (linker, EXPORT_SYMBOL(gf_isom_get_media_timescale) ) -#pragma comment (linker, EXPORT_SYMBOL(gf_isom_get_max_chunk_duration) ) +#pragma comment (linker, EXPORT_SYMBOL(gf_isom_get_chunks_infos) ) #pragma comment (linker, EXPORT_SYMBOL(gf_isom_get_handler_name) ) #pragma comment (linker, EXPORT_SYMBOL(gf_isom_get_media_language) ) #pragma comment (linker, EXPORT_SYMBOL(gf_isom_check_data_reference) ) @@ -661,6 +674,7 @@ #pragma comment (linker, EXPORT_SYMBOL(gf_isom_get_meta_primary_item_id) ) #pragma comment (linker, EXPORT_SYMBOL(gf_isom_is_JPEG2000) ) #pragma comment (linker, EXPORT_SYMBOL(gf_isom_get_rvc_config) ) +#pragma comment (linker, EXPORT_SYMBOL(gf_isom_reset_fragment_info) ) # ifndef GPAC_DISABLE_ISOM_DUMP @@ -815,6 +829,9 @@ #pragma comment (linker, EXPORT_SYMBOL(gf_isom_new_dims_description) ) #pragma comment (linker, EXPORT_SYMBOL(gf_isom_update_dims_description) ) #pragma comment (linker, EXPORT_SYMBOL(gf_isom_set_rvc_config) ) +#pragma comment (linker, EXPORT_SYMBOL(gf_isom_set_sample_rap_group) ) +#pragma comment (linker, EXPORT_SYMBOL(gf_isom_set_sample_roll_group) ) + #ifndef GPAC_DISABLE_ISOM_HINTING #pragma comment (linker, EXPORT_SYMBOL(gf_isom_setup_hint_track) ) #pragma comment (linker, EXPORT_SYMBOL(gf_isom_new_hint_description) ) @@ -1099,8 +1116,8 @@ #pragma comment (linker, EXPORT_SYMBOL(gf_img_parse) ) #pragma comment (linker, EXPORT_SYMBOL(gf_img_jpeg_dec) ) #pragma comment (linker, EXPORT_SYMBOL(gf_img_png_dec) ) -#pragma comment (linker, EXPORT_SYMBOL(gf_img_png_file_dec) ) #pragma comment (linker, EXPORT_SYMBOL(gf_img_png_enc) ) +#pragma comment (linker, EXPORT_SYMBOL(gf_img_file_dec) ) #pragma comment (linker, EXPORT_SYMBOL(gf_m4v_get_profile_name) ) #pragma comment (linker, EXPORT_SYMBOL(gf_mp3_version) ) #pragma comment (linker, EXPORT_SYMBOL(gf_mp3_version_name) ) @@ -1142,7 +1159,9 @@ #pragma comment (linker, EXPORT_SYMBOL(gf_sm_load_run) ) #pragma comment (linker, EXPORT_SYMBOL(gf_sm_load_suspend) ) #pragma comment (linker, EXPORT_SYMBOL(gf_sm_import_bifs_subtitle) ) +#ifndef GPAC_DISABLE_LOADER_BT #pragma comment (linker, EXPORT_SYMBOL(gf_sm_load_bt_from_string) ) +#endif #ifndef GPAC_DISABLE_SCENE_ENCODER #pragma comment (linker, EXPORT_SYMBOL(gf_sm_encode_to_file) ) @@ -1312,14 +1331,18 @@ #pragma comment (linker, EXPORT_SYMBOL(gf_js_add_root) ) #pragma comment (linker, EXPORT_SYMBOL(gf_js_add_named_root) ) #pragma comment (linker, EXPORT_SYMBOL(gf_js_remove_root) ) - +#pragma comment (linker, EXPORT_SYMBOL(gf_js_vrml_flush_event_out) ) #pragma comment (linker, EXPORT_SYMBOL(gf_sg_js_has_instance) ) +#pragma comment (linker, EXPORT_SYMBOL(gf_sg_lock_javascript) ) +#pragma comment (linker, EXPORT_SYMBOL(gf_sg_try_lock_javascript) ) #ifndef GPAC_DISABLE_SVG +#pragma comment (linker, EXPORT_SYMBOL(dom_js_pre_destroy) ) #pragma comment (linker, EXPORT_SYMBOL(gf_sg_js_event_add_listener) ) #pragma comment (linker, EXPORT_SYMBOL(gf_sg_js_event_remove_listener) ) #endif -#endif + +#endif /*GPAC_HAS_SPIDERMONKEY*/ /* scenegraph_vrml.h exports*/ #pragma comment (linker, EXPORT_SYMBOL(gf_node_replace_child) ) @@ -1390,7 +1413,6 @@ #pragma comment (linker, EXPORT_SYMBOL(gf_sg_get_next_available_proto_id) ) #pragma comment (linker, EXPORT_SYMBOL(gf_sg_set_proto_loader) ) #pragma comment (linker, EXPORT_SYMBOL(gf_sg_sfcolor_to_rgba) ) -#pragma comment (linker, EXPORT_SYMBOL(gf_sg_lock_javascript) ) #ifndef GPAC_DISABLE_X3D #pragma comment (linker, EXPORT_SYMBOL(gf_node_x3d_type_by_class_name) ) @@ -1488,9 +1510,9 @@ #pragma comment (linker, EXPORT_SYMBOL(gf_sc_add_audio_listener) ) #pragma comment (linker, EXPORT_SYMBOL(gf_sc_remove_audio_listener) ) #pragma comment (linker, EXPORT_SYMBOL(gf_sc_focus_switch_ring) ) -#pragma comment (linker, EXPORT_SYMBOL(gf_sc_animation_get_scenegraph) ) #ifndef GPAC_DISABLE_SVG +#pragma comment (linker, EXPORT_SYMBOL(gf_sc_animation_get_scenegraph) ) #pragma comment (linker, EXPORT_SYMBOL(gf_sc_svg_convert_length_to_display) ) #endif @@ -1546,6 +1568,15 @@ #pragma comment (linker, EXPORT_SYMBOL(gf_m2ts_get_stream_name) ) #pragma comment (linker, EXPORT_SYMBOL(gf_m2ts_crc32_check) ) +/* carousel.h */ +#pragma comment (linker, EXPORT_SYMBOL(gf_m2ts_get_channel_application_info) ) +#pragma comment (linker, EXPORT_SYMBOL(gf_m2ts_process_dsmcc) ) +#pragma comment (linker, EXPORT_SYMBOL(gf_m2ts_init_dsmcc_overlord) ) +#pragma comment (linker, EXPORT_SYMBOL(gf_m2ts_get_dmscc_overlord) ) +#pragma comment (linker, EXPORT_SYMBOL(gf_m2ts_demux_dmscc_init) ) + + + #ifndef GPAC_DISABLE_MPEG2TS_MUX #pragma comment (linker, EXPORT_SYMBOL(gf_m2ts_mux_new) ) #pragma comment (linker, EXPORT_SYMBOL(gf_m2ts_mux_del) ) @@ -1565,6 +1596,9 @@ #pragma comment (linker, EXPORT_SYMBOL(gf_mpd_init_from_dom) ) #pragma comment (linker, EXPORT_SYMBOL(gf_mpd_del) ) #pragma comment (linker, EXPORT_SYMBOL(gf_m3u8_to_mpd) ) +#pragma comment (linker, EXPORT_SYMBOL(parse_root_playlist) ) +#pragma comment (linker, EXPORT_SYMBOL(variant_playlist_del) ) + #pragma comment (linker, EXPORT_SYMBOL(TSDemux_Demux_Setup)) #pragma comment (linker, EXPORT_SYMBOL(TSDemux_DemuxPlay) ) @@ -1595,8 +1629,13 @@ #pragma comment (linker, EXPORT_SYMBOL(gf_font_manager_refresh_span_bounds) ) /*download.h exports*/ - #pragma comment (linker, EXPORT_SYMBOL(gf_dm_wget) ) - #pragma comment (linker, EXPORT_SYMBOL(gf_dm_wget_with_cache) ) +#pragma comment (linker, EXPORT_SYMBOL(gf_dm_wget) ) +#pragma comment (linker, EXPORT_SYMBOL(gf_dm_wget_with_cache) ) + +#ifndef GPAC_DISABLE_ISOM +#pragma comment (linker, EXPORT_SYMBOL(gf_media_mpd_start) ) +#pragma comment (linker, EXPORT_SYMBOL(gf_media_mpd_end) ) +#endif /* dvb_mpe.h */ #ifdef GPAC_ENST_PRIVATE @@ -1606,10 +1645,3 @@ #pragma comment (linker, EXPORT_SYMBOL(gf_dvb_mpe_shutdown) ) #pragma comment (linker, EXPORT_SYMBOL(gf_m2ts_print_mpe_info) ) #endif - -/* carousel.h */ -#pragma comment (linker, EXPORT_SYMBOL(on_ait_section) ) -#pragma comment (linker, EXPORT_SYMBOL(gf_m2ts_process_ait) ) - - - diff --git a/src/ietf/rtcp.c b/src/ietf/rtcp.c index fa6044a..b0444f0 100644 --- a/src/ietf/rtcp.c +++ b/src/ietf/rtcp.c @@ -53,7 +53,7 @@ GF_Err gf_rtp_decode_rtcp(GF_RTPChannel *ch, char *pck, u32 pck_size, Bool *has_ GF_RTCPHeader rtcp_hdr; GF_BitStream *bs; char sdes_buffer[300]; - u32 i, sender_ssrc, cur_ssrc, val, sdes_type, sdes_len, res, first, nb_bytes, nb_pck; + u32 i, sender_ssrc, cur_ssrc, val, sdes_type, sdes_len, res, first; GF_Err e = GF_OK; if (has_sr) *has_sr=0; @@ -115,14 +115,14 @@ GF_Err gf_rtp_decode_rtcp(GF_RTPChannel *ch, char *pck, u32 pck_size, Bool *has_ ch->last_SR_NTP_sec = gf_bs_read_u32(bs); ch->last_SR_NTP_frac = gf_bs_read_u32(bs); ch->last_SR_rtp_time = gf_bs_read_u32(bs); - nb_pck = gf_bs_read_u32(bs); - nb_bytes = gf_bs_read_u32(bs); + /*nb_pck = */gf_bs_read_u32(bs); + /*nb_bytes =*/gf_bs_read_u32(bs); rtcp_hdr.Length -= 5; if (has_sr) *has_sr=1; #ifndef GPAC_DISABLE_LOG - if ((gf_log_get_level() >= (GF_LOG_INFO)) && (gf_log_get_tools() & (GF_LOG_RTP))) { + if (gf_log_tool_level_on(GF_LOG_RTP, GF_LOG_INFO)) { #ifndef _WIN32_WCE time_t gtime = ch->last_SR_NTP_sec - GF_NTP_SEC_1900_TO_1970; const char *ascTime = asctime(gmtime(>ime)); @@ -351,7 +351,7 @@ static u32 RTCP_FormatReport(GF_RTPChannel *ch, GF_BitStream *bs, u32 NTP_Time) #ifndef GPAC_DISABLE_LOG - if ((gf_log_get_level() >= (GF_LOG_DEBUG)) && (gf_log_get_tools() & (GF_LOG_RTP))) { + if (gf_log_tool_level_on(GF_LOG_RTP, GF_LOG_DEBUG)) { #ifndef _WIN32_WCE time_t gtime = ch->last_SR_NTP_sec - GF_NTP_SEC_1900_TO_1970; const char *ascTime = asctime(gmtime(>ime)); diff --git a/src/ietf/rtp.c b/src/ietf/rtp.c index b280bcb..20d178e 100644 --- a/src/ietf/rtp.c +++ b/src/ietf/rtp.c @@ -277,7 +277,7 @@ GF_Err gf_rtp_initialize(GF_RTPChannel *ch, u32 UDPBufferSize, Bool IsSource, u3 #ifndef GPAC_DISABLE_LOG - if ((gf_log_get_level() >= (GF_LOG_DEBUG)) && (gf_log_get_tools() & (GF_LOG_RTP))) { + if (gf_log_tool_level_on(GF_LOG_RTP, GF_LOG_DEBUG)) { GF_LOG(GF_LOG_DEBUG, GF_LOG_RTP, ("[RTP] Packet Log Format: SSRC SequenceNumber TimeStamp NTP@recvTime deviance, Jiter, PckLost PckTotal BytesTotal\n")); } #endif @@ -329,7 +329,10 @@ u32 gf_rtp_read_rtp(GF_RTPChannel *ch, char *buffer, u32 buffer_size) e = gf_sk_receive(ch->rtp, buffer, buffer_size, 0, &res); if (!res || e || (res < 12)) res = 0; - + if (res){ + ch->total_bytes+=res; + ch->total_pck++; + } //add the packet to our Queue if any if (ch->po) { if (res) { @@ -351,6 +354,7 @@ u32 gf_rtp_read_rtp(GF_RTPChannel *ch, char *buffer, u32 buffer_size) ch->last_nat_keepalive_time = now; } else { if (now - ch->last_nat_keepalive_time >= ch->nat_keepalive_time_period) { +#if 0 char rtp_nat[12]; rtp_nat[0] = (u8) 0xC0; rtp_nat[1] = ch->PayloadType; @@ -364,7 +368,7 @@ u32 gf_rtp_read_rtp(GF_RTPChannel *ch, char *buffer, u32 buffer_size) rtp_nat[9] = (ch->SenderSSRC>>16)&0xFF; rtp_nat[10] = (ch->SenderSSRC>>8)&0xFF; rtp_nat[11] = (ch->SenderSSRC)&0xFF; - +#endif e = gf_sk_send(ch->rtp, buffer, 12); if (e) { GF_LOG(GF_LOG_ERROR, GF_LOG_RTP, ("[RTP] Error sending NAT keep-alive packet: %s - disabling NAT\n", gf_error_to_string(e) )); @@ -478,7 +482,7 @@ GF_Err gf_rtp_decode_rtp(GF_RTPChannel *ch, char *pck, u32 pck_size, GF_RTPHeade ch->last_pck_sn = CurrSeq; #ifndef GPAC_DISABLE_LOG - if ((gf_log_get_level() >= (GF_LOG_DEBUG)) && (gf_log_get_tools() & (GF_LOG_RTP))) { + if (gf_log_tool_level_on(GF_LOG_RTP, GF_LOG_DEBUG)) { ch->total_pck++; ch->total_bytes += pck_size-12; @@ -644,7 +648,6 @@ static u16 NextAvailablePort = 0; GF_EXPORT GF_Err gf_rtp_set_ports(GF_RTPChannel *ch, u16 first_port) { - u32 retry; u16 p; GF_Socket *sock; if (!ch) return GF_BAD_PARAM; @@ -659,7 +662,6 @@ GF_Err gf_rtp_set_ports(GF_RTPChannel *ch, u16 first_port) if (!sock) return GF_IO_ERR; /*should be way enough (more than 100 rtp streams open on the machine)*/ - retry = 100; while (1) { /*try to bind without reuse. If fails this means the port is used on the machine, don't reuse it*/ GF_Err e = gf_sk_bind(sock, NULL, p, NULL, 0, 0); @@ -782,7 +784,7 @@ void gf_rtp_reorderer_reset(GF_RTPReorder *po) po->in = NULL; } -GF_Err gf_rtp_reorderer_add(GF_RTPReorder *po, void *pck, u32 pck_size, u32 pck_seqnum) +GF_Err gf_rtp_reorderer_add(GF_RTPReorder *po, const void * pck, u32 pck_size, u32 pck_seqnum) { GF_POItem *it, *cur; u32 bounds; diff --git a/src/ietf/rtp_depacketizer.c b/src/ietf/rtp_depacketizer.c index 1d952f5..067b698 100644 --- a/src/ietf/rtp_depacketizer.c +++ b/src/ietf/rtp_depacketizer.c @@ -355,9 +355,9 @@ static void gf_rtp_parse_amr(GF_RTPDepacketizer *rtp, GF_RTPHeader *hdr, char *p c = payload[i + 1]; type = ((c & 0x78) >> 3); if (rtp->payt==GF_RTP_PAYT_AMR) { - frame_size = GF_AMR_FRAME_SIZE[type]; + frame_size = (u32)GF_AMR_FRAME_SIZE[type]; } else { - frame_size = GF_AMR_WB_FRAME_SIZE[type]; + frame_size = (u32)GF_AMR_WB_FRAME_SIZE[type]; } rtp->sl_hdr.compositionTimeStampFlag = 1; @@ -381,7 +381,7 @@ static void gf_rtp_parse_h263(GF_RTPDepacketizer *rtp, GF_RTPHeader *hdr, char * { GF_BitStream *bs; Bool P_bit, V_bit; - u32 plen, plen_bits; + u32 plen; u64 offset; char blank[2]; @@ -391,7 +391,7 @@ static void gf_rtp_parse_h263(GF_RTPDepacketizer *rtp, GF_RTPHeader *hdr, char * P_bit = gf_bs_read_int(bs, 1); V_bit = gf_bs_read_int(bs, 1); plen = gf_bs_read_int(bs, 6); - plen_bits = gf_bs_read_int(bs, 3); + /*plen_bits = */gf_bs_read_int(bs, 3); /*VRC not supported yet*/ if (V_bit) { @@ -821,12 +821,12 @@ static void gf_rtp_parse_3gpp_dims(GF_RTPDepacketizer *rtp, GF_RTPHeader *hdr, c static void gf_rtp_parse_ac3(GF_RTPDepacketizer *rtp, GF_RTPHeader *hdr, char *payload, u32 size) { - u8 ft, nb_pck; + u8 ft; rtp->sl_hdr.compositionTimeStampFlag = 1; rtp->sl_hdr.compositionTimeStamp = hdr->TimeStamp; ft = payload[0]; - nb_pck = payload[1]; + /*nb_pck = payload[1];*/ payload += 2; size -= 2; @@ -835,7 +835,7 @@ static void gf_rtp_parse_ac3(GF_RTPDepacketizer *rtp, GF_RTPHeader *hdr, char *p rtp->sl_hdr.accessUnitStartFlag = rtp->sl_hdr.accessUnitEndFlag = 1; while (size) { u32 offset; - if (!gf_ac3_parser(payload, size, &offset, &hdr, 0)) { + if (!gf_ac3_parser((u8*)payload, size, &offset, &hdr, 0)) { return; } if (offset) { diff --git a/src/ietf/rtp_packetizer.c b/src/ietf/rtp_packetizer.c index 80940da..f7c4049 100644 --- a/src/ietf/rtp_packetizer.c +++ b/src/ietf/rtp_packetizer.c @@ -130,7 +130,7 @@ void gf_rtp_builder_init(GP_RTPPacketizer *builder, u8 PayloadType, u32 PathMTU, u32 IV_length, u32 KI_length, char *pref_mode) { - u32 k, totDelta, ismacrypt_flags; + u32 k, ismacrypt_flags; memset(&builder->slMap, 0, sizeof(GP_RTPSLMap)); builder->Path_MTU = PathMTU; @@ -339,7 +339,6 @@ void gf_rtp_builder_init(GP_RTPPacketizer *builder, u8 PayloadType, u32 PathMTU, if (builder->flags & GP_RTP_PCK_SIGNAL_TS) { /*compute CTS delta*/ - totDelta = k*avgTS; builder->slMap.CTSDeltaLength = gf_get_bit_size(k*avgTS); /*compute DTS delta. Delta is ALWAYS from the CTS of the same sample*/ diff --git a/src/ietf/rtp_pck_3gpp.c b/src/ietf/rtp_pck_3gpp.c index d7eb761..ffc7c7c 100644 --- a/src/ietf/rtp_pck_3gpp.c +++ b/src/ietf/rtp_pck_3gpp.c @@ -62,10 +62,10 @@ GF_Err gp_rtp_builder_do_amr(GP_RTPPacketizer *builder, char *data, u32 data_siz u8 size; if (builder->rtp_payt == GF_RTP_PAYT_AMR_WB) { - size = GF_AMR_WB_FRAME_SIZE[ft]; + size = (u32)GF_AMR_WB_FRAME_SIZE[ft]; block_size = 320; } else { - size = GF_AMR_FRAME_SIZE[ft]; + size = (u32)GF_AMR_FRAME_SIZE[ft]; block_size = 160; } @@ -118,7 +118,7 @@ GF_Err gp_rtp_builder_do_amr(GP_RTPPacketizer *builder, char *data, u32 data_siz return GF_OK; } -static GFINLINE u8 qes_get_rate_size(u32 idx, const u32 *rates, u32 nb_rates) +static GFINLINE u8 qes_get_rate_size(u32 idx, const unsigned int *rates, const unsigned int nb_rates) { u32 i; for (i=0; ioffset) { u8 frame_type = data[offset]; - u8 size = qes_get_rate_size(frame_type, (u32 *)GF_SMV_EVRC_RATE_TO_SIZE, GF_SMV_EVRC_RATE_TO_SIZE_NB); + u8 size = qes_get_rate_size(frame_type, GF_SMV_EVRC_RATE_TO_SIZE, GF_SMV_EVRC_RATE_TO_SIZE_NB); /*reserved, not sent)*/ if (frame_type>=5) { diff --git a/src/ietf/rtp_pck_mpeg4.c b/src/ietf/rtp_pck_mpeg4.c index 08a3dbd..c9d5931 100644 --- a/src/ietf/rtp_pck_mpeg4.c +++ b/src/ietf/rtp_pck_mpeg4.c @@ -205,12 +205,11 @@ GF_Err gp_rtp_builder_do_mpeg4(GP_RTPPacketizer *builder, char *data, u32 data_s { char *sl_buffer, *payl_buffer; u32 sl_buffer_size, payl_buffer_size; - u32 auh_size_tmp, rslh_tmp, bytesLeftInPacket, infoSize, pckSize; + u32 auh_size_tmp, bytesLeftInPacket, infoSize, pckSize; u64 pos; u8 flush_pck, no_split; flush_pck = 0; - rslh_tmp = 0; bytesLeftInPacket = data_size; /*flush everything*/ diff --git a/src/ietf/rtp_streamer.c b/src/ietf/rtp_streamer.c index c111b30..be74f8f 100644 --- a/src/ietf/rtp_streamer.c +++ b/src/ietf/rtp_streamer.c @@ -438,6 +438,8 @@ GF_RTPStreamer *gf_rtp_streamer_new_extended(u32 streamType, u32 oti, u32 timeSc streamType, oti, PL_ID, MinSize, MaxSize, avgTS, maxDTSDelta, IV_length, KI_length, mpeg4mode); + if (force_dts_delta) stream->packetizer->slMap.DTSDeltaLength = force_dts_delta; + e = rtp_stream_init_channel(stream, MTU + 12, ip_dest, port, TTL, ifce_addr); if (e) { GF_LOG(GF_LOG_ERROR, GF_LOG_RTP, ("[RTP Packetizer] Failed to create RTP channel - error %s\n", gf_error_to_string(e) )); diff --git a/src/ietf/rtsp_common.c b/src/ietf/rtsp_common.c index 61628f1..3d3da54 100644 --- a/src/ietf/rtsp_common.c +++ b/src/ietf/rtsp_common.c @@ -32,7 +32,7 @@ GF_Err gf_rtsp_read_reply(GF_RTSPSession *sess) { GF_Err e; - u32 res, body_size; + u32 res, body_size = 0; u32 BodyStart = 0; //fetch more data on the socket if needed diff --git a/src/isomedia/box_code_3gpp.c b/src/isomedia/box_code_3gpp.c index 27d5fef..df6b8f5 100644 --- a/src/isomedia/box_code_3gpp.c +++ b/src/isomedia/box_code_3gpp.c @@ -615,6 +615,7 @@ GF_Err styl_Write(GF_Box *s, GF_BitStream *bs) u32 i; GF_TextStyleBox*ptr = (GF_TextStyleBox*)s; e = gf_isom_box_write_header(s, bs); + assert(e == GF_OK); gf_bs_write_u16(bs, ptr->entry_count); for (i=0; ientry_count; i++) gpp_write_style(bs, &ptr->styles[i]); diff --git a/src/isomedia/box_code_base.c b/src/isomedia/box_code_base.c index 89fb162..46a2603 100644 --- a/src/isomedia/box_code_base.c +++ b/src/isomedia/box_code_base.c @@ -3448,6 +3448,7 @@ GF_Err mp4a_AddBox(GF_Box *s, GF_Box *a) GF_Err e; GF_BitStream *bs = gf_bs_new(wave->data+offset, wave->dataSize-offset, GF_BITSTREAM_READ); e = gf_isom_parse_box(&a, bs); + assert(e == GF_OK); gf_bs_del(bs); ptr->esd = (GF_ESDBox *)a; } @@ -4495,6 +4496,8 @@ void stbl_del(GF_Box *s) if (ptr->PaddingBits) gf_isom_box_del((GF_Box *) ptr->PaddingBits); if (ptr->Fragments) gf_isom_box_del((GF_Box *) ptr->Fragments); if (ptr->SubSamples) gf_isom_box_del((GF_Box *) ptr->SubSamples); + if (ptr->sampleGroups) gf_isom_box_array_del(ptr->sampleGroups); + if (ptr->sampleGroupsDescription) gf_isom_box_array_del(ptr->sampleGroupsDescription); gf_free(ptr); } @@ -4564,6 +4567,15 @@ GF_Err stbl_AddBox(GF_SampleTableBox *ptr, GF_Box *a) ptr->SubSamples = (GF_SubSampleInformationBox *)a; break; + case GF_ISOM_BOX_TYPE_SBGP: + if (!ptr->sampleGroups) ptr->sampleGroups = gf_list_new(); + gf_list_add(ptr->sampleGroups, a); + break; + case GF_ISOM_BOX_TYPE_SGPD: + if (!ptr->sampleGroupsDescription) ptr->sampleGroupsDescription = gf_list_new(); + gf_list_add(ptr->sampleGroupsDescription, a); + break; + default: GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[iso file] Warning box %s unknown type - discarding\n", gf_4cc_to_str(a->type))); gf_isom_box_del(a); @@ -4691,6 +4703,14 @@ GF_Err stbl_Write(GF_Box *s, GF_BitStream *bs) e = gf_isom_box_write((GF_Box *) ptr->SubSamples, bs); if (e) return e; } + if (ptr->sampleGroupsDescription) { + e = gf_isom_box_array_write(s, ptr->sampleGroupsDescription, bs); + if (e) return e; + } + if (ptr->sampleGroups) { + e = gf_isom_box_array_write(s, ptr->sampleGroups, bs); + if (e) return e; + } #if WRITE_SAMPLE_FRAGMENTS //sampleFragments @@ -4782,7 +4802,14 @@ GF_Err stbl_Size(GF_Box *s) if (e) return e; ptr->size += ptr->SubSamples->size; } - + if (ptr->sampleGroups) { + e = gf_isom_box_array_size(s, ptr->sampleGroups); + if (e) return e; + } + if (ptr->sampleGroupsDescription) { + e = gf_isom_box_array_size(s, ptr->sampleGroupsDescription); + if (e) return e; + } return GF_OK; } @@ -6004,6 +6031,8 @@ void traf_del(GF_Box *s) if (ptr->subs) gf_isom_box_del((GF_Box *) ptr->subs); if (ptr->tfdt) gf_isom_box_del((GF_Box *) ptr->tfdt); gf_isom_box_array_del(ptr->TrackRuns); + if (ptr->sampleGroups) gf_isom_box_array_del(ptr->sampleGroups); + if (ptr->sampleGroupsDescription) gf_isom_box_array_del(ptr->sampleGroupsDescription); gf_free(ptr); } @@ -6030,11 +6059,20 @@ GF_Err traf_AddBox(GF_Box *s, GF_Box *a) if (ptr->subs) return GF_ISOM_INVALID_FILE; ptr->subs = (GF_SubSampleInformationBox *)a; return GF_OK; + case GF_ISOM_BOX_TYPE_SBGP: + if (!ptr->sampleGroups) ptr->sampleGroups = gf_list_new(); + gf_list_add(ptr->sampleGroups, a); + return GF_OK; + case GF_ISOM_BOX_TYPE_SGPD: + if (!ptr->sampleGroupsDescription) ptr->sampleGroupsDescription = gf_list_new(); + gf_list_add(ptr->sampleGroupsDescription, a); + return GF_OK; default: GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[iso file] Warning box %s unknown type - discarding\n", gf_4cc_to_str(a->type))); gf_isom_box_del(a); return GF_OK; } + return GF_OK; } @@ -6080,12 +6118,22 @@ GF_Err traf_Write(GF_Box *s, GF_BitStream *bs) if (ptr->tfdt) { e = gf_isom_box_write((GF_Box *) ptr->tfdt, bs); if (e) return e; - } - e = gf_isom_box_array_write(s, ptr->TrackRuns, bs); - if (e) return e; + } if (ptr->sdtp) { e = gf_isom_box_write((GF_Box *) ptr->sdtp, bs); + if (e) return e; + } + if (ptr->sampleGroupsDescription) { + e = gf_isom_box_array_write(s, ptr->sampleGroupsDescription, bs); + if (e) return e; + } + if (ptr->sampleGroups) { + e = gf_isom_box_array_write(s, ptr->sampleGroups, bs); + if (e) return e; } + e = gf_isom_box_array_write(s, ptr->TrackRuns, bs); + if (e) return e; + return e; } @@ -6116,6 +6164,16 @@ GF_Err traf_Size(GF_Box *s) if (e) return e; ptr->size += ptr->tfdt->size; } + + if (ptr->sampleGroups) { + e = gf_isom_box_array_size(s, ptr->sampleGroups); + if (e) return e; + } + if (ptr->sampleGroupsDescription) { + e = gf_isom_box_array_size(s, ptr->sampleGroupsDescription); + if (e) return e; + } + return gf_isom_box_array_size(s, ptr->TrackRuns); } @@ -6150,7 +6208,6 @@ static void gf_isom_check_sample_desc(GF_TrackBox *trak) GF_GenericSampleEntryBox *genm; GF_UnknownBox *a; u32 i; - u64 read; i=0; while ((a = (GF_UnknownBox*)gf_list_enum(trak->Media->information->sampleTable->SampleDescription->boxList, &i))) { @@ -6229,7 +6286,6 @@ static void gf_isom_check_sample_desc(GF_TrackBox *trak) genm = (GF_GenericSampleEntryBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_GNRM); genm->size = a->size; bs = gf_bs_new(a->data, a->dataSize, GF_BITSTREAM_READ); - read = 0; gf_bs_read_data(bs, genm->reserved, 6); genm->dataReferenceIndex = gf_bs_read_u16(bs); genm->data_size = (u32) gf_bs_available(bs); @@ -6752,7 +6808,6 @@ GF_Err trun_Size(GF_Box *s) { GF_Err e; u32 i, count; - GF_TrunEntry *p; GF_TrackFragmentRunBox *ptr = (GF_TrackFragmentRunBox *)s; e = gf_isom_full_box_get_size(s); @@ -6766,7 +6821,6 @@ GF_Err trun_Size(GF_Box *s) //if nothing to do, this will be skipped automatically count = gf_list_count(ptr->entries); for (i=0; ientries, i); if (ptr->flags & GF_ISOM_TRUN_DURATION) ptr->size += 4; if (ptr->flags & GF_ISOM_TRUN_SIZE) ptr->size += 4; //SHOULDN'T BE USED IF GF_ISOM_TRUN_FIRST_FLAG IS DEFINED @@ -7737,8 +7791,9 @@ GF_Err sidx_Read(GF_Box *s,GF_BitStream *bs) ptr->refs[i].reference_type = gf_bs_read_int(bs, 1); ptr->refs[i].reference_size = gf_bs_read_int(bs, 31); ptr->refs[i].subsegment_duration = gf_bs_read_u32(bs); - ptr->refs[i].contains_RAP = gf_bs_read_int(bs, 1); - ptr->refs[i].RAP_delta_time = gf_bs_read_int(bs, 31); + ptr->refs[i].starts_with_SAP = gf_bs_read_int(bs, 1); + ptr->refs[i].SAP_type = gf_bs_read_int(bs, 3); + ptr->refs[i].SAP_delta_time = gf_bs_read_int(bs, 28); ptr->size -= 12; } return GF_OK; @@ -7782,8 +7837,9 @@ GF_Err sidx_Write(GF_Box *s, GF_BitStream *bs) gf_bs_write_int(bs, ptr->refs[i].reference_type, 1); gf_bs_write_int(bs, ptr->refs[i].reference_size, 31); gf_bs_write_u32(bs, ptr->refs[i].subsegment_duration); - gf_bs_write_int(bs, ptr->refs[i].contains_RAP, 1); - gf_bs_write_int(bs, ptr->refs[i].RAP_delta_time, 31); + gf_bs_write_int(bs, ptr->refs[i].starts_with_SAP, 1); + gf_bs_write_int(bs, ptr->refs[i].SAP_type, 3); + gf_bs_write_int(bs, ptr->refs[i].SAP_delta_time, 28); } return GF_OK; } @@ -8031,7 +8087,6 @@ void rvcc_del(GF_Box *s) gf_free(s); } -/*this is using chpl format according to some NeroRecode samples*/ GF_Err rvcc_Read(GF_Box *s,GF_BitStream *bs) { GF_RVCConfigurationBox *ptr = (GF_RVCConfigurationBox*)s; @@ -8076,6 +8131,274 @@ GF_Err rvcc_Size(GF_Box *s) +GF_Box *sbgp_New() +{ + GF_SampleGroupBox *p; + GF_SAFEALLOC(p, GF_SampleGroupBox); + p->type = GF_ISOM_BOX_TYPE_SBGP; + return (GF_Box *)p; +} +void sbgp_del(GF_Box *a) +{ + GF_SampleGroupBox *p = (GF_SampleGroupBox *)a; + if (p->sample_entries) gf_free(p->sample_entries); + gf_free(p); +} + +GF_Err sbgp_Read(GF_Box *s, GF_BitStream *bs) +{ + u32 i; + GF_SampleGroupBox *p = (GF_SampleGroupBox *)s; + GF_Err e = gf_isom_full_box_read(s, bs); + if (e) return e; + + p->grouping_type = gf_bs_read_u32(bs); + p->size -= 4; + if (p->version==1) { + p->grouping_type_parameter = gf_bs_read_u32(bs); + p->size -= 4; + } + p->entry_count = gf_bs_read_u32(bs); + p->size -= 4; + p->sample_entries = gf_malloc(sizeof(GF_SampleGroupEntry)*p->entry_count); + if (!p->sample_entries) return GF_IO_ERR; + for (i=0; ientry_count; i++) { + p->sample_entries[i].sample_count = gf_bs_read_u32(bs); + p->sample_entries[i].group_description_index = gf_bs_read_u32(bs); + p->size -= 8; + } + return GF_OK; +} + +#ifndef GPAC_DISABLE_ISOM_WRITE +GF_Err sbgp_Write(GF_Box *s, GF_BitStream *bs) +{ + u32 i; + GF_Err e; + GF_SampleGroupBox *p = (GF_SampleGroupBox*)s; + + e = gf_isom_full_box_write(s, bs); + if (e) return e; + gf_bs_write_u32(bs, p->grouping_type); + if (p->version==1) + gf_bs_write_u32(bs, p->grouping_type_parameter); + + gf_bs_write_u32(bs, p->entry_count); + for (i = 0; ientry_count; i++ ) { + gf_bs_write_u32(bs, p->sample_entries[i].sample_count); + gf_bs_write_u32(bs, p->sample_entries[i].group_description_index); + } + return GF_OK; +} + +GF_Err sbgp_Size(GF_Box *s) +{ + GF_Err e; + GF_SampleGroupBox *p = (GF_SampleGroupBox*)s; + e = gf_isom_full_box_get_size(s); + if (e) return e; + p->size += 8; + if (p->version==1) p->size += 4; + p->size += 8*p->entry_count; + return GF_OK; +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + +static void *sgpd_parse_entry(u32 grouping_type, GF_BitStream *bs, u32 entry_size, u32 *total_bytes) +{ + switch (grouping_type) { + case GF_4CC( 'r', 'o', 'l', 'l' ): + { + GF_RollRecoveryEntry *ptr; + GF_SAFEALLOC(ptr, GF_RollRecoveryEntry); + ptr->roll_distance = gf_bs_read_int(bs, 16); + *total_bytes = 2; + return ptr; + } + case GF_4CC( 'r', 'a', 'p', ' ' ): + { + GF_VisualRandomAccessEntry *ptr; + GF_SAFEALLOC(ptr, GF_VisualRandomAccessEntry); + ptr->num_leading_samples_known = gf_bs_read_int(bs, 1); + ptr->num_leading_samples = gf_bs_read_int(bs, 7); + *total_bytes = 1; + return ptr; + } + default: + { + GF_DefaultSampleGroupDescriptionEntry *ptr; + if (!entry_size) return NULL; + GF_SAFEALLOC(ptr, GF_DefaultSampleGroupDescriptionEntry); + ptr->length = entry_size; + ptr->data = (u8 *) gf_malloc(sizeof(u8)*ptr->length); + gf_bs_read_data(bs, ptr->data, ptr->length); + *total_bytes = entry_size; + return ptr; + } + } + return NULL; +} + +static void sgpd_del_entry(u32 grouping_type, void *entry) +{ + switch (grouping_type) { + case GF_4CC( 'r', 'o', 'l', 'l' ): + case GF_4CC( 'r', 'a', 'p', ' ' ): + gf_free(entry); + return; + + default: + { + GF_DefaultSampleGroupDescriptionEntry *ptr = (GF_DefaultSampleGroupDescriptionEntry *)entry; + if (ptr->data) gf_free(ptr->data); + gf_free(ptr->data); + } + + } +} + +static void sgpd_write_entry(u32 grouping_type, void *entry, GF_BitStream *bs) +{ + switch (grouping_type) { + case GF_4CC( 'r', 'o', 'l', 'l' ): + gf_bs_write_int(bs, ((GF_RollRecoveryEntry*)entry)->roll_distance, 16); + return; + case GF_4CC( 'r', 'a', 'p', ' ' ): + gf_bs_write_int(bs, ((GF_VisualRandomAccessEntry*)entry)->num_leading_samples_known, 1); + gf_bs_write_int(bs, ((GF_VisualRandomAccessEntry*)entry)->num_leading_samples, 7); + return; + default: + { + GF_DefaultSampleGroupDescriptionEntry *ptr = (GF_DefaultSampleGroupDescriptionEntry *)entry; + gf_bs_write_data(bs, ptr->data, ptr->length); + } + } +} +static u32 sgpd_size_entry(u32 grouping_type, void *entry) +{ + switch (grouping_type) { + case GF_4CC( 'r', 'o', 'l', 'l' ): + return 2; + case GF_4CC( 'r', 'a', 'p', ' ' ): + return 1; + default: + return ((GF_DefaultSampleGroupDescriptionEntry *)entry)->length; + } +} + + +GF_Box *sgpd_New() +{ + GF_SampleGroupDescriptionBox *p; + GF_SAFEALLOC(p, GF_SampleGroupDescriptionBox); + p->type = GF_ISOM_BOX_TYPE_SGPD; + /*version 0 is deprecated, use v1 by default*/ + p->version = 1; + p->group_descriptions = gf_list_new(); + + return (GF_Box *)p; +} + +void sgpd_del(GF_Box *a) +{ + GF_SampleGroupDescriptionBox *p = (GF_SampleGroupDescriptionBox *)a; + while (gf_list_count(p->group_descriptions)) { + void *ptr = gf_list_last(p->group_descriptions); + sgpd_del_entry(p->grouping_type, ptr); + gf_list_rem_last(p->group_descriptions); + } + gf_list_del(p->group_descriptions); + gf_free(p); +} + +GF_Err sgpd_Read(GF_Box *s, GF_BitStream *bs) +{ + u32 entry_count; + GF_SampleGroupDescriptionBox *p = (GF_SampleGroupDescriptionBox *)s; + GF_Err e = gf_isom_full_box_read(s, bs); + if (e) return e; + + p->grouping_type = gf_bs_read_u32(bs); + p->size -= 4; + + if (p->version==1) { + p->default_length = gf_bs_read_u32(bs); + p->size -= 4; + } + entry_count = gf_bs_read_u32(bs); + p->size -= 4; + + while (entry_count) { + void *ptr; + u32 parsed_bytes; + u32 size = p->default_length; + if ((p->version==1) && !size) { + size = gf_bs_read_u32(bs); + p->size -= 4; + } + ptr = sgpd_parse_entry(p->grouping_type, bs, size, &parsed_bytes); + if (!ptr) return GF_ISOM_INVALID_FILE; + if (p->size < parsed_bytes) return GF_ISOM_INVALID_FILE; + p->size -= parsed_bytes; + gf_list_add(p->group_descriptions, ptr); + entry_count--; + } + return GF_OK; +} + +#ifndef GPAC_DISABLE_ISOM_WRITE +GF_Err sgpd_Write(GF_Box *s, GF_BitStream *bs) +{ + u32 i; + GF_SampleGroupDescriptionBox *p = (GF_SampleGroupDescriptionBox *)s; + GF_Err e; + e = gf_isom_full_box_write(s, bs); + if (e) return e; + + gf_bs_write_u32(bs, p->grouping_type); + if (p->version==1) gf_bs_write_u32(bs, p->default_length); + gf_bs_write_u32(bs, gf_list_count(p->group_descriptions) ); + + for (i=0; igroup_descriptions); i++) { + void *ptr = gf_list_get(p->group_descriptions, i); + if ((p->version == 1) && !p->default_length) { + u32 size = sgpd_size_entry(p->grouping_type, ptr); + gf_bs_write_u32(bs, size); + } + sgpd_write_entry(p->grouping_type, ptr, bs); + } + return GF_OK; +} + +GF_Err sgpd_Size(GF_Box *s) +{ + u32 i; + GF_SampleGroupDescriptionBox *p = (GF_SampleGroupDescriptionBox *)s; + GF_Err e; + e = gf_isom_full_box_get_size(s); + if (e) return e; + p->size += 8; + if (p->version==1) p->size += 4; + p->default_length = 0; + + for (i=0; igroup_descriptions); i++) { + void *ptr = gf_list_get(p->group_descriptions, i); + u32 size = sgpd_size_entry(p->grouping_type, ptr); + p->size += size; + if (!p->default_length) { + p->default_length = size; + } else if (p->default_length != size) { + p->default_length = 0; + } + } + if (p->version==1) { + if (!p->default_length) p->size += gf_list_count(p->group_descriptions)*4; + } + return GF_OK; +} +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + #endif /*GPAC_DISABLE_ISOM*/ diff --git a/src/isomedia/box_code_isma.c b/src/isomedia/box_code_isma.c index 3b0ca62..b059065 100644 --- a/src/isomedia/box_code_isma.c +++ b/src/isomedia/box_code_isma.c @@ -206,6 +206,7 @@ GF_Err schm_Write(GF_Box *s, GF_BitStream *bs) GF_SchemeTypeBox *ptr = (GF_SchemeTypeBox *) s; if (!s) return GF_BAD_PARAM; e = gf_isom_full_box_write(s, bs); + assert(e == GF_OK); gf_bs_write_u32(bs, ptr->scheme_type); gf_bs_write_u32(bs, ptr->scheme_version); if (ptr->flags & 0x000001) gf_bs_write_data(bs, ptr->URI, strlen(ptr->URI)+1); diff --git a/src/isomedia/box_dump.c b/src/isomedia/box_dump.c index 6e4cf18..4af3783 100644 --- a/src/isomedia/box_dump.c +++ b/src/isomedia/box_dump.c @@ -179,6 +179,11 @@ GF_Err gf_box_dump(void *ptr, FILE * trace) return chpl_dump(a, trace); case GF_ISOM_BOX_TYPE_PDIN: return dpin_dump(a, trace); + case GF_ISOM_BOX_TYPE_SBGP: + return sbgp_dump(a, trace); + case GF_ISOM_BOX_TYPE_SGPD: + return sgpd_dump(a, trace); + case GF_ISOM_BOX_TYPE_RTP_STSD: return ghnt_dump(a, trace); @@ -674,6 +679,8 @@ GF_Err stbl_dump(GF_Box *a, FILE * trace) if (p->PaddingBits) gf_box_dump(p->PaddingBits, trace); if (p->SubSamples) gf_box_dump(p->SubSamples, trace); if (p->Fragments) gf_box_dump(p->Fragments, trace); + if (p->sampleGroupsDescription) gf_box_array_dump(p->sampleGroupsDescription, trace); + if (p->sampleGroups) gf_box_array_dump(p->sampleGroups, trace); fprintf(trace, "\n"); return GF_OK; @@ -1789,7 +1796,7 @@ GF_Err xml_dump(GF_Box *a, FILE * trace) DumpBox(a, trace); gf_full_box_dump(a, trace); fprintf(trace, "xml, p->xml_length, 1, trace); + gf_fwrite(p->xml, p->xml_length, 1, trace); fprintf(trace, "]]>\n"); fprintf(trace, "\n"); return GF_OK; @@ -2248,6 +2255,8 @@ GF_Err traf_dump(GF_Box *a, FILE * trace) if (p->subs) gf_box_dump(p->subs, trace); if (p->sdtp) gf_box_dump(p->sdtp, trace); if (p->tfdt) gf_box_dump(p->tfdt, trace); + if (p->sampleGroupsDescription) gf_box_array_dump(p->sampleGroupsDescription, trace); + if (p->sampleGroups) gf_box_array_dump(p->sampleGroups, trace); gf_box_array_dump(p->TrackRuns, trace); fprintf(trace, "\n"); return GF_OK; @@ -2264,7 +2273,7 @@ GF_Err tfhd_dump(GF_Box *a, FILE * trace) fprintf(trace, " BaseDataOffset=\""LLD"\"", LLD_CAST p->base_data_offset); } if (p->flags & GF_ISOM_TRAF_SAMPLE_DESC) - fprintf(trace, "SampleDescriptionIndex=\"%d\"", p->sample_desc_index); + fprintf(trace, " SampleDescriptionIndex=\"%d\"", p->sample_desc_index); if (p->flags & GF_ISOM_TRAF_SAMPLE_DUR) fprintf(trace, " SampleDuration=\"%d\"", p->def_sample_duration); if (p->flags & GF_ISOM_TRAF_SAMPLE_SIZE) @@ -3265,9 +3274,6 @@ GF_Err ilst_dump(GF_Box *a, FILE * trace) GF_Err ListEntry_dump(GF_Box *a, FILE * trace) { - GF_ItemListBox *p; - - p = (GF_ItemListBox *)a; fprintf(trace, "\n"); DumpBox(a, trace); gf_box_dump(a, trace); @@ -3277,9 +3283,6 @@ GF_Err ListEntry_dump(GF_Box *a, FILE * trace) GF_Err data_dump(GF_Box *a, FILE * trace) { - GF_ItemListBox *p; - - p = (GF_ItemListBox *)a; fprintf(trace, "\n"); DumpBox(a, trace); gf_full_box_dump(a, trace); @@ -3506,7 +3509,7 @@ GF_Err sidx_dump(GF_Box *a, FILE * trace) gf_full_box_dump(a, trace); for (i=0; inb_refs; i++) { - fprintf(trace, "\n", p->refs[i].reference_type, p->refs[i].reference_size, p->refs[i].subsegment_duration, p->refs[i].contains_RAP, p->refs[i].RAP_delta_time); + fprintf(trace, "\n", p->refs[i].reference_type, p->refs[i].reference_size, p->refs[i].subsegment_duration, p->refs[i].starts_with_SAP, p->refs[i].SAP_type, p->refs[i].SAP_delta_time); } fprintf(trace, "\n"); return GF_OK; @@ -3570,4 +3573,54 @@ GF_Err rvcc_dump(GF_Box *a, FILE * trace) return GF_OK; } +GF_Err sbgp_dump(GF_Box *a, FILE * trace) +{ + u32 i; + GF_SampleGroupBox *ptr = (GF_SampleGroupBox*) a; + if (!a) return GF_BAD_PARAM; + + fprintf(trace, "grouping_type) ); + if (ptr->version==1) fprintf(trace, " grouping_type_parameter=\"%d\"", ptr->grouping_type_parameter); + fprintf(trace, ">\n"); + DumpBox(a, trace); + gf_full_box_dump((GF_Box *)a, trace); + for (i=0; ientry_count; i++) { + fprintf(trace, "\n", ptr->sample_entries[i].sample_count, ptr->sample_entries[i].group_description_index ); + } + fprintf(trace, "\n"); + return GF_OK; +} + +GF_Err sgpd_dump(GF_Box *a, FILE * trace) +{ + u32 i; + GF_SampleGroupDescriptionBox *ptr = (GF_SampleGroupDescriptionBox*) a; + if (!a) return GF_BAD_PARAM; + + fprintf(trace, "grouping_type) ); + if (ptr->version==1) fprintf(trace, " default_length=\"%d\"", ptr->default_length); + fprintf(trace, ">\n"); + DumpBox(a, trace); + gf_full_box_dump((GF_Box *)a, trace); + for (i=0; igroup_descriptions); i++) { + void *entry = gf_list_get(ptr->group_descriptions, i); + switch (ptr->grouping_type) { + case GF_4CC( 'r', 'o', 'l', 'l' ): + fprintf(trace, "\n", ((GF_RollRecoveryEntry*)entry)->roll_distance ); + break; + case GF_4CC( 'r', 'a', 'p', ' ' ): + fprintf(trace, "num_leading_samples_known ? "yes" : "no"); + if (((GF_VisualRandomAccessEntry*)entry)->num_leading_samples_known) fprintf(trace, " num_leading_samples=\"%d\" />", ((GF_VisualRandomAccessEntry*)entry)->num_leading_samples); + fprintf(trace, "/>\n"); + break; + default: + fprintf(trace, "length); + DumpData(trace, ((GF_DefaultSampleGroupDescriptionEntry*)entry)->data, ((GF_DefaultSampleGroupDescriptionEntry*)entry)->length); + fprintf(trace, "\"/>\n"); + } + } + fprintf(trace, "\n"); + return GF_OK; +} + #endif /*GPAC_DISABLE_ISOM_DUMP*/ diff --git a/src/isomedia/box_funcs.c b/src/isomedia/box_funcs.c index 1ffd065..d17a27c 100644 --- a/src/isomedia/box_funcs.c +++ b/src/isomedia/box_funcs.c @@ -29,7 +29,7 @@ //Add this funct to handle incomplete files... //bytesExpected is 0 most of the time. If the file is incomplete, bytesExpected //is the number of bytes missing to parse the box... -GF_Err gf_isom_parse_root_box(GF_Box **outBox, GF_BitStream *bs, u64 *bytesExpected) +GF_Err gf_isom_parse_root_box(GF_Box **outBox, GF_BitStream *bs, u64 *bytesExpected, Bool progressive_mode) { GF_Err ret; u64 start; @@ -43,6 +43,7 @@ GF_Err gf_isom_parse_root_box(GF_Box **outBox, GF_BitStream *bs, u64 *bytesExpec ret = gf_isom_parse_box(outBox, bs); if (ret == GF_ISOM_INCOMPLETE_FILE) { *bytesExpected = (*outBox)->size; + GF_LOG(progressive_mode ? GF_LOG_DEBUG : GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] Incomplete box %s\n", gf_4cc_to_str( (*outBox)->type) )); gf_bs_seek(bs, start); gf_isom_box_del(*outBox); *outBox = NULL; @@ -131,7 +132,6 @@ proceed_box: if (size - hdr_size > end ) { newBox->size = size - hdr_size - end; *outBox = newBox; - GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[iso file] Incomplete box %s\n", gf_4cc_to_str(newBox->type) )); return GF_ISOM_INCOMPLETE_FILE; } //we need a special reading for these boxes... @@ -398,6 +398,8 @@ GF_Box *gf_isom_box_new(u32 boxType) case GF_ISOM_BOX_TYPE_VOID: return void_New(); case GF_ISOM_BOX_TYPE_STSF: return stsf_New(); case GF_ISOM_BOX_TYPE_PDIN: return pdin_New(); + case GF_ISOM_BOX_TYPE_SBGP: return sbgp_New(); + case GF_ISOM_BOX_TYPE_SGPD: return sgpd_New(); #ifndef GPAC_DISABLE_ISOM_HINTING case GF_ISOM_BOX_TYPE_RTP_STSD: @@ -649,6 +651,9 @@ void gf_isom_box_del(GF_Box *a) case GF_ISOM_BOX_TYPE_VOID: void_del(a); return; case GF_ISOM_BOX_TYPE_STSF: stsf_del(a); return; case GF_ISOM_BOX_TYPE_PDIN: pdin_del(a); return; + case GF_ISOM_BOX_TYPE_SBGP: sbgp_del(a); return; + case GF_ISOM_BOX_TYPE_SGPD: sgpd_del(a); return; + #ifndef GPAC_DISABLE_ISOM_HINTING case GF_ISOM_BOX_TYPE_RTP_STSD: ghnt_del(a); return; @@ -888,7 +893,9 @@ GF_Err gf_isom_box_read(GF_Box *a, GF_BitStream *bs) case GF_ISOM_BOX_TYPE_VOID: return void_Read(a, bs); case GF_ISOM_BOX_TYPE_STSF: return stsf_Read(a, bs); case GF_ISOM_BOX_TYPE_PDIN: return pdin_Read(a, bs); - + case GF_ISOM_BOX_TYPE_SBGP: return sbgp_Read(a, bs); + case GF_ISOM_BOX_TYPE_SGPD: return sgpd_Read(a, bs); + #ifndef GPAC_DISABLE_ISOM_HINTING case GF_ISOM_BOX_TYPE_RTP_STSD: return ghnt_Read(a, bs); case GF_ISOM_BOX_TYPE_RTPO: return rtpo_Read(a, bs); @@ -1120,6 +1127,8 @@ GF_Err gf_isom_box_write(GF_Box *a, GF_BitStream *bs) case GF_ISOM_BOX_TYPE_VOID: return void_Write(a, bs); case GF_ISOM_BOX_TYPE_STSF: return stsf_Write(a, bs); case GF_ISOM_BOX_TYPE_PDIN: return pdin_Write(a, bs); + case GF_ISOM_BOX_TYPE_SBGP: return sbgp_Write(a, bs); + case GF_ISOM_BOX_TYPE_SGPD: return sgpd_Write(a, bs); #ifndef GPAC_DISABLE_ISOM_HINTING case GF_ISOM_BOX_TYPE_RTP_STSD: return ghnt_Write(a, bs); @@ -1350,7 +1359,9 @@ GF_Err gf_isom_box_size(GF_Box *a) case GF_ISOM_BOX_TYPE_VOID: return void_Size(a); case GF_ISOM_BOX_TYPE_STSF: return stsf_Size(a); case GF_ISOM_BOX_TYPE_PDIN: return pdin_Size(a); - + case GF_ISOM_BOX_TYPE_SBGP: return sbgp_Size(a); + case GF_ISOM_BOX_TYPE_SGPD: return sgpd_Size(a); + #ifndef GPAC_DISABLE_ISOM_HINTING case GF_ISOM_BOX_TYPE_RTP_STSD: return ghnt_Size(a); case GF_ISOM_BOX_TYPE_RTPO: return rtpo_Size(a); diff --git a/src/isomedia/isom_intern.c b/src/isomedia/isom_intern.c index ed99b23..e24ee81 100644 --- a/src/isomedia/isom_intern.c +++ b/src/isomedia/isom_intern.c @@ -30,7 +30,7 @@ /************************************************************** Some Local functions for movie creation **************************************************************/ -GF_Err gf_isom_parse_root_box(GF_Box **outBox, GF_BitStream *bs, u64 *bytesExpected); +GF_Err gf_isom_parse_root_box(GF_Box **outBox, GF_BitStream *bs, u64 *bytesExpected, Bool progressive_mode); #ifndef GPAC_DISABLE_ISOM_FRAGMENTS GF_Err MergeTrack(GF_TrackBox *trak, GF_TrackFragmentBox *traf, u64 moof_offset, Bool is_first_merge); @@ -82,7 +82,7 @@ GF_Err MergeFragment(GF_MovieFragmentBox *moof, GF_ISOFile *mov) #endif -GF_Err gf_isom_parse_movie_boxes(GF_ISOFile *mov, u64 *bytesMissing) +GF_Err gf_isom_parse_movie_boxes(GF_ISOFile *mov, u64 *bytesMissing, Bool progressive_mode) { GF_Box *a; u64 totSize; @@ -105,7 +105,7 @@ GF_Err gf_isom_parse_movie_boxes(GF_ISOFile *mov, u64 *bytesMissing) mov->current_top_box_start = gf_bs_get_position(mov->movieFileMap->bs); #endif - e = gf_isom_parse_root_box(&a, mov->movieFileMap->bs, bytesMissing); + e = gf_isom_parse_root_box(&a, mov->movieFileMap->bs, bytesMissing, progressive_mode); if (e >= 0) { e = GF_OK; @@ -194,8 +194,19 @@ GF_Err gf_isom_parse_movie_boxes(GF_ISOFile *mov, u64 *bytesMissing) #ifndef GPAC_DISABLE_ISOM_FRAGMENTS case GF_ISOM_BOX_TYPE_STYP: - if (((GF_SegmentTypeBox *)a)->majorBrand == GF_4CC('i', 's', 's', 's') || - ((GF_SegmentTypeBox *)a)->majorBrand == GF_4CC('i', 'm', 's', 's')) mov->is_index_segment = 1; + { + u32 brand = ((GF_SegmentTypeBox *)a)->majorBrand; + switch (brand) { + case GF_4CC('s', 'i', 's', 'x'): + case GF_4CC('r', 'i', 's', 'x'): + case GF_4CC('s', 's', 's', 's'): + mov->is_index_segment = 1; + break; + default: + break; + } + } + /*fall-through*/ case GF_ISOM_BOX_TYPE_SIDX: totSize += a->size; @@ -380,7 +391,7 @@ GF_ISOFile *gf_isom_open_file(const char *fileName, u32 OpenMode, const char *tm } //OK, let's parse the movie... - mov->LastError = gf_isom_parse_movie_boxes(mov, &bytes); + mov->LastError = gf_isom_parse_movie_boxes(mov, &bytes, 0); if (mov->LastError) { gf_isom_set_last_error(NULL, mov->LastError); gf_isom_delete_movie(mov); @@ -451,7 +462,7 @@ GF_TrackBox *gf_isom_get_track_from_file(GF_ISOFile *movie, u32 trackNumber) //WARNING: MOVIETIME IS EXPRESSED IN MEDIA TS -GF_Err GetMediaTime(GF_TrackBox *trak, u64 movieTime, u64 *MediaTime, s64 *SegmentStartTime, s64 *MediaOffset, u8 *useEdit) +GF_Err GetMediaTime(GF_TrackBox *trak, Bool force_non_empty, u64 movieTime, u64 *MediaTime, s64 *SegmentStartTime, s64 *MediaOffset, u8 *useEdit) { #if 0 GF_Err e; @@ -459,7 +470,7 @@ GF_Err GetMediaTime(GF_TrackBox *trak, u64 movieTime, u64 *MediaTime, s64 *Segme u64 firstDTS; #endif u32 i; - u64 time, lastSampleTime, m_time; + u64 time, lastSampleTime; s64 mtime; GF_EdtsEntry *ent; Double scale_ts; @@ -485,22 +496,21 @@ GF_Err GetMediaTime(GF_TrackBox *trak, u64 movieTime, u64 *MediaTime, s64 *Segme if (! trak->editBox || !trak->editBox->editList) { *MediaTime = movieTime; //check this is in our media time line - if (*MediaTime > lastSampleTime) *MediaTime = lastSampleTime; + if (!trak->moov->mov->use_segments && (*MediaTime > lastSampleTime)) *MediaTime = lastSampleTime; *useEdit = 0; return GF_OK; } //browse the edit list and get the time - scale_ts = trak->moov->mvhd->timeScale; - scale_ts /= trak->Media->mediaHeader->timeScale; - scale_ts *= ((s64)movieTime ); - m_time = (u64) (scale_ts); + scale_ts = trak->Media->mediaHeader->timeScale; + scale_ts /= trak->moov->mvhd->timeScale; time = 0; ent = NULL; i=0; while ((ent = (GF_EdtsEntry *)gf_list_enum(trak->editBox->editList->entryList, &i))) { - if (time + ent->segmentDuration > m_time) { - goto ent_found; + if ( (time + ent->segmentDuration) * scale_ts > movieTime) { + if (!force_non_empty || (ent->mediaTime >= 0)) + goto ent_found; } time += ent->segmentDuration; } diff --git a/src/isomedia/isom_read.c b/src/isomedia/isom_read.c index 0eaac68..261304c 100644 --- a/src/isomedia/isom_read.c +++ b/src/isomedia/isom_read.c @@ -125,7 +125,7 @@ Bool gf_isom_probe_file(const char *fileName) the file map is regular (through FILE handles) **************************************************************/ GF_EXPORT -GF_Err gf_isom_open_progressive(const char *fileName, GF_ISOFile **the_file, u64 *BytesMissing) +GF_Err gf_isom_open_progressive(const char *fileName, u64 start_range, u64 end_range, GF_ISOFile **the_file, u64 *BytesMissing) { GF_Err e; GF_ISOFile *movie; @@ -150,7 +150,12 @@ GF_Err gf_isom_open_progressive(const char *fileName, GF_ISOFile **the_file, u64 movie->finalName = NULL; #endif /*GPAC_DISABLE_ISOM_WRITE*/ - e = gf_isom_parse_movie_boxes(movie, BytesMissing); + if (end_range>start_range) { + gf_bs_seek(movie->movieFileMap->bs, end_range+1); + gf_bs_truncate(movie->movieFileMap->bs); + gf_bs_seek(movie->movieFileMap->bs, start_range); + } + e = gf_isom_parse_movie_boxes(movie, BytesMissing, 1); if (e == GF_ISOM_INCOMPLETE_FILE) { //if we have a moov, we're fine if (movie->moov) { @@ -641,7 +646,7 @@ GF_Err gf_isom_get_media_time(GF_ISOFile *the_file, u32 trackNumber, u32 movieTi if (!trak || !MediaTime) return GF_BAD_PARAM;; SegmentStartTime = 0; - return GetMediaTime(trak, movieTime, MediaTime, &SegmentStartTime, &mediaOffset, &useEdit); + return GetMediaTime(trak, 0, movieTime, MediaTime, &SegmentStartTime, &mediaOffset, &useEdit); } @@ -1233,8 +1238,11 @@ GF_Err gf_isom_get_sample_for_media_time(GF_ISOFile *the_file, u32 trackNumber, stbl = trak->Media->information->sampleTable; #ifndef GPAC_DISABLE_ISOM_FRAGMENTS - if (desiredTime < trak->dts_at_seg_start) return GF_BAD_PARAM; - desiredTime -= trak->dts_at_seg_start; + if (desiredTime < trak->dts_at_seg_start) { + desiredTime = 0; + } else { + desiredTime -= trak->dts_at_seg_start; + } #endif e = findEntryForTime(stbl, desiredTime, 0, &sampleNumber, &prevSampleNumber); @@ -1366,6 +1374,7 @@ GF_Err gf_isom_get_sample_for_media_time(GF_ISOFile *the_file, u32 trackNumber, GF_EXPORT GF_Err gf_isom_get_sample_for_movie_time(GF_ISOFile *the_file, u32 trackNumber, u64 movieTime, u32 *StreamDescriptionIndex, u8 SearchMode, GF_ISOSample **sample, u32 *sampleNumber) { + Double tsscale; GF_Err e; GF_TrackBox *trak; u64 mediaTime; @@ -1386,7 +1395,7 @@ GF_Err gf_isom_get_sample_for_movie_time(GF_ISOFile *the_file, u32 trackNumber, return GF_EOS; } } - else if (movieTime * trak->moov->mvhd->timeScale > trak->Header->duration * trak->Media->mediaHeader->timeScale) { + else if (!trak->dts_at_seg_start && (movieTime * trak->moov->mvhd->timeScale > trak->Header->duration * trak->Media->mediaHeader->timeScale)) { *sample = NULL; if (sampleNumber) *sampleNumber = 0; *StreamDescriptionIndex = 0; @@ -1397,7 +1406,7 @@ GF_Err gf_isom_get_sample_for_movie_time(GF_ISOFile *the_file, u32 trackNumber, mediaTime = segStartTime = 0; *StreamDescriptionIndex = 0; - e = GetMediaTime(trak, movieTime, &mediaTime, &segStartTime, &mediaOffset, &useEdit); + e = GetMediaTime(trak, (SearchMode==GF_ISOM_SEARCH_SYNC_FORWARD) ? 1 : 0, movieTime, &mediaTime, &segStartTime, &mediaOffset, &useEdit); if (e) return e; /*here we check if we were playing or not and return no sample in normal search modes*/ @@ -1430,6 +1439,9 @@ GF_Err gf_isom_get_sample_for_movie_time(GF_ISOFile *the_file, u32 trackNumber, } } + tsscale = trak->Media->mediaHeader->timeScale; + tsscale /= trak->moov->mvhd->timeScale; + //OK, we have a sample so fetch it e = gf_isom_get_sample_for_media_time(the_file, trackNumber, mediaTime, StreamDescriptionIndex, SearchMode, sample, &sampNum); if (e) return e; @@ -1438,9 +1450,8 @@ GF_Err gf_isom_get_sample_for_movie_time(GF_ISOFile *the_file, u32 trackNumber, //to the media time scale (used by SLConfig) - add the edit start time but stay in //the track TS if (useEdit) { - u64 _ts = segStartTime; - _ts *= trak->Media->mediaHeader->timeScale; - _ts /= trak->moov->mvhd->timeScale; + u64 _ts = (u64)(segStartTime * tsscale); + (*sample)->DTS += _ts; /*watchout, the sample fetched may be before the first sample in the edit list (when seeking)*/ if ( (*sample)->DTS > (u64) mediaOffset) { @@ -1450,6 +1461,10 @@ GF_Err gf_isom_get_sample_for_movie_time(GF_ISOFile *the_file, u32 trackNumber, } } if (sampleNumber) *sampleNumber = sampNum; +#ifndef GPAC_DISABLE_ISOM_FRAGMENTS + if ( (*sample) ) (*sample)->DTS += trak->dts_at_seg_start; +#endif + return GF_OK; } @@ -1704,31 +1719,64 @@ void gf_isom_delete(GF_ISOFile *movie) } GF_EXPORT -u32 gf_isom_get_max_chunk_duration(GF_ISOFile *movie, u32 trackNumber) +GF_Err gf_isom_get_chunks_infos(GF_ISOFile *movie, u32 trackNumber, u32 *dur_min, u32 *dur_avg, u32 *dur_max, u32 *size_min, u32 *size_avg, u32 *size_max) { GF_TrackBox *trak; - u32 i, sample_per_chunk, sample_dur; + u32 i, k, sample_idx, dmin, dmax, smin, smax, tot_chunks; + u64 davg, savg; GF_SampleToChunkBox *stsc; GF_TimeToSampleBox *stts; - if (!movie || !trackNumber || !movie->moov) return 0; + if (!movie || !trackNumber || !movie->moov) return GF_BAD_PARAM; trak = gf_isom_get_track_from_file(movie, trackNumber); - if (!trak) return 0; + if (!trak) return GF_BAD_PARAM; stsc = trak->Media->information->sampleTable->SampleToChunk; stts = trak->Media->information->sampleTable->TimeToSample; - sample_per_chunk = 0; + dmin = smin = (u32) -1; + dmax = smax = 0; + davg = savg = 0; + sample_idx = 1; + tot_chunks = 0; for (i=0; inb_entries; i++) { - if (stsc->entries[i].samplesPerChunk > sample_per_chunk) sample_per_chunk = stsc->entries[i].samplesPerChunk; - } - sample_dur = 0; - for (i=0; inb_entries; i++) { - if (stts->entries[i].sampleDelta > sample_dur) sample_dur = stts->entries[i].sampleDelta; + u32 nb_chunk = 0; + while (1) { + u32 chunk_dur = 0; + u32 chunk_size = 0; + for (k=0; kentries[i].samplesPerChunk; k++) { + u64 dts; + u32 dur; + u32 size; + stbl_GetSampleDTS_and_Duration(stts, k+sample_idx, &dts, &dur); + chunk_dur += dur; + stbl_GetSampleSize(trak->Media->information->sampleTable->SampleSize, k+sample_idx, &size); + chunk_size += size; + + } + if (dmin>chunk_dur) dmin = chunk_dur; + if (dmaxchunk_size) smin = chunk_size; + if (smaxentries[i].samplesPerChunk; + if (i+1==stsc->nb_entries) break; + nb_chunk ++; + if (stsc->entries[i].firstChunk + nb_chunk == stsc->entries[i+1].firstChunk) break; + } } + if (tot_chunks) davg /= tot_chunks; + + if (dur_min) *dur_min = dmin; + if (dur_avg) *dur_avg = (u32) davg; + if (dur_max) *dur_max = dmax; - //rescale to ms - i = 1000 * sample_dur * sample_per_chunk / trak->Media->mediaHeader->timeScale; - return i; + if (size_min) *size_min = smin; + if (size_avg) *size_avg = (u32) savg; + if (size_max) *size_max = smax; + return GF_OK; } GF_EXPORT @@ -1882,7 +1930,7 @@ GF_Err gf_isom_refresh_fragmented(GF_ISOFile *movie, u64 *MissingBytes) if (prevsize==size) return GF_OK; //ok parse root boxes - return gf_isom_parse_movie_boxes(movie, MissingBytes); + return gf_isom_parse_movie_boxes(movie, MissingBytes, 1); #endif } @@ -1934,7 +1982,7 @@ GF_Err gf_isom_release_segment(GF_ISOFile *movie, Bool reset_tables) return GF_OK; } -GF_Err gf_isom_open_segment(GF_ISOFile *movie, const char *fileName) +GF_Err gf_isom_open_segment(GF_ISOFile *movie, const char *fileName, u64 start_range, u64 end_range) { #ifdef GPAC_DISABLE_ISOM_FRAGMENTS return GF_NOT_SUPPORTED; @@ -1951,6 +1999,15 @@ GF_Err gf_isom_open_segment(GF_ISOFile *movie, const char *fileName) e = gf_isom_datamap_new(fileName, NULL, GF_ISOM_DATA_MAP_READ_ONLY, &movie->movieFileMap); if (e) return e; + movie->current_top_box_start = 0; + + if (end_range > start_range) { + gf_bs_seek(movie->movieFileMap->bs, end_range+1); + gf_bs_truncate(movie->movieFileMap->bs); + gf_bs_seek(movie->movieFileMap->bs, start_range); + movie->current_top_box_start = start_range; + } + for (i=0; imoov->trackList); i++) { GF_TrackBox *trak = gf_list_get(movie->moov->trackList, i); if (trak->Media->information->dataHandler == NULL) { @@ -1958,9 +2015,8 @@ GF_Err gf_isom_open_segment(GF_ISOFile *movie, const char *fileName) } } - movie->current_top_box_start = 0; //ok parse root boxes - return gf_isom_parse_movie_boxes(movie, &MissingBytes); + return gf_isom_parse_movie_boxes(movie, &MissingBytes, 1); #endif } @@ -2618,5 +2674,89 @@ GF_Err gf_isom_get_rvc_config(GF_ISOFile *movie, u32 track, u32 sampleDescriptio return GF_OK; } +GF_EXPORT +Bool gf_isom_moov_first(GF_ISOFile *movie) +{ + u32 i; + for (i=0; iTopBoxes); i++) { + GF_Box *b = gf_list_get(movie->TopBoxes, i); + if (b->type == GF_ISOM_BOX_TYPE_MOOV) return 1; + if (b->type == GF_ISOM_BOX_TYPE_MDAT) return 0; + } + return 0; +} + +void gf_isom_reset_fragment_info(GF_ISOFile *movie) +{ + u32 i; + if (!movie) return; + for (i=0; imoov->trackList); i++) { + GF_TrackBox *trak = gf_list_get(movie->moov->trackList, i); + trak->dts_at_seg_start = 0; + trak->sample_count_at_seg_start = 0; + trak->Media->information->sampleTable->SampleSize->sampleCount = 0; + } + movie->NextMoofNumber = 0; +} + +GF_Err gf_isom_get_sample_rap_roll_info(GF_ISOFile *the_file, u32 trackNumber, u32 sample_number, Bool *is_rap, Bool *has_roll, s32 *roll_distance) +{ + GF_TrackBox *trak; + u32 i, count; + + if (is_rap) *is_rap = 0; + if (has_roll) *has_roll = 0; + if (roll_distance) *roll_distance = 0; + + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return GF_BAD_PARAM; + if (!trak->Media->information->sampleTable->sampleGroups) return GF_OK; + + count = gf_list_count(trak->Media->information->sampleTable->sampleGroups); + for (i=0; iMedia->information->sampleTable->sampleGroups, i); + for (j=0; jentry_count; j++) { + last_sample_in_entry = first_sample_in_entry + sg->sample_entries[j].sample_count - 1; + if ((sample_numberlast_sample_in_entry)) { + first_sample_in_entry = last_sample_in_entry+1; + continue; + } + /*we found our sample*/ + group_desc_index = sg->sample_entries[j].group_description_index; + break; + } + /*no sampleGroup info associated*/ + if (!group_desc_index) continue; + + sgdesc = NULL; + for (j=0; jMedia->information->sampleTable->sampleGroupsDescription); j++) { + sgdesc = gf_list_get(trak->Media->information->sampleTable->sampleGroupsDescription, j); + if (sgdesc->grouping_type==sg->grouping_type) break; + sgdesc = NULL; + } + /*no sampleGroup description found for this group (invalid file)*/ + if (!sgdesc) continue; + + switch (sgdesc->grouping_type) { + case GF_4CC('r','a','p',' '): + if (is_rap) *is_rap = 1; + break; + case GF_4CC('r','o','l','l'): + if (has_roll) *has_roll = 1; + if (roll_distance) { + GF_RollRecoveryEntry *roll_entry = (GF_RollRecoveryEntry *) gf_list_get(sgdesc->group_descriptions, group_desc_index - 1); + if (roll_entry) *roll_distance = roll_entry->roll_distance; + } + break; + } + } + return GF_OK; +} #endif /*GPAC_DISABLE_ISOM*/ diff --git a/src/isomedia/isom_write.c b/src/isomedia/isom_write.c index 048ffcb..b898dc9 100644 --- a/src/isomedia/isom_write.c +++ b/src/isomedia/isom_write.c @@ -545,7 +545,6 @@ GF_Err gf_isom_new_mpeg4_description(GF_ISOFile *movie, GF_Err e; u32 dataRefIndex; GF_ESD *new_esd; - GF_TrackReferenceBox *tref; e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); if (e) return e; @@ -555,8 +554,6 @@ GF_Err gf_isom_new_mpeg4_description(GF_ISOFile *movie, !esd || !esd->decoderConfig || !esd->slConfig) return GF_BAD_PARAM; - tref = NULL; - //get or create the data ref e = Media_FindDataRef(trak->Media->information->dataInformation->dref, URLname, URNname, &dataRefIndex); if (e) return e; @@ -2416,7 +2413,10 @@ GF_Err gf_isom_clone_track(GF_ISOFile *orig_file, u32 orig_track, GF_ISOFile *de stbl = trak->Media->information->sampleTable; stbl_temp = (GF_SampleTableBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_STBL); + /*clone sampleDescription table*/ stbl_temp->SampleDescription = stbl->SampleDescription; + /*also clone sampleGroups description tables if any*/ + stbl_temp->sampleGroupsDescription = stbl->sampleGroupsDescription; trak->Media->information->sampleTable = stbl_temp; bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); @@ -2432,6 +2432,7 @@ GF_Err gf_isom_clone_track(GF_ISOFile *orig_file, u32 orig_track, GF_ISOFile *de trak->Media->information->sampleTable = stbl; stbl_temp->SampleDescription = NULL; + stbl_temp->sampleGroupsDescription = NULL; gf_isom_box_del((GF_Box *)stbl_temp); if (e) return e; @@ -2488,6 +2489,30 @@ GF_Err gf_isom_clone_track(GF_ISOFile *orig_file, u32 orig_track, GF_ISOFile *de return GF_OK; } +GF_Err gf_isom_clone_sample_descriptions(GF_ISOFile *the_file, u32 trackNumber, GF_ISOFile *orig_file, u32 orig_track, Bool reset_existing) +{ + u32 i; + GF_TrackBox *dst_trak, *src_trak; + GF_Err e = CanAccessMovie(the_file, GF_ISOM_OPEN_WRITE); + if (e) return e; + + dst_trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!dst_trak || !dst_trak->Media) return GF_BAD_PARAM; + src_trak = gf_isom_get_track_from_file(orig_file, orig_track); + if (!src_trak || !src_trak->Media) return GF_BAD_PARAM; + + if (reset_existing) { + gf_isom_box_array_del(dst_trak->Media->information->sampleTable->SampleDescription->boxList); + dst_trak->Media->information->sampleTable->SampleDescription->boxList = gf_list_new(); + } + + for (i=0; iMedia->information->sampleTable->SampleDescription->boxList); i++) { + u32 outDesc; + e = gf_isom_clone_sample_description(the_file, trackNumber, orig_file, orig_track, i+1, NULL, NULL, &outDesc); + if (e) break; + } + return e; +} GF_Err gf_isom_clone_sample_description(GF_ISOFile *the_file, u32 trackNumber, GF_ISOFile *orig_file, u32 orig_track, u32 orig_desc_index, char *URLname, char *URNname, u32 *outDescriptionIndex) { @@ -3102,7 +3127,7 @@ GF_Err gf_isom_set_media_timescale(GF_ISOFile *the_file, u32 trackNumber, u32 ne -Bool gf_isom_is_same_sample_description(GF_ISOFile *f1, u32 tk1, GF_ISOFile *f2, u32 tk2) +Bool gf_isom_is_same_sample_description(GF_ISOFile *f1, u32 tk1, u32 sdesc_index1, GF_ISOFile *f2, u32 tk2, u32 sdesc_index2) { u32 i, count; GF_TrackBox *trak1, *trak2; @@ -3121,12 +3146,19 @@ Bool gf_isom_is_same_sample_description(GF_ISOFile *f1, u32 tk1, GF_ISOFile *f2, if (trak1->Media->handler->handlerType != trak2->Media->handler->handlerType) return 0; count = gf_list_count(trak1->Media->information->sampleTable->SampleDescription->boxList); - if (count != gf_list_count(trak2->Media->information->sampleTable->SampleDescription->boxList)) return 0; + if (count != gf_list_count(trak2->Media->information->sampleTable->SampleDescription->boxList)) { + if (!sdesc_index1 && !sdesc_index2) return 0; + } need_memcmp = 1; for (i=0; iMedia->information->sampleTable->SampleDescription->boxList, i); GF_Box *ent2 = (GF_Box *)gf_list_get(trak2->Media->information->sampleTable->SampleDescription->boxList, i); + + if (sdesc_index1) ent1 = (GF_Box *)gf_list_get(trak1->Media->information->sampleTable->SampleDescription->boxList, sdesc_index1 - 1); + if (sdesc_index2) ent2 = (GF_Box *)gf_list_get(trak2->Media->information->sampleTable->SampleDescription->boxList, sdesc_index2 - 1); + + if (!ent1 || !ent2) return 0; if (ent1->type != ent2->type) return 0; switch (ent1->type) { @@ -3137,8 +3169,8 @@ Bool gf_isom_is_same_sample_description(GF_ISOFile *f1, u32 tk1, GF_ISOFile *f2, case GF_ISOM_BOX_TYPE_ENCA: case GF_ISOM_BOX_TYPE_ENCV: case GF_ISOM_BOX_TYPE_ENCS: - Media_GetESD(trak1->Media, i+1, &esd1, 1); - Media_GetESD(trak2->Media, i+1, &esd2, 1); + Media_GetESD(trak1->Media, sdesc_index1 , &esd1, 1); + Media_GetESD(trak2->Media, sdesc_index2 , &esd2, 1); if (!esd1 || !esd2) continue; need_memcmp = 0; if (esd1->decoderConfig->streamType != esd2->decoderConfig->streamType) return 0; @@ -3194,6 +3226,8 @@ Bool gf_isom_is_same_sample_description(GF_ISOFile *f1, u32 tk1, GF_ISOFile *f2, } break; } + + if (sdesc_index1 && sdesc_index2) break; } if (!need_memcmp) return 1; ret = 0; @@ -3243,6 +3277,7 @@ u64 gf_isom_estimate_size(GF_ISOFile *movie) i=0; while ((a = (GF_Box*)gf_list_enum(movie->TopBoxes, &i))) { e = gf_isom_box_size(a); + assert (e == GF_OK); mdat_size += a->size; } return mdat_size; @@ -4008,6 +4043,188 @@ GF_Err gf_isom_set_rvc_config(GF_ISOFile *movie, u32 track, u32 sampleDescriptio } +GF_Err gf_isom_add_sample_group_entry(GF_List *sampleGroups, u32 sample_number, u32 grouping_type, u32 sampleGroupDescriptionIndex) +{ + GF_SampleGroupBox *sgroup = NULL; + u32 i, count, last_sample_in_entry; + + count = gf_list_count(sampleGroups); + for (i=0; igrouping_type==grouping_type) break; + sgroup = NULL; + } + if (!sgroup) { + sgroup = (GF_SampleGroupBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_SBGP); + sgroup->grouping_type = grouping_type; + gf_list_add(sampleGroups, sgroup); + } + /*used in fragments, means we are adding the last sample*/ + if (!sample_number) { + sample_number = 1; + if (sgroup->entry_count) { + for (i=0; ientry_count; i++) { + sample_number += sgroup->sample_entries[i].sample_count; + } + } + } + + if (!sgroup->entry_count) { + u32 idx = 0; + sgroup->entry_count = (sample_number>1) ? 2 : 1; + sgroup->sample_entries = gf_malloc(sizeof(GF_SampleGroupEntry) * sgroup->entry_count ); + if (sample_number>1) { + sgroup->sample_entries[0].sample_count = sample_number-1; + sgroup->sample_entries[0].group_description_index = 0; + idx = 1; + } + sgroup->sample_entries[idx].sample_count = 1; + sgroup->sample_entries[idx].group_description_index = sampleGroupDescriptionIndex; + return GF_OK; + } + last_sample_in_entry = 0; + for (i=0; ientry_count; i++) { + /*TODO*/ + if (last_sample_in_entry + sgroup->sample_entries[i].sample_count > sample_number) return GF_NOT_SUPPORTED; + last_sample_in_entry += sgroup->sample_entries[i].sample_count; + } + + if ((sgroup->sample_entries[sgroup->entry_count-1].group_description_index==sampleGroupDescriptionIndex) && (last_sample_in_entry+1==sample_number)) { + sgroup->sample_entries[sgroup->entry_count-1].sample_count++; + return GF_OK; + } + /*last entry was an empty desc (no group associated), just add the number of samples missing until new one, then add new one*/ + if (! sgroup->sample_entries[sgroup->entry_count-1].group_description_index) { + sgroup->sample_entries[sgroup->entry_count-1].sample_count += sample_number - 1 - last_sample_in_entry; + sgroup->sample_entries = gf_realloc(sgroup->sample_entries, sizeof(GF_SampleGroupEntry) * (sgroup->entry_count + 1) ); + sgroup->sample_entries[sgroup->entry_count].sample_count = 1; + sgroup->sample_entries[sgroup->entry_count].group_description_index = sampleGroupDescriptionIndex; + sgroup->entry_count++; + return GF_OK; + } + /*we are adding a sample with no desc, add entry at the end*/ + if (!sampleGroupDescriptionIndex) { + sgroup->sample_entries = gf_realloc(sgroup->sample_entries, sizeof(GF_SampleGroupEntry) * (sgroup->entry_count + 1) ); + sgroup->sample_entries[sgroup->entry_count].sample_count = 1; + sgroup->sample_entries[sgroup->entry_count].group_description_index = 0; + sgroup->entry_count++; + return GF_OK; + } + /*need to insert two entries ...*/ + sgroup->sample_entries = gf_realloc(sgroup->sample_entries, sizeof(GF_SampleGroupEntry) * (sgroup->entry_count + 2) ); + + sgroup->sample_entries[sgroup->entry_count].sample_count = sample_number - 1 - last_sample_in_entry; + sgroup->sample_entries[sgroup->entry_count].group_description_index = 0; + + sgroup->sample_entries[sgroup->entry_count+1].sample_count = 1; + sgroup->sample_entries[sgroup->entry_count+1].group_description_index = sampleGroupDescriptionIndex; + sgroup->entry_count+=2; + return GF_OK; +} + +/*for now not exported*/ +static GF_Err gf_isom_set_sample_group_info(GF_ISOFile *movie, u32 track, u32 sample_number, u32 grouping_type, void *udta, void *(*sg_create_entry)(void *udta), Bool (*sg_compare_entry)(void *udta, void *entry)) +{ + GF_Err e; + GF_TrackBox *trak; + GF_List *groupList; + void *entry; + GF_SampleGroupDescriptionBox *sgdesc = NULL; + u32 i, count, entry_idx; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + trak = gf_isom_get_track_from_file(movie, track); + if (!trak) return GF_BAD_PARAM; + + /*look in stbl for sample sampleGroupsDescription*/ + if (!trak->Media->information->sampleTable->sampleGroupsDescription) + trak->Media->information->sampleTable->sampleGroupsDescription = gf_list_new(); + + groupList = trak->Media->information->sampleTable->sampleGroupsDescription; + count = gf_list_count(groupList); + for (i=0; igrouping_type==grouping_type) break; + sgdesc = NULL; + } + if (!sgdesc) { + sgdesc = (GF_SampleGroupDescriptionBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_SGPD); + sgdesc->grouping_type = grouping_type; + gf_list_add(groupList, sgdesc); + } + entry = NULL; + for (i=0; igroup_descriptions); i++) { + entry = gf_list_get(sgdesc->group_descriptions, i); + if (sg_compare_entry(udta, entry)) break; + entry = NULL; + } + if (!entry) { + entry = sg_create_entry(udta); + if (!entry) return GF_IO_ERR; + gf_list_add(sgdesc->group_descriptions, entry); + } + + entry_idx = 1 + gf_list_find(sgdesc->group_descriptions, entry); + + /*look in stbl for sample sampleGroups*/ + if (!trak->Media->information->sampleTable->sampleGroups) + trak->Media->information->sampleTable->sampleGroups = gf_list_new(); + + groupList = trak->Media->information->sampleTable->sampleGroups; + + return gf_isom_add_sample_group_entry(trak->Media->information->sampleTable->sampleGroups, sample_number, grouping_type, entry_idx); +} + +void *sg_rap_create_entry(void *udta) +{ + GF_VisualRandomAccessEntry *entry; + u32 *num_leading_samples = (u32 *) udta; + assert(udta); + GF_SAFEALLOC(entry, GF_VisualRandomAccessEntry); + entry->num_leading_samples = *num_leading_samples; + entry->num_leading_samples_known = entry->num_leading_samples ? 1 : 0; + return entry; +} + +Bool sg_rap_compare_entry(void *udta, void *entry) +{ + u32 *num_leading_samples = (u32 *) udta; + if (*num_leading_samples == ((GF_VisualRandomAccessEntry *)entry)->num_leading_samples) return 1; + return 0; +} + +GF_Err gf_isom_set_sample_rap_group(GF_ISOFile *movie, u32 track, u32 sample_number, u32 num_leading_samples) +{ + return gf_isom_set_sample_group_info(movie, track, sample_number, GF_4CC( 'r', 'a', 'p', ' ' ), &num_leading_samples, sg_rap_create_entry, sg_rap_compare_entry); +} + + + +void *sg_roll_create_entry(void *udta) +{ + GF_RollRecoveryEntry *entry; + s16 *roll_distance = (s16 *) udta; + GF_SAFEALLOC(entry, GF_RollRecoveryEntry); + entry->roll_distance = *roll_distance; + return entry; +} + +Bool sg_roll_compare_entry(void *udta, void *entry) +{ + s16 *roll_distance = (s16 *) udta; + if (*roll_distance == ((GF_RollRecoveryEntry *)entry)->roll_distance) return 1; + return 0; +} + +GF_Err gf_isom_set_sample_roll_group(GF_ISOFile *movie, u32 track, u32 sample_number, s16 roll_distance) +{ + return gf_isom_set_sample_group_info(movie, track, sample_number, GF_4CC( 'r', 'o', 'l', 'l' ), &roll_distance, sg_roll_create_entry, sg_roll_compare_entry); +} + + + #endif /*!defined(GPAC_DISABLE_ISOM) && !defined(GPAC_DISABLE_ISOM_WRITE)*/ diff --git a/src/isomedia/media.c b/src/isomedia/media.c index 87977f0..e8ab639 100644 --- a/src/isomedia/media.c +++ b/src/isomedia/media.c @@ -457,12 +457,21 @@ GF_Err Media_FindSyncSample(GF_SampleTableBox *stbl, u32 searchFromSample, u32 * (*sampleNumber) = searchFromSample; return GF_OK; } + + /*check sample groups - prev & next are overwritten if RAP group is found, but are not re-initialized otherwise*/ + stbl_SearchSAPs(stbl, searchFromSample, &isRAP, &prev, &next); + if (isRAP) { + (*sampleNumber) = searchFromSample; + return GF_OK; + } + //nothing yet, go for next time... if (mode == GF_ISOM_SEARCH_SYNC_FORWARD) { if (next) *sampleNumber = next; } else { if (prev) *sampleNumber = prev; } + return GF_OK; } diff --git a/src/isomedia/meta.c b/src/isomedia/meta.c index 89ab536..6fb0a9d 100644 --- a/src/isomedia/meta.c +++ b/src/isomedia/meta.c @@ -83,9 +83,9 @@ GF_Err gf_isom_extract_meta_xml(GF_ISOFile *file, Bool root_meta, u32 track_num, } if (!xml || !xml->xml || !xml->xml_length) return GF_BAD_PARAM; - didfile = gf_f64_open(outName, "wt"); + didfile = gf_f64_open(outName, "wb"); if (!didfile) return GF_IO_ERR; - fwrite(xml->xml, xml->xml_length, 1, didfile); + gf_fwrite(xml->xml, xml->xml_length, 1, didfile); fclose(didfile); if (is_binary) *is_binary = (xml->type==GF_ISOM_BOX_TYPE_BXML) ? 1 : 0; diff --git a/src/isomedia/movie_fragments.c b/src/isomedia/movie_fragments.c index 096bb86..38f87cd 100644 --- a/src/isomedia/movie_fragments.c +++ b/src/isomedia/movie_fragments.c @@ -129,6 +129,39 @@ GF_Err gf_isom_finalize_for_fragment(GF_ISOFile *movie, Bool use_segments) return GF_OK; } +GF_Err gf_isom_change_track_fragment_defaults(GF_ISOFile *movie, u32 TrackID, + u32 DefaultSampleDescriptionIndex, + u32 DefaultSampleDuration, + u32 DefaultSampleSize, + u8 DefaultSampleIsSync, + u8 DefaultSamplePadding, + u16 DefaultDegradationPriority) +{ + GF_MovieExtendsBox *mvex; + GF_TrackExtendsBox *trex; + GF_TrackBox *trak; + + if (!movie || !movie->moov) return GF_BAD_PARAM; + //this is only allowed in write mode + if (movie->openMode != GF_ISOM_OPEN_WRITE) return GF_ISOM_INVALID_MODE; + + trak = gf_isom_get_track_from_id(movie->moov, TrackID); + if (!trak) return GF_BAD_PARAM; + + mvex = movie->moov->mvex; + if (!mvex) return GF_BAD_PARAM; + + trex = GetTrex(movie->moov, TrackID); + if (!trex) return GF_BAD_PARAM; + + trex->def_sample_desc_index = DefaultSampleDescriptionIndex; + trex->def_sample_duration = DefaultSampleDuration; + trex->def_sample_size = DefaultSampleSize; + trex->def_sample_flags = GF_ISOM_FORMAT_FRAG_FLAGS(DefaultSamplePadding, DefaultSampleIsSync, DefaultDegradationPriority); + + return GF_OK; +} + GF_Err gf_isom_setup_track_fragment(GF_ISOFile *movie, u32 TrackID, u32 DefaultSampleDescriptionIndex, @@ -167,12 +200,7 @@ GF_Err gf_isom_setup_track_fragment(GF_ISOFile *movie, u32 TrackID, mvex_AddBox((GF_Box*)mvex, (GF_Box *) trex); } trex->track = trak; - trex->def_sample_desc_index = DefaultSampleDescriptionIndex; - trex->def_sample_duration = DefaultSampleDuration; - trex->def_sample_size = DefaultSampleSize; - trex->def_sample_flags = GF_ISOM_FORMAT_FRAG_FLAGS(DefaultSamplePadding, DefaultSampleIsSync, DefaultDegradationPriority); - - return GF_OK; + return gf_isom_change_track_fragment_defaults(movie, TrackID, DefaultSampleDescriptionIndex, DefaultSampleDuration, DefaultSampleSize, DefaultSampleIsSync, DefaultSamplePadding, DefaultDegradationPriority); } @@ -478,39 +506,98 @@ u32 UpdateRuns(GF_ISOFile *movie, GF_TrackFragmentBox *traf) return sampleCount; } -Bool moof_get_rap_time_offset(GF_MovieFragmentBox *moof, u32 refTrackID, u32 *rap_delta) +static u32 moof_get_sap_info(GF_MovieFragmentBox *moof, u32 refTrackID, u32 *sap_delta, Bool *starts_with_sap) { - u32 i, j, delta; + u32 i, j, count, delta, earliest_cts, sap_type, sap_sample_num, cur_sample; + Bool first = 1; GF_TrunEntry *ent; - GF_TrackFragmentBox *traf; + GF_TrackFragmentBox *traf=NULL; GF_TrackFragmentRunBox *trun; - *rap_delta = 0; + *sap_delta = 0; + *starts_with_sap = 0; for (i=0; iTrackList); i++) { traf = gf_list_get(moof->TrackList, i); if (traf->tfhd->trackID==refTrackID) break; traf=NULL; } if (!traf) return 0; + earliest_cts = 0; + + + /*first check if we have a roll/rap sample in this traf, and mark its sample count*/ + sap_type = 0; + sap_sample_num = 0; + /*check RAP and ROLL*/ + count = traf->sampleGroups ? gf_list_count(traf->sampleGroups) : 0; + for (i=0; isampleGroups, i); + + + switch (sg->grouping_type) { + case GF_4CC('r','a','p',' '): + rap_type = 1; + break; + case GF_4CC('r','o','l','l'): + break; + default: + continue; + } + /*first entry is SAP*/ + first_sample = 1; + for (j=0; jentry_count; j++) { + if (! sg->sample_entries[j].group_description_index) { + first_sample += sg->sample_entries[j].sample_count; + continue; + } + if (!j) { + *starts_with_sap = 1; + sap_sample_num = 0; + } + if (!sap_sample_num || (sap_sample_num>first_sample)) { + sap_type = rap_type ? 3 : 4; + sap_sample_num = first_sample; + } + break; + } + } + /*then browse all samples, looking for SYNC flag or sap_sample_num*/ + cur_sample = 1; delta = 0; i=0; while ((trun = gf_list_enum(traf->TrackRuns, &i))) { if (trun->flags & GF_ISOM_TRUN_FIRST_FLAG) { if (GF_ISOM_GET_FRAG_SYNC(trun->flags)) { ent = gf_list_get(trun->entries, 0); - *rap_delta = delta + ent->CTS_Offset; + if (!delta) earliest_cts = ent->CTS_Offset; + *sap_delta = delta + ent->CTS_Offset - ent->CTS_Offset; + *starts_with_sap = first; return 1; } } j=0; while ((ent = gf_list_enum(trun->entries, &j))) { + if (!delta) earliest_cts = ent->CTS_Offset; + if (GF_ISOM_GET_FRAG_SYNC(ent->flags)) { - *rap_delta = delta + ent->CTS_Offset; + *sap_delta = delta + ent->CTS_Offset - earliest_cts; + *starts_with_sap = first; return 1; } + /*we found our roll or rap sample*/ + if (cur_sample==sap_sample_num) { + *sap_delta = delta + ent->CTS_Offset - earliest_cts; + return sap_type; + } delta += ent->Duration; + first = 0; + cur_sample++; } } + /*not found*/ return 0; } @@ -518,7 +605,7 @@ u32 moof_get_duration(GF_MovieFragmentBox *moof, u32 refTrackID) { u32 i, j, duration; GF_TrunEntry *ent; - GF_TrackFragmentBox *traf; + GF_TrackFragmentBox *traf = NULL; GF_TrackFragmentRunBox *trun; for (i=0; iTrackList); i++) { traf = gf_list_get(moof->TrackList, i); @@ -542,7 +629,7 @@ u32 moof_get_earliest_cts(GF_MovieFragmentBox *moof, u32 refTrackID) { u32 i, j, cts, duration; GF_TrunEntry *ent; - GF_TrackFragmentBox *traf; + GF_TrackFragmentBox *traf=NULL; GF_TrackFragmentRunBox *trun; for (i=0; iTrackList); i++) { traf = gf_list_get(moof->TrackList, i); @@ -601,17 +688,23 @@ GF_Err StoreFragment(GF_ISOFile *movie, Bool load_mdat_only, s32 data_offset_dif } if (load_mdat_only) { + u64 offset; u64 pos = gf_bs_get_position(bs); //we assume we never write large MDATs in fragment mode which should always be true movie->moof->mdat_size = (u32) (pos - movie->moof->fragment_offset); movie->moof->mdat = gf_malloc(sizeof(char) * movie->moof->mdat_size); if (!movie->moof->mdat) return GF_OUT_OF_MEM; - gf_bs_seek(bs, movie->segment_start); + + offset = movie->segment_start; + + gf_bs_seek(bs, offset); /*write mdat size*/ gf_bs_write_u32(bs, (u32) movie->moof->mdat_size); - gf_bs_seek(bs, movie->segment_start); + + gf_bs_seek(bs, offset); gf_bs_read_data(bs, movie->moof->mdat, movie->moof->mdat_size); - gf_bs_seek(bs, movie->segment_start); + + gf_bs_seek(bs, offset); gf_bs_truncate(bs); return GF_OK; } @@ -727,20 +820,78 @@ static GF_Err sidx_rewrite(GF_SegmentIndexBox *sidx, GF_BitStream *bs, u64 start return e; } +GF_Err gf_isom_allocate_sidx(GF_ISOFile *movie, s32 subsegs_per_sidx, Bool daisy_chain_sidx, u32 nb_segs, u32 *frags_per_segment, u32 *start_range, u32 *end_range) +{ + GF_BitStream *bs; + GF_Err e; + + //and only at setup + if (!movie || !(movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) ) return GF_BAD_PARAM; + if (movie->openMode != GF_ISOM_OPEN_WRITE) return GF_ISOM_INVALID_MODE; + if (movie->root_sidx) return GF_BAD_PARAM; + if (movie->moof) return GF_BAD_PARAM; + if (gf_list_count(movie->moof_list)) return GF_BAD_PARAM; + + movie->root_sidx = (GF_SegmentIndexBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_SIDX); + /*we don't write anything between sidx and following moov*/ + movie->root_sidx->first_offset = 0; + + /*for now we only store one ref per subsegment*/ + movie->root_sidx->nb_refs = nb_segs; + + movie->root_sidx->refs = gf_malloc(sizeof(GF_SIDXReference) * movie->root_sidx->nb_refs); + memset(movie->root_sidx->refs, 0, sizeof(GF_SIDXReference) * movie->root_sidx->nb_refs); + + movie->root_sidx_index = 0; + + /*remember start of sidx*/ + movie->root_sidx_offset = gf_bs_get_position(movie->editFileMap->bs); + + bs = movie->editFileMap->bs; + + e = gf_isom_box_size((GF_Box *) movie->root_sidx); + if (e) return e; + e = gf_isom_box_write((GF_Box *) movie->root_sidx, bs); + if (e) return e; + + if (start_range) *start_range = (u32) movie->root_sidx_offset; + if (end_range) *end_range = (u32) gf_bs_get_position(bs); + + return GF_OK; +} + +typedef struct +{ + GF_SegmentIndexBox *sidx; + u64 start_offset, end_offset; +} SIDXEntry; -GF_Err gf_isom_close_segment(GF_ISOFile *movie, s32 frags_per_sidx, u32 referenceTrackID, u64 ref_track_decode_time, Bool daisy_chain_sidx, Bool last_segment) +GF_Err gf_isom_close_segment(GF_ISOFile *movie, s32 subsegments_per_sidx, u32 referenceTrackID, u64 ref_track_decode_time, u64 ref_track_next_cts, Bool daisy_chain_sidx, Bool last_segment, u64 *index_start_range, u64 *index_end_range) { GF_SegmentIndexBox *sidx=NULL; - GF_SegmentIndexBox *prev_sidx=NULL; GF_SegmentIndexBox *root_sidx=NULL; + GF_List *daisy_sidx = NULL; u64 sidx_start, sidx_end; - Bool first_sidx = 0; + Bool first_frag_in_subseg; Bool no_sidx = 0; - u32 count, nb_subsegs=0, idx, cur_dur, sidx_dur, sidx_idx; - u64 last_top_box_pos, root_prev_offset, local_sidx_start, local_sidx_end; + u32 count, idx, cur_dur, sidx_dur, sidx_idx, idx_offset, frag_count; + u64 last_top_box_pos, root_prev_offset, local_sidx_start, local_sidx_end, prev_earliest_cts, prev_earliest_dts; GF_TrackBox *trak = NULL; GF_Err e; + /*number of subsegment in this segment (eg nb references in the first SIDX found)*/ + u32 nb_subsegs=0; + /*number of subsegment per sidx (eg number of references of any sub-SIDX*/ + u32 subseg_per_sidx; + /*number of fragments per subsegment*/ + u32 frags_per_subseg; + /*number of fragments per subsidx*/ + u32 frags_per_subsidx; + sidx_start = sidx_end = 0; + + if (index_start_range) *index_start_range = 0; + if (index_end_range) *index_end_range = 0; + //and only at setup if (!movie || !(movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) ) return GF_BAD_PARAM; if (movie->openMode != GF_ISOM_OPEN_WRITE) return GF_ISOM_INVALID_MODE; @@ -758,8 +909,20 @@ GF_Err gf_isom_close_segment(GF_ISOFile *movie, s32 frags_per_sidx, u32 referenc gf_bs_seek(movie->editFileMap->bs, movie->segment_start); gf_bs_truncate(movie->editFileMap->bs); + idx_offset = 0; + if (referenceTrackID) { trak = gf_isom_get_track_from_id(movie->moov, referenceTrackID); + } + + if (subsegments_per_sidx < 0) { + referenceTrackID = 0; + subsegments_per_sidx = 0; + no_sidx = 1; + } + + /*write STYP if we write to a different file or if we write the last segment*/ + if ((!movie->append_segment && !movie->segment_start) || last_segment) { /*modify brands STYP*/ @@ -769,108 +932,169 @@ GF_Err gf_isom_close_segment(GF_ISOFile *movie, s32 frags_per_sidx, u32 referenc /*"lmsg" brand: this is the last DASH Segment*/ gf_isom_modify_alternate_brand(movie, GF_4CC('l','m','s','g'), 1); } - } - /*write STYP*/ - movie->brand->type = GF_ISOM_BOX_TYPE_STYP; - e = gf_isom_box_size((GF_Box *) movie->brand); - if (e) return e; - e = gf_isom_box_write((GF_Box *) movie->brand, movie->editFileMap->bs); - if (e) return e; - - if (frags_per_sidx < 0) { - referenceTrackID = 0; - frags_per_sidx = 0; - no_sidx = 1; + + movie->brand->type = GF_ISOM_BOX_TYPE_STYP; + e = gf_isom_box_size((GF_Box *) movie->brand); + if (e) return e; + e = gf_isom_box_write((GF_Box *) movie->brand, movie->editFileMap->bs); + if (e) return e; } + frags_per_subseg = 0; + subseg_per_sidx = 0; + frags_per_subsidx = 0; + + prev_earliest_cts = 0; + prev_earliest_dts = ref_track_decode_time; + + if (daisy_chain_sidx) + daisy_sidx = gf_list_new(); + /*prepare SIDX: we write a blank SIDX box with the right number of entries, and will rewrite it later on*/ if (referenceTrackID) { Bool is_root_sidx=0; - sidx = (GF_SegmentIndexBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_SIDX); + + prev_earliest_cts = ref_track_decode_time + moof_get_earliest_cts(gf_list_get(movie->moof_list, 0), referenceTrackID); + + if (movie->root_sidx) { + sidx = movie->root_sidx; + } else { + sidx = (GF_SegmentIndexBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_SIDX); + } sidx->reference_ID = referenceTrackID; sidx->timescale = trak->Media->mediaHeader->timeScale; /*we don't write anything between sidx and following moov*/ sidx->first_offset = 0; - sidx->earliest_presentation_time = ref_track_decode_time; - /*we consider that each fragment is a subsegment - this could be controled by another parameter*/ - if (!frags_per_sidx) { + /*we allocated our sidx to have one ref per "segment" (eg per call to close_segment)*/ + if (movie->root_sidx) { + if (!movie->root_sidx_index) { + sidx->earliest_presentation_time = prev_earliest_cts; + } nb_subsegs = 1; - frags_per_sidx = count; + frags_per_subseg = count; + frags_per_subsidx = count; + subseg_per_sidx = 1; + + idx_offset = movie->root_sidx_index; + sidx_end = gf_bs_get_position(movie->editFileMap->bs); } else { - nb_subsegs = count/frags_per_sidx; - if (nb_subsegs*frags_per_sidxearliest_presentation_time = prev_earliest_cts; + + /*if more subsegments requested than fragments available, make a single sidx*/ + if ((s32) count <= subsegments_per_sidx) + subsegments_per_sidx = 0; + + if (daisy_chain_sidx && (subsegments_per_sidx<2)) + subsegments_per_sidx = 2; + + /*single SIDX, each fragment is a subsegment and we reference all subsegments*/ + if (!subsegments_per_sidx) { + nb_subsegs = count; + /*we consider that each fragment is a subsegment - this could be controled by another parameter*/ + frags_per_subseg = 1; + frags_per_subsidx = count; + subseg_per_sidx = count; + + sidx->nb_refs = nb_subsegs; + daisy_chain_sidx = 0; + } + /*daisy-chain SIDX: each SIDX describes a subsegment made of frags_per_subseg fragments plus next */ + else if (daisy_chain_sidx) { + frags_per_subsidx = count/subsegments_per_sidx; + if (frags_per_subsidx * subsegments_per_sidx < count) frags_per_subsidx++; + + nb_subsegs = subsegments_per_sidx; - /*single SIDX, reference all fragments*/ - if (nb_subsegs==1) { - sidx->nb_refs = frags_per_sidx; - daisy_chain_sidx = 0; - } - /*daisy-chain SIDX, reference all fragments plus next */ - else if (daisy_chain_sidx) { - sidx->nb_refs = frags_per_sidx + 1; - /*we will have to adjust earliest cpresentation time*/ - first_sidx = 1; - } - /*root SIDX referencing all subsegments*/ - else { - sidx->nb_refs = nb_subsegs; - is_root_sidx = 1; - } - sidx->refs = gf_malloc(sizeof(GF_SIDXReference)*sidx->nb_refs); - memset(sidx->refs, 0, sizeof(GF_SIDXReference)*sidx->nb_refs); + /*we consider that each fragment is a subsegment - this could be controled by another parameter*/ + frags_per_subseg = 1; + subseg_per_sidx = frags_per_subsidx / frags_per_subseg; + if (subseg_per_sidx * frags_per_subseg < frags_per_subsidx) subseg_per_sidx++; - /*remember start of sidx*/ - sidx_start = gf_bs_get_position(movie->editFileMap->bs); + sidx->nb_refs = subseg_per_sidx + 1; + } + /*hierarchical SIDX*/ + else { + frags_per_subsidx = count/subsegments_per_sidx; + if (frags_per_subsidx * subsegments_per_sidx < count) frags_per_subsidx++; + + nb_subsegs = subsegments_per_sidx; - e = gf_isom_box_size((GF_Box *) sidx); - if (e) return e; - e = gf_isom_box_write((GF_Box *) sidx, movie->editFileMap->bs); - if (e) return e; + /*we consider that each fragment is a subsegment - this could be controled by another parameter*/ + frags_per_subseg = 1; + subseg_per_sidx = frags_per_subsidx / frags_per_subseg; + if (subseg_per_sidx * frags_per_subseg < frags_per_subsidx) subseg_per_sidx++; - sidx_end = gf_bs_get_position(movie->editFileMap->bs); - - count = idx = 0; + sidx->nb_refs = nb_subsegs; + is_root_sidx = 1; + } + + sidx->refs = gf_malloc(sizeof(GF_SIDXReference)*sidx->nb_refs); + memset(sidx->refs, 0, sizeof(GF_SIDXReference)*sidx->nb_refs); + + /*remember start of sidx*/ + sidx_start = gf_bs_get_position(movie->editFileMap->bs); + + e = gf_isom_box_size((GF_Box *) sidx); + if (e) return e; + e = gf_isom_box_write((GF_Box *) sidx, movie->editFileMap->bs); + if (e) return e; + + sidx_end = gf_bs_get_position(movie->editFileMap->bs); + + if (daisy_sidx) { + SIDXEntry *entry; + GF_SAFEALLOC(entry, SIDXEntry); + entry->sidx = sidx; + entry->start_offset = sidx_start; + gf_list_add(daisy_sidx, entry); + } + } if (is_root_sidx) { root_sidx = sidx; sidx = NULL; } + count = idx = 0; } + last_top_box_pos = root_prev_offset = sidx_end; sidx_idx = 0; sidx_dur = 0; - last_top_box_pos = root_prev_offset = sidx_end; local_sidx_start = local_sidx_end = 0; /*cumulated segments duration since start of the sidx */ + frag_count = frags_per_subsidx; cur_dur = 0; + first_frag_in_subseg = 1; e = GF_OK; while (gf_list_count(movie->moof_list)) { s32 offset_diff; u32 moof_size; - u32 dur; movie->moof = gf_list_get(movie->moof_list, 0); gf_list_rem(movie->moof_list, 0); - if (!root_sidx && sidx && first_sidx) { - first_sidx = 0; - sidx->earliest_presentation_time = ref_track_decode_time + moof_get_earliest_cts(movie->moof, referenceTrackID); - } - /*hierarchical or daisy-chain SIDXs*/ if (!no_sidx && !sidx && (root_sidx || daisy_chain_sidx) ) { + u32 subsegments_remaining; sidx = (GF_SegmentIndexBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_SIDX); sidx->reference_ID = referenceTrackID; sidx->timescale = trak->Media->mediaHeader->timeScale; sidx->earliest_presentation_time = ref_track_decode_time + sidx_dur + moof_get_earliest_cts(movie->moof, referenceTrackID); + frag_count = frags_per_subsidx; + + /*last segment, add only one ref*/ + subsegments_remaining = 1 + gf_list_count(movie->moof_list); + if (subseg_per_sidx*frags_per_subseg > subsegments_remaining) { + subseg_per_sidx = subsegments_remaining / frags_per_subseg; + if (subseg_per_sidx * frags_per_subseg < subsegments_remaining) subseg_per_sidx++; + } /*we don't write anything between sidx and following moov*/ sidx->first_offset = 0; - sidx->nb_refs = frags_per_sidx; + sidx->nb_refs = subseg_per_sidx; if (daisy_chain_sidx && (nb_subsegs>1)) { sidx->nb_refs += 1; } @@ -893,6 +1117,14 @@ GF_Err gf_isom_close_segment(GF_ISOFile *movie, s32 frags_per_sidx, u32 referenc /*adjust prev offset*/ last_top_box_pos = local_sidx_end; + + if (daisy_sidx) { + SIDXEntry *entry; + GF_SAFEALLOC(entry, SIDXEntry); + entry->sidx = sidx; + entry->start_offset = local_sidx_start; + gf_list_add(daisy_sidx, entry); + } } offset_diff = (s32) (gf_bs_get_position(movie->editFileMap->bs) - movie->moof->fragment_offset); @@ -901,90 +1133,160 @@ GF_Err gf_isom_close_segment(GF_ISOFile *movie, s32 frags_per_sidx, u32 referenc if (!e) { e = StoreFragment(movie, 0, offset_diff, &moof_size); + if (sidx) { - /*we refer to next moof*/ - sidx->refs[idx].reference_type = 0; - sidx->refs[idx].contains_RAP = moof_get_rap_time_offset(movie->moof, referenceTrackID, & sidx->refs[idx].RAP_delta_time); - if (sidx->refs[idx].contains_RAP) { - sidx->refs[idx].RAP_delta_time += cur_dur; - - if (root_sidx && !root_sidx->refs[sidx_idx].contains_RAP) { - root_sidx->refs[sidx_idx].contains_RAP = 1; - root_sidx->refs[sidx_idx].RAP_delta_time = sidx->refs[idx].RAP_delta_time + sidx_dur; - } - else if (prev_sidx && !prev_sidx->refs[prev_sidx->nb_refs - 1].contains_RAP) { - prev_sidx->refs[prev_sidx->nb_refs - 1].contains_RAP = 1; - prev_sidx->refs[prev_sidx->nb_refs - 1].RAP_delta_time = sidx->refs[idx].RAP_delta_time; + u32 cur_index = idx_offset + idx; + + /*do not compute earliest CTS if single segment sidx since we only have set the info for one subsegment*/ + if (!movie->root_sidx && first_frag_in_subseg) { + u64 first_cts = ref_track_decode_time + sidx_dur + cur_dur + moof_get_earliest_cts(movie->moof, referenceTrackID); + u32 subseg_dur = (u32) (first_cts - prev_earliest_cts); + if (cur_index) { + sidx->refs[cur_index-1].subsegment_duration = subseg_dur; + if (root_sidx) root_sidx->refs[sidx_idx].subsegment_duration += subseg_dur; } + prev_earliest_cts = first_cts; + first_frag_in_subseg = 0; + } + + /*we refer to next moof*/ + sidx->refs[cur_index].reference_type = 0; + if (!sidx->refs[cur_index].SAP_type) { + sidx->refs[cur_index].SAP_type = moof_get_sap_info(movie->moof, referenceTrackID, & sidx->refs[cur_index].SAP_delta_time, & sidx->refs[cur_index].starts_with_SAP); + if (sidx->refs[cur_index].SAP_type) { + if (root_sidx && !root_sidx->refs[sidx_idx].SAP_type) { + root_sidx->refs[sidx_idx].SAP_type = sidx->refs[cur_index].SAP_type; + root_sidx->refs[sidx_idx].SAP_delta_time = sidx->refs[cur_index].SAP_delta_time; + root_sidx->refs[sidx_idx].starts_with_SAP = sidx->refs[cur_index].starts_with_SAP; + } + } } - - dur = moof_get_duration(movie->moof, referenceTrackID); - sidx->refs[idx].subsegment_duration += dur; - cur_dur += dur; + cur_dur += moof_get_duration(movie->moof, referenceTrackID); + /*reference size is end of the moof we just wrote minus last_box_pos*/ - sidx->refs[idx].reference_size = (u32) ( gf_bs_get_position(movie->editFileMap->bs) - last_top_box_pos) ; + sidx->refs[cur_index].reference_size += (u32) ( gf_bs_get_position(movie->editFileMap->bs) - last_top_box_pos) ; last_top_box_pos = gf_bs_get_position(movie->editFileMap->bs); - idx++; count++; + + /*we are switching subsegment*/ + frag_count--; + + if (count==frags_per_subseg) { + count = 0; + first_frag_in_subseg = 1; + idx++; + } + /*switching to next SIDX*/ - if (count==frags_per_sidx) { + if ((idx==subseg_per_sidx) || !frag_count) { + u32 subseg_dur; + u64 next_cts; + /*update last ref duration*/ + if (gf_list_count(movie->moof_list)) { + next_cts = ref_track_decode_time + sidx_dur + cur_dur + moof_get_earliest_cts(gf_list_get(movie->moof_list, 0), referenceTrackID); + } else { + next_cts = ref_track_next_cts; + } + subseg_dur = (u32) (next_cts - prev_earliest_cts); + if (movie->root_sidx) { + sidx->refs[idx_offset].subsegment_duration = subseg_dur; + } + /*if daisy chain and not the last sidx, we have an extra entry at the end*/ + else if (daisy_chain_sidx && (nb_subsegs>1)) { + sidx->refs[sidx->nb_refs - 2].subsegment_duration = subseg_dur; + } else { + sidx->refs[sidx->nb_refs-1].subsegment_duration = subseg_dur; + } + if (root_sidx) root_sidx->refs[sidx_idx].subsegment_duration += subseg_dur; if (root_sidx) { root_sidx->refs[sidx_idx].reference_size = (u32) (gf_bs_get_position(movie->editFileMap->bs) - local_sidx_start); - root_sidx->refs[sidx_idx].subsegment_duration = cur_dur; if (!sidx_idx) { root_sidx->earliest_presentation_time = sidx->earliest_presentation_time; } - sidx_rewrite(sidx, movie->editFileMap->bs, local_sidx_start); gf_isom_box_del((GF_Box*)sidx); sidx = NULL; } else if (daisy_chain_sidx) { - if (prev_sidx) { - if (prev_sidx->refs[prev_sidx->nb_refs - 1].contains_RAP) { - prev_sidx->refs[prev_sidx->nb_refs - 1].contains_RAP = 1; - prev_sidx->refs[prev_sidx->nb_refs - 1].RAP_delta_time += sidx_dur; - } - prev_sidx->refs[prev_sidx->nb_refs - 1].subsegment_duration = sidx_dur; - prev_sidx->refs[prev_sidx->nb_refs - 1].reference_size = (u32) (gf_bs_get_position(movie->editFileMap->bs) - local_sidx_start); - prev_sidx->refs[prev_sidx->nb_refs - 1].reference_type = 1; - sidx_rewrite(prev_sidx, movie->editFileMap->bs, sidx_start); - gf_isom_box_del((GF_Box*)prev_sidx); - - sidx_start = local_sidx_start; - sidx_end = local_sidx_end; - } + SIDXEntry *entry = gf_list_last(daisy_sidx); + entry->end_offset = gf_bs_get_position(movie->editFileMap->bs); nb_subsegs--; - prev_sidx = sidx; sidx = NULL; } sidx_dur += cur_dur; cur_dur = 0; count = 0; idx=0; + if (movie->root_sidx) + movie->root_sidx_index++; sidx_idx++; } } } - gf_isom_box_del((GF_Box *) movie->moof); + gf_isom_box_del((GF_Box *) movie->moof); movie->moof = NULL; } + if (movie->root_sidx) { + if (last_segment) { + assert(movie->root_sidx_index == movie->root_sidx->nb_refs); + + sidx_rewrite(movie->root_sidx, movie->editFileMap->bs, movie->root_sidx_offset); + gf_isom_box_del((GF_Box*) movie->root_sidx); + movie->root_sidx = NULL; + } + return GF_OK; + } + if (sidx) { + assert(!root_sidx); sidx_rewrite(sidx, movie->editFileMap->bs, sidx_start); gf_isom_box_del((GF_Box*)sidx); } - if (prev_sidx) { - sidx_rewrite(prev_sidx, movie->editFileMap->bs, sidx_start); - gf_isom_box_del((GF_Box*)prev_sidx); + if (daisy_sidx) { + u32 i, j; + u64 last_entry_end_offset = 0; + u32 count = gf_list_count(daisy_sidx); + for (i=count; i>1; i--) { + SIDXEntry *entry = gf_list_get(daisy_sidx, i-2); + SIDXEntry *next_entry = gf_list_get(daisy_sidx, i-1); + + if (!last_entry_end_offset) { + last_entry_end_offset = next_entry->end_offset; + /*rewrite last sidx*/ + sidx_rewrite(next_entry->sidx, movie->editFileMap->bs, next_entry->start_offset); + } + /*copy over SAP info for last item (which points to next item !)*/ + entry->sidx->refs[entry->sidx->nb_refs-1] = next_entry->sidx->refs[0]; + /*and rewrite reference type, size and dur*/ + entry->sidx->refs[entry->sidx->nb_refs-1].reference_type = 1; + entry->sidx->refs[entry->sidx->nb_refs-1].reference_size = (u32) (last_entry_end_offset - next_entry->start_offset); + entry->sidx->refs[entry->sidx->nb_refs-1].subsegment_duration = 0; + for (j=0; jsidx->nb_refs; j++) { + entry->sidx->refs[entry->sidx->nb_refs-1].subsegment_duration += next_entry->sidx->refs[j].subsegment_duration; + } + sidx_rewrite(entry->sidx, movie->editFileMap->bs, entry->start_offset); + } + while (gf_list_count(daisy_sidx)) { + SIDXEntry *entry = gf_list_last(daisy_sidx); + gf_isom_box_del((GF_Box*)entry->sidx); + gf_free(entry); + gf_list_rem_last(daisy_sidx); + } + gf_list_del(daisy_sidx); } if (root_sidx) { sidx_rewrite(root_sidx, movie->editFileMap->bs, sidx_start); gf_isom_box_del((GF_Box*)root_sidx); } + if ((root_sidx || sidx) && !daisy_chain_sidx) { + if (index_start_range) *index_start_range = sidx_start; + if (index_end_range) *index_end_range = sidx_end; + } + if (movie->append_segment) { char bloc[1024]; u32 seg_size = (u32) gf_bs_get_size(movie->editFileMap->bs); @@ -1004,7 +1306,7 @@ GF_Err gf_isom_close_segment(GF_ISOFile *movie, s32 frags_per_sidx, u32 referenc GF_Err gf_isom_close_fragments(GF_ISOFile *movie) { if (movie->use_segments) { - return gf_isom_close_segment(movie, 0, 0, 0, 0, 1); + return gf_isom_close_segment(movie, 0, 0, 0, 0, 0, 1, NULL, NULL); } else { return StoreFragment(movie, 0, 0, NULL); } @@ -1026,13 +1328,14 @@ GF_Err gf_isom_start_segment(GF_ISOFile *movie, char *SegName) gf_isom_datamap_del(movie->editFileMap); e = gf_isom_datamap_new(SegName, NULL, GF_ISOM_DATA_MAP_WRITE, & movie->editFileMap); movie->segment_start = 0; - return e; - } - assert(gf_list_count(movie->moof_list) == 0); - movie->segment_start = gf_bs_get_position(movie->editFileMap->bs); - /*if movieFileMap is not null, we are concatenating segments to the original move, force a copy*/ - if (movie->movieFileMap) - movie->append_segment = 1; + if (e) return e; + } else { + assert(gf_list_count(movie->moof_list) == 0); + movie->segment_start = gf_bs_get_position(movie->editFileMap->bs); + /*if movieFileMap is not null, we are concatenating segments to the original move, force a copy*/ + if (movie->movieFileMap) + movie->append_segment = 1; + } return GF_OK; } @@ -1062,20 +1365,6 @@ GF_Err gf_isom_start_fragment(GF_ISOFile *movie, Bool moof_first) if (e) return e; } - /* Inserting free data for testing HTTP streaming */ -#if 0 - if (0) { - GF_FreeSpaceBox *fb = (GF_FreeSpaceBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_FREE); - fb->dataSize = free_data_insert_size; - //gf_list_add(movie->TopBoxes, fb); - e = gf_isom_box_size((GF_Box *) fb); - if (e) return e; - e = gf_isom_box_write((GF_Box *) fb, movie->editFileMap->bs); - if (e) return e; - gf_isom_box_del((GF_Box *)fb); - } -#endif - //create new fragment movie->moof = (GF_MovieFragmentBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_MOOF); movie->moof->mfhd = (GF_MovieFragmentHeaderBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_MFHD); @@ -1310,34 +1599,66 @@ GF_Err gf_isom_fragment_copy_subsample(GF_ISOFile *dest, u32 TrackID, GF_ISOFile u32 i, count, last_sample; GF_SampleEntry *sub_sample; GF_Err e; + GF_TrackBox *trak; GF_TrackFragmentBox *traf; if (!dest->moof || !(dest->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) ) return GF_BAD_PARAM; - if (! gf_isom_sample_get_subsample_entry(orig, track, sampleNumber, &sub_sample)) return GF_OK; - traf = GetTraf(dest, TrackID); if (!traf || !traf->tfhd->sample_desc_index) return GF_BAD_PARAM; - /*compute last sample number in traf*/ - last_sample = 0; - count = gf_list_count(traf->TrackRuns); - for (i=0; iTrackRuns, i); - last_sample += trun->sample_count; - } + trak = gf_isom_get_track_from_file(orig, track); + if (!trak) return GF_BAD_PARAM; - /*create subsample if needed*/ - if (!traf->subs) { - traf->subs = (GF_SubSampleInformationBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_SUBS); - traf->subs->version = 0; + /*copy subsample info if any*/ + if ( gf_isom_sample_get_subsample_entry(orig, track, sampleNumber, &sub_sample)) { + if (!traf || !traf->tfhd->sample_desc_index) return GF_BAD_PARAM; + + /*compute last sample number in traf*/ + last_sample = 0; + count = gf_list_count(traf->TrackRuns); + for (i=0; iTrackRuns, i); + last_sample += trun->sample_count; + } + + /*create subsample if needed*/ + if (!traf->subs) { + traf->subs = (GF_SubSampleInformationBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_SUBS); + traf->subs->version = 0; + } + + + count = gf_list_count(sub_sample->SubSamples); + for (i=0; iSubSamples, i); + e = gf_isom_add_subsample_info(traf->subs, last_sample, entry->subsample_size, entry->subsample_priority, entry->reserved, entry->discardable); + if (e) return e; + } } - + /*copy sampleToGroup info if any*/ + if (trak->Media->information->sampleTable->sampleGroups) { + count = gf_list_count(trak->Media->information->sampleTable->sampleGroups); + for (i=0; iMedia->information->sampleTable->sampleGroups, i); + for (j=0; jentry_count; j++) { + last_sample_in_entry = first_sample_in_entry + sg->sample_entries[j].sample_count - 1; + if ((sampleNumberlast_sample_in_entry)) { + first_sample_in_entry = last_sample_in_entry+1; + continue; + } + /*found our sample, add it to trak->sampleGroups*/ + if (!traf->sampleGroups) + traf->sampleGroups = gf_list_new(); - count = gf_list_count(sub_sample->SubSamples); - for (i=0; iSubSamples, i); - e = gf_isom_add_subsample_info(traf->subs, last_sample, entry->subsample_size, entry->subsample_priority, entry->reserved, entry->discardable); - if (e) return e; + e = gf_isom_add_sample_group_entry(traf->sampleGroups, 0, sg->grouping_type, sg->sample_entries[j].group_description_index); + break; + } + } } return GF_OK; } diff --git a/src/isomedia/stbl_read.c b/src/isomedia/stbl_read.c index 51f8e25..49a239d 100644 --- a/src/isomedia/stbl_read.c +++ b/src/isomedia/stbl_read.c @@ -223,11 +223,10 @@ GF_Err stbl_GetSampleDTS(GF_TimeToSampleBox *stts, u32 SampleNumber, u64 *DTS) { return stbl_GetSampleDTS_and_Duration(stts, SampleNumber, DTS, NULL); } -//Set the RAP flag of a sample +//Retrieve closes RAP for a given sample - if sample is RAP, sets the RAP flag GF_Err stbl_GetSampleRAP(GF_SyncSampleBox *stss, u32 SampleNumber, u8 *IsRAP, u32 *prevRAP, u32 *nextRAP) { u32 i; - if (prevRAP) *prevRAP = 0; if (nextRAP) *nextRAP = 0; @@ -256,6 +255,99 @@ GF_Err stbl_GetSampleRAP(GF_SyncSampleBox *stss, u32 SampleNumber, u8 *IsRAP, u3 return GF_OK; } +GF_Err stbl_SearchSAPs(GF_SampleTableBox *stbl, u32 SampleNumber, u8 *IsRAP, u32 *prevRAP, u32 *nextRAP) +{ + u32 i, j, count, count2; + assert(prevRAP); + assert(nextRAP); + (*IsRAP) = 0; + + if (!stbl->sampleGroups || !stbl->sampleGroupsDescription) return GF_OK; + + count = gf_list_count(stbl->sampleGroups); + count2 = gf_list_count(stbl->sampleGroupsDescription); + for (i=0; isampleGroups, i); + switch (sg->grouping_type) { + case GF_4CC('r','a','p',' '): + is_rap_group = 1; + break; + case GF_4CC('r','o','l','l'): + break; + default: + continue; + } + for (j=0; jsampleGroupsDescription, j); + if (sgdp->grouping_type==sg->grouping_type) break; + sgdp = NULL; + } + if (! sgdp) continue; + + first_sample_in_entry=1; + for (j=0; jentry_count; j++) { + u32 first_rap_in_entry, last_rap_in_entry; + last_sample_in_entry = first_sample_in_entry + sg->sample_entries[j].sample_count - 1; + + /*samples in this entry are not RAPs, continue*/ + if (! sg->sample_entries[j].group_description_index) { + first_sample_in_entry += sg->sample_entries[j].sample_count; + continue; + } + if (!is_rap_group) { + GF_RollRecoveryEntry *entry = gf_list_get(sgdp->group_descriptions, sg->sample_entries[j].group_description_index - 1); + roll_distance = entry ? entry->roll_distance : 0; + } + + /*we consider the first sample in a roll or rap group entry to be the RAP (eg, we have to decode from this sample anyway) + except if roll_distance is strictly negative in which case we have to rewind our sample numbers from roll_distance*/ + if (roll_distance < 0) { + if ((s32) first_sample_in_entry + roll_distance>=0) first_rap_in_entry = first_sample_in_entry + roll_distance; + else first_rap_in_entry = 0; + + if ((s32) last_sample_in_entry + roll_distance>=0) last_rap_in_entry = last_sample_in_entry + roll_distance; + else last_rap_in_entry = 0; + } else { + first_rap_in_entry = first_sample_in_entry; + last_rap_in_entry = last_sample_in_entry; + } + + /*store previous & next sample RAP - note that we do not store the closest previous RAP, only the first of the previous RAP group + as RAPs are usually isolated this should not be an issue*/ + if (prevRAP && (first_rap_in_entry <= SampleNumber)) { + *prevRAP = first_rap_in_entry; + } + if (nextRAP) { + *nextRAP = last_rap_in_entry; + } + + /*sample lies in this (rap) group, it is rap*/ + if (is_rap_group) { + if ((first_rap_in_entry <= SampleNumber) && (SampleNumber <= last_rap_in_entry)) { + (*IsRAP) = 1; + return GF_OK; + } + } else { + /*prevRAP or nextRAP matches SampleNumber, sample is RAP*/ + if ((*prevRAP == SampleNumber) || (*nextRAP == SampleNumber)) { + (*IsRAP) = 1; + return GF_OK; + } + } + + /*first sample in entry is after our target sample, abort*/ + if (first_rap_in_entry > SampleNumber) { + break; + } + } + } + return GF_OK; +} + //get the number of "ghost chunk" (implicit chunks described by an entry) void GetGhostNum(GF_StscEntry *ent, u32 EntryIndex, u32 count, GF_SampleTableBox *stbl) { diff --git a/src/isomedia/stbl_write.c b/src/isomedia/stbl_write.c index c4b6e1b..9ed4a76 100644 --- a/src/isomedia/stbl_write.c +++ b/src/isomedia/stbl_write.c @@ -731,9 +731,8 @@ GF_Err stbl_SetSampleSize(GF_SampleSizeBox *stsz, u32 SampleNumber, u32 size) GF_Err stbl_SetSampleRAP(GF_SyncSampleBox *stss, u32 SampleNumber, u8 isRAP) { - u32 i, nextSamp; + u32 i; - nextSamp = 0; //check if we have already a sync sample for (i = 0; i < stss->nb_entries; i++) { diff --git a/src/isomedia/track.c b/src/isomedia/track.c index ef03064..3534b80 100644 --- a/src/isomedia/track.c +++ b/src/isomedia/track.c @@ -358,19 +358,27 @@ GF_Err SetTrackDuration(GF_TrackBox *trak) e = Media_SetDuration(trak); if (e) return e; + //assert the timeScales are non-NULL + if (!trak->moov->mvhd->timeScale || !trak->Media->mediaHeader->timeScale) return GF_ISOM_INVALID_FILE; + trackDuration = (trak->Media->mediaHeader->duration * trak->moov->mvhd->timeScale) / trak->Media->mediaHeader->timeScale; + //if we have an edit list, the duration is the sum of all the editList //entries' duration (always expressed in MovieTimeScale) - if (trak->editBox && trak->editBox->editList) { + if (trak->editBox && !trak->editBox->last_is_empty && trak->editBox->editList) { trackDuration = 0; elst = trak->editBox->editList; i=0; while ((ent = (GF_EdtsEntry*)gf_list_enum(elst->entryList, &i))) { trackDuration += ent->segmentDuration; + if (ent->mediaRate && !ent->segmentDuration) { + trak->editBox->last_is_empty = 1; + } + } + + if (trak->editBox->last_is_empty) { + ent = (GF_EdtsEntry*) gf_list_last(elst->entryList); + ent->segmentDuration = trackDuration; } - } else { - //assert the timeScales are non-NULL - if (!trak->moov->mvhd->timeScale || !trak->Media->mediaHeader->timeScale) return GF_ISOM_INVALID_FILE; - trackDuration = (trak->Media->mediaHeader->duration * trak->moov->mvhd->timeScale) / trak->Media->mediaHeader->timeScale; } trak->Header->duration = trackDuration; trak->Header->modificationTime = gf_isom_get_mp4time(); @@ -483,6 +491,80 @@ GF_Err MergeTrack(GF_TrackBox *trak, GF_TrackFragmentBox *traf, u64 moof_offset, if (degr) stbl_AppendDegradation(trak->Media->information->sampleTable, degr); } } + /*merge sample groups*/ + if (traf->sampleGroups) { + GF_List *groups; + GF_List *groupDescs; + if (!trak->Media->information->sampleTable->sampleGroups) + trak->Media->information->sampleTable->sampleGroups = gf_list_new(); + + if (!trak->Media->information->sampleTable->sampleGroupsDescription) + trak->Media->information->sampleTable->sampleGroupsDescription = gf_list_new(); + + groupDescs = trak->Media->information->sampleTable->sampleGroupsDescription; + for (i=0; isampleGroupsDescription); i++) { + GF_SampleGroupDescriptionBox *new_sgdesc = NULL; + GF_SampleGroupDescriptionBox *sgdesc = gf_list_get(traf->sampleGroupsDescription, i); + for (j=0; jgrouping_type==sgdesc->grouping_type) break; + new_sgdesc = NULL; + } + /*new description, move it to our sample table*/ + if (!new_sgdesc) { + gf_list_add(groupDescs, sgdesc); + gf_list_rem(traf->sampleGroupsDescription, i); + i--; + } + /*merge descriptions*/ + else { + u32 idx = gf_list_count(new_sgdesc->group_descriptions); + for (j=idx; jgroup_descriptions); j++) { + void *ptr = gf_list_get(sgdesc->group_descriptions, j); + if (ptr) { + gf_list_add(new_sgdesc->group_descriptions, ptr); + gf_list_rem(sgdesc->group_descriptions, j); + j--; + } + } + } + } + + groups = trak->Media->information->sampleTable->sampleGroups; + for (i=0; isampleGroups); i++) { + GF_SampleGroupBox *stbl_group = NULL; + GF_SampleGroupBox *frag_group = gf_list_get(traf->sampleGroups, i); + + + for (j=0; jgrouping_type==stbl_group->grouping_type) && (frag_group->grouping_type_parameter==stbl_group->grouping_type_parameter)) + break; + stbl_group = NULL; + } + if (!stbl_group) { + stbl_group = (GF_SampleGroupBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_SBGP); + stbl_group->grouping_type = frag_group->grouping_type; + stbl_group->grouping_type_parameter = frag_group->grouping_type_parameter; + stbl_group->version = frag_group->version; + gf_list_add(groups, stbl_group); + } + if (frag_group->entry_count && stbl_group->entry_count && + (frag_group->sample_entries[0].group_description_index==stbl_group->sample_entries[stbl_group->entry_count-1].group_description_index) + ) { + stbl_group->sample_entries[stbl_group->entry_count - 1].sample_count += frag_group->sample_entries[0].sample_count; + if (frag_group->entry_count>1) { + stbl_group->sample_entries = gf_realloc(stbl_group->sample_entries, sizeof(GF_SampleGroupEntry) * (stbl_group->entry_count + frag_group->entry_count - 1)); + memcpy(&stbl_group->sample_entries[stbl_group->entry_count], &frag_group->sample_entries[1], sizeof(GF_SampleGroupEntry) * (frag_group->entry_count - 1)); + stbl_group->entry_count += frag_group->entry_count - 1; + } + } else { + stbl_group->sample_entries = gf_realloc(stbl_group->sample_entries, sizeof(GF_SampleGroupEntry) * (stbl_group->entry_count + frag_group->entry_count)); + memcpy(&stbl_group->sample_entries[stbl_group->entry_count], &frag_group->sample_entries[0], sizeof(GF_SampleGroupEntry) * frag_group->entry_count); + stbl_group->entry_count += frag_group->entry_count; + } + } + } return GF_OK; } @@ -534,7 +616,6 @@ GF_Err NewMedia(GF_MediaBox **mdia, u32 MediaType, u32 TimeScale) GF_MediaInformationBox *minf; GF_DataInformationBox *dinf; GF_SampleTableBox *stbl; - GF_SampleDescriptionBox *stsd; GF_DataReferenceBox *dref; char *str; @@ -548,7 +629,6 @@ GF_Err NewMedia(GF_MediaBox **mdia, u32 MediaType, u32 TimeScale) hdlr = NULL; dinf = NULL; stbl = NULL; - stsd = NULL; dref = NULL; mediaInfo = NULL; diff --git a/src/laser/lsr_dec.c b/src/laser/lsr_dec.c index 86bc803..7b5dd8d 100644 --- a/src/laser/lsr_dec.c +++ b/src/laser/lsr_dec.c @@ -4075,7 +4075,6 @@ static void *lsr_read_update_value_indexed(GF_LASeRCodec *lsr, GF_Node*node, u32 static void lsr_read_update_value(GF_LASeRCodec *lsr, GF_Node *node, u32 att_tag, u32 fieldType, void *val, u32 node_tag) { u32 is_default, has_escape, escape_val = 0; - SVG_Paint *paint; SVG_Number num, *n; is_default = has_escape = 0; @@ -4084,7 +4083,6 @@ static void lsr_read_update_value(GF_LASeRCodec *lsr, GF_Node *node, u32 att_tag GF_LSR_READ_INT(lsr, *(SVG_Boolean*)val, 1, "val"); break; case SVG_Paint_datatype: - paint = (SVG_Paint *)val; lsr_read_paint(lsr, (SVG_Paint*)val, "val"); break; /* @@ -4951,7 +4949,6 @@ static GF_Err lsr_read_command_list(GF_LASeRCodec *lsr, GF_List *com_list, SVG_E { GF_Node *n; GF_Command *com; - GF_Err e; u32 i, type, count; if (cond) { @@ -4984,10 +4981,10 @@ static GF_Err lsr_read_command_list(GF_LASeRCodec *lsr, GF_List *com_list, SVG_E case LSR_UPDATE_ADD: case LSR_UPDATE_INSERT: case LSR_UPDATE_REPLACE: - e = lsr_read_add_replace_insert(lsr, com_list, type); + lsr_read_add_replace_insert(lsr, com_list, type); break; case LSR_UPDATE_DELETE: - e = lsr_read_delete(lsr, com_list); + lsr_read_delete(lsr, com_list); break; case LSR_UPDATE_NEW_SCENE: case LSR_UPDATE_REFRESH_SCENE: /*TODO FIXME, depends on decoder state*/ @@ -5013,24 +5010,24 @@ static GF_Err lsr_read_command_list(GF_LASeRCodec *lsr, GF_List *com_list, SVG_E lsr_read_byte_align_string(lsr, NULL, "textContent"); break; case LSR_UPDATE_SEND_EVENT: - e = lsr_read_send_event(lsr, com_list); + lsr_read_send_event(lsr, com_list); break; case LSR_UPDATE_RESTORE: - e = lsr_read_restore(lsr, com_list); + lsr_read_restore(lsr, com_list); break; case LSR_UPDATE_SAVE: - e = lsr_read_save(lsr, com_list); + lsr_read_save(lsr, com_list); break; case LSR_UPDATE_EXTEND: { - u32 extID, len; + u32 extID; GF_Node *n; GF_LSR_READ_INT(lsr, extID, lsr->info->cfg.extensionIDBits, "extensionID"); - len = lsr_read_vluimsbf5(lsr, "len"); + /*len = */lsr_read_vluimsbf5(lsr, "len"); if (extID==2) { - u32 j, sub_count; + u32 j; lsr_read_vluimsbf5(lsr, "reserved"); - sub_count = lsr_read_vluimsbf5(lsr, "occ2"); + /*sub_count = */lsr_read_vluimsbf5(lsr, "occ2"); for (j=0; jvalue, "val"); } } else if (svg_type==SVG_Transform_Rotate_datatype) { - Fixed angle; SVG_Point_Angle *p = (SVG_Point_Angle *)val; + Fixed angle = gf_muldiv(p->angle, INT2FIX(180), GF_PI); count = (p->x || p->y) ? 3 : 1; lsr_write_vluimsbf5(lsr, count, "count"); - angle = gf_muldiv(p->angle, INT2FIX(180), GF_PI); - lsr_write_fixed_16_8(lsr, p->angle, "val"); + lsr_write_fixed_16_8(lsr, angle, "val"); if (count==3) { lsr_write_fixed_16_8(lsr, p->x, "val"); lsr_write_fixed_16_8(lsr, p->y, "val"); @@ -3481,9 +3480,7 @@ static GF_Err lsr_write_add_replace_insert(GF_LASeRCodec *lsr, GF_Command *com) } if (type!=LSR_UPDATE_INSERT) { if (com->fromNodeID) { - GF_Node *opNode; u8 opAttType; - opNode = gf_sg_find_node(lsr->sg, com->fromNodeID); opAttType = gf_lsr_anim_type_from_attribute(com->fromFieldIndex); GF_LSR_WRITE_INT(lsr, 1, 1, "has_operandAttribute"); GF_LSR_WRITE_INT(lsr, opAttType, 8, "operandAttribute"); diff --git a/src/media_tools/ait.c b/src/media_tools/ait.c new file mode 100644 index 0000000..8adb546 --- /dev/null +++ b/src/media_tools/ait.c @@ -0,0 +1,774 @@ +/* + * Copyright (c) TELECOM ParisTech 2011 + */ + +#include + +#ifndef GPAC_DISABLE_MPEG2TS + +/* static functions */ +static Bool check_ait_already_received(GF_List* ChannelAppList,u32 pid,char* data); +static void gf_ait_application_decode_destroy(GF_M2TS_AIT_APPLICATION_DECODE* application_decode); +static GF_Err gf_m2ts_decode_ait(GF_M2TS_AIT *ait, char *data, u32 data_size, u32 table_id); +static Bool gf_m2ts_is_dmscc_app(GF_M2TS_CHANNEL_APPLICATION_INFO* ChanAppInfo); +static void gf_m2ts_free_ait_application(GF_M2TS_AIT_APPLICATION* application); +static void gf_ait_destroy(GF_M2TS_AIT* ait); +static void gf_m2ts_process_ait(GF_M2TS_Demuxer *ts, GF_M2TS_AIT* ait); + +GF_M2TS_ES *gf_ait_section_new(u32 service_id) +{ + GF_M2TS_ES *es; + GF_M2TS_AIT_CARRY *ses; + GF_SAFEALLOC(ses, GF_M2TS_AIT_CARRY); + es = (GF_M2TS_ES *)ses; + es->flags = GF_M2TS_ES_IS_SECTION; + ses->service_id = service_id; + return es; +} + + +void on_ait_section(GF_M2TS_Demuxer *ts, u32 evt_type, void *par) +{ + GF_M2TS_SL_PCK *pck = (GF_M2TS_SL_PCK *)par; + char *data; + u32 u32_data_size; + u32 u32_table_id; + + + + if (evt_type == GF_M2TS_EVT_AIT_FOUND) { + GF_M2TS_AIT* ait; + GF_M2TS_AIT_CARRY* ait_carry = (GF_M2TS_AIT_CARRY*)pck->stream; + data = pck->data; + + if(!check_ait_already_received(ts->ChannelAppList,ait_carry->pid,data)){ + GF_SAFEALLOC(ait, GF_M2TS_AIT); + u32_data_size = pck->data_len; + u32_table_id = data[0]; + ait->pid = ait_carry->pid; + ait->service_id = ait_carry->service_id; + gf_m2ts_decode_ait(ait, data, u32_data_size, u32_table_id); + gf_m2ts_process_ait(ts,ait); + gf_ait_destroy(ait); + if (ts->on_event) ts->on_event(ts, GF_M2TS_EVT_AIT_FOUND, pck->stream); + } + + } +} + +static GF_Err gf_m2ts_decode_ait(GF_M2TS_AIT *ait, char *data, u32 data_size, u32 table_id) +{ + + GF_BitStream *bs; + u8 temp_descriptor_tag; + u32 data_shift, app_desc_data_shift, ait_app_data_shift; + u32 nb_of_protocol; + + data_shift = 0; + temp_descriptor_tag = 0; + ait_app_data_shift = 0; + bs = gf_bs_new(data,data_size,GF_BITSTREAM_READ); + + ait->common_descriptors = gf_list_new(); + if(ait->common_descriptors == NULL){ + GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[Process AIT] Error during common_descriptors list initialization, abording processing \n")); + return GF_CORRUPTED_DATA; + } + ait->application_decoded = gf_list_new(); + if(ait->application_decoded == NULL){ + GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[Process AIT] Error during application list initialization, abording processing \n")); + return GF_CORRUPTED_DATA; + } + + ait->table_id = gf_bs_read_int(bs,8); + ait->section_syntax_indicator = gf_bs_read_int(bs,1); + gf_bs_read_int(bs,3); + ait->section_length = gf_bs_read_int(bs,12); + if( (data[1] & 0x0C) != 0){ + GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[Process AIT] section length is not correct abroding \n")); + }else if( ait->section_length > AIT_SECTION_LENGTH_MAX){ + GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[Process AIT] section length should not exceed 1021. Wrong section, abording processing \n")); + } + ait->test_application_flag = gf_bs_read_int(bs,1); + if(ait->test_application_flag == 1){ + GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[Process AIT] test application flag is at 1. API transmitted for testing, abording processing \n")); + return GF_CORRUPTED_DATA; + } + ait->application_type = gf_bs_read_int(bs,15); + if(ait->application_type != APPLICATION_TYPE_HTTP_APPLICATION){ + GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[Process AIT] application type should 0x10. Wrong section, abording processing \n")); + return GF_CORRUPTED_DATA; + } + gf_bs_read_int(bs,2); + ait->version_number = gf_bs_read_int(bs,5); + + ait->current_next_indicator = gf_bs_read_int(bs,1); + if(!ait->current_next_indicator){ + GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[Process AIT] current next indicator should be at 1 \n")); + return GF_CORRUPTED_DATA; + } + ait->section_number = gf_bs_read_int(bs,8); + ait->last_section_number = gf_bs_read_int(bs,8); + gf_bs_read_int(bs,4);/* bit shifting */ + ait->common_descriptors_length = gf_bs_read_int(bs,12); + gf_bs_read_int(bs,(unsigned int)ait->common_descriptors_length/8); + gf_bs_read_int(bs,4);/* bit shifting */ + ait->application_loop_length = gf_bs_read_int(bs,12); + + data_shift = (u32)(gf_bs_get_position(bs)) + ait->common_descriptors_length/8; + + while (ait_app_data_shift < ait->application_loop_length) { + + GF_M2TS_AIT_APPLICATION_DECODE* application; + GF_SAFEALLOC(application,GF_M2TS_AIT_APPLICATION_DECODE); + application->application_descriptors = gf_list_new(); + application->index_app_desc_id = 0; + + /* application loop */ + application->organisation_id = gf_bs_read_int(bs,32); + application->application_id= gf_bs_read_int(bs,16); + application->application_control_code= gf_bs_read_int(bs,8); + gf_bs_read_int(bs,4);/* bit shifting */ + application->application_descriptors_loop_length= gf_bs_read_int(bs,12); + + ait_app_data_shift += 9; + app_desc_data_shift = 0; + nb_of_protocol = 0; + + while (app_desc_data_shift< application->application_descriptors_loop_length) { + temp_descriptor_tag = gf_bs_read_int(bs,8); + switch (temp_descriptor_tag) { + case APPLICATION_DESCRIPTOR: + { + u64 pre_processing_pos; + u8 i; + GF_M2TS_APPLICATION_DESCRIPTOR *application_descriptor; + GF_SAFEALLOC(application_descriptor, GF_M2TS_APPLICATION_DESCRIPTOR); + application_descriptor->descriptor_tag = temp_descriptor_tag; + application->application_descriptors_id[application->index_app_desc_id] = temp_descriptor_tag; + application->index_app_desc_id++; + application_descriptor->descriptor_length = gf_bs_read_int(bs,8); + pre_processing_pos = gf_bs_get_position(bs); + application_descriptor->application_profiles_length = gf_bs_read_int(bs,8); + application_descriptor->application_profile = gf_bs_read_int(bs,16); + application_descriptor->version_major = gf_bs_read_int(bs,8); + application_descriptor->version_minor = gf_bs_read_int(bs,8); + application_descriptor->version_micro = gf_bs_read_int(bs,8); + application_descriptor->service_bound_flag = gf_bs_read_int(bs,1); + application_descriptor->visibility = gf_bs_read_int(bs,2); + gf_bs_read_int(bs,5); /*bit shift*/ + application_descriptor->application_priority = gf_bs_read_int(bs,8); + if (nb_of_protocol > 0) { + for (i=0; itransport_protocol_label[i] = gf_bs_read_int(bs,8); + } + } else { + application_descriptor->transport_protocol_label[0] = gf_bs_read_int(bs,8); + } + if (pre_processing_pos+application_descriptor->descriptor_length != gf_bs_get_position(bs) || application_descriptor->application_profile == 2 /* PVR feature */) { + GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[Process AIT] Descriptor data processed length error. Difference between byte shifting %d and descriptor length %d \n",(gf_bs_get_position(bs) - pre_processing_pos),application_descriptor->descriptor_length)); + gf_free(application_descriptor); + return GF_CORRUPTED_DATA; + } + gf_list_add(application->application_descriptors,application_descriptor); + app_desc_data_shift += (2+ application_descriptor->descriptor_length); + break; + } + case APPLICATION_NAME_DESCRIPTOR: + { + u64 pre_processing_pos; + GF_M2TS_APPLICATION_NAME_DESCRIPTOR* name_descriptor; + GF_SAFEALLOC(name_descriptor, GF_M2TS_APPLICATION_NAME_DESCRIPTOR); + application->application_descriptors_id[application->index_app_desc_id] = temp_descriptor_tag; + application->index_app_desc_id++; + name_descriptor->descriptor_tag = temp_descriptor_tag; + name_descriptor->descriptor_length = gf_bs_read_int(bs,8); + pre_processing_pos = gf_bs_get_position(bs); + name_descriptor->ISO_639_language_code = gf_bs_read_int(bs,24); + name_descriptor->application_name_length = gf_bs_read_int(bs,8); + name_descriptor->application_name_char = (char*) gf_calloc(name_descriptor->application_name_length+1,sizeof(char)); + gf_bs_read_data(bs,name_descriptor->application_name_char,name_descriptor->application_name_length); + name_descriptor->application_name_char[name_descriptor->application_name_length] = 0 ; + if (pre_processing_pos+name_descriptor->descriptor_length != gf_bs_get_position(bs)) { + GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[Process AIT] Descriptor data processed length error. Difference between byte shifting %d and descriptor length %d \n",(gf_bs_get_position(bs) - pre_processing_pos),name_descriptor->descriptor_length)); + gf_free(name_descriptor->application_name_char); + gf_free(name_descriptor); + return GF_CORRUPTED_DATA; + } + gf_list_add(application->application_descriptors,name_descriptor); + app_desc_data_shift += (2+ name_descriptor->descriptor_length); + break; + } + case TRANSPORT_PROTOCOL_DESCRIPTOR: + { + u64 pre_processing_pos; + GF_M2TS_TRANSPORT_PROTOCOL_DESCRIPTOR* protocol_descriptor; + GF_SAFEALLOC(protocol_descriptor, GF_M2TS_TRANSPORT_PROTOCOL_DESCRIPTOR); + application->application_descriptors_id[application->index_app_desc_id] = temp_descriptor_tag; + application->index_app_desc_id++; + nb_of_protocol++; + protocol_descriptor->descriptor_tag = temp_descriptor_tag; + protocol_descriptor->descriptor_length = gf_bs_read_int(bs,8); + pre_processing_pos = gf_bs_get_position(bs); + protocol_descriptor->protocol_id = gf_bs_read_int(bs,16); + protocol_descriptor->transport_protocol_label = gf_bs_read_int(bs,8); + switch (protocol_descriptor->protocol_id) { + case CAROUSEL: + { + GF_M2TS_OBJECT_CAROUSEL_SELECTOR_BYTE* Carousel_selector_byte; + GF_SAFEALLOC(Carousel_selector_byte, GF_M2TS_OBJECT_CAROUSEL_SELECTOR_BYTE); + Carousel_selector_byte->remote_connection = gf_bs_read_int(bs,1); + gf_bs_read_int(bs,7); /* bit shifting */ + if (Carousel_selector_byte->remote_connection) { + Carousel_selector_byte->original_network_id = gf_bs_read_int(bs,16); + Carousel_selector_byte->transport_stream_id = gf_bs_read_int(bs,16); + Carousel_selector_byte->service_id = gf_bs_read_int(bs,16); + } + Carousel_selector_byte->component_tag = gf_bs_read_int(bs,8); + protocol_descriptor->selector_byte = Carousel_selector_byte; + break; + } + case TRANSPORT_HTTP: + { + u32 i; + GF_M2TS_TRANSPORT_HTTP_SELECTOR_BYTE* Transport_http_selector_byte; + GF_SAFEALLOC(Transport_http_selector_byte, GF_M2TS_TRANSPORT_HTTP_SELECTOR_BYTE); + Transport_http_selector_byte->URL_base_length = gf_bs_read_int(bs,8); + //printf("Transport_http_selector_byte->URL_base_length %d \n",Transport_http_selector_byte->URL_base_length); + Transport_http_selector_byte->URL_base_byte = (char*)gf_calloc(Transport_http_selector_byte->URL_base_length+1,sizeof(char)); + gf_bs_read_data(bs,Transport_http_selector_byte->URL_base_byte ,(u32)(Transport_http_selector_byte->URL_base_length)); + Transport_http_selector_byte->URL_base_byte[Transport_http_selector_byte->URL_base_length] = 0; + Transport_http_selector_byte->URL_extension_count = gf_bs_read_int(bs,8); + if (Transport_http_selector_byte->URL_extension_count) { + Transport_http_selector_byte->URL_extentions = (GF_M2TS_TRANSPORT_HTTP_URL_EXTENTION*) gf_calloc(Transport_http_selector_byte->URL_extension_count,sizeof(GF_M2TS_TRANSPORT_HTTP_URL_EXTENTION)); + for (i=0; i < Transport_http_selector_byte->URL_extension_count;i++) { + Transport_http_selector_byte->URL_extentions[i].URL_extension_length = gf_bs_read_int(bs,8); + Transport_http_selector_byte->URL_extentions[i].URL_extension_byte = (char*)gf_calloc(Transport_http_selector_byte->URL_extentions[i].URL_extension_length+1,sizeof(char)); + gf_bs_read_data(bs,Transport_http_selector_byte->URL_extentions[i].URL_extension_byte,(u32)(Transport_http_selector_byte->URL_extentions[i].URL_extension_length)); + Transport_http_selector_byte->URL_extentions[i].URL_extension_byte[Transport_http_selector_byte->URL_extentions[i].URL_extension_length] = 0; + } + } + protocol_descriptor->selector_byte = Transport_http_selector_byte; + break; + } + default: + { + GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[Process AIT] Protocol ID %d unsupported, ignoring the selector byte \n",protocol_descriptor->protocol_id)); + } + } + if (pre_processing_pos+protocol_descriptor->descriptor_length != gf_bs_get_position(bs)) { + GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[Process AIT] Descriptor data processed length error. Difference between byte shifting %d and descriptor length %d \n",(gf_bs_get_position(bs) - pre_processing_pos),protocol_descriptor->descriptor_length)); + if (protocol_descriptor->protocol_id == CAROUSEL) { + GF_M2TS_OBJECT_CAROUSEL_SELECTOR_BYTE* Carousel_selector_byte = (GF_M2TS_OBJECT_CAROUSEL_SELECTOR_BYTE*) protocol_descriptor->selector_byte; + gf_free(Carousel_selector_byte); + } else if(protocol_descriptor->protocol_id ==TRANSPORT_HTTP) { + u32 i; + GF_M2TS_TRANSPORT_HTTP_SELECTOR_BYTE* Transport_http_selector_byte = (GF_M2TS_TRANSPORT_HTTP_SELECTOR_BYTE*) protocol_descriptor->selector_byte; + gf_free(Transport_http_selector_byte->URL_base_byte); + for(i = 0; i < Transport_http_selector_byte->URL_extension_count;i++){ + gf_free(Transport_http_selector_byte->URL_extentions[i].URL_extension_byte); + } + gf_free(Transport_http_selector_byte); + } + gf_free(protocol_descriptor); + return GF_CORRUPTED_DATA; + } + gf_list_add(application->application_descriptors,protocol_descriptor); + app_desc_data_shift += (2+ protocol_descriptor->descriptor_length); + break; + } + case SIMPLE_APPLICATION_LOCATION_DESCRIPTOR: + { + u64 pre_processing_pos; + GF_M2TS_SIMPLE_APPLICATION_LOCATION* Simple_application_location; + GF_SAFEALLOC(Simple_application_location, GF_M2TS_SIMPLE_APPLICATION_LOCATION); + application->application_descriptors_id[application->index_app_desc_id] = temp_descriptor_tag; + application->index_app_desc_id++; + Simple_application_location->descriptor_tag = temp_descriptor_tag; + Simple_application_location->descriptor_length = gf_bs_read_int(bs,8); + pre_processing_pos = gf_bs_get_position(bs); + Simple_application_location->initial_path_bytes = (char*)gf_calloc(Simple_application_location->descriptor_length+1,sizeof(char)); + gf_bs_read_data(bs,Simple_application_location->initial_path_bytes ,(u32)(Simple_application_location->descriptor_length)); + Simple_application_location->initial_path_bytes[Simple_application_location->descriptor_length] = 0; + if (pre_processing_pos+Simple_application_location->descriptor_length != gf_bs_get_position(bs)) { + GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[Process AIT] Descriptor data processed length error. Difference between byte shifting %d and descriptor length %d \n",(gf_bs_get_position(bs) - pre_processing_pos),Simple_application_location->descriptor_length)); + gf_free(Simple_application_location->initial_path_bytes); + gf_free(Simple_application_location); + return GF_CORRUPTED_DATA; + } + gf_list_add(application->application_descriptors,Simple_application_location); + app_desc_data_shift += (2+ Simple_application_location->descriptor_length); + break; + } + case APPLICATION_USAGE_DESCRIPTOR: + { + u64 pre_processing_pos; + GF_M2TS_APPLICATION_USAGE* Application_usage; + GF_SAFEALLOC(Application_usage, GF_M2TS_APPLICATION_USAGE); + application->application_descriptors_id[application->index_app_desc_id] = temp_descriptor_tag; + application->index_app_desc_id++; + Application_usage->descriptor_tag = temp_descriptor_tag; + Application_usage->descriptor_length = gf_bs_read_int(bs,8); + pre_processing_pos = gf_bs_get_position(bs); + Application_usage->usage_type = gf_bs_read_int(bs,8); + if (pre_processing_pos+Application_usage->descriptor_length != gf_bs_get_position(bs)) { + GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[Process AIT] Descriptor data processed length error. Difference between byte shifting %d and descriptor length %d \n",(gf_bs_get_position(bs) - pre_processing_pos),Application_usage->descriptor_length)); + gf_free(Application_usage); + return GF_CORRUPTED_DATA; + } + gf_list_add(application->application_descriptors,Application_usage); + app_desc_data_shift += (2+ Application_usage->descriptor_length); + break; + } + case APPLICATION_BOUNDARY_DESCRIPTOR: + { + u64 pre_processing_pos; + u32 i; + GF_M2TS_APPLICATION_BOUNDARY_DESCRIPTOR* boundary_descriptor; + GF_SAFEALLOC(boundary_descriptor, GF_M2TS_APPLICATION_BOUNDARY_DESCRIPTOR); + application->application_descriptors_id[application->index_app_desc_id] = temp_descriptor_tag; + application->index_app_desc_id++; + boundary_descriptor->descriptor_tag = temp_descriptor_tag; + boundary_descriptor->descriptor_length = gf_bs_read_int(bs,8); + pre_processing_pos = gf_bs_get_position(bs); + boundary_descriptor->boundary_extension_count = gf_bs_read_int(bs,8); + if (boundary_descriptor->boundary_extension_count > 0) { + boundary_descriptor->boundary_extension_info = (GF_M2TS_APPLICATION_BOUNDARY_EXTENSION_INFO*)gf_calloc(boundary_descriptor->boundary_extension_count,sizeof(GF_M2TS_APPLICATION_BOUNDARY_EXTENSION_INFO)); + for (i=0;iboundary_extension_count;i++) { + boundary_descriptor->boundary_extension_info[i].boundary_extension_length = gf_bs_read_int(bs,8); + if (boundary_descriptor->boundary_extension_info[i].boundary_extension_length > 0) { + boundary_descriptor->boundary_extension_info[i].boundary_extension_byte = (char*)gf_calloc(boundary_descriptor->boundary_extension_info[i].boundary_extension_length+1,sizeof(char)); + gf_bs_read_data(bs,boundary_descriptor->boundary_extension_info[i].boundary_extension_byte ,(u32)(boundary_descriptor->boundary_extension_info[i].boundary_extension_length)); + boundary_descriptor->boundary_extension_info[i].boundary_extension_byte[boundary_descriptor->boundary_extension_info[i].boundary_extension_length] = 0; + } + } + } + if (pre_processing_pos+boundary_descriptor->descriptor_length != gf_bs_get_position(bs)) { + GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[Process AIT] Descriptor data processed length error. Difference between byte shifting %d and descriptor length %d \n",(gf_bs_get_position(bs) - pre_processing_pos),boundary_descriptor->descriptor_length)); + if (boundary_descriptor->boundary_extension_count > 0) { + u32 i; + for (i=0;iboundary_extension_count;i++) { + if (boundary_descriptor->boundary_extension_info[i].boundary_extension_length > 0) { + gf_free(boundary_descriptor->boundary_extension_info[i].boundary_extension_byte); + } + } + gf_free(boundary_descriptor->boundary_extension_info); + } + gf_free(boundary_descriptor); + return GF_CORRUPTED_DATA; + } + gf_list_add(application->application_descriptors,boundary_descriptor); + app_desc_data_shift += (2+ boundary_descriptor->descriptor_length); + break; + } + default: + { + u8 length; + GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[Process AIT] Descriptor tag %d unknown, ignoring the descriptor \n",temp_descriptor_tag)); + /* get descriptor's length */ + length = gf_bs_read_int(bs,8); + /* bit shifting (eq descriptor's length)*/ + gf_bs_read_int(bs,8*length); + } + + } + + } + ait_app_data_shift += application->application_descriptors_loop_length; + gf_list_add(ait->application_decoded,application); + } + + data_shift +=ait->application_loop_length; + ait->CRC_32 = gf_bs_read_int(bs,32); + data_shift += 4; + + + if (data_shift != data_size) { + GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[Process AIT] AIT processed length error. Difference between byte shifting %d and data size %d \n",data_shift,data_size)); + return GF_CORRUPTED_DATA; + } + + return GF_OK; +} + +static void gf_m2ts_process_ait(GF_M2TS_Demuxer *ts, GF_M2TS_AIT* ait){ + + u32 nb_app_desc,k,desc_id; + + u32 nb_of_app,j,i; + GF_M2TS_CHANNEL_APPLICATION_INFO* ChanAppInfo; + char* url_base; + char* url_appli_path; + + nb_of_app = gf_list_count(ait->application_decoded); + url_base = NULL; + url_appli_path = NULL; + j=0; + i=0; + + /* Link the AIT and the channel */ + ChanAppInfo = gf_m2ts_get_channel_application_info(ts->ChannelAppList,ait->service_id); + + if(!ChanAppInfo){ + GF_SAFEALLOC(ChanAppInfo,GF_M2TS_CHANNEL_APPLICATION_INFO); + ChanAppInfo->service_id = ait->service_id; + ChanAppInfo->Application = gf_list_new(); + ChanAppInfo->ait_pid = ait->pid; + gf_list_add(ts->ChannelAppList,ChanAppInfo); + } + + ChanAppInfo->nb_application = nb_of_app; + + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[DSMCC] ChanAppInfo->nb_application %d \n",ChanAppInfo->nb_application)); + + for(j=0 ; jApplication,Application); + Application->http_url = NULL; + Application->carousel_url = NULL; + + application_decoded = (GF_M2TS_AIT_APPLICATION_DECODE*)gf_list_get(ait->application_decoded,j); + nb_app_desc = gf_list_count(application_decoded->application_descriptors); + Application->application_control_code = application_decoded->application_control_code; + Application->application_id = application_decoded->application_id; + Application->broadband = 0; + Application->broadcast = 0; + ChanAppInfo->version_number = ait->version_number; + + k=0; + for(k=0 ; kapplication_descriptors_id[k]; + switch(desc_id){ + case APPLICATION_DESCRIPTOR: + { + GF_M2TS_APPLICATION_DESCRIPTOR* application_descriptor = (GF_M2TS_APPLICATION_DESCRIPTOR*)gf_list_get(application_decoded->application_descriptors,k); + Application->priority = application_descriptor->application_priority; + Application->application_profile = application_descriptor->application_profile; + + break; + } + case APPLICATION_NAME_DESCRIPTOR: + { + GF_M2TS_APPLICATION_NAME_DESCRIPTOR* name_descriptor = (GF_M2TS_APPLICATION_NAME_DESCRIPTOR*)gf_list_get(application_decoded->application_descriptors,k); + Application->appli_name = gf_strdup(name_descriptor->application_name_char); + break; + } + case TRANSPORT_PROTOCOL_DESCRIPTOR: + { + GF_M2TS_TRANSPORT_PROTOCOL_DESCRIPTOR* protocol_descriptor = (GF_M2TS_TRANSPORT_PROTOCOL_DESCRIPTOR*)gf_list_get(application_decoded->application_descriptors,k); + + switch(protocol_descriptor->protocol_id){ + case CAROUSEL: + { + GF_Err e; + GF_M2TS_OBJECT_CAROUSEL_SELECTOR_BYTE* Carousel_selector_byte = (GF_M2TS_OBJECT_CAROUSEL_SELECTOR_BYTE*)protocol_descriptor->selector_byte; + if(ts->process_dmscc){ + GF_M2TS_DSMCC_OVERLORD* dsmcc_overlord = gf_m2ts_get_dmscc_overlord(ts->dsmcc_controler,ait->service_id); + Application->broadcast = 1; + if(!dsmcc_overlord){ + char app_url[1024], char_service_id[128]; + + memset(&app_url,0,1024*sizeof(char)); + memset(&char_service_id,0,128*sizeof(char)); + + dsmcc_overlord = gf_m2ts_init_dsmcc_overlord(ait->service_id); + dsmcc_overlord->application_id = Application->application_id; + sprintf(char_service_id,"%d",dsmcc_overlord->service_id); + dsmcc_overlord->root_dir = (char*)gf_calloc(strlen(ts->dsmcc_root_dir)+2+strlen(char_service_id),sizeof(char)); + sprintf(dsmcc_overlord->root_dir,"%s%c%s",ts->dsmcc_root_dir,GF_PATH_SEPARATOR,char_service_id); + e = gf_mkdir(dsmcc_overlord->root_dir); + if(e){ + GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[Process DSMCC] Error during the creation of the directory %s \n",dsmcc_overlord->root_dir)); + if(e == GF_BAD_PARAM){ + gf_cleanup_dir(dsmcc_overlord->root_dir); + } + } + sprintf(app_url,"%s%cindex.html",dsmcc_overlord->root_dir,GF_PATH_SEPARATOR); + Application->carousel_url = gf_strdup(app_url); + gf_list_add(ts->dsmcc_controler,dsmcc_overlord); + } + } + if(Carousel_selector_byte->remote_connection){ + Application->carousel_pid = Carousel_selector_byte->service_id; + } + Application->component_tag = Carousel_selector_byte->component_tag; + + break; + } + case TRANSPORT_HTTP: + { + GF_M2TS_TRANSPORT_HTTP_SELECTOR_BYTE* Transport_http_selector_byte = (GF_M2TS_TRANSPORT_HTTP_SELECTOR_BYTE*)protocol_descriptor->selector_byte; + url_base = Transport_http_selector_byte->URL_base_byte; + Application->broadband = 1; + } + default: + { + } + } + break; + } + case SIMPLE_APPLICATION_LOCATION_DESCRIPTOR: + { + GF_M2TS_SIMPLE_APPLICATION_LOCATION* Simple_application_location = (GF_M2TS_SIMPLE_APPLICATION_LOCATION*)gf_list_get(application_decoded->application_descriptors,k); + url_appli_path = Simple_application_location->initial_path_bytes; + break; + } + case APPLICATION_USAGE_DESCRIPTOR: + { + break; + } + default: + { + GF_LOG(GF_LOG_DEBUG, GF_LOG_MODULE, ("[Process AIT] Descriptor tag %d unknown, ignoring the descriptor \n",desc_id)); + } + } + + if((url_base != NULL) && ( url_appli_path != NULL)){ + u32 url_length = (strlen(url_base)+strlen(url_appli_path)); + Application->http_url = (char*)gf_calloc(url_length + 1,sizeof(char)); + sprintf(Application->http_url,"%s%s",url_base,url_appli_path); + Application->http_url[url_length]=0; + Application->url_received = 1; + url_base = NULL; + url_appli_path = NULL; + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("[Process AIT] SERVICE ID %d URL %s \n",ChanAppInfo->service_id,Application->http_url)); + } + + } + } + if(ts->process_dmscc){ + GF_M2TS_DSMCC_OVERLORD* dsmcc_overlord = gf_m2ts_get_dmscc_overlord(ts->dsmcc_controler,ait->service_id); + + if(dsmcc_overlord && !gf_m2ts_is_dmscc_app(ChanAppInfo)){ + gf_cleanup_dir(dsmcc_overlord->root_dir); + gf_rmdir(dsmcc_overlord->root_dir); + gf_m2ts_delete_dsmcc_overlord(dsmcc_overlord); + + } + } +} + +static Bool gf_m2ts_is_dmscc_app(GF_M2TS_CHANNEL_APPLICATION_INFO* ChanAppInfo){ + + u32 nb_app,i; + + nb_app = gf_list_count(ChanAppInfo->Application); + for(i=0;iApplication,i); + if(Application->broadband){ + return 1; + } + } + return 0; +} + +GF_M2TS_CHANNEL_APPLICATION_INFO* gf_m2ts_get_channel_application_info(GF_List* ChannelAppList, u32 ait_service_id){ + u32 i,nb_chanapp; + + nb_chanapp = gf_list_count(ChannelAppList); + + for(i = 0; i < nb_chanapp; i++){ + GF_M2TS_CHANNEL_APPLICATION_INFO* ChanAppInfo = (GF_M2TS_CHANNEL_APPLICATION_INFO*)gf_list_get(ChannelAppList,i); + if(ChanAppInfo->service_id == ait_service_id){ + return ChanAppInfo; + } + } + + return NULL; + +} + +static Bool check_ait_already_received(GF_List* ChannelAppList,u32 pid,char* data) +{ + u32 nb_of_ait; + u32 version_number; + + nb_of_ait = 0; + + nb_of_ait = gf_list_count(ChannelAppList); + + if(nb_of_ait){ + u32 i; + for(i = 0;i < nb_of_ait;i++){ + GF_M2TS_CHANNEL_APPLICATION_INFO* ChanAppInfo = (GF_M2TS_CHANNEL_APPLICATION_INFO*) gf_list_get(ChannelAppList,i); + if(ChanAppInfo->ait_pid == pid){ + version_number = (data[5] &0x3E)>>1; + GF_LOG(GF_LOG_DEBUG, GF_LOG_MODULE, ("[Process AIT] AIT ait_list_version number %d, ait->version_number %d \n",ChanAppInfo->version_number,version_number)); + if(ChanAppInfo->version_number >= version_number){ + GF_LOG(GF_LOG_DEBUG, GF_LOG_MODULE, ("[Process AIT] AIT already received, abording processing \n")); + return 1; + }else{ + while(ChanAppInfo->nb_application != 0){ + GF_M2TS_AIT_APPLICATION* Application = gf_list_get(ChanAppInfo->Application,0); + gf_m2ts_free_ait_application(Application); + gf_list_rem(ChanAppInfo->Application,0); + ChanAppInfo->nb_application = gf_list_count(ChanAppInfo->Application); + } + GF_LOG(GF_LOG_DEBUG, GF_LOG_MODULE, ("[Process AIT] Discard old applications - New AIT received \n")); + return 0; + } + } + } + }else{ + GF_LOG(GF_LOG_DEBUG, GF_LOG_MODULE, ("[Process AIT] No AIT received \n")); + } + + return 0; +} + +static void gf_ait_destroy(GF_M2TS_AIT* ait) +{ + u32 common_descr_numb, app_numb; + + /* delete de Elementary Stream part of the AIT structure */ + common_descr_numb = 0; + app_numb = 0; + + /* delete the common descriptors */ + common_descr_numb = gf_list_count(ait->common_descriptors); + while(common_descr_numb != 0){ + //TODO + }; + gf_list_del(ait->common_descriptors); + + /* delete the applications and their descriptors */ + app_numb = gf_list_count(ait->application_decoded); + while(app_numb != 0){ + GF_M2TS_AIT_APPLICATION_DECODE* application = gf_list_get(ait->application_decoded,0); + gf_ait_application_decode_destroy(application); + gf_list_rem(ait->application_decoded,0); + app_numb = gf_list_count(ait->application_decoded); + } + gf_list_del(ait->application_decoded); + gf_free(ait); +} + +static void gf_ait_application_decode_destroy(GF_M2TS_AIT_APPLICATION_DECODE* application_decode) +{ + + u32 app_desc_num,i,app_descr_index; + + app_desc_num = app_descr_index = i = 0; + app_desc_num = gf_list_count(application_decode->application_descriptors); + + while (app_desc_num != 0) { + u32 descr_tag; + descr_tag = application_decode->application_descriptors_id[i]; + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[DSMCC] descr_tag %d\n", descr_tag)); + switch(descr_tag) { + case APPLICATION_DESCRIPTOR: + { + GF_M2TS_APPLICATION_DESCRIPTOR* application_descriptor = (GF_M2TS_APPLICATION_DESCRIPTOR*)gf_list_get(application_decode->application_descriptors,0); + gf_free(application_descriptor); + break; + } + case APPLICATION_NAME_DESCRIPTOR: + { + GF_M2TS_APPLICATION_NAME_DESCRIPTOR* name_descriptor = (GF_M2TS_APPLICATION_NAME_DESCRIPTOR*)gf_list_get(application_decode->application_descriptors,0); + gf_free(name_descriptor->application_name_char); + gf_free(name_descriptor); + break; + } + case TRANSPORT_PROTOCOL_DESCRIPTOR: + { + GF_M2TS_TRANSPORT_PROTOCOL_DESCRIPTOR* protocol_descriptor = (GF_M2TS_TRANSPORT_PROTOCOL_DESCRIPTOR*)gf_list_get(application_decode->application_descriptors,0); + switch (protocol_descriptor->protocol_id) { + case CAROUSEL: + { + GF_M2TS_OBJECT_CAROUSEL_SELECTOR_BYTE* Carousel_selector_byte = (GF_M2TS_OBJECT_CAROUSEL_SELECTOR_BYTE*)protocol_descriptor->selector_byte; + gf_free(Carousel_selector_byte); + break; + } + case TRANSPORT_HTTP: + { + u32 i; + GF_M2TS_TRANSPORT_HTTP_SELECTOR_BYTE* Transport_http_selector_byte = (GF_M2TS_TRANSPORT_HTTP_SELECTOR_BYTE*)protocol_descriptor->selector_byte; + gf_free(Transport_http_selector_byte->URL_base_byte); + if (Transport_http_selector_byte->URL_extension_count) { + for (i=0; i < Transport_http_selector_byte->URL_extension_count;i++) { + gf_free(Transport_http_selector_byte->URL_extentions[i].URL_extension_byte); + } + gf_free(Transport_http_selector_byte->URL_extentions); + } + gf_free(Transport_http_selector_byte); + break; + } + default: + { + GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[Process AIT] Protocol ID %d unsupported, ignoring the selector byte \n",protocol_descriptor->protocol_id)); + } + } + gf_free(protocol_descriptor); + break; + } + case SIMPLE_APPLICATION_LOCATION_DESCRIPTOR: + { + GF_M2TS_SIMPLE_APPLICATION_LOCATION* Simple_application_location = (GF_M2TS_SIMPLE_APPLICATION_LOCATION*)gf_list_get(application_decode->application_descriptors,0); + gf_free(Simple_application_location->initial_path_bytes); + gf_free(Simple_application_location); + break; + } + case APPLICATION_USAGE_DESCRIPTOR: + { + GF_M2TS_APPLICATION_USAGE* Application_usage = (GF_M2TS_APPLICATION_USAGE*)gf_list_get(application_decode->application_descriptors,0); + gf_free(Application_usage); + break; + } + case APPLICATION_BOUNDARY_DESCRIPTOR: + { + u32 i; + GF_M2TS_APPLICATION_BOUNDARY_DESCRIPTOR* boundary_descriptor = (GF_M2TS_APPLICATION_BOUNDARY_DESCRIPTOR*)gf_list_get(application_decode->application_descriptors, 0); + if (boundary_descriptor->boundary_extension_count > 0) { + for (i=0; iboundary_extension_count; i++) { + if (boundary_descriptor->boundary_extension_info[i].boundary_extension_length > 0) { + gf_free(boundary_descriptor->boundary_extension_info[i].boundary_extension_byte); + } + } + gf_free(boundary_descriptor->boundary_extension_info); + } + gf_free(boundary_descriptor); + break; + } + default: + { + } + } + gf_list_rem(application_decode->application_descriptors,0); + app_desc_num = gf_list_count(application_decode->application_descriptors); + i++; + } + gf_list_del(application_decode->application_descriptors); + gf_free(application_decode); +} + +static void gf_m2ts_free_ait_application(GF_M2TS_AIT_APPLICATION* Application){ + + if(Application->http_url){ + gf_free(Application->http_url); + } + if(Application->carousel_url){ + gf_free(Application->carousel_url); + } + + if(Application->appli_name){ + gf_free(Application->appli_name); + } + gf_free(Application); +} + +void gf_m2ts_delete_channel_application_info(GF_M2TS_CHANNEL_APPLICATION_INFO* ChannelApp){ + + while(gf_list_count(ChannelApp->Application)){ + GF_M2TS_AIT_APPLICATION* Application = (GF_M2TS_AIT_APPLICATION*)gf_list_get(ChannelApp->Application,0); + gf_m2ts_free_ait_application(Application); + gf_list_rem(ChannelApp->Application,0); + } + + gf_list_del(ChannelApp->Application); + gf_free(ChannelApp); +} +#endif //GPAC_DISABLE_MPEG2TS + + diff --git a/src/media_tools/av_parsers.c b/src/media_tools/av_parsers.c index 154563d..fa6f6c7 100644 --- a/src/media_tools/av_parsers.c +++ b/src/media_tools/av_parsers.c @@ -280,7 +280,7 @@ static GF_Err M4V_Reset(GF_M4VParser *m4v, u64 start) static GF_Err gf_m4v_parse_config_mpeg12(GF_M4VParser *m4v, GF_M4VDecSpecInfo *dsi) { - char p[4]; + unsigned char p[4]; u32 ext_type; s32 o_type; u8 go, par; @@ -909,14 +909,12 @@ GF_Err gf_m4a_parse_config(GF_BitStream *bs, GF_M4ADecSpecInfo *cfg, Bool size_k case 22: case 23: { - Bool fl_flag, ext_flag; - u32 delay; + Bool ext_flag; /*frame length flag*/ - fl_flag = gf_bs_read_int(bs, 1); + /*fl_flag = */gf_bs_read_int(bs, 1); /*depends on core coder*/ - delay = 0; if (gf_bs_read_int(bs, 1)) - delay = gf_bs_read_int(bs, 14); + /*delay = */gf_bs_read_int(bs, 14); ext_flag = gf_bs_read_int(bs, 1); if (!cfg->nb_chan) { } @@ -1133,11 +1131,6 @@ const char *gf_mp3_version_name(u32 hdr) #ifndef GPAC_DISABLE_AV_PARSERS -static u8 MP3_GetLayerV(u32 hdr) -{ - return ((hdr >> 17) & 0x3); -} - GF_EXPORT u8 gf_mp3_layer(u32 hdr) { @@ -1151,56 +1144,45 @@ u8 gf_mp3_num_channels(u32 hdr) return 2; } - -static GFINLINE u32 get_MP3SamplingRates(u32 version, u32 idx) -{ - switch (version) { - case 0: - switch (idx) { - case 0: return 11025; - case 1: return 12000; - case 2: return 8000; - default: return 0; - } - break; - case 1: - return 0; - case 2: - switch (idx) { - case 0: return 22050; - case 1: return 24000; - case 2: return 16000; - default: return 0; - } - break; - case 3: - switch (idx) { - case 0: return 44100; - case 1: return 48000; - case 2: return 32000; - default: return 0; - } - break; - } - return 0; -} - GF_EXPORT u16 gf_mp3_sampling_rate(u32 hdr) { + u16 res; /* extract the necessary fields from the MP3 header */ u8 version = gf_mp3_version(hdr); u8 sampleRateIndex = (hdr >> 10) & 0x3; - return get_MP3SamplingRates(version, sampleRateIndex); + + switch (sampleRateIndex) { + case 0: + res = 44100; + break; + case 1: + res = 48000; + break; + case 2: + res = 32000; + break; + default: + GF_LOG(GF_LOG_ERROR, GF_LOG_CODING, ("[MPEG-1/2 Audio] Samplerate index not valid\n")); + return 0; + } + /*reserved or MPEG-1*/ + if (version & 1) return res; + + /*MPEG-2*/ + res /= 2; + /*MPEG-2.5*/ + if (version == 0) res /= 2; + return res; } GF_EXPORT u16 gf_mp3_window_size(u32 hdr) { u8 version = gf_mp3_version(hdr); - u8 layer = MP3_GetLayerV(hdr); + u8 layer = gf_mp3_layer(hdr); - if (layer == 1) { + if (layer == 3) { if (version == 3) return 1152; return 576; } @@ -1222,175 +1204,67 @@ u8 gf_mp3_object_type_indication(u32 hdr) } } +/*aligned bitrate parsing with libMAD*/ -static GFINLINE u32 get_MP3BitRates(u32 idx1, u32 idx2) -{ - switch (idx1) { - /* MPEG-1, Layer III */ - case 0: - switch (idx2) { - case 0: return 32; - case 1: return 40; - case 2: return 48; - case 3: return 56; - case 4: return 64; - case 5: return 80; - case 6: return 96; - case 7: return 112; - case 8: return 128; - case 9: return 160; - case 10: return 192; - case 11: return 224; - case 12: return 256; - case 13: return 320; - default: return 0; - } - break; - /* MPEG-1, Layer II */ - case 1: - switch (idx2) { - case 0: return 32; - case 1: return 48; - case 2: return 56; - case 3: return 64; - case 4: return 80; - case 5: return 96; - case 6: return 112; - case 7: return 128; - case 8: return 160; - case 9: return 192; - case 10: return 224; - case 11: return 256; - case 12: return 320; - case 13: return 384; - default: return 0; - } - break; - /* MPEG-1, Layer I */ - case 2: - switch (idx2) { - case 0: return 32; - case 1: return 64; - case 2: return 96; - case 3: return 128; - case 4: return 160; - case 5: return 192; - case 6: return 224; - case 7: return 256; - case 8: return 288; - case 9: return 320; - case 10: return 352; - case 11: return 384; - case 12: return 416; - case 13: return 448; - default: return 0; - } - break; - /* MPEG-2 or 2.5, Layer II or III */ - case 3: - switch (idx2) { - case 0: return 8; - case 1: return 16; - case 2: return 24; - case 3: return 32; - case 4: return 40; - case 5: return 48; - case 6: return 56; - case 7: return 64; - case 8: return 80; - case 9: return 96; - case 10: return 112; - case 11: return 128; - case 12: return 144; - case 13: return 160; - default: return 0; - } - break; - /* MPEG-2 or 2.5, Layer I */ - case 4: - switch (idx2) { - case 0: return 32; - case 1: return 48; - case 2: return 56; - case 3: return 64; - case 4: return 80; - case 5: return 96; - case 6: return 112; - case 7: return 128; - case 8: return 144; - case 9: return 160; - case 10: return 176; - case 11: return 192; - case 12: return 224; - case 13: return 256; - default: return 0; - } - break; - default: - return 0; - } - return 0; -} +static +u32 const bitrate_table[5][15] = { + /* MPEG-1 */ + { 0, 32000, 64000, 96000, 128000, 160000, 192000, 224000, /* Layer I */ + 256000, 288000, 320000, 352000, 384000, 416000, 448000 }, + { 0, 32000, 48000, 56000, 64000, 80000, 96000, 112000, /* Layer II */ + 128000, 160000, 192000, 224000, 256000, 320000, 384000 }, + { 0, 32000, 40000, 48000, 56000, 64000, 80000, 96000, /* Layer III */ + 112000, 128000, 160000, 192000, 224000, 256000, 320000 }, + + /* MPEG-2 LSF */ + { 0, 32000, 48000, 56000, 64000, 80000, 96000, 112000, /* Layer I */ + 128000, 144000, 160000, 176000, 192000, 224000, 256000 }, + { 0, 8000, 16000, 24000, 32000, 40000, 48000, 56000, /* Layers */ + 64000, 80000, 96000, 112000, 128000, 144000, 160000 } /* II & III */ +}; -GF_EXPORT -u16 gf_mp3_frame_size(u32 hdr) +u32 gf_mp3_bit_rate(u32 hdr) { - u32 val; - u8 bitRateIndex1; u8 version = gf_mp3_version(hdr); - u8 layer = MP3_GetLayerV(hdr); - u8 bitRateIndex2 = (hdr >> 12) & 0xF; - u8 sampleRateIndex = (hdr >> 10) & 0x3; - Bool isPadded = (hdr >> 9) & 0x1; - u32 frameSize = 0; + u8 layer = gf_mp3_layer(hdr); + u8 bitRateIndex = (hdr >> 12) & 0xF; - if (version == 3) { - bitRateIndex1 = layer - 1; - } else { - if (layer == 3) { - bitRateIndex1 = 4; - } else { - bitRateIndex1 = 3; - } + if (bitRateIndex == 15) { + GF_LOG(GF_LOG_ERROR, GF_LOG_CODING, ("[MPEG-1/2 Audio] Bitrate index not valid\n")); + return 0; } - /* compute frame size */ - val = get_MP3SamplingRates(version, sampleRateIndex); - if (!(version & 1)) val <<= 1; - if (!val) return 0; - frameSize = 144 * 1000 * get_MP3BitRates(bitRateIndex1, bitRateIndex2-1); - frameSize /= val; - - if (isPadded) { - if (layer == 3) { - frameSize += 4; - } else { - frameSize++; - } - } - return (u16) frameSize; + /*MPEG-1*/ + if (version & 1) + return bitrate_table[layer - 1][bitRateIndex]; + /*MPEG-2/2.5*/ + else + return bitrate_table[3 + (layer >> 1)][bitRateIndex]; } -u16 gf_mp3_bit_rate(u32 hdr) + +GF_EXPORT +u16 gf_mp3_frame_size(u32 hdr) { - u8 bitRateIndex1; u8 version = gf_mp3_version(hdr); - u8 layer = MP3_GetLayerV(hdr); - u8 bitRateIndex2 = (hdr >> 12) & 0xF; - if (version == 3) { - bitRateIndex1 = layer - 1; + u8 layer = gf_mp3_layer(hdr); + u32 pad = ( (hdr >> 9) & 0x1) ? 1 : 0; + u32 bitrate = gf_mp3_bit_rate(hdr); + u32 samplerate = gf_mp3_sampling_rate(hdr); + + u32 frameSize = 0; + if (!samplerate || !bitrate) return 0; + + if (layer==1) { + frameSize = (( 12 * bitrate / samplerate) + pad) * 4; } else { - if (layer == 3) { - bitRateIndex1 = 4; - } else { - bitRateIndex1 = 3; - } + u32 slots_per_frame = 144; + if ((layer == 3) && !(version & 1)) slots_per_frame = 72; + frameSize = (slots_per_frame * bitrate / samplerate) + pad; } - - /* compute frame size */ - return get_MP3BitRates(bitRateIndex1, bitRateIndex2-1); + return (u16) frameSize; } @@ -1829,7 +1703,10 @@ s32 AVC_ReadSeqInfo(char *sps_data, u32 sps_size, AVCState *avc, u32 subseq_sps, sps_data_without_emulation_bytes = gf_malloc(sps_size*sizeof(char)); sps_data_without_emulation_bytes_size = avc_remove_emulation_bytes(sps_data, sps_data_without_emulation_bytes, sps_size); bs = gf_bs_new(sps_data_without_emulation_bytes, sps_data_without_emulation_bytes_size, GF_BITSTREAM_READ); - + if (!bs) { + sps_id = -1; + goto exit; + } if (vui_flag_pos) *vui_flag_pos = 0; profile_idc = gf_bs_read_int(bs, 8); @@ -1845,6 +1722,10 @@ s32 AVC_ReadSeqInfo(char *sps_data, u32 sps_size, AVCState *avc, u32 subseq_sps, by subset SPS. According to the SVC standard, subset SPS can have the same sps_id than its base layer, but it does not refer to the same SPS. */ sps_id = avc_get_ue(bs) + GF_SVC_SSPS_ID_SHIFT * subseq_sps; + if (sps_id >=32) { + sps_id = -1; + goto exit; + } sps = &avc->sps[sps_id]; sps->state |= subseq_sps ? AVC_SUBSPS_PARSED : AVC_SPS_PARSED; @@ -2084,11 +1965,24 @@ s32 AVC_ReadPictParamSet(char *pps_data, u32 pps_size, AVCState *avc) pps_data_without_emulation_bytes = gf_malloc(pps_size*sizeof(char)); pps_data_without_emulation_bytes_size = avc_remove_emulation_bytes(pps_data, pps_data_without_emulation_bytes, pps_size); bs = gf_bs_new(pps_data_without_emulation_bytes, pps_data_without_emulation_bytes_size, GF_BITSTREAM_READ); + if (!bs) { + pps_id = -1; + goto exit; + } pps_id = avc_get_ue(bs); + if (pps_id>=255) { + pps_id = -1; + goto exit; + } pps = &avc->pps[pps_id]; if (!pps->status) pps->status = 1; pps->sps_id = avc_get_ue(bs); + if (pps->sps_id >= 32) { + pps->sps_id = 0; + pps_id = -1; + goto exit; + } /*sps_id may be refer to regular SPS or subseq sps, depending on the coded slice refering to the pps*/ if (!avc->sps[pps->sps_id].state && !avc->sps[pps->sps_id + GF_SVC_SSPS_ID_SHIFT].state) { pps_id = -1; @@ -2138,10 +2032,10 @@ static s32 SVC_ReadNal_header_extension(GF_BitStream *bs, SVC_NALUHeader *NalHea static s32 avc_parse_slice(GF_BitStream *bs, AVCState *avc, Bool svc_idr_flag, AVCSliceInfo *si) { - s32 first_mb_in_slice, pps_id; + s32 pps_id; /*s->current_picture.reference= h->nal_ref_idc != 0;*/ - first_mb_in_slice = avc_get_ue(bs); + /*first_mb_in_slice = */avc_get_ue(bs); si->slice_type = avc_get_ue(bs); if (si->slice_type > 9) return -1; @@ -2183,10 +2077,10 @@ static s32 avc_parse_slice(GF_BitStream *bs, AVCState *avc, Bool svc_idr_flag, A static s32 svc_parse_slice(GF_BitStream *bs, AVCState *avc, AVCSliceInfo *si) { - s32 first_mb_in_slice, pps_id; + s32 pps_id; /*s->current_picture.reference= h->nal_ref_idc != 0;*/ - first_mb_in_slice = avc_get_ue(bs); + /*first_mb_in_slice = */avc_get_ue(bs); si->slice_type = avc_get_ue(bs); if (si->slice_type > 9) return -1; @@ -2561,7 +2455,7 @@ u32 AVC_ReformatSEI_NALU(char *buffer, u32 nal_size, AVCState *avc) if ((hdr & 0x1F) != GF_AVC_NALU_SEI) return 0; /*PPS still contains emulation bytes*/ - sei_without_emulation_bytes = gf_malloc(nal_size*sizeof(char)); + sei_without_emulation_bytes = gf_malloc(nal_size + 1/*for SEI null string termination*/); sei_without_emulation_bytes_size = avc_remove_emulation_bytes(buffer, sei_without_emulation_bytes, nal_size); bs = gf_bs_new(sei_without_emulation_bytes, sei_without_emulation_bytes_size, GF_BITSTREAM_READ); @@ -2599,10 +2493,12 @@ u32 AVC_ReformatSEI_NALU(char *buffer, u32 nal_size, AVCState *avc) break; case 5: /*user unregistered */ { - u8 prev = sei_without_emulation_bytes[start+2+psize]; - sei_without_emulation_bytes[start+2+psize] = 0; - GF_LOG(GF_LOG_DEBUG, GF_LOG_CODING, ("[avc-h264] SEI user message %s\n", sei_without_emulation_bytes+start+2)); - sei_without_emulation_bytes[start+2+psize] = prev; + char prev; + assert(start+psize+1 < nal_size+1); + prev = sei_without_emulation_bytes[start+psize+1]; + sei_without_emulation_bytes[start+psize+1] = 0; + GF_LOG(GF_LOG_DEBUG, GF_LOG_CODING, ("[avc-h264] SEI user message %s\n", sei_without_emulation_bytes+start+16)); + sei_without_emulation_bytes[start+psize+1] = prev; } break; diff --git a/src/media_tools/avilib.c b/src/media_tools/avilib.c index f220bdd..8ac5b65 100644 --- a/src/media_tools/avilib.c +++ b/src/media_tools/avilib.c @@ -43,7 +43,7 @@ //#define NEW_RIFF_THRES (10*1024*1024) // Maximum number of indices per stream -#define NR_IXNN_CHUNKS 32 +#define NR_IXNN_CHUNKS 96 #define DEBUG_ODML @@ -85,7 +85,7 @@ static u32 avi_write (FILE *fd, char *buf, u32 len) u32 r = 0; while (r < len) { - n = fwrite (buf + r, 1, len - r, fd); + n = gf_fwrite (buf + r, 1, len - r, fd); if (n < 0) return n; diff --git a/src/media_tools/carousel.c b/src/media_tools/carousel.c deleted file mode 100644 index 99cc818..0000000 --- a/src/media_tools/carousel.c +++ /dev/null @@ -1,409 +0,0 @@ -/* -* GPAC - Multimedia Framework C SDK -* -* Authors: Telecom Paristech -* Copyright (c)2006-200X ENST - All rights reserved -* -* This file is part of GPAC / MPEG2-TS sub-project -* -* GPAC is gf_free software; you can redistribute it and/or modify -* it under the terms of the GNU Lesser General Public License as published by -* the gf_free Software Foundation; either version 2, or (at your option) -* any later version. -* -* GPAC is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU Lesser General Public License for more details. -* -* You should have received a copy of the GNU Lesser General Public -* License along with this library; see the file COPYING. If not, write to -* the gf_free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. -* -*/ - -#include - - -GF_M2TS_ES *gf_ait_section_new(u32 service_id) -{ - GF_M2TS_ES *es; - - GF_M2TS_AIT *ses; - GF_SAFEALLOC(ses, GF_M2TS_AIT); - es = (GF_M2TS_ES *)ses; - es->flags = GF_M2TS_ES_IS_SECTION; - ses->service_id = service_id; - return es; -} - - -void on_ait_section(GF_M2TS_Demuxer *ts, u32 evt_type, void *par) -{ - GF_M2TS_SL_PCK *pck = (GF_M2TS_SL_PCK *)par; - char *data; - u32 u32_data_size; - u32 u32_table_id; - - if (evt_type == GF_M2TS_EVT_AIT_FOUND) { - data = pck->data; - u32_data_size = pck->data_len; - u32_table_id = data[0]; - - gf_m2ts_process_ait((GF_M2TS_AIT*)pck->stream, data, u32_data_size, u32_table_id); - - } -} - -GF_Err gf_m2ts_process_ait(GF_M2TS_AIT *ait, char *data, u32 data_size, u32 table_id) -{ - - GF_BitStream *bs; - u8 temp_descriptor_tag; - u32 data_shift, app_desc_data_shift, ait_app_data_shift; - u32 nb_of_ait; - - data_shift = 0; - temp_descriptor_tag = 0; - nb_of_ait = 0; - ait_app_data_shift = 0; - bs = gf_bs_new(data,data_size,GF_BITSTREAM_READ); - - ait->common_descriptors = gf_list_new(); - ait->application = gf_list_new(); - - ait->table_id = gf_bs_read_int(bs,8); - ait->section_syntax_indicator = gf_bs_read_int(bs,1); - gf_bs_read_int(bs,3); - ait->section_length = gf_bs_read_int(bs,12); - if( (data[1] & 0x0C) != 0){ - GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[Process AIT] section length is not correct abroding \n")); - }else if( ait->section_length > AIT_SECTION_LENGTH_MAX){ - GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[Process AIT] section length should not exceed 1021. Wrong section, abording processing \n")); - } - ait->test_application_flag = gf_bs_read_int(bs,1); - ait->application_type = gf_bs_read_int(bs,15); - if(ait->application_type != APPLICATION_TYPE_HTTP_APPLICATION){ - GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[Process AIT] application type should 0x10. Wrong section, abording processing \n")); - } - gf_bs_read_int(bs,2); - ait->version_number = gf_bs_read_int(bs,5); - - ait->current_next_indicator = gf_bs_read_int(bs,1); - if(!ait->current_next_indicator){ - GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[Process AIT] current next indicator should be at 1 \n")); - } - ait->section_number = gf_bs_read_int(bs,8); - ait->last_section_number = gf_bs_read_int(bs,8); - gf_bs_read_int(bs,4);/* bit shifting */ - ait->common_descriptors_length = gf_bs_read_int(bs,12); - gf_bs_read_int(bs,(unsigned int)ait->common_descriptors_length/8); - gf_bs_read_int(bs,4);/* bit shifting */ - ait->application_loop_length = gf_bs_read_int(bs,12); - - data_shift = (u32)(gf_bs_get_position(bs)) + ait->common_descriptors_length/8; - - while(ait_app_data_shiftapplication_loop_length){ - - GF_M2TS_AIT_APPLICATION* application; - GF_SAFEALLOC(application,GF_M2TS_AIT_APPLICATION); - application->application_descriptors = gf_list_new(); - application->index_app_desc_id = 0; - - /* application loop */ - application->organisation_id = gf_bs_read_int(bs,32); - application->application_id= gf_bs_read_int(bs,16); - application->application_control_code= gf_bs_read_int(bs,8); - gf_bs_read_int(bs,4);/* bit shifting */ - application->application_descriptors_loop_length= gf_bs_read_int(bs,12); - - ait_app_data_shift += 9; - app_desc_data_shift = 0; - - while(app_desc_data_shift< application->application_descriptors_loop_length){ - temp_descriptor_tag = gf_bs_read_int(bs,8); - switch(temp_descriptor_tag){ - case APPLICATION_DESCRIPTOR: - { - u64 pre_processing_pos; - GF_M2TS_APPLICATION_DESCRIPTOR* application_descriptor; - GF_SAFEALLOC(application_descriptor, GF_M2TS_APPLICATION_DESCRIPTOR); - application_descriptor->descriptor_tag = temp_descriptor_tag; - application->application_descriptors_id[application->index_app_desc_id] = temp_descriptor_tag; - application->index_app_desc_id++; - application_descriptor->descriptor_length = gf_bs_read_int(bs,8); - pre_processing_pos = gf_bs_get_position(bs); - application_descriptor->application_profiles_length = gf_bs_read_int(bs,8); - application_descriptor->application_profile = gf_bs_read_int(bs,16); - application_descriptor->version_major = gf_bs_read_int(bs,8); - application_descriptor->version_minor = gf_bs_read_int(bs,8); - application_descriptor->version_micro = gf_bs_read_int(bs,8); - application_descriptor->service_bound_flag = gf_bs_read_int(bs,1); - application_descriptor->visibility = gf_bs_read_int(bs,2); - gf_bs_read_int(bs,5); /*bit shift*/ - application_descriptor->application_priority = gf_bs_read_int(bs,8); - application_descriptor->transport_protocol_label = gf_bs_read_int(bs,8); - if(pre_processing_pos+application_descriptor->descriptor_length != gf_bs_get_position(bs)){ - GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[Process AIT] Descriptor data processed length error. Difference between byte shifting %d and descriptor length %d \n",(gf_bs_get_position(bs) - pre_processing_pos),application_descriptor->descriptor_length)); - } - gf_list_add(application->application_descriptors,application_descriptor); - app_desc_data_shift += (2+ application_descriptor->descriptor_length); - break; - } - case APPLICATION_NAME_DESCRIPTOR: - { - u64 pre_processing_pos; - GF_M2TS_APPLICATION_NAME_DESCRIPTOR* name_descriptor; - GF_SAFEALLOC(name_descriptor, GF_M2TS_APPLICATION_NAME_DESCRIPTOR); - application->application_descriptors_id[application->index_app_desc_id] = temp_descriptor_tag; - application->index_app_desc_id++; - name_descriptor->descriptor_tag = temp_descriptor_tag; - name_descriptor->descriptor_length = gf_bs_read_int(bs,8); - pre_processing_pos = gf_bs_get_position(bs); - name_descriptor->ISO_639_language_code = gf_bs_read_int(bs,24); - name_descriptor->application_name_length = gf_bs_read_int(bs,8); - name_descriptor->application_name_char = (char*) gf_calloc(name_descriptor->application_name_length+1,sizeof(char)); - gf_bs_read_data(bs,name_descriptor->application_name_char,name_descriptor->application_name_length); - name_descriptor->application_name_char[name_descriptor->application_name_length] = 0 ; - if(pre_processing_pos+name_descriptor->descriptor_length != gf_bs_get_position(bs)){ - GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[Process AIT] Descriptor data processed length error. Difference between byte shifting %d and descriptor length %d \n",(gf_bs_get_position(bs) - pre_processing_pos),name_descriptor->descriptor_length)); - } - gf_list_add(application->application_descriptors,name_descriptor); - app_desc_data_shift += (2+ name_descriptor->descriptor_length); - break; - } - case TRANSPORT_PROTOCOL_DESCRIPTOR: - { - u64 pre_processing_pos; - GF_M2TS_TRANSPORT_PROTOCOL_DESCRIPTOR* protocol_descriptor; - GF_SAFEALLOC(protocol_descriptor, GF_M2TS_TRANSPORT_PROTOCOL_DESCRIPTOR); - application->application_descriptors_id[application->index_app_desc_id] = temp_descriptor_tag; - application->index_app_desc_id++; - protocol_descriptor->descriptor_tag = temp_descriptor_tag; - protocol_descriptor->descriptor_length = gf_bs_read_int(bs,8); - pre_processing_pos = gf_bs_get_position(bs); - protocol_descriptor->protocol_id = gf_bs_read_int(bs,16); - protocol_descriptor->transport_protocol_label = gf_bs_read_int(bs,8); - switch(protocol_descriptor->protocol_id){ - case CAROUSEL: - { - GF_M2TS_OBJECT_CAROUSEL_SELECTOR_BYTE* Carousel_selector_byte; - GF_SAFEALLOC(Carousel_selector_byte, GF_M2TS_OBJECT_CAROUSEL_SELECTOR_BYTE); - Carousel_selector_byte->remote_connection = gf_bs_read_int(bs,1); - gf_bs_read_int(bs,7); /* bit shifting */ - if(Carousel_selector_byte->remote_connection){ - Carousel_selector_byte->original_network_id = gf_bs_read_int(bs,16); - Carousel_selector_byte->transport_stream_id = gf_bs_read_int(bs,16); - Carousel_selector_byte->service_id = gf_bs_read_int(bs,16); - } - Carousel_selector_byte->component_tag = gf_bs_read_int(bs,8); - protocol_descriptor->selector_byte = Carousel_selector_byte; - break; - } - case TRANSPORT_HTTP: - { - u32 i; - GF_M2TS_TRANSPORT_HTTP_SELECTOR_BYTE* Transport_http_selector_byte; - GF_SAFEALLOC(Transport_http_selector_byte, GF_M2TS_TRANSPORT_HTTP_SELECTOR_BYTE); - Transport_http_selector_byte->URL_base_length = gf_bs_read_int(bs,8); - //printf("Transport_http_selector_byte->URL_base_length %d \n",Transport_http_selector_byte->URL_base_length); - Transport_http_selector_byte->URL_base_byte = (char*)gf_calloc(Transport_http_selector_byte->URL_base_length,sizeof(char)); - gf_bs_read_data(bs,Transport_http_selector_byte->URL_base_byte ,(u32)(Transport_http_selector_byte->URL_base_length)); - Transport_http_selector_byte->URL_base_byte[Transport_http_selector_byte->URL_base_length] = 0; - Transport_http_selector_byte->URL_extension_count = gf_bs_read_int(bs,8); - if(Transport_http_selector_byte->URL_extension_count){ - Transport_http_selector_byte->URL_extentions = (GF_M2TS_TRANSPORT_HTTP_URL_EXTENTION*) gf_calloc(Transport_http_selector_byte->URL_extension_count,sizeof(GF_M2TS_TRANSPORT_HTTP_URL_EXTENTION)); - for(i = 0; i < Transport_http_selector_byte->URL_extension_count;i++){ - Transport_http_selector_byte->URL_extentions[i].URL_extension_length = gf_bs_read_int(bs,8); - Transport_http_selector_byte->URL_extentions[i].URL_extension_byte = (char*)gf_calloc(Transport_http_selector_byte->URL_extentions[i].URL_extension_length,sizeof(char)); - gf_bs_read_data(bs,Transport_http_selector_byte->URL_extentions[i].URL_extension_byte,(u32)(Transport_http_selector_byte->URL_extentions[i].URL_extension_length)); - Transport_http_selector_byte->URL_extentions[i].URL_extension_byte[Transport_http_selector_byte->URL_extentions[i].URL_extension_length] = 0; - } - } - protocol_descriptor->selector_byte = Transport_http_selector_byte; - break; - } - default: - { - GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[Process AIT] Protocol ID %d unsupported, ignoring the selector byte \n",protocol_descriptor->protocol_id)); - } - } - if(pre_processing_pos+protocol_descriptor->descriptor_length != gf_bs_get_position(bs)){ - GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[Process AIT] Descriptor data processed length error. Difference between byte shifting %d and descriptor length %d \n",(gf_bs_get_position(bs) - pre_processing_pos),protocol_descriptor->descriptor_length)); - } - gf_list_add(application->application_descriptors,protocol_descriptor); - app_desc_data_shift += (2+ protocol_descriptor->descriptor_length); - break; - } - case SIMPLE_APPLICATION_LOCATION_DESCRIPTOR: - { - u64 pre_processing_pos; - GF_M2TS_SIMPLE_APPLICATION_LOCATION* Simple_application_location; - GF_SAFEALLOC(Simple_application_location, GF_M2TS_SIMPLE_APPLICATION_LOCATION); - application->application_descriptors_id[application->index_app_desc_id] = temp_descriptor_tag; - application->index_app_desc_id++; - Simple_application_location->descriptor_tag = temp_descriptor_tag; - Simple_application_location->descriptor_length = gf_bs_read_int(bs,8); - pre_processing_pos = gf_bs_get_position(bs); - Simple_application_location->initial_path_bytes = (char*)gf_calloc(Simple_application_location->descriptor_length,sizeof(char)); - gf_bs_read_data(bs,Simple_application_location->initial_path_bytes ,(u32)(Simple_application_location->descriptor_length)); - Simple_application_location->initial_path_bytes[Simple_application_location->descriptor_length] = 0; - if(pre_processing_pos+Simple_application_location->descriptor_length != gf_bs_get_position(bs)){ - GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[Process AIT] Descriptor data processed length error. Difference between byte shifting %d and descriptor length %d \n",(gf_bs_get_position(bs) - pre_processing_pos),Simple_application_location->descriptor_length)); - } - gf_list_add(application->application_descriptors,Simple_application_location); - app_desc_data_shift += (2+ Simple_application_location->descriptor_length); - break; - } - case APPLICATION_USAGE_DESCRIPTOR: - { - u64 pre_processing_pos; - GF_M2TS_APPLICATION_USAGE* Application_usage; - GF_SAFEALLOC(Application_usage, GF_M2TS_APPLICATION_USAGE); - application->application_descriptors_id[application->index_app_desc_id] = temp_descriptor_tag; - application->index_app_desc_id++; - Application_usage->descriptor_tag = temp_descriptor_tag; - Application_usage->descriptor_length = gf_bs_read_int(bs,8); - pre_processing_pos = gf_bs_get_position(bs); - Application_usage->usage_type = gf_bs_read_int(bs,8); - if(pre_processing_pos+Application_usage->descriptor_length != gf_bs_get_position(bs)){ - GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[Process AIT] Descriptor data processed length error. Difference between byte shifting %d and descriptor length %d \n",(gf_bs_get_position(bs) - pre_processing_pos),Application_usage->descriptor_length)); - } - gf_list_add(application->application_descriptors,Application_usage); - app_desc_data_shift += (2+ Application_usage->descriptor_length); - break; - } - default: - { - GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[Process AIT] Descriptor tag %d unknown, ignoring the descriptor \n",temp_descriptor_tag)); - } - - } - - } - ait_app_data_shift += application->application_descriptors_loop_length; - gf_list_add(ait->application,application); - } - - data_shift +=ait->application_loop_length; - ait->CRC_32 = gf_bs_read_int(bs,32); - data_shift += 4; - - if(data_shift != data_size){ - GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[Process AIT] AIT processed length error. Difference between byte shifting %d and data size %d \n",data_shift,data_size)); - } - - return GF_OK; -} - -void gf_ait_destroy(GF_M2TS_AIT* ait) -{ -// u32 common_descr_numb, app_numb; -// GF_M2TS_ES *es = (GF_M2TS_ES *)ait; -// -// /* delete de Elementary Stream part of the AIT structure */ -// gf_m2ts_es_del(es); -// -// common_descr_numb = 0; -// app_numb = 0; -// -// /* delete the common descriptors */ -// common_descr_numb = gf_list_count(ait->common_descriptors); -// // while(common_app_numb != 0){ -// // -// // }; -// -// /* delete the applications and their descriptors */ -// app_numb = gf_list_count(ait->application); -// while(app_numb != 0){ -// u32 app_desc_num; -// u32 app_descr_index; -// -// app_desc_num = 0; -// app_descr_index = 0; -// app_desc_num = gf_list_count(ait->application->application_descriptors); -// while(app_desc_num != 0){ -// u32 descr_tag; -// ait->application->index_app_desc_id--; -// descr_tag = ait->application->application_descriptors_id[ait->application->index_app_desc_id]; -// -// switch(descr_tag){ -// case APPLICATION_DESCRIPTOR: -// { -// GF_M2TS_APPLICATION_DESCRIPTOR* application_descriptor = (GF_M2TS_APPLICATION_DESCRIPTOR*)gf_list_get(ait->application->application_descriptors,ait->application->index_app_desc_id); -// gf_free(application_descriptor); -// break; -// } -// case APPLICATION_NAME_DESCRIPTOR: -// { -// u64 pre_processing_pos; -// GF_M2TS_APPLICATION_NAME_DESCRIPTOR* name_descriptor = (GF_M2TS_APPLICATION_NAME_DESCRIPTOR*)gf_list_get(ait->application->application_descriptors,ait->application->index_app_desc_id); -// gf_free(name_descriptor->application_name_char); -// gf_free(name_descriptor); -// break; -// } -// case TRANSPORT_PROTOCOL_DESCRIPTOR: -// { -// u64 pre_processing_pos; -// GF_M2TS_TRANSPORT_PROTOCOL_DESCRIPTOR* protocol_descriptor = (GF_M2TS_TRANSPORT_PROTOCOL_DESCRIPTOR*)gf_list_get(ait->application->application_descriptors,ait->application->index_app_desc_id); -// -// -// switch(protocol_descriptor->protocol_id){ -// case CAROUSEL: -// { -// GF_M2TS_OBJECT_CAROUSEL_SELECTOR_BYTE* Carousel_selector_byte = (GF_M2TS_OBJECT_CAROUSEL_SELECTOR_BYTE*)protocol_descriptor->selector_byte; -// gf_free(Carousel_selector_byte); -// break; -// } -// case TRANSPORT_HTTP: -// { -// u32 i; -// GF_M2TS_TRANSPORT_HTTP_SELECTOR_BYTE* Transport_http_selector_byte = (GF_M2TS_TRANSPORT_HTTP_SELECTOR_BYTE*)protocol_descriptor->selector_byte; -// gf_free(Transport_http_selector_byte->URL_base_byte); -// if(Transport_http_selector_byte->URL_extension_count){ -// for(i = 0; i < Transport_http_selector_byte->URL_extension_count;i++){ -// Transport_http_selector_byte->URL_extentions[i].URL_extension_length = gf_bs_read_int(bs,8); -// gf_free(Transport_http_selector_byte->URL_extentions[i].URL_extension_byte) -// } -// gf_free(Transport_http_selector_byte->URL_extentions); -// } -// gf_free(Transport_http_selector_byte); -// break; -// } -// default: -// { -// GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[Process AIT] Protocol ID %d unsupported, ignoring the selector byte \n",protocol_descriptor->protocol_id)); -// } -// } -// gf_free(protocol_descriptor); -// break; -// } -// case SIMPLE_APPLICATION_LOCATION_DESCRIPTOR: -// { -// GF_M2TS_SIMPLE_APPLICATION_LOCATION* Simple_application_location = (GF_M2TS_SIMPLE_APPLICATION_LOCATION*)gf_list_get(ait->application->application_descriptors,ait->application->index_app_desc_id); -// gf_free(Simple_application_location->initial_path_bytes); -// gf_free(Simple_application_location); -// break; -// } -// case APPLICATION_USAGE_DESCRIPTOR: -// { -// GF_M2TS_APPLICATION_USAGE* Application_usage = (GF_M2TS_SIMPLE_APPLICATION_LOCATION*)gf_list_get(ait->application->application_descriptors,ait->application->index_app_desc_id); -// gf_free(Application_usage); -// break; -// } -// default: -// { -// GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[Process AIT] Descriptor tag %d unknown, ignoring the descriptor \n",temp_descriptor_tag)); -// } -// -// } -// gf_list_rem(ait->application->application_descriptors,ait->application->index_app_desc_id); -// -// } -// gf_list_del(ait->application->application_descriptors); -// } -// gf_list_del(ait->application); -// -// }; - -} \ No newline at end of file diff --git a/src/media_tools/dsmcc.c b/src/media_tools/dsmcc.c new file mode 100644 index 0000000..6b3e02c --- /dev/null +++ b/src/media_tools/dsmcc.c @@ -0,0 +1,1938 @@ +/* + * Copyright (c) TELECOM ParisTech 2011 + */ + +#include + +#ifndef GPAC_DISABLE_MPEG2TS +/* DSMCC */ + + + +/* static functions */ +static GF_Err dsmcc_download_data_validation(GF_M2TS_DSMCC_OVERLORD* dsmcc_overlord,GF_M2TS_DSMCC_DOWNLOAD_DATA_BLOCK* DownloadDataBlock,GF_M2TS_DSMCC_MODULE* dsmcc_module,u32 downloadId); +static GF_Err gf_m2ts_dsmcc_process_compatibility_descriptor(GF_M2TS_DSMCC_COMPATIBILITY_DESCRIPTOR *CompatibilityDesc, char* data,GF_BitStream *bs,u32* data_shift); +static GF_Err gf_m2ts_dsmcc_process_message_header(GF_M2TS_DSMCC_MESSAGE_DATA_HEADER *MessageHeader, char* data,GF_BitStream *bs,u32* data_shift,u32 mode); +static GF_Err gf_m2ts_dsmcc_download_data(GF_M2TS_DSMCC_OVERLORD *dsmcc_overlord,GF_M2TS_DSMCC_SECTION *dsmcc, char *data, GF_BitStream *bs,u32* data_shift); +static GF_Err gf_m2ts_dsmcc_section_delete(GF_M2TS_DSMCC_SECTION *dsmcc); +static GF_Err gf_m2ts_dsmcc_delete_message_header(GF_M2TS_DSMCC_MESSAGE_DATA_HEADER *MessageHeader); +static GF_Err gf_m2ts_dsmcc_delete_compatibility_descriptor(GF_M2TS_DSMCC_COMPATIBILITY_DESCRIPTOR *CompatibilityDesc); + + +static GF_Err dsmcc_module_delete(GF_M2TS_DSMCC_MODULE* dsmcc_module); +//static GF_Err dsmcc_module_state(GF_M2TS_DSMCC_OVERLORD* dsmcc_overlord,u32 moduleId,u32 moduleVersion); +static GF_Err dsmcc_create_module_validation(GF_M2TS_DSMCC_INFO_MODULES* InfoModules, u32 downloadId, GF_M2TS_DSMCC_OVERLORD* dsmcc_overlord,u32 nb_module); +static GF_Err dsmcc_module_complete(GF_M2TS_DSMCC_OVERLORD* dsmcc_overlord,GF_M2TS_DSMCC_MODULE* dsmcc_module,u32 moduleIndex); +static GF_Err dsmcc_get_biop_module_info(GF_M2TS_DSMCC_MODULE* dsmcc_module,char* data,u8 data_size); +static GF_M2TS_DSMCC_MODULE* dsmcc_create_module(GF_M2TS_DSMCC_OVERLORD* dsmcc_overlord, GF_M2TS_DSMCC_DOWNLOAD_INFO_RESP_INDIC* DownloadInfoIndication); + +/* BIOP */ +static GF_Err dsmcc_process_biop_data(GF_M2TS_DSMCC_OVERLORD* dsmcc_overlord,GF_M2TS_DSMCC_MODULE* dsmcc_module,char* data,u32 data_size); +static GF_M2TS_DSMCC_BIOP_HEADER* dsmcc_process_biop_header(GF_BitStream* bs); +static GF_Err dsmcc_process_biop_file(GF_BitStream* bs,GF_M2TS_DSMCC_BIOP_HEADER* BIOP_Header,GF_M2TS_DSMCC_OVERLORD*dsmcc_overlord,u16 moduleId,u32 downloadId); +static GF_Err dsmcc_process_biop_directory(GF_BitStream* bs,GF_M2TS_DSMCC_BIOP_HEADER* BIOP_Header,GF_M2TS_DSMCC_OVERLORD*dsmcc_overlord,Bool IsServiceGateway); +static GF_Err dsmcc_process_biop_stream_event(GF_BitStream* bs,GF_M2TS_DSMCC_BIOP_HEADER* BIOP_Header,GF_M2TS_DSMCC_SERVICE_GATEWAY* ServiceGateway); +static GF_Err dsmcc_process_biop_stream_message(GF_BitStream* bs,GF_M2TS_DSMCC_BIOP_HEADER* BIOP_Header,GF_M2TS_DSMCC_SERVICE_GATEWAY* ServiceGateway); + +/* Tools */ +static void dsmcc_biop_get_context(GF_BitStream* bs,GF_M2TS_DSMCC_SERVICE_CONTEXT* Context,u32 serviceContextList_count); +static void dsmcc_biop_descriptor(GF_BitStream* bs,GF_List* list,u32 size); +static GF_Err dsmcc_biop_get_ior(GF_BitStream* bs,GF_M2TS_DSMCC_IOR* IOR); +static GF_M2TS_DSMCC_DIR* dsmcc_get_directory(GF_List* List, u32 objectKey_data); +static GF_M2TS_DSMCC_FILE* dsmcc_get_file(GF_List* ServiceGatewayList,u16 moduleId,u32 downloadId,u32 objectKey_data); +static GF_Err dsmcc_check_element_validation(GF_List* List,char* Parent_name,GF_M2TS_DSMCC_BIOP_NAME Name); +static char* dsmcc_get_file_namepath(GF_M2TS_DSMCC_DIR* Dir,char* name); + +/* Free */ +static void dsmcc_free_biop_context(GF_M2TS_DSMCC_SERVICE_CONTEXT* Context,u32 serviceContextList_count); +static void dsmcc_free_biop_name(GF_M2TS_DSMCC_BIOP_NAME* Name, u32 nb_name); +static void dsmcc_free_biop_descriptor(GF_List* list); +static void dsmcc_free_biop_ior(GF_M2TS_DSMCC_IOR* IOR); +static void dsmcc_free_biop_header(GF_M2TS_DSMCC_BIOP_HEADER* BIOP_Header); +static void dsmcc_free_biop_directory(GF_M2TS_DSMCC_BIOP_DIRECTORY* BIOP_Directory); +static void dsmcc_free_biop_file(GF_M2TS_DSMCC_BIOP_FILE* BIOP_File); +static void dsmcc_free_biop_stream_event(GF_M2TS_DSMCC_BIOP_STREAM_EVENT* BIOP_StreamEvent); +static void dsmcc_free_biop_stream_message(GF_M2TS_DSMCC_BIOP_STREAM_MESSAGE* BIOP_StreamMessage); + +GF_M2TS_DSMCC_OVERLORD* gf_m2ts_init_dsmcc_overlord(u32 service_id) { + GF_M2TS_DSMCC_OVERLORD* dsmcc_overlord; + GF_SAFEALLOC(dsmcc_overlord,GF_M2TS_DSMCC_OVERLORD); + dsmcc_overlord->dsmcc_modules = gf_list_new(); + dsmcc_overlord->service_id = service_id; + return dsmcc_overlord; +} + +GF_M2TS_DSMCC_OVERLORD* gf_m2ts_get_dmscc_overlord(GF_List* Dsmcc_controller,u32 service_id) +{ + u16 nb_dsmcc,i; + + nb_dsmcc = gf_list_count(Dsmcc_controller); + + if(!nb_dsmcc){ + return NULL; + }else{ + for(i =0;iservice_id == service_id){ + return dsmcc_overlord; + } + } + } + return NULL; +} + +void on_dsmcc_section(GF_M2TS_Demuxer *ts, u32 evt_type, void *par) +{ + GF_M2TS_SL_PCK *pck = (GF_M2TS_SL_PCK *)par; + char *data; + u32 u32_data_size; + u32 u32_table_id; + GF_M2TS_DSMCC_OVERLORD* dsmcc_overlord; + + dsmcc_overlord = gf_m2ts_get_dmscc_overlord(ts->dsmcc_controler,pck->stream->service_id); + + if (dsmcc_overlord && evt_type == GF_M2TS_EVT_DSMCC_FOUND) { + GF_Err e; + GF_M2TS_DSMCC_SECTION* dsmcc; + data = pck->data; + u32_data_size = pck->data_len; + u32_table_id = data[0]; + GF_SAFEALLOC(dsmcc,GF_M2TS_DSMCC_SECTION); + + e = gf_m2ts_process_dsmcc(dsmcc_overlord,dsmcc,data,u32_data_size,u32_table_id); + if (ts->on_event) ts->on_event(ts, GF_M2TS_EVT_DSMCC_FOUND, pck); + assert(e == GF_OK); + } +} + +GF_Err gf_m2ts_process_dsmcc(GF_M2TS_DSMCC_OVERLORD* dsmcc_overlord,GF_M2TS_DSMCC_SECTION *dsmcc, char *data, u32 data_size, u32 table_id) +{ + GF_BitStream *bs; + u32 data_shift,reserved_test; + + data_shift = 0; + //first_section = *first_section_received; + bs = gf_bs_new(data,data_size,GF_BITSTREAM_READ); + + dsmcc->table_id = gf_bs_read_int(bs,8); + dsmcc->section_syntax_indicator = gf_bs_read_int(bs,1); + dsmcc->private_indicator = gf_bs_read_int(bs,1); + reserved_test = gf_bs_read_int(bs,2); + if (reserved_test != 3) { + GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[Process DSMCC] test reserved flag is not at 3. Corrupted section, abording processing\n")); + return GF_CORRUPTED_DATA; + } + dsmcc->dsmcc_section_length = gf_bs_read_int(bs,12); + if (dsmcc->dsmcc_section_length > DSMCC_SECTION_LENGTH_MAX) { + GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[Process DSMCC] section length should not exceed 4096. Wrong section, abording processing \n")); + return GF_CORRUPTED_DATA; + } + dsmcc->table_id_extension = gf_bs_read_int(bs,16); + + /*bit shifting */ + gf_bs_read_int(bs,2); + + dsmcc->version_number = gf_bs_read_int(bs,5); + if(dsmcc->version_number != 0 &&(dsmcc->table_id == GF_M2TS_TABLE_ID_DSM_CC_ENCAPSULATED_DATA || dsmcc->table_id == GF_M2TS_TABLE_ID_DSM_CC_UN_MESSAGE)){ + GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[Process DSMCC] Version number should be 0 for Encapsulated Data or UN Message, abording processing \n")); + return GF_CORRUPTED_DATA; + } + + dsmcc->current_next_indicator = gf_bs_read_int(bs,1); + if (!dsmcc->current_next_indicator) { + GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[Process DSMCC] current next indicator should be at 1 \n")); + return GF_CORRUPTED_DATA; + } + dsmcc->section_number = gf_bs_read_int(bs,8); + /*if(dsmcc->section_number >0 && ((*first_section_received) == 0)){ + GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[Process DSMCC] Wainting for the first section \n")); + return GF_CORRUPTED_DATA; + } + *first_section_received = 1;*/ + dsmcc->last_section_number = gf_bs_read_int(bs,8); + //printf("\nsection_number %d last_section_number %d\n",dsmcc->section_number,dsmcc->last_section_number); + //printf("dsmcc->table_id %d \n",dsmcc->table_id); + switch (dsmcc->table_id) { + case GF_M2TS_TABLE_ID_DSM_CC_ENCAPSULATED_DATA: + { + data_shift = (u32)(gf_bs_get_position(bs)); + break; + } + case GF_M2TS_TABLE_ID_DSM_CC_UN_MESSAGE: + case GF_M2TS_TABLE_ID_DSM_CC_DOWNLOAD_DATA_MESSAGE: + { + data_shift = (u32)(gf_bs_get_position(bs)); + gf_m2ts_dsmcc_download_data(dsmcc_overlord,dsmcc,data,bs,&data_shift); + break; + } + case GF_M2TS_TABLE_ID_DSM_CC_STREAM_DESCRIPTION: + { + data_shift = (u32)(gf_bs_get_position(bs)); + break; + } + default: + { + GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[Process DSMCC] Unknown DSMCC Section Type \n")); + break; + } + } + + if (dsmcc->section_syntax_indicator == 0) { + dsmcc->checksum = gf_bs_read_int(bs,32); + } else { + dsmcc->CRC_32= gf_bs_read_int(bs,32); + } + + data_shift = (u32)(gf_bs_get_position(bs)); + + /*if(data_shift != dsmcc->dsmcc_section_length){. + GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[Process AIT] AIT processed length error. Difference between byte shifting %d and data size %d \n",data_shift,data_size)); + return GF_CORRUPTED_DATA; + }*/ + + //gf_m2ts_dsmcc_extract_info(ts,dsmcc); + gf_m2ts_dsmcc_section_delete(dsmcc); + + return GF_OK; +} + +static GF_Err gf_m2ts_dsmcc_download_data(GF_M2TS_DSMCC_OVERLORD *dsmcc_overlord,GF_M2TS_DSMCC_SECTION *dsmcc, char *data, GF_BitStream *bs,u32* data_shift) +{ + GF_M2TS_DSMCC_DOWNLOAD_DATA_MESSAGE* DataMessage; + GF_SAFEALLOC(DataMessage,GF_M2TS_DSMCC_DOWNLOAD_DATA_MESSAGE); + + /* Header */ + gf_m2ts_dsmcc_process_message_header(&DataMessage->DownloadDataHeader,data,bs,data_shift,1); + dsmcc->DSMCC_Extension = DataMessage; + + //printf("DataMessage->DownloadDataHeader.messageId %d \n",DataMessage->DownloadDataHeader.messageId); + + switch (DataMessage->DownloadDataHeader.messageId) + { + case DOWNLOAD_INFO_REQUEST: + { + GF_M2TS_DSMCC_DOWNLOAD_INFO_REQUEST* DownloadInfoRequest; + GF_SAFEALLOC(DownloadInfoRequest,GF_M2TS_DSMCC_DOWNLOAD_INFO_REQUEST); + DataMessage->dataMessagePayload = DownloadInfoRequest; + + /* Payload */ + DownloadInfoRequest->bufferSize = gf_bs_read_int(bs,32); + DownloadInfoRequest->maximumBlockSize = gf_bs_read_int(bs,16); + gf_m2ts_dsmcc_process_compatibility_descriptor(&DownloadInfoRequest->CompatibilityDescr,data,bs,data_shift); + DownloadInfoRequest->privateDataLength = gf_bs_read_int(bs,16); + DownloadInfoRequest->privateDataByte = (char*)gf_calloc(DownloadInfoRequest->privateDataLength,sizeof(char)); + gf_bs_read_data(bs,DownloadInfoRequest->privateDataByte,(u32)(DownloadInfoRequest->privateDataLength)); + break; + } + case DOWNLOAD_INFO_REPONSE_INDICATION: + { + u32 i,nb_modules; + GF_M2TS_DSMCC_DOWNLOAD_INFO_RESP_INDIC* DownloadInfoIndication; + GF_SAFEALLOC(DownloadInfoIndication,GF_M2TS_DSMCC_DOWNLOAD_INFO_RESP_INDIC); + DataMessage->dataMessagePayload = DownloadInfoIndication; + + /* Payload */ + DownloadInfoIndication->downloadId = gf_bs_read_int(bs,32); + DownloadInfoIndication->blockSize = gf_bs_read_int(bs,16); + DownloadInfoIndication->windowSize = gf_bs_read_int(bs,8); + DownloadInfoIndication->ackPeriod = gf_bs_read_int(bs,8); + DownloadInfoIndication->tCDownloadWindow = gf_bs_read_int(bs,32); + DownloadInfoIndication->tCDownloadScenario = gf_bs_read_int(bs,32); + + /* Compatibility Descr */ + gf_m2ts_dsmcc_process_compatibility_descriptor(&DownloadInfoIndication->CompatibilityDescr,data,bs,data_shift); + DownloadInfoIndication->numberOfModules = gf_bs_read_int(bs,16); + /* Versioning of the DownloadInfoIndication is made by the field transactionId (here known as downloadId) */ + if(DataMessage->DownloadDataHeader.downloadId > dsmcc_overlord->transactionId){ + dsmcc_overlord->transactionId = DataMessage->DownloadDataHeader.downloadId; + nb_modules = gf_list_count(dsmcc_overlord->dsmcc_modules); + for(i = 0;inumberOfModules;i++){ + DownloadInfoIndication->Modules.moduleId = gf_bs_read_int(bs,16); + DownloadInfoIndication->Modules.moduleSize = gf_bs_read_int(bs,32); + DownloadInfoIndication->Modules.moduleVersion = gf_bs_read_int(bs,8); + DownloadInfoIndication->Modules.moduleInfoLength = gf_bs_read_int(bs,8); + DownloadInfoIndication->Modules.moduleInfoByte = (char*)gf_calloc(DownloadInfoIndication->Modules.moduleInfoLength,sizeof(char)); + gf_bs_read_data(bs,DownloadInfoIndication->Modules.moduleInfoByte,(u32)(DownloadInfoIndication->Modules.moduleInfoLength)); + if(!dsmcc_create_module_validation(&DownloadInfoIndication->Modules,DownloadInfoIndication->downloadId,dsmcc_overlord,nb_modules)){ + /* Creation of the module */ + GF_M2TS_DSMCC_MODULE*dsmcc_module = dsmcc_create_module(dsmcc_overlord,DownloadInfoIndication); + if(DownloadInfoIndication->Modules.moduleInfoLength){ + GF_Err e; + e = dsmcc_get_biop_module_info(dsmcc_module,DownloadInfoIndication->Modules.moduleInfoByte,DownloadInfoIndication->Modules.moduleInfoLength); + if(e){ + GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[Process DSMCC] Error in BIOP Module Info for module %d, abording the processing \n",dsmcc_module->moduleId)); + gf_free(DownloadInfoIndication->Modules.moduleInfoByte); + DownloadInfoIndication->Modules.moduleInfoByte = NULL; + return GF_CORRUPTED_DATA; + } + } + } + gf_free(DownloadInfoIndication->Modules.moduleInfoByte); + DownloadInfoIndication->Modules.moduleInfoByte = NULL; + } + DownloadInfoIndication->privateDataLength = gf_bs_read_int(bs,16); + DownloadInfoIndication->privateDataByte = (char*)gf_calloc(DownloadInfoIndication->privateDataLength,sizeof(char)); + gf_bs_read_data(bs,DownloadInfoIndication->privateDataByte,(u32)(DownloadInfoIndication->privateDataLength)); + } + break; + } + case DOWNLOAD_DATA_BLOCK: + { + u32 data_shift,modules_count,i; + GF_M2TS_DSMCC_DOWNLOAD_DATA_BLOCK* DownloadDataBlock; + GF_SAFEALLOC(DownloadDataBlock,GF_M2TS_DSMCC_DOWNLOAD_DATA_BLOCK); + DataMessage->dataMessagePayload = DownloadDataBlock; + modules_count = 0; + modules_count = gf_list_count(dsmcc_overlord->dsmcc_modules); + + if(!modules_count){ + GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[Process DSMCC] Download Information Indicator has not been received yet, waiting before processing data block \n")); + break; + } + + + DownloadDataBlock->moduleId = gf_bs_read_int(bs,16); + //printf("DownloadDataBlock->moduleId %d \n",DownloadDataBlock->moduleId); + DownloadDataBlock->moduleVersion = gf_bs_read_int(bs,8); + DownloadDataBlock->reserved = gf_bs_read_int(bs,8); + if(DownloadDataBlock->reserved != 0xFF){ + GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[Process DSMCC] DataHeader reserved slot does not have the correct value, abording the processing \n")); + return GF_CORRUPTED_DATA; + } + DownloadDataBlock->blockNumber = gf_bs_read_int(bs,16); + + for(i=0;idsmcc_modules,i); + /* Test if the data are compatible with the module configuration */ + if(!dsmcc_download_data_validation(dsmcc_overlord,DownloadDataBlock,dsmcc_module,DataMessage->DownloadDataHeader.downloadId)){ + //printf("DownloadDataBlock->blockNumber %d \n\n",DownloadDataBlock->blockNumber); + DownloadDataBlock->dataBlocksize = (DataMessage->DownloadDataHeader.messageLength - 6); + if(dsmcc_module->block_size < DownloadDataBlock->dataBlocksize){ + GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[Process DSMCC] Error block_size should be >= to DownloadDataBlock->dataBlocksize , abording the processing \n")); + return GF_CORRUPTED_DATA; + } + DownloadDataBlock->blockDataByte = (char*)gf_calloc(DownloadDataBlock->dataBlocksize,sizeof(char)); + data_shift = (u32)(gf_bs_get_position(bs)); + gf_bs_read_data(bs,DownloadDataBlock->blockDataByte,DownloadDataBlock->dataBlocksize); + memcpy(dsmcc_module->buffer+dsmcc_module->byte_sift,DownloadDataBlock->blockDataByte,DownloadDataBlock->dataBlocksize*sizeof(char)); + dsmcc_module->byte_sift += DownloadDataBlock->dataBlocksize; + dsmcc_module->last_section_number = dsmcc->last_section_number; + dsmcc_module->section_number++; + if(dsmcc_module->section_number == (dsmcc_module->last_section_number+1)){ + dsmcc_module_complete(dsmcc_overlord,dsmcc_module,i); + } + break; + } + } + + break; + } + case DOWNLOAD_DATA_REQUEST: + { + GF_M2TS_DSMCC_DOWNLOAD_DATA_REQUEST_MESSAGE* DownloadDataRequest; + GF_SAFEALLOC(DownloadDataRequest,GF_M2TS_DSMCC_DOWNLOAD_DATA_REQUEST_MESSAGE); + DataMessage->dataMessagePayload = DownloadDataRequest; + DownloadDataRequest->moduleId = gf_bs_read_int(bs,16); + DownloadDataRequest->blockNumber = gf_bs_read_int(bs,16); + DownloadDataRequest->downloadReason = gf_bs_read_int(bs,8); + break; + } + case DOWNLOAD_DATA_CANCEL: + + { + GF_M2TS_DSMCC_DOWNLOAD_CANCEL* DownloadCancel; + GF_SAFEALLOC(DownloadCancel,GF_M2TS_DSMCC_DOWNLOAD_CANCEL); + DataMessage->dataMessagePayload = DownloadCancel; + DownloadCancel->downloadId = gf_bs_read_int(bs,32); + DownloadCancel->moduleId = gf_bs_read_int(bs,16); + DownloadCancel->blockNumber = gf_bs_read_int(bs,16); + DownloadCancel->downloadCancelReason = gf_bs_read_int(bs,8); + DownloadCancel->reserved = gf_bs_read_int(bs,8); + DownloadCancel->privateDataLength = gf_bs_read_int(bs,16); + if(DownloadCancel->privateDataLength){ + DownloadCancel->privateDataByte = (char*)gf_calloc(DownloadCancel->privateDataLength,sizeof(char)); + gf_bs_read_data(bs,DownloadCancel->privateDataByte,(u32)(DownloadCancel->privateDataLength)); + } + break; + + } + case DOWNLOAD_SERVER_INITIATE: + + { + u32 localbyteshift; + GF_Err e; + GF_M2TS_DSMCC_DOWNLOAD_SERVER_INIT* DownloadServerInit; + GF_SAFEALLOC(DownloadServerInit,GF_M2TS_DSMCC_DOWNLOAD_SERVER_INIT); + DataMessage->dataMessagePayload = DownloadServerInit; + localbyteshift = (u32)(gf_bs_get_position(bs)); + gf_bs_read_data(bs,DownloadServerInit->serverId,20); + gf_m2ts_dsmcc_process_compatibility_descriptor(&DownloadServerInit->CompatibilityDescr,data,bs,data_shift); + DownloadServerInit->privateDataLength = gf_bs_read_int(bs,16); + if(DownloadServerInit->privateDataLength){ + + u32 i; + + GF_M2TS_DSMCC_SERVICE_GATEWAY_INFO* ServiceGateWayInfo; + GF_SAFEALLOC(ServiceGateWayInfo,GF_M2TS_DSMCC_SERVICE_GATEWAY_INFO); + + /* IOR */ + e = dsmcc_biop_get_ior(bs,&ServiceGateWayInfo->IOR); + if(e){ + GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[Process DSMCC] Corrupted IOR, abording the processing \n")); + gf_free(ServiceGateWayInfo); + return GF_CORRUPTED_DATA; + } + ServiceGateWayInfo->downloadTaps_count = gf_bs_read_int(bs,8); + ServiceGateWayInfo->Taps = (GF_M2TS_DSMCC_BIOP_TAPS*)gf_calloc(ServiceGateWayInfo->downloadTaps_count,sizeof(GF_M2TS_DSMCC_BIOP_TAPS)); + for(i=0;idownloadTaps_count;i++){ + ServiceGateWayInfo->Taps[i].id = gf_bs_read_int(bs,16); + ServiceGateWayInfo->Taps[i].use = gf_bs_read_int(bs,16); + ServiceGateWayInfo->Taps[i].assocTag = gf_bs_read_int(bs,16); + ServiceGateWayInfo->Taps[i].selector_length = gf_bs_read_int(bs,8); + ServiceGateWayInfo->Taps[i].selector_type = gf_bs_read_int(bs,16); + ServiceGateWayInfo->Taps[i].transactionId = gf_bs_read_int(bs,32); + ServiceGateWayInfo->Taps[i].timeout = gf_bs_read_int(bs,32); + } + ServiceGateWayInfo->serviceContextList_count = gf_bs_read_int(bs,8); + ServiceGateWayInfo->ServiceContext = (GF_M2TS_DSMCC_SERVICE_CONTEXT*)gf_calloc(ServiceGateWayInfo->serviceContextList_count,sizeof(GF_M2TS_DSMCC_SERVICE_CONTEXT)); + dsmcc_biop_get_context(bs,ServiceGateWayInfo->ServiceContext,ServiceGateWayInfo->serviceContextList_count); + ServiceGateWayInfo->userInfoLength = gf_bs_read_int(bs,16); + if(ServiceGateWayInfo->userInfoLength != 0){ + ServiceGateWayInfo->userInfo_data = (char*)gf_calloc(ServiceGateWayInfo->userInfoLength,sizeof(char)); + gf_bs_read_data(bs,ServiceGateWayInfo->userInfo_data,(u32)(ServiceGateWayInfo->userInfoLength)); + } + + if(!dsmcc_overlord->ServiceGateway && gf_list_count(ServiceGateWayInfo->IOR.taggedProfile)){ + GF_M2TS_DSMCC_BIOP_TAGGED_PROFILE* taggedProfile = (GF_M2TS_DSMCC_BIOP_TAGGED_PROFILE*)gf_list_get(ServiceGateWayInfo->IOR.taggedProfile,0); + dsmcc_overlord->ServiceGateway = (GF_M2TS_DSMCC_SERVICE_GATEWAY*)gf_calloc(1,sizeof(GF_M2TS_DSMCC_SERVICE_GATEWAY)); + dsmcc_overlord->ServiceGateway->downloadId = taggedProfile->BIOPProfileBody->ObjectLocation.carouselId; + dsmcc_overlord->ServiceGateway->moduleId = taggedProfile->BIOPProfileBody->ObjectLocation.moduleId; + dsmcc_overlord->ServiceGateway->service_id = dsmcc_overlord->service_id; + dsmcc_overlord->ServiceGateway->File = gf_list_new(); + dsmcc_overlord->ServiceGateway->Dir = gf_list_new(); + } + } + + break; + } + + default: + { + GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[Process DSMCC] Unknown dataMessagePayload Type \n")); + break; + } + } + + //dsmcc->DSMCC_Extension = DataMessage; + + return GF_OK; +} + + + + +static GF_Err gf_m2ts_dsmcc_process_message_header(GF_M2TS_DSMCC_MESSAGE_DATA_HEADER *MessageHeader, char* data,GF_BitStream *bs,u32* data_shift,u32 mode) +{ + u32 byte_shift; + + byte_shift = *data_shift; + + MessageHeader->protocolDiscriminator = gf_bs_read_int(bs,8); + if (MessageHeader->protocolDiscriminator != 0x11) { + GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[Process DSMCC] DataHeader Protocol Discriminator slot does not have the correct value, abording the processing \n")); + return GF_CORRUPTED_DATA; + } + MessageHeader->dsmccType = gf_bs_read_int(bs,8); + MessageHeader->messageId = gf_bs_read_int(bs,16); + /* mode 0 for Message Header - 1 for Download Data header */ + if (mode == 0) { + MessageHeader->transactionId = gf_bs_read_int(bs,32); + } else if (mode == 1) { + MessageHeader->downloadId = gf_bs_read_int(bs,32); + } + //printf("MessageHeader->downloadId %d \n",MessageHeader->downloadId); + MessageHeader->reserved = gf_bs_read_int(bs,8); + if (MessageHeader->reserved != 0xFF) { + GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[Process DSMCC] DataHeader reserved slot does not have the correct value, abording the processing \n")); + return GF_CORRUPTED_DATA; + } + MessageHeader->adaptationLength = gf_bs_read_int(bs,8); + MessageHeader->header_length = ((u32)(gf_bs_get_position(bs)) - byte_shift); + MessageHeader->messageLength = gf_bs_read_int(bs,16); + + if (MessageHeader->adaptationLength > 0) { + MessageHeader->DsmccAdaptationHeader = (GF_M2TS_DSMCC_ADAPTATION_HEADER*)gf_calloc(1, sizeof(GF_M2TS_DSMCC_ADAPTATION_HEADER)); + MessageHeader->DsmccAdaptationHeader->adaptationType = gf_bs_read_int(bs,8); + + MessageHeader->DsmccAdaptationHeader->adaptationDataByte = (char*)gf_calloc(MessageHeader->adaptationLength-1,sizeof(char)); + gf_bs_read_data(bs,MessageHeader->DsmccAdaptationHeader->adaptationDataByte,(u32)(MessageHeader->adaptationLength)); + + } + + *data_shift = (u32)(gf_bs_get_position(bs)); + + return GF_OK; +} + +static GF_Err gf_m2ts_dsmcc_process_compatibility_descriptor(GF_M2TS_DSMCC_COMPATIBILITY_DESCRIPTOR *CompatibilityDesc, char* data,GF_BitStream *bs,u32* data_shift) +{ + u32 i,j,byte_shift; + + byte_shift = (u32)(gf_bs_get_position(bs)); + + CompatibilityDesc->compatibilityDescriptorLength = gf_bs_read_int(bs,16); + + if(CompatibilityDesc->compatibilityDescriptorLength){ + CompatibilityDesc->descriptorCount = gf_bs_read_int(bs,16); + if(CompatibilityDesc->descriptorCount){ + CompatibilityDesc->Descriptor = (GF_M2TS_DSMCC_DESCRIPTOR*)gf_calloc(CompatibilityDesc->descriptorCount,sizeof(GF_M2TS_DSMCC_DESCRIPTOR)); + for(i=0;idescriptorCount;i++){ + CompatibilityDesc->Descriptor[i].descriptorType = gf_bs_read_int(bs,8); + CompatibilityDesc->Descriptor[i].descriptorLength = gf_bs_read_int(bs,8); + CompatibilityDesc->Descriptor[i].specifierType = gf_bs_read_int(bs,8); + CompatibilityDesc->Descriptor[i].specifierData = gf_bs_read_int(bs,21); + CompatibilityDesc->Descriptor[i].model = gf_bs_read_int(bs,16); + CompatibilityDesc->Descriptor[i].version = gf_bs_read_int(bs,16); + CompatibilityDesc->Descriptor[i].subDescriptorCount = gf_bs_read_int(bs,8); + if(CompatibilityDesc->Descriptor[i].subDescriptorCount){ + CompatibilityDesc->Descriptor[i].SubDescriptor = (GF_M2TS_DSMCC_SUBDESCRIPTOR*)gf_calloc(CompatibilityDesc->Descriptor[i].subDescriptorCount,sizeof(GF_M2TS_DSMCC_SUBDESCRIPTOR)); + for(j=0;j>CompatibilityDesc->Descriptor[i].subDescriptorCount;j++){ + CompatibilityDesc->Descriptor[i].SubDescriptor[j].subDescriptorType = gf_bs_read_int(bs,8); + CompatibilityDesc->Descriptor[i].SubDescriptor[j].subDescriptorLength = gf_bs_read_int(bs,8); + if(CompatibilityDesc->Descriptor[i].SubDescriptor[j].subDescriptorLength){ + CompatibilityDesc->Descriptor[i].SubDescriptor[j].additionalInformation = (char*)gf_calloc(CompatibilityDesc->Descriptor[i].SubDescriptor[j].subDescriptorLength,sizeof(char)); + gf_bs_read_data(bs,CompatibilityDesc->Descriptor[i].SubDescriptor[j].additionalInformation,(u32)(CompatibilityDesc->Descriptor[i].SubDescriptor[j].subDescriptorLength)); + } + + } + } + } + } + } + + *data_shift = (u32)(gf_bs_get_position(bs)); + if(*data_shift != byte_shift+2+CompatibilityDesc->compatibilityDescriptorLength){ + GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[Process DSMCC] Descriptor length not respected, difference between %d and %d \n",(*data_shift - byte_shift),2+CompatibilityDesc->compatibilityDescriptorLength)); + return GF_CORRUPTED_DATA; + } + + return GF_OK; +} + +static GF_Err dsmcc_create_module_validation(GF_M2TS_DSMCC_INFO_MODULES* InfoModules, u32 downloadId, GF_M2TS_DSMCC_OVERLORD* dsmcc_overlord,u32 nb_module){ + + u32 i; + for (i=0; iprocessed[i]; + if ((InfoModules->moduleId == dsmcc_process.moduleId) && (downloadId == dsmcc_process.downloadId)) { + if (InfoModules->moduleVersion <= dsmcc_process.version_number) { + GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[Process DSMCC] Module already intialized \n")); + return GF_CORRUPTED_DATA; + } else if (InfoModules->moduleVersion > dsmcc_process.version_number) { + GF_M2TS_DSMCC_MODULE* dsmcc_module = (GF_M2TS_DSMCC_MODULE*)gf_list_get(dsmcc_overlord->dsmcc_modules,i); + dsmcc_module_delete(dsmcc_module); + gf_list_rem(dsmcc_overlord->dsmcc_modules,i); + dsmcc_process.version_number = InfoModules->moduleVersion; + dsmcc_process.done = 0; + return GF_OK; + } + } + } + return GF_OK; +} + +//static GF_Err dsmcc_module_state(GF_M2TS_DSMCC_OVERLORD* dsmcc_overlord,u32 moduleId,u32 moduleVersion){ +// +// u32 i,nb_module; +// nb_module = gf_list_count(dsmcc_overlord->dsmcc_modules); +// /* This test comes from the fact that the moduleVersion only borrow the least 5 significant bits of the moduleVersion conveys in DownloadDataBlock */ +// /* If the moduleVersion is eq to 0x1F, it does not tell if it is clearly 0x1F or a superior value. So in this case it is better to process the data */ +// /* If the moduleVersion is eq to 0x0, it means that the data conveys a DownloadDataResponse, so it has to be processed */ +// if (moduleVersion != 0 || moduleVersion < 0x1F) { +// for (i=0; iprocessed[i]; +// if ((moduleId == dsmcc_process.moduleId) && (moduleVersion <= dsmcc_process.version_number)) { +// if (dsmcc_process.done) { +// GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[Process DSMCC] Module already processed \n")); +// return GF_CORRUPTED_DATA; +// } else { +// return GF_OK; +// } +// } +// } +// } +// return GF_OK; +//} + +static GF_Err dsmcc_download_data_validation(GF_M2TS_DSMCC_OVERLORD* dsmcc_overlord,GF_M2TS_DSMCC_DOWNLOAD_DATA_BLOCK* DownloadDataBlock,GF_M2TS_DSMCC_MODULE* dsmcc_module,u32 downloadId) +{ + /* It checks if the module Id is eq to the SGW's module Id if Got_ServiceGateway is null (means that the SWG has not been processed yet + If then Got_ServiceGateway = 1, all the module are processed */ + if(dsmcc_overlord->ServiceGateway){ + if ((dsmcc_overlord->Got_ServiceGateway || dsmcc_module->moduleId == dsmcc_overlord->ServiceGateway->moduleId)&& + ((dsmcc_module->moduleId == DownloadDataBlock->moduleId) && (dsmcc_module->section_number == DownloadDataBlock->blockNumber) && + (dsmcc_module->downloadId == downloadId) && (dsmcc_module->version_number == DownloadDataBlock->moduleVersion))){ + return GF_OK; + } + } + + return GF_CORRUPTED_DATA; +} + +static GF_Err dsmcc_module_complete(GF_M2TS_DSMCC_OVERLORD* dsmcc_overlord,GF_M2TS_DSMCC_MODULE* dsmcc_module,u32 moduleIndex) +{ + u32 i,nb_module; + GF_Err e; + e = GF_OK; + nb_module = gf_list_count(dsmcc_overlord->dsmcc_modules); + for (i=0; iprocessed[i]; + if ((dsmcc_module->moduleId == dsmcc_process.moduleId) && dsmcc_module->version_number == dsmcc_process.version_number && dsmcc_module->downloadId == dsmcc_process.downloadId) { + /*process buffer*/ + if(dsmcc_module->Gzip){ + u32 uncomp_size; + char* uncompressed_data; + + gf_gz_decompress_payload(dsmcc_module->buffer,dsmcc_module->byte_sift,&uncompressed_data, &uncomp_size); + //dsmcc_process_biop_data(dsmcc_overlord,dsmcc_module,uncompressed_data,dsmcc_module->original_size); + + if(dsmcc_module->original_size == uncomp_size){ + e = dsmcc_process_biop_data(dsmcc_overlord,dsmcc_module,uncompressed_data,dsmcc_module->original_size); + }else{ + GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[Process DSMCC] Buffer size is not equal to the module size. Flushing the data \n")); + gf_free(dsmcc_module->buffer); + dsmcc_module->buffer = NULL; + dsmcc_module->buffer = (char*)gf_calloc(dsmcc_module->size,sizeof(char)); + dsmcc_module->section_number = 0; + return GF_CORRUPTED_DATA; + } + }else{ + e = dsmcc_process_biop_data(dsmcc_overlord,dsmcc_module,dsmcc_module->buffer,dsmcc_module->size); + } + if(e){ + GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[Process DSMCC] Error during the processing of the module data. Flushing the data \n")); + gf_free(dsmcc_module->buffer); + dsmcc_module->buffer = NULL; + dsmcc_module->buffer = (char*)gf_calloc(dsmcc_module->size,sizeof(char)); + dsmcc_module->section_number = 0; + return GF_CORRUPTED_DATA; + }else{ + dsmcc_process.done = 1; + dsmcc_module_delete(dsmcc_module); + gf_list_rem(dsmcc_overlord->dsmcc_modules,moduleIndex); + } + } + } + + return GF_OK; +} + +/* Delete structure of the DSMCC data processing */ + +static GF_Err gf_m2ts_dsmcc_delete_compatibility_descriptor(GF_M2TS_DSMCC_COMPATIBILITY_DESCRIPTOR *CompatibilityDesc) +{ + u32 i,j; + if (CompatibilityDesc->compatibilityDescriptorLength) { + if (CompatibilityDesc->descriptorCount) { + for (i=0; idescriptorCount; i++) { + if (CompatibilityDesc->Descriptor[i].subDescriptorCount) { + for (j=0; j>CompatibilityDesc->Descriptor[i].subDescriptorCount; j++) { + if (CompatibilityDesc->Descriptor[i].SubDescriptor[j].subDescriptorLength) { + gf_free(CompatibilityDesc->Descriptor[i].SubDescriptor[j].additionalInformation); + } + } + gf_free(CompatibilityDesc->Descriptor[i].SubDescriptor); + } + } + gf_free(CompatibilityDesc->Descriptor); + } + } + return GF_OK; +} + +static GF_Err gf_m2ts_dsmcc_delete_message_header(GF_M2TS_DSMCC_MESSAGE_DATA_HEADER *MessageHeader) +{ + if (MessageHeader->adaptationLength > 0) { + gf_free(MessageHeader->DsmccAdaptationHeader->adaptationDataByte); + gf_free(MessageHeader->DsmccAdaptationHeader); + } + return GF_OK; +} + +static GF_Err gf_m2ts_dsmcc_section_delete(GF_M2TS_DSMCC_SECTION *dsmcc) +{ + GF_M2TS_DSMCC_DOWNLOAD_DATA_MESSAGE* DataMessage = (GF_M2TS_DSMCC_DOWNLOAD_DATA_MESSAGE*)dsmcc->DSMCC_Extension; + + if(!DataMessage){ + return GF_OK; + } + + switch (DataMessage->DownloadDataHeader.messageId) + { + case DOWNLOAD_INFO_REQUEST: + { + GF_M2TS_DSMCC_DOWNLOAD_INFO_REQUEST* DownloadInfoRequest = (GF_M2TS_DSMCC_DOWNLOAD_INFO_REQUEST*)DataMessage->dataMessagePayload; + if(DownloadInfoRequest->privateDataLength){ + gf_free(DownloadInfoRequest->privateDataByte); + } + gf_free(DownloadInfoRequest); + break; + } + case DOWNLOAD_INFO_REPONSE_INDICATION: + { + GF_M2TS_DSMCC_DOWNLOAD_INFO_RESP_INDIC* DownloadInfoIndication = (GF_M2TS_DSMCC_DOWNLOAD_INFO_RESP_INDIC*)DataMessage->dataMessagePayload; + + /* Compatibility Descr */ + gf_m2ts_dsmcc_delete_compatibility_descriptor(&DownloadInfoIndication->CompatibilityDescr); + + if (DownloadInfoIndication->privateDataLength) { + gf_free(DownloadInfoIndication->privateDataByte); + } + gf_free(DownloadInfoIndication); + break; + } + case DOWNLOAD_DATA_BLOCK: + { + GF_M2TS_DSMCC_DOWNLOAD_DATA_BLOCK* DownloadDataBlock = (GF_M2TS_DSMCC_DOWNLOAD_DATA_BLOCK*)DataMessage->dataMessagePayload; + if (DownloadDataBlock->dataBlocksize) { + gf_free(DownloadDataBlock->blockDataByte); + } + gf_free(DownloadDataBlock); + break; + } + case DOWNLOAD_DATA_REQUEST: + { + GF_M2TS_DSMCC_DOWNLOAD_DATA_REQUEST_MESSAGE* DownloadDataRequest = (GF_M2TS_DSMCC_DOWNLOAD_DATA_REQUEST_MESSAGE*)DataMessage->dataMessagePayload; + gf_free(DownloadDataRequest); + break; + } + case DOWNLOAD_DATA_CANCEL: + { + GF_M2TS_DSMCC_DOWNLOAD_CANCEL* DownloadCancel = (GF_M2TS_DSMCC_DOWNLOAD_CANCEL*)DataMessage->dataMessagePayload; + if (DownloadCancel->privateDataLength) { + gf_free(DownloadCancel->privateDataByte); + } + gf_free(DownloadCancel); + break; + } + case DOWNLOAD_SERVER_INITIATE: + { + GF_M2TS_DSMCC_DOWNLOAD_SERVER_INIT* DownloadServerInit = (GF_M2TS_DSMCC_DOWNLOAD_SERVER_INIT*)DataMessage->dataMessagePayload; + gf_m2ts_dsmcc_delete_compatibility_descriptor(&DownloadServerInit->CompatibilityDescr); + if (DownloadServerInit->privateDataLength) { + gf_free(DownloadServerInit->privateDataByte); + } + gf_free(DownloadServerInit); + break; + } + default: + { + GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[Process DSMCC] Unknown dataMessagePayload Type \n")); + break; + } + } + + /* Header */ + gf_m2ts_dsmcc_delete_message_header(&DataMessage->DownloadDataHeader); + gf_free(DataMessage); + gf_free(dsmcc); + dsmcc = NULL; + return GF_OK; +} + + +static GF_Err dsmcc_module_delete(GF_M2TS_DSMCC_MODULE* dsmcc_module){ + + gf_free(dsmcc_module->buffer); + gf_free(dsmcc_module); + dsmcc_module = NULL; + return GF_OK; +} + +/* BIOP MESSAGE */ + +static GF_Err dsmcc_get_biop_module_info(GF_M2TS_DSMCC_MODULE* dsmcc_module,char* data,u8 data_size){ + + GF_M2TS_DSMCC_BIOP_MODULE_INFO* BIOP_ModuleInfo; + GF_BitStream *bs; + u8 i; + + + bs = gf_bs_new(data,data_size,GF_BITSTREAM_READ); + GF_SAFEALLOC(BIOP_ModuleInfo,GF_M2TS_DSMCC_BIOP_MODULE_INFO); + BIOP_ModuleInfo->descriptor = gf_list_new(); + + BIOP_ModuleInfo->moduleTimeOut = gf_bs_read_int(bs,32); + BIOP_ModuleInfo->blockTimeOut = gf_bs_read_int(bs,32); + BIOP_ModuleInfo->minBlockTime = gf_bs_read_int(bs,32); + BIOP_ModuleInfo->taps_count = gf_bs_read_int(bs,8); + if(!BIOP_ModuleInfo->taps_count){ + GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[Process DSMCC] Corrupted payload for BIOP Module Info \n")); + gf_list_del(BIOP_ModuleInfo->descriptor); + gf_free(BIOP_ModuleInfo); + return GF_CORRUPTED_DATA; + } + + BIOP_ModuleInfo->Taps = (GF_M2TS_DSMCC_BIOP_TAPS*)gf_calloc(BIOP_ModuleInfo->taps_count,sizeof(GF_M2TS_DSMCC_BIOP_TAPS)); + for(i = 0; i < BIOP_ModuleInfo->taps_count; i++){ + BIOP_ModuleInfo->Taps[i].id = gf_bs_read_int(bs,16); + BIOP_ModuleInfo->Taps[i].use = gf_bs_read_int(bs,16); + BIOP_ModuleInfo->Taps[i].assocTag = gf_bs_read_int(bs,16); + BIOP_ModuleInfo->Taps[i].selector_length = gf_bs_read_int(bs,8); + if(BIOP_ModuleInfo->Taps[i].selector_length){ + BIOP_ModuleInfo->Taps[i].selector_data = (char*)gf_calloc(BIOP_ModuleInfo->Taps[i].selector_length,sizeof(char)); + gf_bs_read_data(bs,BIOP_ModuleInfo->Taps[i].selector_data,(u8)(BIOP_ModuleInfo->Taps[i].selector_length)); + } + if(i == 0 && (BIOP_ModuleInfo->Taps[i].id != 0x00 || BIOP_ModuleInfo->Taps[i].use != 0x17 || BIOP_ModuleInfo->Taps[i].selector_length != 0x00)){ + GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[Process DSMCC] Corrupted payload for BIOP Module Info \n")); + gf_free(BIOP_ModuleInfo->Taps); + gf_list_del(BIOP_ModuleInfo->descriptor); + gf_free(BIOP_ModuleInfo); + return GF_CORRUPTED_DATA; + } + } + BIOP_ModuleInfo->userInfoLength = gf_bs_read_int(bs,8); + if(BIOP_ModuleInfo->userInfoLength){ + + u32 nb_desc,j; + u8* descr_tag; + + dsmcc_biop_descriptor(bs,BIOP_ModuleInfo->descriptor,(u32)(BIOP_ModuleInfo->userInfoLength)); + + nb_desc = gf_list_count(BIOP_ModuleInfo->descriptor); + j = 0; + while(jdescriptor,j); + + switch(*descr_tag){ + + case CACHING_PRIORITY_DESCRIPTOR: + { + //GF_M2TS_DSMCC_BIOP_CACHING_PRIORITY_DESCRIPTOR* CachingPriorityDescr = (GF_M2TS_DSMCC_BIOP_CACHING_PRIORITY_DESCRIPTOR*)gf_list_get(BIOP_ModuleInfo->descriptor,j); + break; + } + case COMPRESSED_MODULE_DESCRIPTOR: + { + u8 comp_meth; + GF_M2TS_DSMCC_BIOP_COMPRESSED_MODULE_DESCRIPTOR* CompModuleDescr = (GF_M2TS_DSMCC_BIOP_COMPRESSED_MODULE_DESCRIPTOR*)gf_list_get(BIOP_ModuleInfo->descriptor,j); + /*if CompModuleDescr->compression_method least significant nibble is eq to 0x08, the terminal shall support the Deflate compression algorithm (GZIP)*/ + comp_meth = (CompModuleDescr->compression_method &0x0F); + if(comp_meth == 0x08){ + dsmcc_module->Gzip = 1; + } + dsmcc_module->original_size = CompModuleDescr->original_size; + break; + + } + default: + { + GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[Process DSMCC] Unsupported descriptor Type \n")); + break; + } + } + j++; + } + } + + dsmcc_free_biop_descriptor(BIOP_ModuleInfo->descriptor); + + for(i = 0; i < BIOP_ModuleInfo->taps_count; i++){ + if(BIOP_ModuleInfo->Taps[i].selector_length){ + gf_free(BIOP_ModuleInfo->Taps[i].selector_data); + } + } + gf_free(BIOP_ModuleInfo->Taps); + gf_free(BIOP_ModuleInfo); + + return GF_OK; +} + +static GF_Err dsmcc_process_biop_data(GF_M2TS_DSMCC_OVERLORD* dsmcc_overlord,GF_M2TS_DSMCC_MODULE* dsmcc_module,char* data,u32 data_size){ + + GF_BitStream *bs; + GF_Err e; + Bool Error; + u32 byte_shift; + GF_M2TS_DSMCC_BIOP_HEADER* BIOP_Header; + GF_M2TS_DSMCC_SERVICE_GATEWAY* ServiceGateway = dsmcc_overlord->ServiceGateway; + + e = GF_OK; + Error = 0; + + bs = gf_bs_new(data,data_size,GF_BITSTREAM_READ); + + byte_shift = (u32)(gf_bs_get_position(bs)); + + while(byte_shift < data_size){ + + BIOP_Header = dsmcc_process_biop_header(bs); + + if(BIOP_Header){ + if(!strcmp(BIOP_Header->objectKind_data,"fil")){ + e = dsmcc_process_biop_file(bs,BIOP_Header,dsmcc_overlord,dsmcc_module->moduleId,dsmcc_module->downloadId); + }else if(!strcmp(BIOP_Header->objectKind_data,"dir")){ + e = dsmcc_process_biop_directory(bs,BIOP_Header,dsmcc_overlord,0); + }else if(!strcmp(BIOP_Header->objectKind_data,"srg")){ + e = dsmcc_process_biop_directory(bs,BIOP_Header,dsmcc_overlord,1); + if(e == GF_OK){ + dsmcc_overlord->Got_ServiceGateway = 1; + } + }else if(!strcmp(BIOP_Header->objectKind_data,"str")){ + dsmcc_process_biop_stream_message(bs,BIOP_Header,ServiceGateway); + }else if(!strcmp(BIOP_Header->objectKind_data,"ste")){ + dsmcc_process_biop_stream_event(bs,BIOP_Header,ServiceGateway); + }else{ + GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[Process DSMCC] Unsupported BIOP Message, abording process \n")); + } + } + byte_shift = (u32)(gf_bs_get_position(bs)); + if((e || !BIOP_Header) && (byte_shift < data_size)){ + /* Error inside the data. Read next byte until a new "BIOP" is found or data_size is reached */ + gf_bs_read_int(bs,8); + Error = 1; + } + if(BIOP_Header){ + dsmcc_free_biop_header(BIOP_Header); + } + + } + + if(Error){ + return GF_CORRUPTED_DATA; + } + + return GF_OK; +} + +static GF_M2TS_DSMCC_BIOP_HEADER* dsmcc_process_biop_header(GF_BitStream* bs){ + + GF_M2TS_DSMCC_BIOP_HEADER* BIOP_Header; + GF_SAFEALLOC(BIOP_Header,GF_M2TS_DSMCC_BIOP_HEADER); + + BIOP_Header->magic = gf_bs_read_int(bs,32); + if(BIOP_Header->magic != 0x42494F50){ + GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[Process DSMCC] Wrong BIOP Header, abording process \n")); + return NULL; + } + BIOP_Header->biop_version_major = gf_bs_read_int(bs,8); + BIOP_Header->biop_version_minor = gf_bs_read_int(bs,8); + BIOP_Header->byte_order = gf_bs_read_int(bs,8); + BIOP_Header->message_type = gf_bs_read_int(bs,8); + BIOP_Header->message_size = gf_bs_read_int(bs,32); + BIOP_Header->objectKey_length = gf_bs_read_int(bs,8); + if(BIOP_Header->objectKey_length){ + BIOP_Header->objectKey_data = gf_bs_read_int(bs,BIOP_Header->objectKey_length*8); + } + BIOP_Header->objectKind_length = gf_bs_read_int(bs,32); + if(BIOP_Header->objectKind_length){ + BIOP_Header->objectKind_data = (char*)gf_calloc(BIOP_Header->objectKind_length,sizeof(char)); + gf_bs_read_data(bs,BIOP_Header->objectKind_data,(u32)(BIOP_Header->objectKind_length)); + } + BIOP_Header->objectInfo_length = gf_bs_read_int(bs,16); + + return BIOP_Header; +} + +static GF_Err dsmcc_process_biop_file(GF_BitStream* bs,GF_M2TS_DSMCC_BIOP_HEADER* BIOP_Header,GF_M2TS_DSMCC_OVERLORD*dsmcc_overlord,u16 moduleId,u32 downloadId){ + + u32 nb_desc,descr_size; + GF_M2TS_DSMCC_BIOP_FILE* BIOP_File; + GF_M2TS_DSMCC_SERVICE_GATEWAY* ServiceGateway; + GF_M2TS_DSMCC_FILE* File; + FILE* pFile; + char* FileType; + u8* descr_tag; + + GF_SAFEALLOC(BIOP_File,GF_M2TS_DSMCC_BIOP_FILE); + + ServiceGateway = dsmcc_overlord->ServiceGateway; + + BIOP_File->Header = BIOP_Header; + BIOP_File->ContentSize = gf_bs_read_int(bs,64); + + descr_size = BIOP_File->Header->objectInfo_length-8; + + if(descr_size){ + dsmcc_biop_descriptor(bs,BIOP_File->descriptor,descr_size); + } + + gf_bs_read_int(bs,(u32)(descr_size)); + + nb_desc = gf_list_count(BIOP_File->descriptor); + + while(nb_desc){ + + /* get the descriptor tag */ + descr_tag = (u8*)gf_list_get(BIOP_File->descriptor,0); + + switch(*descr_tag){ + case CONTENT_TYPE_DESCRIPTOR: + { + GF_M2TS_DSMCC_BIOP_CONTENT_TYPE_DESRIPTOR* ContentTypeDescr = (GF_M2TS_DSMCC_BIOP_CONTENT_TYPE_DESRIPTOR*)gf_list_get(BIOP_File->descriptor,0); + FileType = strdup(ContentTypeDescr->content_type_data_byte); + } + default: + { + GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[Process DSMCC] Unsupported descriptor Type \n")); + break; + } + } + nb_desc--; + } + + BIOP_File->serviceContextList_count = gf_bs_read_int(bs,8); + if(BIOP_File->serviceContextList_count){ + BIOP_File->ServiceContext = (GF_M2TS_DSMCC_SERVICE_CONTEXT*)gf_calloc(BIOP_File->serviceContextList_count,sizeof(GF_M2TS_DSMCC_SERVICE_CONTEXT)); + dsmcc_biop_get_context(bs,BIOP_File->ServiceContext,BIOP_File->serviceContextList_count); + } + BIOP_File->messageBody_length = gf_bs_read_int(bs,32); + BIOP_File->content_length = gf_bs_read_int(bs,32); + if(BIOP_File->content_length){ + BIOP_File->content_byte = (char*)gf_calloc(BIOP_File->content_length,sizeof(char)); + gf_bs_read_data(bs,BIOP_File->content_byte,(u32)(BIOP_File->content_length)); + + } + GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("module_Id %d \n",moduleId)); + File = dsmcc_get_file(ServiceGateway->File,moduleId,downloadId,BIOP_File->Header->objectKey_data); + if(File){ + GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("Fichier: %s module_Id %d place :%d \n",File->Path,moduleId,File->objectKey_data)); + pFile = fopen(File->Path,"wb"); + if (pFile!=NULL){ + gf_fwrite(BIOP_File->content_byte,1,BIOP_File->content_length ,pFile); + fclose(pFile); + GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("Fichier créé \n\n")); + if(!strcmp(File->name,"index.html")){ + dsmcc_overlord->get_index = 1; + } + } + }else{ + GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[DSMCC] File vould not be created\n")); + } + + dsmcc_free_biop_file(BIOP_File); + + return GF_OK; +} + +static GF_Err dsmcc_process_biop_directory(GF_BitStream* bs,GF_M2TS_DSMCC_BIOP_HEADER* BIOP_Header,GF_M2TS_DSMCC_OVERLORD*dsmcc_overlord,Bool IsServiceGateway){ + + GF_M2TS_DSMCC_BIOP_DIRECTORY* BIOP_Directory; + GF_M2TS_DSMCC_DIR* Dir; + char* ParentName; + char* FileType; + u32 i; + GF_M2TS_DSMCC_SERVICE_GATEWAY* ServiceGateway; + + GF_SAFEALLOC(BIOP_Directory,GF_M2TS_DSMCC_BIOP_DIRECTORY); + + ServiceGateway = dsmcc_overlord->ServiceGateway; + + /* Get the Header */ + BIOP_Directory->Header = BIOP_Header; + + if(BIOP_Directory->Header->objectInfo_length != 0x0){ + GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[Process DSMCC] ObjectInfo_length value is not correct \n")); + return GF_CORRUPTED_DATA; + } + + if(IsServiceGateway){ + /* create a "dir" struct with no parent */ + Dir = (GF_M2TS_DSMCC_DIR*)ServiceGateway; + ServiceGateway->objectKey_data = BIOP_Directory->Header->objectKey_data; + ServiceGateway->parent = NULL; + ServiceGateway->name = (char*)gf_strdup(dsmcc_overlord->root_dir); + ParentName = ServiceGateway->name; + }else{ + /* get the dir related to the payload */ + Dir = dsmcc_get_directory(ServiceGateway->Dir,BIOP_Directory->Header->objectKey_data); + } + + BIOP_Directory->serviceContextList_count = gf_bs_read_int(bs,8); + if(BIOP_Directory->serviceContextList_count){ + BIOP_Directory->ServiceContext = (GF_M2TS_DSMCC_SERVICE_CONTEXT*)gf_calloc(BIOP_Directory->serviceContextList_count,sizeof(GF_M2TS_DSMCC_SERVICE_CONTEXT)); + dsmcc_biop_get_context(bs,BIOP_Directory->ServiceContext,BIOP_Directory->serviceContextList_count); + } + BIOP_Directory->messageBody_length = gf_bs_read_int(bs,32); + BIOP_Directory->bindings_count = gf_bs_read_int(bs,16); + BIOP_Directory->Name = (GF_M2TS_DSMCC_BIOP_NAME*)gf_calloc(BIOP_Directory->bindings_count,sizeof(GF_M2TS_DSMCC_BIOP_NAME)); + + /* Get the linked files */ + for(i = 0; ibindings_count;i++){ + u32 descr_length,nb_desc,j; + u8* descr_tag; + GF_Err e; + + BIOP_Directory->Name[i].nameComponents_count = gf_bs_read_int(bs,8); + BIOP_Directory->Name[i].id_length = gf_bs_read_int(bs,8); + if(BIOP_Directory->Name[i].id_length){ + BIOP_Directory->Name[i].id_data = (char*)gf_calloc(BIOP_Directory->Name[i].id_length,sizeof(char)); + gf_bs_read_data(bs,BIOP_Directory->Name[i].id_data,(u32)(BIOP_Directory->Name[i].id_length)); + } + BIOP_Directory->Name[i].kind_length = gf_bs_read_int(bs,8); + if(BIOP_Directory->Name[i].kind_length > 0x04){ + GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[Process DSMCC] kind_length value is not valid\n")); + return GF_CORRUPTED_DATA; + } + if(BIOP_Directory->Name[i].kind_length){ + BIOP_Directory->Name[i].kind_data = (char*)gf_calloc(BIOP_Directory->Name[i].kind_length,sizeof(char)); + gf_bs_read_data(bs,BIOP_Directory->Name[i].kind_data,(u32)(BIOP_Directory->Name[i].kind_length)); + } + + BIOP_Directory->Name[i].BindingType = gf_bs_read_int(bs,8); + /* IOR */ + e = dsmcc_biop_get_ior(bs,&BIOP_Directory->Name[i].IOR); + if(e){ + GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[Process DSMCC] Error in IOR processing\n")); + return GF_CORRUPTED_DATA; + } + + BIOP_Directory->Name[i].objectInfo_length = gf_bs_read_int(bs,16); + if(!strcmp(BIOP_Directory->Name[i].kind_data,"fil")){ + BIOP_Directory->Name[i].ContentSize = gf_bs_read_int(bs,64); + descr_length = BIOP_Directory->Name[i].objectInfo_length - 8; + }else{ + descr_length = BIOP_Directory->Name[i].objectInfo_length; + } + + if(descr_length){ + BIOP_Directory->Name[i].descriptor = gf_list_new(); + dsmcc_biop_descriptor(bs,BIOP_Directory->Name[i].descriptor,descr_length); + } + + gf_bs_read_int(bs,(u32)(descr_length)); + + nb_desc = gf_list_count(BIOP_Directory->Name[i].descriptor); + j = 0; + while(jName[i].descriptor ,0); + + switch(*descr_tag){ + case CONTENT_TYPE_DESCRIPTOR: + { + GF_M2TS_DSMCC_BIOP_CONTENT_TYPE_DESRIPTOR* ContentTypeDescr = (GF_M2TS_DSMCC_BIOP_CONTENT_TYPE_DESRIPTOR*)gf_list_get(BIOP_Directory->Name[i].descriptor ,j); + FileType = strdup(ContentTypeDescr->content_type_data_byte); + } + default: + { + GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[Process DSMCC] Unsupported descriptor Type \n")); + break; + } + } + j++; + } + + if(!strcmp(BIOP_Directory->Name[i].kind_data,"dir")){ + if(!dsmcc_check_element_validation(ServiceGateway->Dir,ServiceGateway->name,BIOP_Directory->Name[i])){ + GF_Err e; + GF_M2TS_DSMCC_DIR* Directory; + GF_M2TS_DSMCC_BIOP_TAGGED_PROFILE* taggedProfile = (GF_M2TS_DSMCC_BIOP_TAGGED_PROFILE*)gf_list_get(BIOP_Directory->Name[i].IOR.taggedProfile,0); + GF_SAFEALLOC(Directory,GF_M2TS_DSMCC_DIR); + Directory->name = (char*)gf_strdup(BIOP_Directory->Name[i].id_data); + Directory->File = gf_list_new(); + Directory->objectKey_data = taggedProfile->BIOPProfileBody->ObjectLocation.objectKey_data; + Directory->downloadId = taggedProfile->BIOPProfileBody->ObjectLocation.carouselId; + Directory->moduleId = taggedProfile->BIOPProfileBody->ObjectLocation.moduleId; + Directory->parent = Dir; + Directory->Path = dsmcc_get_file_namepath(Dir,Directory->name); + e = gf_mkdir(Directory->Path); + if(e){ + GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[Process DSMCC] Error during the creation of the directory %s \n",Directory->Path)); + } + gf_list_add(ServiceGateway->Dir,Directory); + } + }else if(!strcmp(BIOP_Directory->Name[i].kind_data,"fil")){ + if(!dsmcc_check_element_validation(ServiceGateway->File,ServiceGateway->name,BIOP_Directory->Name[i])){ + GF_M2TS_DSMCC_FILE* File; + GF_M2TS_DSMCC_BIOP_TAGGED_PROFILE* taggedProfile = (GF_M2TS_DSMCC_BIOP_TAGGED_PROFILE*)gf_list_get(BIOP_Directory->Name[i].IOR.taggedProfile,0); + GF_SAFEALLOC(File,GF_M2TS_DSMCC_FILE); + File->name = (char*)gf_strdup(BIOP_Directory->Name[i].id_data); + File->objectKey_data = taggedProfile->BIOPProfileBody->ObjectLocation.objectKey_data; + File->downloadId = taggedProfile->BIOPProfileBody->ObjectLocation.carouselId; + File->moduleId = taggedProfile->BIOPProfileBody->ObjectLocation.moduleId; + File->parent = Dir; + File->Path = dsmcc_get_file_namepath(Dir,File->name); + gf_list_add(ServiceGateway->File,File); + } + } + } + dsmcc_free_biop_directory(BIOP_Directory); + + return GF_OK; +} + +static GF_Err dsmcc_process_biop_stream_event(GF_BitStream* bs,GF_M2TS_DSMCC_BIOP_HEADER* BIOP_Header,GF_M2TS_DSMCC_SERVICE_GATEWAY* ServiceGateway){ + + u32 i; + GF_M2TS_DSMCC_BIOP_STREAM_EVENT* BIOP_StreamEvent; + //GF_M2TS_DSMCC_FILE* File; + u32 eventdata,Info_length; + + GF_SAFEALLOC(BIOP_StreamEvent,GF_M2TS_DSMCC_BIOP_STREAM_EVENT); + + eventdata = 0; + + BIOP_StreamEvent->Header = BIOP_Header; + /* Get Info */ + BIOP_StreamEvent->Info.aDescription_length = gf_bs_read_int(bs,8); + if(BIOP_StreamEvent->Info.aDescription_length){ + BIOP_StreamEvent->Info.aDescription_bytes = (char*)gf_calloc(BIOP_StreamEvent->Info.aDescription_length,sizeof(char)); + gf_bs_read_data(bs,BIOP_StreamEvent->Info.aDescription_bytes,(u8)(BIOP_StreamEvent->Info.aDescription_length)); + } + BIOP_StreamEvent->Info.duration_aSeconds = gf_bs_read_int(bs,32); + BIOP_StreamEvent->Info.duration_aMicroseconds = gf_bs_read_int(bs,32); + BIOP_StreamEvent->Info.audio = gf_bs_read_int(bs,8); + BIOP_StreamEvent->Info.video = gf_bs_read_int(bs,8); + BIOP_StreamEvent->Info.data = gf_bs_read_int(bs,8); + + /* Event List */ + BIOP_StreamEvent->eventNames_count = gf_bs_read_int(bs,16); + if(BIOP_StreamEvent->eventNames_count){ + BIOP_StreamEvent->EventList = (GF_M2TS_DSMCC_BIOP_EVENT_LIST*)gf_calloc(BIOP_StreamEvent->eventNames_count,sizeof(GF_M2TS_DSMCC_BIOP_EVENT_LIST)); + for(i=0;ieventNames_count;i++){ + BIOP_StreamEvent->EventList[i].eventName_length = gf_bs_read_int(bs,8); + eventdata += BIOP_StreamEvent->EventList[i].eventName_length; + if(BIOP_StreamEvent->EventList[i].eventName_length){ + BIOP_StreamEvent->EventList[i].eventName_data_byte = (char*)gf_calloc(BIOP_StreamEvent->EventList[i].eventName_length,sizeof(char)); + gf_bs_read_data(bs,BIOP_StreamEvent->EventList[i].eventName_data_byte,(u8)(BIOP_StreamEvent->EventList[i].eventName_length)); + } + } + } + + Info_length = BIOP_StreamEvent->Header->objectInfo_length - (BIOP_StreamEvent->Info.aDescription_length + 14 + BIOP_StreamEvent->eventNames_count + eventdata); + + BIOP_StreamEvent->serviceContextList_count = gf_bs_read_int(bs,8); + if(BIOP_StreamEvent->serviceContextList_count){ + BIOP_StreamEvent->ServiceContext = (GF_M2TS_DSMCC_SERVICE_CONTEXT*)gf_calloc(BIOP_StreamEvent->serviceContextList_count,sizeof(GF_M2TS_DSMCC_SERVICE_CONTEXT)); + dsmcc_biop_get_context(bs,BIOP_StreamEvent->ServiceContext,BIOP_StreamEvent->serviceContextList_count); + } + + BIOP_StreamEvent->messageBody_length = gf_bs_read_int(bs,32); + BIOP_StreamEvent->taps_count = gf_bs_read_int(bs,8); + BIOP_StreamEvent->Taps = (GF_M2TS_DSMCC_BIOP_TAPS*)gf_calloc(BIOP_StreamEvent->taps_count,sizeof(GF_M2TS_DSMCC_BIOP_TAPS)); + for(i=0;itaps_count;i++){ + BIOP_StreamEvent->Taps[i].id = gf_bs_read_int(bs,16); + BIOP_StreamEvent->Taps[i].use = gf_bs_read_int(bs,16); + BIOP_StreamEvent->Taps[i].assocTag = gf_bs_read_int(bs,16); + BIOP_StreamEvent->Taps[i].selector_length = gf_bs_read_int(bs,8); + if(BIOP_StreamEvent->Taps[i].selector_length != 0x00){ + GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[Process DSMCC] Error in Stream Event : selector_length has a wrong value, abording the processing \n")); + return GF_CORRUPTED_DATA; + } + } + BIOP_StreamEvent->eventIds_count = gf_bs_read_int(bs,8); + if(BIOP_StreamEvent->eventIds_count != BIOP_StreamEvent->eventNames_count){ + GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[Process DSMCC] Error in Stream Event : eventIds_count has a wrong value, abording the processing \n")); + return GF_CORRUPTED_DATA; + }else if(BIOP_StreamEvent->eventIds_count){ + BIOP_StreamEvent->eventId = (u16*)gf_calloc(BIOP_StreamEvent->eventIds_count,sizeof(u16)); + for(i =0;ieventIds_count;i++){ + BIOP_StreamEvent->eventId[i] = gf_bs_read_int(bs,16); + } + } + + dsmcc_free_biop_stream_event(BIOP_StreamEvent); + + return GF_OK; +} + +static GF_Err dsmcc_process_biop_stream_message(GF_BitStream* bs,GF_M2TS_DSMCC_BIOP_HEADER* BIOP_Header,GF_M2TS_DSMCC_SERVICE_GATEWAY* ServiceGateway){ + + u32 i; + GF_M2TS_DSMCC_BIOP_STREAM_MESSAGE* BIOP_StreamMessage; + u32 eventdata,Info_length; + + GF_SAFEALLOC(BIOP_StreamMessage,GF_M2TS_DSMCC_BIOP_STREAM_MESSAGE); + + eventdata = 0; + + BIOP_StreamMessage->Header = BIOP_Header; + /* Get Info */ + BIOP_StreamMessage->Info.aDescription_length = gf_bs_read_int(bs,8); + if(BIOP_StreamMessage->Info.aDescription_length){ + BIOP_StreamMessage->Info.aDescription_bytes = (char*)gf_calloc(BIOP_StreamMessage->Info.aDescription_length,sizeof(char)); + gf_bs_read_data(bs,BIOP_StreamMessage->Info.aDescription_bytes,(u8)(BIOP_StreamMessage->Info.aDescription_length)); + } + BIOP_StreamMessage->Info.duration_aSeconds = gf_bs_read_int(bs,32); + BIOP_StreamMessage->Info.duration_aMicroseconds = gf_bs_read_int(bs,32); + BIOP_StreamMessage->Info.audio = gf_bs_read_int(bs,8); + BIOP_StreamMessage->Info.video = gf_bs_read_int(bs,8); + BIOP_StreamMessage->Info.data = gf_bs_read_int(bs,8); + + + Info_length = BIOP_StreamMessage->Header->objectInfo_length - (BIOP_StreamMessage->Info.aDescription_length + 10); + + BIOP_StreamMessage->serviceContextList_count = gf_bs_read_int(bs,8); + if(BIOP_StreamMessage->serviceContextList_count){ + BIOP_StreamMessage->ServiceContext = (GF_M2TS_DSMCC_SERVICE_CONTEXT*)gf_calloc(BIOP_StreamMessage->serviceContextList_count,sizeof(GF_M2TS_DSMCC_SERVICE_CONTEXT)); + dsmcc_biop_get_context(bs,BIOP_StreamMessage->ServiceContext,BIOP_StreamMessage->serviceContextList_count); + } + + BIOP_StreamMessage->messageBody_length = gf_bs_read_int(bs,32); + BIOP_StreamMessage->taps_count = gf_bs_read_int(bs,8); + BIOP_StreamMessage->Taps = (GF_M2TS_DSMCC_BIOP_TAPS*)gf_calloc(BIOP_StreamMessage->taps_count,sizeof(GF_M2TS_DSMCC_BIOP_TAPS)); + for(i=0;itaps_count;i++){ + BIOP_StreamMessage->Taps[i].id = gf_bs_read_int(bs,16); + BIOP_StreamMessage->Taps[i].use = gf_bs_read_int(bs,16); + BIOP_StreamMessage->Taps[i].assocTag = gf_bs_read_int(bs,16); + BIOP_StreamMessage->Taps[i].selector_length = gf_bs_read_int(bs,8); + if(BIOP_StreamMessage->Taps[i].selector_length != 0x00){ + GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[Process DSMCC] Error in Stream Event : selector_length has a wrong value, abording the processing \n")); + return GF_CORRUPTED_DATA; + } + } + + dsmcc_free_biop_stream_message(BIOP_StreamMessage); + + return GF_OK; +} + + +/* Tools */ + +static GF_M2TS_DSMCC_MODULE* dsmcc_create_module(GF_M2TS_DSMCC_OVERLORD* dsmcc_overlord, GF_M2TS_DSMCC_DOWNLOAD_INFO_RESP_INDIC* DownloadInfoIndication) +{ + u32 module_index; + GF_M2TS_DSMCC_MODULE* dsmcc_module; + GF_SAFEALLOC(dsmcc_module,GF_M2TS_DSMCC_MODULE); + dsmcc_module->downloadId = DownloadInfoIndication->downloadId; + dsmcc_module->moduleId = DownloadInfoIndication->Modules.moduleId; + dsmcc_module->size = DownloadInfoIndication->Modules.moduleSize; + dsmcc_module->version_number = DownloadInfoIndication->Modules.moduleVersion; + dsmcc_module->block_size = DownloadInfoIndication->blockSize; + dsmcc_module->buffer = (char*)gf_calloc(dsmcc_module->size,sizeof(char)); + module_index = gf_list_count(dsmcc_overlord->dsmcc_modules); + dsmcc_overlord->processed[module_index].moduleId = dsmcc_module->moduleId; + dsmcc_overlord->processed[module_index].downloadId = dsmcc_module->downloadId; + dsmcc_overlord->processed[module_index].version_number = dsmcc_module->version_number; + gf_list_add(dsmcc_overlord->dsmcc_modules,dsmcc_module); + + return dsmcc_module; +} + +static void dsmcc_biop_descriptor(GF_BitStream* bs,GF_List* list,u32 size){ + u8 descr_tag; + u32 data_shift,start_pos; + + start_pos = (u32)(gf_bs_get_position(bs)); + data_shift = 0; + + while(size > data_shift){ + descr_tag = gf_bs_read_int(bs,8); + + switch(descr_tag){ + + case CACHING_PRIORITY_DESCRIPTOR: + { + GF_M2TS_DSMCC_BIOP_CACHING_PRIORITY_DESCRIPTOR* CachingPriorityDescr; + GF_SAFEALLOC(CachingPriorityDescr,GF_M2TS_DSMCC_BIOP_CACHING_PRIORITY_DESCRIPTOR); + CachingPriorityDescr->descriptor_tag = descr_tag; + CachingPriorityDescr->descriptor_length = gf_bs_read_int(bs,8); + CachingPriorityDescr->priority_value = gf_bs_read_int(bs,8); + CachingPriorityDescr->transparency_level = gf_bs_read_int(bs,8); + gf_list_add(list,CachingPriorityDescr); + break; + } + case COMPRESSED_MODULE_DESCRIPTOR: + { + GF_M2TS_DSMCC_BIOP_COMPRESSED_MODULE_DESCRIPTOR* CompModuleDescr; + GF_SAFEALLOC(CompModuleDescr,GF_M2TS_DSMCC_BIOP_COMPRESSED_MODULE_DESCRIPTOR); + CompModuleDescr->descriptor_tag = descr_tag; + CompModuleDescr->descriptor_length = gf_bs_read_int(bs,8); + CompModuleDescr->compression_method = gf_bs_read_int(bs,8); + /* if CompModuleDescr->compression_method least significant nibble is eq to 0x08, the terminal shall support the Deflate compression algorithm (GZIP) */ + CompModuleDescr->original_size = gf_bs_read_int(bs,32); + gf_list_add(list,CompModuleDescr); + break; + + } + case CONTENT_TYPE_DESCRIPTOR: + { + GF_M2TS_DSMCC_BIOP_CONTENT_TYPE_DESRIPTOR* ContentTypeDescr; + GF_SAFEALLOC(ContentTypeDescr,GF_M2TS_DSMCC_BIOP_CONTENT_TYPE_DESRIPTOR); + ContentTypeDescr->descriptor_tag = descr_tag; + ContentTypeDescr->descriptor_length = gf_bs_read_int(bs,8); + ContentTypeDescr->content_type_data_byte = (char*)gf_calloc(ContentTypeDescr->descriptor_length,sizeof(char)); + gf_bs_read_data(bs,ContentTypeDescr->content_type_data_byte,(u32)(ContentTypeDescr->descriptor_length)); + gf_list_add(list,ContentTypeDescr); + } + default: + { + u32 size; + GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[Process DSMCC] Unsupported Descriptor Type \n")); + /* byte shift - in descriptors length is on thr second byte*/ + size = gf_bs_read_int(bs,8); + gf_bs_read_int(bs,8*size); + break; + } + } + data_shift = (u32)(gf_bs_get_position(bs)) - start_pos; + } +} + +static void dsmcc_biop_get_context(GF_BitStream* bs,GF_M2TS_DSMCC_SERVICE_CONTEXT* Context,u32 serviceContextList_count) +{ + u32 i; + + for(i=0;itype_id_length = gf_bs_read_int(bs,32); + if(IOR->type_id_length > 0xA){ + //return GF_CORRUPTED_DATA; + } + IOR->type_id_byte = (char*)gf_calloc(IOR->type_id_length,sizeof(char)); + gf_bs_read_data(bs,IOR->type_id_byte,(u32)(IOR->type_id_length)); + IOR->taggedProfiles_count = gf_bs_read_int(bs,32); + IOR->taggedProfile = gf_list_new(); + for(i = 0; i < IOR->taggedProfiles_count;i++){ + GF_M2TS_DSMCC_BIOP_TAGGED_PROFILE* taggedProfile; + GF_SAFEALLOC(taggedProfile,GF_M2TS_DSMCC_BIOP_TAGGED_PROFILE); + gf_list_add(IOR->taggedProfile,taggedProfile); + taggedProfile->profileId_tag = gf_bs_read_int(bs,32); + taggedProfile->profile_data_length = gf_bs_read_int(bs,32); + taggedProfile->profile_data_byte_order = gf_bs_read_int(bs,8); + if(taggedProfile->profile_data_byte_order != 0x00){ + GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[Process DSMCC] Error in IOR processing : profile data byte order has a wrong value, abording the processing \n")); + return GF_CORRUPTED_DATA; + } + taggedProfile->lite_component_count = gf_bs_read_int(bs,8); + left_lite_component = taggedProfile->lite_component_count; + switch(taggedProfile->profileId_tag){ + case TAG_BIOP: + { + /* Object Location */ + u32 j; + GF_SAFEALLOC(taggedProfile->BIOPProfileBody,GF_M2TS_DSMCC_BIOP_PROFILE_BODY); + taggedProfile->BIOPProfileBody->ObjectLocation.componentId_tag = gf_bs_read_int(bs,32); + if(taggedProfile->BIOPProfileBody->ObjectLocation.componentId_tag != 0x49534F50){ + GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[Process DSMCC] Error in Profile Body : component tag has a wrong value, abording the processing \n")); + return GF_CORRUPTED_DATA; + } + taggedProfile->BIOPProfileBody->ObjectLocation.component_data_length = gf_bs_read_int(bs,8); + taggedProfile->BIOPProfileBody->ObjectLocation.carouselId = gf_bs_read_int(bs,32); + taggedProfile->BIOPProfileBody->ObjectLocation.moduleId = gf_bs_read_int(bs,16); + taggedProfile->BIOPProfileBody->ObjectLocation.version_major = gf_bs_read_int(bs,8); + if(taggedProfile->BIOPProfileBody->ObjectLocation.version_major != 0x01){ + GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[Process DSMCC] Error in Profile Body : version major has a wrong value, abording the processing \n")); + return GF_CORRUPTED_DATA; + } + taggedProfile->BIOPProfileBody->ObjectLocation.version_minor = gf_bs_read_int(bs,8); + if(taggedProfile->BIOPProfileBody->ObjectLocation.version_minor != 0x00){ + GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[Process DSMCC] Error in Profile Body : version minor has a wrong value, abording the processing \n")); + return GF_CORRUPTED_DATA; + } + taggedProfile->BIOPProfileBody->ObjectLocation.objectKey_length = gf_bs_read_int(bs,8); + if(taggedProfile->BIOPProfileBody->ObjectLocation.objectKey_length){ + taggedProfile->BIOPProfileBody->ObjectLocation.objectKey_data = gf_bs_read_int(bs,taggedProfile->BIOPProfileBody->ObjectLocation.objectKey_length*8); + } + /* ConnBinder */ + + taggedProfile->BIOPProfileBody->ConnBinder.componentId_tag = gf_bs_read_int(bs,32); + if(taggedProfile->BIOPProfileBody->ConnBinder.componentId_tag != 0x49534F40){ + GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[Process DSMCC] Error in ConnBinder : componentId tag has a wrong value, abording the processing \n")); + return GF_CORRUPTED_DATA; + } + taggedProfile->BIOPProfileBody->ConnBinder.component_data_length = gf_bs_read_int(bs,8); + taggedProfile->BIOPProfileBody->ConnBinder.taps_count = gf_bs_read_int(bs,8); + taggedProfile->BIOPProfileBody->ConnBinder.Taps = (GF_M2TS_DSMCC_BIOP_TAPS*)gf_calloc(taggedProfile->BIOPProfileBody->ConnBinder.taps_count,sizeof(GF_M2TS_DSMCC_BIOP_TAPS)); + for(j=0;jBIOPProfileBody->ConnBinder.taps_count;j++){ + taggedProfile->BIOPProfileBody->ConnBinder.Taps[i].id = gf_bs_read_int(bs,16); + if(taggedProfile->BIOPProfileBody->ConnBinder.Taps[i].id != 0x00){ + GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[Process DSMCC] Error in ConnBinder : id has a wrong value, abording the processing \n")); + return GF_CORRUPTED_DATA; + } + taggedProfile->BIOPProfileBody->ConnBinder.Taps[i].use = gf_bs_read_int(bs,16); + if(taggedProfile->BIOPProfileBody->ConnBinder.Taps[i].use != 0x016){ + GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[Process DSMCC] Error in ConnBinder : use has a wrong value, abording the processing \n")); + return GF_CORRUPTED_DATA; + } + taggedProfile->BIOPProfileBody->ConnBinder.Taps[i].assocTag = gf_bs_read_int(bs,16); + taggedProfile->BIOPProfileBody->ConnBinder.Taps[i].selector_length = gf_bs_read_int(bs,8); + if(taggedProfile->BIOPProfileBody->ConnBinder.Taps[i].selector_length != 0x0A){ + GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[Process DSMCC] Error in ConnBinder : selector_length has a wrong value, abording the processing \n")); + return GF_CORRUPTED_DATA; + } + taggedProfile->BIOPProfileBody->ConnBinder.Taps[i].selector_type = gf_bs_read_int(bs,16); + if(taggedProfile->BIOPProfileBody->ConnBinder.Taps[i].selector_type != 0x001){ + GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[Process DSMCC] Error in ConnBinder : selector_type has a wrong value, abording the processing \n")); + return GF_CORRUPTED_DATA; + } + taggedProfile->BIOPProfileBody->ConnBinder.Taps[i].transactionId = gf_bs_read_int(bs,32); + taggedProfile->BIOPProfileBody->ConnBinder.Taps[i].timeout = gf_bs_read_int(bs,32); + } + if(taggedProfile->BIOPProfileBody->ConnBinder.component_data_length-18 != 0){ + taggedProfile->BIOPProfileBody->ConnBinder.additional_tap_byte = (char*)gf_calloc(taggedProfile->BIOPProfileBody->ConnBinder.component_data_length-18,sizeof(char)); + gf_bs_read_data(bs,taggedProfile->BIOPProfileBody->ConnBinder.additional_tap_byte,(u32)(taggedProfile->BIOPProfileBody->ConnBinder.component_data_length-18)); + } + left_lite_component = left_lite_component - 2; + break; + } + case TAG_LITE_OPTIONS: + { + /* Service Location */ + u32 j; + GF_SAFEALLOC(taggedProfile->ServiceLocation,GF_M2TS_DSMCC_BIOP_SERVICE_LOCATION); + taggedProfile->ServiceLocation->componentId_tag = gf_bs_read_int(bs,32); + if(taggedProfile->ServiceLocation->componentId_tag != 0x49534F46){ + GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[Process DSMCC] Error in Service Location : component tag has a wrong value, abording the processing \n")); + return GF_CORRUPTED_DATA; + } + taggedProfile->ServiceLocation->component_data_length = gf_bs_read_int(bs,8); + taggedProfile->ServiceLocation->serviceDomain_length = gf_bs_read_int(bs,8); + if(taggedProfile->ServiceLocation->serviceDomain_length != 0x14){ + GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[Process DSMCC] Error in Service Location : Service Domain Length tag has a wrong value, abording the processing \n")); + return GF_CORRUPTED_DATA; + } + taggedProfile->ServiceLocation->serviceDomain_data.AFI = gf_bs_read_int(bs,8); + taggedProfile->ServiceLocation->serviceDomain_data.type = gf_bs_read_int(bs,8); + taggedProfile->ServiceLocation->serviceDomain_data.carouselId = gf_bs_read_int(bs,32); + taggedProfile->ServiceLocation->serviceDomain_data.specifierType = gf_bs_read_int(bs,8); + taggedProfile->ServiceLocation->serviceDomain_data.specifierData = gf_bs_read_int(bs,24); + taggedProfile->ServiceLocation->serviceDomain_data.transport_stream_id = gf_bs_read_int(bs,16); + taggedProfile->ServiceLocation->serviceDomain_data.original_network_id = gf_bs_read_int(bs,16); + taggedProfile->ServiceLocation->serviceDomain_data.service_id = gf_bs_read_int(bs,16); + taggedProfile->ServiceLocation->serviceDomain_data.reserved = gf_bs_read_int(bs,32); + if(taggedProfile->ServiceLocation->serviceDomain_data.reserved != 0xFFFFFFFF){ + GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[Process DSMCC] Error in Service Domain Data : reserved has a wrong value, abording the processing \n")); + return GF_CORRUPTED_DATA; + } + taggedProfile->ServiceLocation->nameComponents_count = gf_bs_read_int(bs,32); + taggedProfile->ServiceLocation->NameComponent = (GF_M2TS_DSMCC_BIOP_NAME_COMPONENT*)gf_calloc(taggedProfile->ServiceLocation->nameComponents_count,sizeof(GF_M2TS_DSMCC_BIOP_NAME_COMPONENT)); + for(j = 0; j < taggedProfile->ServiceLocation->nameComponents_count; j++){ + taggedProfile->ServiceLocation->NameComponent[j].id_length = gf_bs_read_int(bs,32); + if(taggedProfile->ServiceLocation->NameComponent[j].id_length != 0){ + taggedProfile->ServiceLocation->NameComponent[j].id_data = (char*)gf_calloc(taggedProfile->ServiceLocation->NameComponent[j].id_length,sizeof(char)); + gf_bs_read_data(bs,taggedProfile->ServiceLocation->NameComponent[i].id_data,(u32)(taggedProfile->ServiceLocation->NameComponent[j].id_length)); + } + taggedProfile->ServiceLocation->NameComponent[j].kind_length = gf_bs_read_int(bs,32); + if(taggedProfile->ServiceLocation->NameComponent[j].kind_length != 0){ + taggedProfile->ServiceLocation->NameComponent[j].kind_data = (char*)gf_calloc(taggedProfile->ServiceLocation->NameComponent[j].kind_length,sizeof(char)); + gf_bs_read_data(bs,taggedProfile->ServiceLocation->NameComponent[j].kind_data,(u32)(taggedProfile->ServiceLocation->NameComponent[j].kind_length)); + } + } + taggedProfile->ServiceLocation->initialContext_length = gf_bs_read_int(bs,8); + if(taggedProfile->ServiceLocation->initialContext_length != 0){ + taggedProfile->ServiceLocation->InitialContext_data_byte = (char*)gf_calloc(taggedProfile->ServiceLocation->initialContext_length,sizeof(char)); + gf_bs_read_data(bs,taggedProfile->ServiceLocation->InitialContext_data_byte,(u32)(taggedProfile->ServiceLocation->initialContext_length)); + } + left_lite_component = left_lite_component - 1; + break; + } + default: + { + GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[Process DSMCC] Unsupported Service gateway profile tag \n")); + break; + } + } + taggedProfile->LiteComponent = (GF_M2TS_DSMCC_BIOP_LITE_COMPONENT*)gf_calloc(left_lite_component,sizeof(GF_M2TS_DSMCC_BIOP_LITE_COMPONENT)); + for(i = 0; iLiteComponent[i].componentId_tag = gf_bs_read_int(bs,32); + taggedProfile->LiteComponent[i].component_data_length = gf_bs_read_int(bs,8); + if(taggedProfile->LiteComponent[i].component_data_length != 0){ + taggedProfile->LiteComponent[i].component_data_byte = (char*)gf_calloc(taggedProfile->ServiceLocation->initialContext_length,sizeof(char)); + gf_bs_read_data(bs,taggedProfile->LiteComponent[i].component_data_byte,(u32)(taggedProfile->LiteComponent[i].component_data_length)); + } + + } + + } + return GF_OK; +} + +static GF_Err dsmcc_check_element_validation(GF_List* List,char* Parent_name,GF_M2TS_DSMCC_BIOP_NAME Name) +{ + u32 nb_elt,i; + + GF_M2TS_DSMCC_BIOP_TAGGED_PROFILE* taggedProfile = (GF_M2TS_DSMCC_BIOP_TAGGED_PROFILE*)gf_list_get(Name.IOR.taggedProfile,0); + nb_elt = gf_list_count(List); + + for(i = 0; i < nb_elt;i++){ + GF_M2TS_DSMCC_FILE* File = (GF_M2TS_DSMCC_FILE*)gf_list_get(List,i); + if(!strcmp(File->name,Name.id_data) && !strcmp(File->parent,Parent_name) && + (File->objectKey_data == taggedProfile->BIOPProfileBody->ObjectLocation.objectKey_data) && File->moduleId == taggedProfile->BIOPProfileBody->ObjectLocation.moduleId){ + /* File or Dir already received */ + return GF_BAD_PARAM; + } + } + + return GF_OK; +} + +static GF_M2TS_DSMCC_DIR* dsmcc_get_directory(GF_List* List, u32 objectKey_data){ + u32 nb_elt,i; + + nb_elt = gf_list_count(List); + + for(i = 0;iobjectKey_data == objectKey_data){ + return Dir; + } + if(gf_list_count(Dir->Dir)){ + TmpDir = dsmcc_get_directory(Dir->Dir,objectKey_data); + if(TmpDir){ + return TmpDir; + } + } + } + return NULL; +} + +static GF_M2TS_DSMCC_FILE* dsmcc_get_file(GF_List* ServiceGateway_List, u16 moduleId,u32 downloadId,u32 objectKey_data){ + u32 nb_elt,i; + nb_elt = gf_list_count(ServiceGateway_List); + for(i = 0;iobjectKey_data == objectKey_data) && File->moduleId == moduleId && File->downloadId == downloadId){ + return File; + } + } + return NULL; +} + +static char* dsmcc_get_file_namepath(GF_M2TS_DSMCC_DIR* Dir,char* name){ + char* Path; + GF_M2TS_DSMCC_DIR* directory = Dir; + + Path = gf_calloc(512,sizeof(char)); + sprintf(Path,"%s%c%s",directory->name,GF_PATH_SEPARATOR,name); + while(directory->parent != NULL){ + GF_M2TS_DSMCC_DIR* tempdir; + char* tempPath = gf_strdup(Path); + tempdir = (GF_M2TS_DSMCC_DIR*)directory->parent; + sprintf(Path,"%s%c%s",tempdir->name,GF_PATH_SEPARATOR,tempPath); + gf_free(tempPath); + directory = tempdir; + } + return Path; +} + + +/* Free struct */ + +static void dsmcc_delete_module(GF_M2TS_DSMCC_MODULE* module) +{ + if(module->buffer){ + gf_free(module->buffer); + } + + gf_free(module); +} + +static void dmscc_delete_file(GF_M2TS_DSMCC_FILE* File){ + + if(File->name){ + gf_free(File->name); + } + if(File->Path){ + gf_free(File->Path); + } +} + +static void dmscc_delete_dir(GF_M2TS_DSMCC_DIR* Dir){ + + u32 nb_file, nb_dir,i; + + if(Dir->name){ + gf_free(Dir->name); + } + if(Dir->Path){ + gf_free(Dir->Path); + } + + nb_file = gf_list_count(Dir->File); + for(i=0;iFile,i); + dmscc_delete_file(File); + } + gf_list_del(Dir->File); + + nb_dir = gf_list_count(Dir->Dir); + for(i=0;iDir,i); + dmscc_delete_dir(Sub_Dir); + } + gf_list_del(Dir->Dir); + gf_free(Dir); +} + +static void dmscc_delete_servicegateway(GF_M2TS_DSMCC_SERVICE_GATEWAY* Servicegateway){ + + u32 nb_file, nb_dir,i; + + if(Servicegateway->name){ + gf_free(Servicegateway->name); + } + nb_file = gf_list_count(Servicegateway->File); + for(i=0;iFile,i); + dmscc_delete_file(File); + } + gf_list_del(Servicegateway->File); + + nb_dir = gf_list_count(Servicegateway->Dir); + for(i=0;iDir,i); + dmscc_delete_dir(Dir); + } + gf_list_del(Servicegateway->Dir); + gf_free(Servicegateway); +} + +void gf_m2ts_delete_dsmcc_overlord(GF_M2TS_DSMCC_OVERLORD* dsmcc_overlord) +{ + u16 nb_module,i; + + nb_module = gf_list_count(dsmcc_overlord->dsmcc_modules); + for(i=0;idsmcc_modules,i); + dsmcc_delete_module(module); + } + gf_list_del(dsmcc_overlord->dsmcc_modules); + + nb_module = gf_list_count(dsmcc_overlord->Unprocessed_module); + for(i=0;iUnprocessed_module,i); + dsmcc_delete_module(module); + } + gf_list_del(dsmcc_overlord->dsmcc_modules); + dmscc_delete_servicegateway(dsmcc_overlord->ServiceGateway); + + if(dsmcc_overlord->root_dir){ + gf_free(dsmcc_overlord->root_dir); + } + + gf_free(dsmcc_overlord); +} + +static void dsmcc_free_biop_header(GF_M2TS_DSMCC_BIOP_HEADER* BIOP_Header){ + + gf_free(BIOP_Header->objectKind_data); + gf_free(BIOP_Header); +} + +static void dsmcc_free_biop_directory(GF_M2TS_DSMCC_BIOP_DIRECTORY* BIOP_Directory){ + + gf_free(BIOP_Directory->objectInfo_data); + dsmcc_free_biop_context(BIOP_Directory->ServiceContext,BIOP_Directory->serviceContextList_count); + dsmcc_free_biop_name(BIOP_Directory->Name,BIOP_Directory->bindings_count); + gf_free(BIOP_Directory); +} + +static void dsmcc_free_biop_file(GF_M2TS_DSMCC_BIOP_FILE* BIOP_File){ + + dsmcc_free_biop_descriptor(BIOP_File->descriptor); + dsmcc_free_biop_context(BIOP_File->ServiceContext,BIOP_File->serviceContextList_count); + if(BIOP_File->content_length){ + gf_free(BIOP_File->content_byte); + } + gf_free(BIOP_File); +} + +static void dsmcc_free_biop_stream_event(GF_M2TS_DSMCC_BIOP_STREAM_EVENT* BIOP_StreamEvent){ + + u32 i; + + if(BIOP_StreamEvent->Info.aDescription_length){ + gf_free(BIOP_StreamEvent->Info.aDescription_bytes); + } + + if(BIOP_StreamEvent->objectInfo_byte){ + gf_free(BIOP_StreamEvent->objectInfo_byte); + } + + if(BIOP_StreamEvent->eventNames_count){ + for(i=0;ieventNames_count;i++){ + if(BIOP_StreamEvent->EventList[i].eventName_length){ + gf_free(BIOP_StreamEvent->EventList[i].eventName_data_byte); + } + } + gf_free(BIOP_StreamEvent->EventList); + } + + if(BIOP_StreamEvent->taps_count){ + gf_free(BIOP_StreamEvent->Taps); + } + + dsmcc_free_biop_context(BIOP_StreamEvent->ServiceContext,BIOP_StreamEvent->serviceContextList_count); + + if(BIOP_StreamEvent->eventId){ + gf_free(BIOP_StreamEvent->eventId); + } +} + +static void dsmcc_free_biop_stream_message(GF_M2TS_DSMCC_BIOP_STREAM_MESSAGE* BIOP_StreamMessage){ + + if(BIOP_StreamMessage->Info.aDescription_length){ + gf_free(BIOP_StreamMessage->Info.aDescription_bytes); + } + + if(BIOP_StreamMessage->objectInfo_byte){ + gf_free(BIOP_StreamMessage->objectInfo_byte); + } + + if(BIOP_StreamMessage->taps_count){ + gf_free(BIOP_StreamMessage->Taps); + } + + dsmcc_free_biop_context(BIOP_StreamMessage->ServiceContext,BIOP_StreamMessage->serviceContextList_count); +} + +static void dsmcc_free_biop_ior(GF_M2TS_DSMCC_IOR* IOR) +{ + u32 i,left_lite_component; + + if(IOR->type_id_length){ + gf_free(IOR->type_id_byte); + } + for(i = 0; i < IOR->taggedProfiles_count;i++){ + GF_M2TS_DSMCC_BIOP_TAGGED_PROFILE* taggedProfile = gf_list_get(IOR->taggedProfile,0); + left_lite_component = taggedProfile->lite_component_count; + + switch(taggedProfile->profileId_tag){ + + case TAG_BIOP: + { + /* Object Location */ + GF_SAFEALLOC(taggedProfile->BIOPProfileBody,GF_M2TS_DSMCC_BIOP_PROFILE_BODY); + + gf_free(taggedProfile->BIOPProfileBody->ConnBinder.Taps); + if(taggedProfile->BIOPProfileBody->ConnBinder.component_data_length-18 != 0){ + gf_free(taggedProfile->BIOPProfileBody->ConnBinder.additional_tap_byte); + } + gf_free(taggedProfile->BIOPProfileBody); + left_lite_component = left_lite_component - 2; + break; + } + case TAG_LITE_OPTIONS: + { + /* Service Location */ + u32 j; + + for(j = 0; j < taggedProfile->ServiceLocation->nameComponents_count; j++){ + + if(taggedProfile->ServiceLocation->NameComponent[j].id_length != 0){ + gf_free(taggedProfile->ServiceLocation->NameComponent[j].id_data); + } + if(taggedProfile->ServiceLocation->NameComponent[j].kind_length != 0){ + gf_free(taggedProfile->ServiceLocation->NameComponent[j].kind_data); + } + } + gf_free(taggedProfile->ServiceLocation->NameComponent); + + if(taggedProfile->ServiceLocation->initialContext_length != 0){ + gf_free(taggedProfile->ServiceLocation->InitialContext_data_byte); + } + left_lite_component = left_lite_component - 1; + break; + } + } + + for(i = 0; iLiteComponent[i].component_data_length != 0){ + gf_free(taggedProfile->LiteComponent[i].component_data_byte); + } + + } + gf_free(taggedProfile->LiteComponent); + gf_list_rem(IOR->taggedProfile,0); + gf_free(taggedProfile); + } + gf_list_del(IOR->taggedProfile); +} + +static void dsmcc_free_biop_descriptor(GF_List* list){ + u8* descr_tag; + u32 descr_count; + + descr_count = gf_list_count(list); + + + while(gf_list_count(list)){ + + descr_tag = (u8*)gf_list_get(list,0); + + switch(*descr_tag){ + + case CACHING_PRIORITY_DESCRIPTOR: + { + GF_M2TS_DSMCC_BIOP_CACHING_PRIORITY_DESCRIPTOR* CachingPriorityDescr = (GF_M2TS_DSMCC_BIOP_CACHING_PRIORITY_DESCRIPTOR*)gf_list_get(list,0); + gf_list_rem(list,0); + gf_free(CachingPriorityDescr); + break; + } + case COMPRESSED_MODULE_DESCRIPTOR: + { + GF_M2TS_DSMCC_BIOP_COMPRESSED_MODULE_DESCRIPTOR* CompModuleDescr = (GF_M2TS_DSMCC_BIOP_COMPRESSED_MODULE_DESCRIPTOR*)gf_list_get(list,0); + gf_list_rem(list,0); + gf_free(CompModuleDescr); + break; + + } + case CONTENT_TYPE_DESCRIPTOR: + { + GF_M2TS_DSMCC_BIOP_CONTENT_TYPE_DESRIPTOR* ContentTypeDescr = (GF_M2TS_DSMCC_BIOP_CONTENT_TYPE_DESRIPTOR*)gf_list_get(list,0); + gf_list_rem(list,0); + if(ContentTypeDescr->descriptor_length){ + gf_free(ContentTypeDescr->content_type_data_byte); + } + gf_free(ContentTypeDescr); + break; + } + default: + { + + break; + } + } + + } + gf_list_del(list); +} + + +static void dsmcc_free_biop_name(GF_M2TS_DSMCC_BIOP_NAME* Name, u32 nb_name){ + u32 i; + + for(i =0;i=1900 && *year<=2100 && *month && *month<12 && *day && *day<=31); + assert(*year>=1900 && *year<=2100 && *month && *month<=12 && *day && *day<=31); } #if 0 /*disabled since mktime doesn't exist on Windows Mobile*/ diff --git a/src/media_tools/dvb_mpe.c b/src/media_tools/dvb_mpe.c index 9562f81..0349a32 100644 --- a/src/media_tools/dvb_mpe.c +++ b/src/media_tools/dvb_mpe.c @@ -2,16 +2,16 @@ #include #include + +#ifndef GPAC_DISABLE_MPEG2TS + + static void gf_m2ts_Delete_IpPacket(GF_M2TS_IP_Packet *ip_packet); static void empty_list(GF_List * list) { void *obj; - u32 nb; - - nb = gf_list_count(list); - while(gf_list_count(list)){ obj = gf_list_get(list,0); gf_list_rem(list,0); @@ -40,11 +40,13 @@ static void on_dvb_mpe_section(GF_M2TS_Demuxer *ts, u32 evt_type, void *par) case GF_M2TS_TABLE_ID_MPE_FEC: case GF_M2TS_TABLE_ID_DSM_CC_PRIVATE: - if(ts->ip_platform != NULL) { - gf_m2ts_process_mpe(ts, (GF_M2TS_SECTION_MPE*)pck->stream, data, u32_data_size, u32_table_id); - } else { + if ((ts->ip_platform != NULL) || ts->direct_mpe) { + GF_M2TS_SECTION_MPE* mpe = (GF_M2TS_SECTION_MPE*)pck->stream; + gf_m2ts_process_mpe(ts, mpe, data, u32_data_size, u32_table_id); + } + else { //printf("Time Slice Parameters for MPE-FEC have not been found yet \n"); - } + } break; default: return; @@ -70,7 +72,6 @@ void gf_dvb_mpe_init(GF_M2TS_Demuxer *ts) void gf_dvb_mpe_shutdown(GF_M2TS_Demuxer *ts) { - u32 i_streams, i_targets; GF_M2TS_IP_Stream *ip_stream_buff; GF_M2TS_IP_PLATFORM * ip_platform; @@ -80,8 +81,6 @@ void gf_dvb_mpe_shutdown(GF_M2TS_Demuxer *ts) if (!ip_platform) return; - i_streams = 0; - i_targets = 0; if (ip_platform->ip_streams){ while(gf_list_count(ip_platform->ip_streams)){ ip_stream_buff=gf_list_get(ip_platform->ip_streams, 0); @@ -117,6 +116,7 @@ GF_M2TS_ES *gf_dvb_mpe_section_new() GF_M2TS_SECTION_MPE *ses; GF_SAFEALLOC(ses, GF_M2TS_SECTION_MPE); + ses->mff = NULL; es = (GF_M2TS_ES *)ses; es->flags = GF_M2TS_ES_IS_SECTION | GF_M2TS_ES_IS_MPE; return es; @@ -159,12 +159,11 @@ void gf_m2ts_process_mpe(GF_M2TS_Demuxer *ts, GF_M2TS_SECTION_MPE *mpe, unsigned { GF_M2TS_IP_Stream *ip_stream_buff; GF_M2TS_IP_PLATFORM * ip_platform = ts->ip_platform; - u32 delta_t; u32 table_boundry_flag; u32 frame_boundry_flag; u32 offset; u32 i_streams,j; - u32 id,section_length, section_number, last_section_number; + u32 section_number, last_section_number; s32 len_left = data_size; assert( ts ); @@ -178,11 +177,9 @@ void gf_m2ts_process_mpe(GF_M2TS_Demuxer *ts, GF_M2TS_SECTION_MPE *mpe, unsigned } /*get number of rows of mpe_fec_frame from descriptor*/ - id = data[0]; - section_length = (data[1] & 0xF)<<8|data[2]; section_number = data[6]; last_section_number = data[7]; - //printf( "table_id: %x section_length: %d section_number: %d last : %d \n",id, section_length, section_number, last_section_number); + //printf( "table_id: %x section_length: %d section_number: %d last : %d \n", data[0], (data[1] & 0xF)<<8|data[2], section_number, last_section_number); if (ts->direct_mpe) { if (table_id != GF_M2TS_TABLE_ID_DSM_CC_PRIVATE) return; @@ -201,7 +198,7 @@ void gf_m2ts_process_mpe(GF_M2TS_Demuxer *ts, GF_M2TS_SECTION_MPE *mpe, unsigned /*get number of rows of mpe_fec_frame from descriptor*/ /* Real-Time Parameters */ - delta_t = (data[8]<<4)|(data[9]>>4); + //delta_t = (data[8]<<4)|(data[9]>>4); table_boundry_flag = (data[9] >> 3 )& 0x1; frame_boundry_flag = (data[9] >> 2 )& 0x1; @@ -422,8 +419,7 @@ void gf_m2ts_mpe_send_datagram(GF_M2TS_Demuxer *ts, u32 mpe_pid, unsigned char * socket_simu(&ip_pck, ts, 0); - fprintf(stdout, "MPE PID %d - send datagram %d bytes to %d.%d.%d.%d port:%d\n", mpe_pid, ip_pck.u32_udp_data_size-8, ip_pck.u8_rx_adr[0], ip_pck.u8_rx_adr[1], ip_pck.u8_rx_adr[2], ip_pck.u8_rx_adr[3], ip_pck.u32_rx_udp_port); - + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("MPE PID %d - send datagram %d bytes to %d.%d.%d.%d port:%d\n", mpe_pid, ip_pck.u32_udp_data_size-8, ip_pck.u8_rx_adr[0], ip_pck.u8_rx_adr[1], ip_pck.u8_rx_adr[2], ip_pck.u8_rx_adr[3], ip_pck.u32_rx_udp_port)); } @@ -1079,6 +1075,7 @@ void socket_simu(GF_M2TS_IP_Packet *ip_packet, GF_M2TS_Demuxer *ts, Bool yield) if (gf_sk_is_multicast_address(name) ) { e = gf_sk_setup_multicast(Sock_Struct->sock, name, ip_packet->u32_rx_udp_port, 1/*TTL - FIXME this should be in a cfg file*/, 0, NULL/*FIXME this should be in a cfg file*/); + GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("Setting up multicast socket for MPE on %s:%d\n", name, ip_packet->u32_rx_udp_port )); } else { /* binding of the socket to send data to port 4600 on the local machine @@ -1086,6 +1083,7 @@ void socket_simu(GF_M2TS_IP_Packet *ip_packet, GF_M2TS_Demuxer *ts, Bool yield) the second adress is "localhost" and the port is the destination port on localhost */ e = gf_sk_bind(Sock_Struct->sock, "127.0.0.1", ip_packet->u32_rx_udp_port,/*name*/"127.0.0.1", ip_packet->u32_rx_udp_port, 0); + GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("Setting up socket for MPE on 127.0.0.1:%d\n", ip_packet->u32_rx_udp_port )); } if (e != GF_OK) { @@ -1259,10 +1257,10 @@ void setColRS( MPE_FEC_FRAME * mff, u32 offset, u8 * pds, u32 length ) { if ( mff->current_offset_rs != offset) { printf ("there is an error hole in the RS from %d to %d \n", mff->current_offset_rs, offset ); - mff->current_offset_rs = offset; - setErrorIndicator( mff->p_error_rs , mff->current_offset_rs , (offset - mff->current_offset_rs)*sizeof(u32)); + setErrorIndicator( mff->p_error_rs , mff->current_offset_rs , (offset - mff->current_offset_rs)*sizeof(u32)); + mff->current_offset_rs = offset; } - assert(mff->rows == length); + assert(mff->rows == length); memcpy(mff->p_rs + mff->current_offset_rs , pds, length*sizeof(u8) ); mff->current_offset_rs = offset + length ; @@ -1270,9 +1268,7 @@ void setColRS( MPE_FEC_FRAME * mff, u32 offset, u8 * pds, u32 length ) void getColRS(MPE_FEC_FRAME * mff, u32 offset, u8 * pds, u32 length) { - memcpy(pds,mff->p_rs + offset, length); - } void getErrorPositions(MPE_FEC_FRAME *mff, u32 row, u32 * errPositions) @@ -1310,7 +1306,7 @@ u32 getErrasurePositions( MPE_FEC_FRAME *mff , u32 row, u32 *errasures) return nb; } -void setErrorIndicator(u32 * data , u32 offset,u32 length) +void setErrorIndicator(u32 * data , u32 offset, u32 length) { // printf("setting the error indication \n"); memset(data+offset, 1, length); @@ -1321,3 +1317,6 @@ void setErrorIndicator(u32 * data , u32 offset,u32 length) printf ("private descriptor \n"); return ; }*/ + + +#endif //GPAC_DISABLE_MPEG2TS diff --git a/src/media_tools/filestreamer.c b/src/media_tools/filestreamer.c index a12f126..feaf651 100644 --- a/src/media_tools/filestreamer.c +++ b/src/media_tools/filestreamer.c @@ -335,7 +335,6 @@ GF_ISOMRTPStreamer *gf_isom_streamer_new(const char *file_name, const char *ip_d GF_ISOMRTPStreamer *streamer; GF_Err e = GF_OK; const char *opt = NULL; - const char *dest_ip; /*GF_Config *configFile = NULL; */ u32 i, max_ptime, au_sn_len; u8 payt; @@ -352,7 +351,6 @@ GF_ISOMRTPStreamer *gf_isom_streamer_new(const char *file_name, const char *ip_d GF_SAFEALLOC(streamer, GF_ISOMRTPStreamer); streamer->dest_ip = gf_strdup(ip_dest); - dest_ip = ip_dest; payt = 96; max_ptime = au_sn_len = 0; diff --git a/src/media_tools/img.c b/src/media_tools/img.c index 0f5d39c..ade42ed 100644 --- a/src/media_tools/img.c +++ b/src/media_tools/img.c @@ -319,6 +319,7 @@ GF_Err gf_img_jpeg_dec(char *jpg, u32 jpg_size, u32 *width, u32 *height, u32 *pi jpeg_destroy_decompress(&jpx.cinfo); return GF_BUFFER_TOO_SMALL; } + if (!dst_nb_comp) dst_nb_comp = jpx.cinfo.num_components; scan_line = NULL; /*decode*/ @@ -411,35 +412,6 @@ static void gf_png_user_error_fn(png_structp png_ptr,png_const_charp error_msg) longjmp(png_jmpbuf(png_ptr), 1); } -GF_Err gf_img_png_file_dec(char *png_filename, u32 *width, u32 *height, u32 *pixel_format, char **dst, u32 *dst_size) -{ - u32 fsize, readen; - FILE *f; - char *data; - GF_Err e; - f = gf_f64_open(png_filename, "rb"); - if (!f) return GF_URL_ERROR; - - gf_f64_seek(f, 0, SEEK_END); - fsize = (u32)gf_f64_tell(f); - gf_f64_seek(f, 0, SEEK_SET); - data = gf_malloc(fsize); - readen = fread(data, sizeof(char), fsize, f); - if (readen != fsize){ - fclose( f ); - return GF_IO_ERR; - } - *dst_size = 0; - e = gf_img_png_dec(data, fsize, width, height, pixel_format, NULL, dst_size); - if (*dst_size) { - *dst = gf_malloc(*dst_size); - fclose( f ); - return gf_img_png_dec(data, fsize, width, height, pixel_format, *dst, dst_size); - } else { - fclose( f ); - return e; - } -} GF_EXPORT GF_Err gf_img_png_dec(char *png, u32 png_size, u32 *width, u32 *height, u32 *pixel_format, char *dst, u32 *dst_size) @@ -694,3 +666,49 @@ GF_Err gf_img_png_enc(char *data, u32 width, u32 height, s32 stride, u32 pixel_f #endif /*GPAC_HAS_PNG*/ +GF_Err gf_img_file_dec(char *png_filename, u32 *hint_oti, u32 *width, u32 *height, u32 *pixel_format, char **dst, u32 *dst_size) +{ + u32 fsize, read, oti; + FILE *f; + char *data; + GF_Err e; + f = gf_f64_open(png_filename, "rb"); + if (!f) return GF_URL_ERROR; + + oti = 0; + if (!hint_oti || ! *hint_oti) { + char *ext = strrchr(png_filename, '.'); + if (!ext) return GF_NOT_SUPPORTED; + if (!stricmp(ext, ".png")) oti = GPAC_OTI_IMAGE_PNG; + else if (!stricmp(ext, ".jpg") || !stricmp(ext, ".jpeg")) oti = GPAC_OTI_IMAGE_JPEG; + } + gf_f64_seek(f, 0, SEEK_END); + fsize = (u32)gf_f64_tell(f); + gf_f64_seek(f, 0, SEEK_SET); + data = gf_malloc(fsize); + read = fread(data, sizeof(char), fsize, f); + fclose( f ); + if (read != fsize) return GF_IO_ERR; + + e = GF_NOT_SUPPORTED; + *dst_size = 0; + if (oti == GPAC_OTI_IMAGE_JPEG) { +#ifdef GPAC_HAS_JPEG + e = gf_img_jpeg_dec(data, fsize, width, height, pixel_format, NULL, dst_size, 0); + if (*dst_size) { + *dst = gf_malloc(*dst_size); + return gf_img_jpeg_dec(data, fsize, width, height, pixel_format, NULL, dst_size, 0); + } +#endif + } else if (oti == GPAC_OTI_IMAGE_PNG) { +#ifdef GPAC_HAS_PNG + e = gf_img_png_dec(data, fsize, width, height, pixel_format, NULL, dst_size); + if (*dst_size) { + *dst = gf_malloc(*dst_size); + return gf_img_png_dec(data, fsize, width, height, pixel_format, *dst, dst_size); + } +#endif + } + return e; +} + diff --git a/src/media_tools/ismacryp.c b/src/media_tools/ismacryp.c index e6e5dc5..aec2100 100644 --- a/src/media_tools/ismacryp.c +++ b/src/media_tools/ismacryp.c @@ -436,7 +436,7 @@ GF_EXPORT GF_Err gf_ismacryp_decrypt_file(GF_ISOFile *mp4, const char *drm_file) { GF_Err e; - u32 i, idx, count, common_idx, nb_tracks, scheme_type, cur_tk; + u32 i, idx, count, common_idx, nb_tracks, scheme_type; const char *scheme_URI, *KMS_URI; ISMACrypInfo *info; Bool is_oma; @@ -463,7 +463,6 @@ GF_Err gf_ismacryp_decrypt_file(GF_ISOFile *mp4, const char *drm_file) } nb_tracks = gf_isom_get_track_count(mp4); - cur_tk = 1; e = GF_OK; for (i=0; idecoderConfig->streamType) { + case GF_STREAM_AUDIO: + if (esd->decoderConfig->decoderSpecificInfo && esd->decoderConfig->decoderSpecificInfo->data) { + /*5 first bits of AAC config*/ + u8 audio_object_type = (esd->decoderConfig->decoderSpecificInfo->data[0] & 0xF8) >> 3; + sprintf(szCodec, "mp4a.%02x.%02x", esd->decoderConfig->objectTypeIndication, audio_object_type); + } else { + sprintf(szCodec, "mp4a.%02x", esd->decoderConfig->objectTypeIndication); + } + break; + case GF_STREAM_VISUAL: + if (esd->decoderConfig->decoderSpecificInfo) { + GF_M4VDecSpecInfo dsi; + gf_m4v_get_config(esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength, &dsi); + sprintf(szCodec, "mp4v.%02x.%02x", esd->decoderConfig->objectTypeIndication, dsi.VideoPL); + } else { + sprintf(szCodec, "mp4v.%02x", esd->decoderConfig->objectTypeIndication); + } + break; + default: + sprintf(szCodec, "mp4s.%02x", esd->decoderConfig->objectTypeIndication); + break; + } + gf_odf_desc_del((GF_Descriptor *)esd); + return GF_OK; + + case GF_ISOM_SUBTYPE_AVC_H264: + case GF_ISOM_SUBTYPE_AVC2_H264: + case GF_ISOM_SUBTYPE_SVC_H264: + avcc = gf_isom_avc_config_get(movie, track, 1); + sps = gf_list_get(avcc->sequenceParameterSets, 0); + sprintf(szCodec, "%s.%02x%02x%02x", gf_4cc_to_str(subtype), (u8) sps->data[1], (u8) sps->data[2], (u8) sps->data[3]); + gf_odf_avc_cfg_del(avcc); + return GF_OK; + + default: + GF_LOG(GF_LOG_WARNING, GF_LOG_AUTHOR, ("[ISOM Tools] codec parameters not known - setting codecs string to default value \"%s\"\n", gf_4cc_to_str(subtype) )); + sprintf(szCodec, "%s", gf_4cc_to_str(subtype)); + return GF_OK; + } + return GF_OK; +} + #ifndef GPAC_DISABLE_ISOM_WRITE static const u32 ISMA_VIDEO_OD_ID = 20; @@ -76,11 +129,10 @@ static const u8 ISMA_BIFS_AI[] = GF_EXPORT GF_Err gf_media_make_isma(GF_ISOFile *mp4file, Bool keepESIDs, Bool keepImage, Bool no_ocr) { - u32 AudioTrack, VideoTrack, Tracks, i, mType, bifsT, odT, descIndex, VideoType, VID, AID, bifsID, odID; + u32 AudioTrack, VideoTrack, Tracks, i, mType, bifsT, odT, descIndex, VID, AID, bifsID, odID; u32 bifs, w, h; Bool is_image, image_track; GF_ESD *a_esd, *v_esd, *_esd; - Bool update_vid_esd; GF_ObjectDescriptor *od; GF_ODUpdate *odU; GF_ODCodec *codec; @@ -174,7 +226,6 @@ GF_Err gf_media_make_isma(GF_ISOFile *mp4file, Bool keepESIDs, Bool keepImage, B odU = (GF_ODUpdate *) gf_odf_com_new(GF_ODF_OD_UPDATE_TAG); a_esd = v_esd = NULL; - update_vid_esd = 0; gf_isom_set_root_od_id(mp4file, 1); @@ -194,7 +245,6 @@ GF_Err gf_media_make_isma(GF_ISOFile *mp4file, Bool keepESIDs, Bool keepImage, B w = h = 0; if (VideoTrack) { bifs = 1; - VideoType = 0; od = (GF_ObjectDescriptor *) gf_odf_desc_new(GF_ODF_OD_TAG); od->objectDescriptorID = ISMA_VIDEO_OD_ID; @@ -610,27 +660,57 @@ GF_Err gf_media_make_psp(GF_ISOFile *mp4) typedef struct { + Bool is_ref_track; Bool done; u32 TrackID; u32 SampleNum, SampleCount; u32 FragmentLength; u32 OriginalTrack; + u32 finalSampleDescriptionIndex; u32 TimeScale, MediaType, DefaultDuration, InitialTSOffset; u64 last_sample_cts, next_sample_dts; + Bool all_sample_raps; } TrackFragmenter; +static u64 get_next_sap_time(GF_ISOFile *input, u32 track, u32 sample_count, u32 sample_num) +{ + GF_ISOSample *samp; + u64 time; + Bool is_rap, has_roll; + u32 i, found_sample = 0; + for (i=sample_num; i<=sample_count; i++) { + if (gf_isom_get_sample_sync(input, track, i)) { + found_sample = i; + break; + } + gf_isom_get_sample_rap_roll_info(input, track, i, &is_rap, &has_roll, NULL); + if (is_rap || has_roll) { + found_sample = i; + break; + } + } + if (!found_sample) return 0; + samp = gf_isom_get_sample_info(input, track, found_sample, NULL, NULL); + time = samp->DTS; + gf_isom_sample_del(&samp); + return time; +} + GF_EXPORT -GF_Err gf_media_fragment_file(GF_ISOFile *input, char *output_file, Double max_duration_sec, u32 dash_mode, Double dash_duration_sec, char *seg_rad_name, char *seg_ext, s32 fragments_per_sidx, Bool daisy_chain_sidx, Bool use_url_template, const char *dash_ctx_file) +GF_Err gf_media_fragment_file(GF_ISOFile *input, const char *output_file, const char *mpd_name, Double max_duration_sec, u32 dash_mode, Double dash_duration_sec, char *seg_rad_name, char *seg_ext, s32 subsegs_per_sidx, Bool daisy_chain_sidx, Bool use_url_template, Bool single_segment_mode, const char *dash_ctx_file, GF_ISOFile *sample_descs, u32 rep_idx) { u8 NbBits; u32 i, TrackNum, descIndex, j, count, nb_sync, ref_track_id, nb_tracks_done; u32 defaultDuration, defaultSize, defaultDescriptionIndex, defaultRandomAccess, nb_samp, nb_done; + u32 nb_video, nb_audio, nb_text, nb_scene; u8 defaultPadding; u16 defaultDegradationPriority; GF_Err e; char sOpt[100], sKey[100]; - u32 cur_seg, fragment_index; + char szCodecs[200], szCodec[100]; + char szComponents[1000]; + u32 cur_seg, fragment_index, nb_fragments, max_sap_type; GF_ISOFile *output; GF_ISOSample *sample, *next; GF_List *fragmenters; @@ -638,14 +718,17 @@ GF_Err gf_media_fragment_file(GF_ISOFile *input, char *output_file, Double max_d Double average_duration, file_duration, period_duration, max_segment_duration; u32 nb_segments, width, height, sample_rate, nb_channels; char langCode[5]; + u32 index_start_range, index_end_range; + Bool force_switch_segment = 0; Bool switch_segment = 0; Bool split_seg_at_rap = (dash_mode==2) ? 1 : 0; Bool split_at_rap = 0; Bool has_rap = 0; Bool next_sample_rap = 0; Bool flush_all_samples = 0; + Bool simulation_pass = 0; u64 last_ref_cts = 0; - u64 start_range, end_range, file_size, init_seg_size, ref_track_cur_dur; + u64 start_range, end_range, file_size, init_seg_size, ref_track_first_dts, ref_track_next_cts; u32 tfref_timescale = 0; u32 bandwidth = 0; TrackFragmenter *tf, *tfref; @@ -656,8 +739,13 @@ GF_Err gf_media_fragment_file(GF_ISOFile *input, char *output_file, Double max_d GF_Config *dash_ctx = NULL; Bool store_dash_params = 0; Bool dash_moov_setup = 0; - Bool segments_start_with_rap = 1; + Bool segments_start_with_sap = 1; Bool first_sample_in_segment = 0; + u32 *segments_info = NULL; + u32 nb_segments_info = 0; + Bool audio_only = 1; + + SegmentDuration = 0; nb_samp = 0; fragmenters = NULL; @@ -699,11 +787,7 @@ GF_Err gf_media_fragment_file(GF_ISOFile *input, char *output_file, Double max_d if (store_dash_params) { gf_cfg_set_key(dash_ctx, "DASH", "InitializationSegment", SegName); } - - - strcpy(SegName, output_file); - strcat(SegName, ".mpd"); - mpd = gf_f64_open(SegName, "wt"); + mpd = gf_f64_open(mpd_name, "a+t"); mpd_segs = gf_temp_file_new(); } else { output = gf_isom_open(output_file, GF_ISOM_OPEN_WRITE, NULL); @@ -719,14 +803,23 @@ GF_Err gf_media_fragment_file(GF_ISOFile *input, char *output_file, Double max_d if (e) goto err_exit; } - MaxFragmentDuration = (u32) (max_duration_sec * 1000); + MaxFragmentDuration = (u32) (max_duration_sec * 1000); MaxSegmentDuration = (u32) (dash_duration_sec * 1000); + + /*in single segment mode, only one big SIDX is written between the end of the moov and the first fragment. + To speed-up writing, we do a first fragmentation pass without writing any sample to compute the number of segments and fragments per segment + in order to allocate / write to file the sidx before the fragmentation. The sidx will then be rewritten when closing the last segment*/ + if (single_segment_mode) simulation_pass = 1; + index_start_range = index_end_range = 0; + tfref = NULL; file_duration = 0; width = height = sample_rate = nb_channels = 0; langCode[0]=0; langCode[4]=0; + szCodecs[0] = 0; + nb_video = nb_audio = nb_text = nb_scene = 0; //duplicates all tracks for (i=0; iTrackID = gf_isom_get_track_id(output, TrackNum); tf->SampleCount = gf_isom_get_sample_count(input, i+1); @@ -784,9 +888,13 @@ GF_Err gf_media_fragment_file(GF_ISOFile *input, char *output_file, Double max_d if (gf_isom_get_sync_point_count(input, i+1)>nb_sync) { tfref = tf; nb_sync = gf_isom_get_sync_point_count(input, i+1); + } else if (!gf_isom_has_sync_points(input, i+1)) { + tf->all_sample_raps = 1; } - /*figure out if we have an initial TS*/ + tf->finalSampleDescriptionIndex = 1; + + /*figure out if we have an initial TS*/ if (!dash_moov_setup) { if (gf_isom_get_edit_segment_count(input, i+1)) { u64 EditTime, SegmentDuration, MediaTime; @@ -798,6 +906,53 @@ GF_Err gf_media_fragment_file(GF_ISOFile *input, char *output_file, Double max_d /*and remove edit segments*/ gf_isom_remove_edit_segments(output, TrackNum); } + /*locate sample description in list if given*/ + if (sample_descs) { + u32 s_count; + u32 sample_descs_track = gf_isom_get_track_by_id(sample_descs, tf->TrackID); + if (!sample_descs_track) { + e = GF_BAD_PARAM; + goto err_exit; + + } + + //the initialization segment is not yet setup for fragmentation + if (! gf_isom_is_track_fragmented(sample_descs, tf->TrackID)) { + e = gf_isom_setup_track_fragment(sample_descs, sample_descs_track, + defaultDescriptionIndex, defaultDuration, + defaultSize, (u8) defaultRandomAccess, + defaultPadding, defaultDegradationPriority); + if (e) goto err_exit; + } + /*otherwise override the fragment defauls so that we are consistent with the shared init segment*/ + else { + e = gf_isom_get_fragment_defaults(sample_descs, sample_descs_track, + &defaultDuration, &defaultSize, &defaultDescriptionIndex, &defaultRandomAccess, &defaultPadding, &defaultDegradationPriority); + if (e) goto err_exit; + + e = gf_isom_change_track_fragment_defaults(output, TrackNum, + defaultDuration, defaultSize, defaultDescriptionIndex, defaultRandomAccess, defaultPadding, defaultDegradationPriority); + if (e) goto err_exit; + + + } + + /*reset all sample desc and clone with new ones*/ + gf_isom_clone_sample_descriptions(output, TrackNum, sample_descs, sample_descs_track, 1); + + /*and search in new ones the new index*/ + s_count = gf_isom_get_sample_description_count(sample_descs, sample_descs_track); + if (s_count>1) { + u32 k; + /*remove all sample descs*/ + for (k=0; kfinalSampleDescriptionIndex = k+1; + } + } + } + } } /*restore track decode times*/ else { @@ -806,7 +961,7 @@ GF_Err gf_media_fragment_file(GF_ISOFile *input, char *output_file, Double max_d opt = (char *)gf_cfg_get_key(dash_ctx, sKey, "NextDecodingTime"); if (opt) tf->InitialTSOffset = atoi(opt); } - + switch (mtype) { case GF_ISOM_MEDIA_TEXT: gf_isom_get_media_language(input, i+1, langCode); @@ -832,21 +987,48 @@ GF_Err gf_media_fragment_file(GF_ISOFile *input, char *output_file, Double max_d file_duration = ((Double) gf_isom_get_media_duration(input, i+1)) / tf->TimeScale; } gf_list_add(fragmenters, tf); + nb_samp += count; } + /*format component info*/ + szComponents[0] = 0; + for (i=0; iMediaType) { + case GF_ISOM_MEDIA_TEXT: + gf_isom_get_media_language(input, i+1, langCode); + sprintf(szComponent, " \n", tf->TrackID, langCode); + break; + case GF_ISOM_MEDIA_VISUAL: + sprintf(szComponent, " \n", tf->TrackID); + break; + case GF_ISOM_MEDIA_SCENE: + case GF_ISOM_MEDIA_DIMS: + sprintf(szComponent, " \n", tf->TrackID, langCode); + break; + case GF_ISOM_MEDIA_AUDIO: + sprintf(szComponent, " \n", tf->TrackID, langCode); + break; + } + strcat(szComponents, szComponent); + } + if (!tfref) tfref = gf_list_get(fragmenters, 0); + tfref->is_ref_track = 1; tfref_timescale = tfref->TimeScale; ref_track_id = tfref->TrackID; + if (tfref->all_sample_raps) split_seg_at_rap = 1; //flush movie e = gf_isom_finalize_for_fragment(output, dash_mode ? 1 : 0); if (e) goto err_exit; start_range = 0; - end_range = gf_isom_get_file_size(output); - file_size = end_range; - init_seg_size = end_range; + file_size = gf_isom_get_file_size(output); + end_range = file_size - 1; + init_seg_size = file_size; if (dash_ctx) { if (store_dash_params) { @@ -859,11 +1041,13 @@ GF_Err gf_media_fragment_file(GF_ISOFile *input, char *output_file, Double max_d } } +restart_fragmentation_pass: + average_duration = 0; nb_segments = 0; nb_tracks_done = 0; - ref_track_cur_dur = tfref ? tfref->InitialTSOffset : 0; + ref_track_first_dts = (u64) -1; nb_done = 0; maxFragDurationOverSegment=0; @@ -874,6 +1058,13 @@ GF_Err gf_media_fragment_file(GF_ISOFile *input, char *output_file, Double max_d cur_seg=1; fragment_index=1; period_duration = 0; + split_at_rap = 0; + has_rap = 0; + next_sample_rap = 0; + flush_all_samples = 0; + force_switch_segment = 0; + max_sap_type = 0; + ref_track_next_cts = 0; /*setup previous URL list*/ if (dash_ctx) { @@ -893,7 +1084,10 @@ GF_Err gf_media_fragment_file(GF_ISOFile *input, char *output_file, Double max_d if (opt) period_duration = atof(opt); } gf_isom_set_next_moof_number(output, fragment_index); + + max_segment_duration = 0; + nb_fragments=0; while ( (count = gf_list_count(fragmenters)) ) { @@ -901,39 +1095,60 @@ GF_Err gf_media_fragment_file(GF_ISOFile *input, char *output_file, Double max_d SegmentDuration = 0; switch_segment = 0; first_sample_in_segment = 1; - start_range = gf_isom_get_file_size(output); - if (seg_rad_name) { - sprintf(SegName, "%s%d.%s", seg_rad_name, cur_seg, seg_ext); - e = gf_isom_start_segment(output, SegName); - if (!use_url_template) { - fprintf(mpd_segs, " \n", SegName); - if (dash_ctx) { - char szKey[100], szVal[4046]; - sprintf(szKey, "UrlInfo%d", gf_cfg_get_key_count(dash_ctx, "URLs") + 1 ); - sprintf(szVal, "", SegName); - gf_cfg_set_key(dash_ctx, "URLs", szKey, szVal); + + if (simulation_pass) { + segments_info = gf_realloc(segments_info, sizeof(u32) * (nb_segments_info+1) ); + segments_info[nb_segments_info] = 0; + nb_segments_info++; + e = GF_OK; + } else { + start_range = gf_isom_get_file_size(output); + if (seg_rad_name) { + sprintf(SegName, "%s%d.%s", seg_rad_name, cur_seg, seg_ext); + e = gf_isom_start_segment(output, SegName); + if (!use_url_template) { + fprintf(mpd_segs, " \n", SegName); + if (dash_ctx) { + char szKey[100], szVal[4046]; + sprintf(szKey, "UrlInfo%d", gf_cfg_get_key_count(dash_ctx, "URLs") + 1 ); + sprintf(szVal, "", SegName); + gf_cfg_set_key(dash_ctx, "URLs", szKey, szVal); + } } + } else { + e = gf_isom_start_segment(output, NULL); } - } else { - e = gf_isom_start_segment(output, NULL); } + cur_seg++; if (e) goto err_exit; } maxFragDurationOverSegment=0; - e = gf_isom_start_fragment(output, 1); - if (e) goto err_exit; + sample = NULL; - for (i=0; idone) continue; - gf_isom_set_traf_base_media_decode_time(output, tf->TrackID, tf->InitialTSOffset + tf->next_sample_dts); + if (simulation_pass) { + segments_info[nb_segments_info-1] ++; + e = GF_OK; + } else { + e = gf_isom_start_fragment(output, 1); + if (e) goto err_exit; + + + for (i=0; idone) continue; + gf_isom_set_traf_base_media_decode_time(output, tf->TrackID, tf->InitialTSOffset + tf->next_sample_dts); + } + } //process track by track for (i=0; iOriginalTrack, tf->SampleNum + 1, &descIndex); + if (!sample) { + e = gf_isom_last_error(input); + goto err_exit; + } + + /*also get SAP type - this is not needed if sample is not NULL as SAP tye was computed for "next sample" in previous loop*/ + if (sample->IsRAP) { + SAP_type = 1; + } else { + SAP_type = 0; + e = gf_isom_get_sample_rap_roll_info(input, tf->OriginalTrack, tf->SampleNum + 1, &is_rap, &has_roll, &roll_distance); + if (e==GF_OK) { + if (is_rap) SAP_type = 3; + else if (has_roll && (roll_distance>=0) ) SAP_type = 4; + } + } } gf_isom_get_sample_padding_bits(input, tf->OriginalTrack, tf->SampleNum+1, &NbBits); @@ -967,24 +1200,46 @@ GF_Err gf_media_fragment_file(GF_ISOFile *input, char *output_file, Double max_d defaultDuration = tf->DefaultDuration; } - if (segments_start_with_rap && first_sample_in_segment && (tf==tfref)) { - first_sample_in_segment = 0; - if (!sample->IsRAP) segments_start_with_rap = 0; - } - + if (tf==tfref) { + if (segments_start_with_sap && first_sample_in_segment ) { + first_sample_in_segment = 0; + if (! SAP_type) segments_start_with_sap = 0; + } + if (ref_track_first_dts > sample->DTS) + ref_track_first_dts = sample->DTS; - e = gf_isom_fragment_add_sample(output, tf->TrackID, sample, descIndex, - defaultDuration, NbBits, 0); - if (e) - goto err_exit; + if (next) { + u64 next_cts = next->DTS + next->CTS_Offset; + if (ref_track_next_ctsOriginalTrack); + if (cts>ref_track_next_cts) ref_track_next_cts = cts; + else ref_track_next_cts += defaultDuration; + } + } - /*copy subsample information*/ - e = gf_isom_fragment_copy_subsample(output, tf->TrackID, input, tf->OriginalTrack, tf->SampleNum + 1); - if (e) - goto err_exit; + if (SAP_type > max_sap_type) max_sap_type = SAP_type; - gf_set_progress("ISO File Fragmenting", nb_done, nb_samp); - nb_done++; + if (simulation_pass) { + e = GF_OK; + } else { + /*override descIndex with final index used in file*/ + descIndex = tf->finalSampleDescriptionIndex; + e = gf_isom_fragment_add_sample(output, tf->TrackID, sample, descIndex, + defaultDuration, NbBits, 0); + if (e) + goto err_exit; + + /*copy subsample information*/ + e = gf_isom_fragment_copy_subsample(output, tf->TrackID, input, tf->OriginalTrack, tf->SampleNum + 1); + if (e) + goto err_exit; + + gf_set_progress("ISO File Fragmenting", nb_done, nb_samp); + nb_done++; + } tf->last_sample_cts = sample->DTS + sample->CTS_Offset; tf->next_sample_dts = sample->DTS + defaultDuration; @@ -994,20 +1249,54 @@ GF_Err gf_media_fragment_file(GF_ISOFile *input, char *output_file, Double max_d tf->FragmentLength += defaultDuration; tf->SampleNum += 1; - if (next && next->IsRAP) { + /*compute SAP type*/ + if (sample) { + if (sample->IsRAP) { + SAP_type = 1; + } else { + SAP_type = 0; + e = gf_isom_get_sample_rap_roll_info(input, tf->OriginalTrack, tf->SampleNum + 1, &is_rap, &has_roll, NULL); + if (e==GF_OK) { + if (is_rap) + SAP_type = 3; + else if (has_roll && (roll_distance>=0) ) + SAP_type = 4; + } + } + } + + if (next && SAP_type) { if (tf==tfref) { if (split_seg_at_rap) { - u32 frag_dur = tf->FragmentLength*1000/tf->TimeScale; + u64 next_sap_time; + /*duration of fragment if we add this rap*/ + u32 frag_dur = (tf->FragmentLength+defaultDuration)*1000/tf->TimeScale; next_sample_rap = 1; - /*if media segment about to be produced is longer than max segment length, force split*/ - if (SegmentDuration + frag_dur > MaxSegmentDuration) { - split_at_rap = 1; + next_sap_time = get_next_sap_time(input, tf->OriginalTrack, tf->SampleCount, tf->SampleNum + 2); + /*if no more SAP after this one, do not switch segment*/ + if (next_sap_time) { + u32 scaler; + /*this is the fragment duration from last sample added to next SAP*/ + frag_dur += (u32) (next_sap_time - tf->next_sample_dts - defaultDuration)*1000/tf->TimeScale; + /*if media segment about to be produced is longer than max segment length, force segment split*/ + if (SegmentDuration + frag_dur > MaxSegmentDuration) { + split_at_rap = 1; + /*force new segment*/ + force_switch_segment = 1; + } + + /*if adding this SAP will result in stoping the fragment "soon" after it, stop now and start with SAP + if all samples are RAPs, just stop fragment if we exceed the requested duration by adding the next sample + otherwise, take 3 samples (should be refined of course)*/ + scaler = 3; + if (tf->all_sample_raps) scaler = 1; + if ( (tf->FragmentLength + scaler*defaultDuration)*1000 >= MaxFragmentDuration * tf->TimeScale) + stop_frag = 1; } - - if (split_at_rap) { + if (split_at_rap && !tf->all_sample_raps) { stop_frag = 1; /*override fragment duration for the rest of this fragment*/ - MaxFragmentDuration = frag_dur; + MaxFragmentDuration = tf->FragmentLength*1000/tf->TimeScale; } } else if (!has_rap) { if (tf->FragmentLength == defaultDuration) has_rap = 2; @@ -1061,7 +1350,12 @@ GF_Err gf_media_fragment_file(GF_ISOFile *input, char *output_file, Double max_d SegmentDuration += maxFragDurationOverSegment; maxFragDurationOverSegment=0; - if ((SegmentDuration >= MaxSegmentDuration) && (!split_seg_at_rap || next_sample_rap)) { + /*next fragment will exceed segment length, abort fragment now (all samples RAPs)*/ + if (tfref && tfref->all_sample_raps && (SegmentDuration + MaxFragmentDuration >= MaxSegmentDuration)) { + force_switch_segment = 1; + } + + if (force_switch_segment || ((SegmentDuration >= MaxSegmentDuration) && (!split_seg_at_rap || next_sample_rap))) { average_duration += SegmentDuration; nb_segments++; if (max_segment_duration * 1000 <= SegmentDuration) { @@ -1079,6 +1373,7 @@ GF_Err gf_media_fragment_file(GF_ISOFile *input, char *output_file, Double max_d } fprintf(stdout, "\n "); #endif + force_switch_segment=0; switch_segment=1; SegmentDuration=0; split_at_rap = 0; @@ -1086,26 +1381,31 @@ GF_Err gf_media_fragment_file(GF_ISOFile *input, char *output_file, Double max_d /*restore fragment duration*/ MaxFragmentDuration = (u32) (max_duration_sec * 1000); - gf_isom_close_segment(output, fragments_per_sidx, ref_track_id, ref_track_cur_dur, daisy_chain_sidx, flush_all_samples ? 1 : 0); - if (tfref) ref_track_cur_dur = tfref->InitialTSOffset + tfref->next_sample_dts; - - if (!seg_rad_name) { - file_size = end_range = gf_isom_get_file_size(output); - fprintf(mpd_segs, " \n", start_range, end_range); - if (dash_ctx) { - char szKey[100], szVal[4046]; - sprintf(szKey, "UrlInfo%d", gf_cfg_get_key_count(dash_ctx, "URLs") + 1 ); - sprintf(szVal, "", start_range, end_range); - gf_cfg_set_key(dash_ctx, "URLs", szKey, szVal); - + if (!simulation_pass) { + u64 idx_start_range, idx_end_range; + + gf_isom_close_segment(output, subsegs_per_sidx, ref_track_id, ref_track_first_dts, ref_track_next_cts, daisy_chain_sidx, flush_all_samples ? 1 : 0, &idx_start_range, &idx_end_range); + ref_track_first_dts = (u64) -1; + + if (!seg_rad_name) { + file_size = gf_isom_get_file_size(output); + end_range = file_size - 1; + if (!single_segment_mode) { + fprintf(mpd_segs, " \n", start_range, end_range, idx_start_range, idx_end_range); + if (dash_ctx) { + char szKey[100], szVal[4046]; + sprintf(szKey, "UrlInfo%d", gf_cfg_get_key_count(dash_ctx, "URLs") + 1 ); + sprintf(szVal, "", start_range, end_range, idx_start_range, idx_end_range); + gf_cfg_set_key(dash_ctx, "URLs", szKey, szVal); + } + } + } else { + file_size += gf_isom_get_file_size(output); } - } else { - file_size += gf_isom_get_file_size(output); } - } - /*next fragment will exceed segment length, abort fragment at next rap*/ - if (split_seg_at_rap && (SegmentDuration + MaxFragmentDuration >= MaxSegmentDuration)) { + /*next fragment will exceed segment length, abort fragment at next rap (possibly after MaxSegmentDuration)*/ + if (split_seg_at_rap && SegmentDuration && (SegmentDuration + MaxFragmentDuration >= MaxSegmentDuration)) { split_at_rap = 1; } } @@ -1113,65 +1413,79 @@ GF_Err gf_media_fragment_file(GF_ISOFile *input, char *output_file, Double max_d if (nb_tracks_done==count) break; } + if (simulation_pass) { + /*OK, we have all segments and frags per segments*/ + gf_isom_allocate_sidx(output, subsegs_per_sidx, daisy_chain_sidx, nb_segments_info, segments_info, &index_start_range, &index_end_range ); + gf_free(segments_info); + segments_info = NULL; + simulation_pass = 0; + /*reset fragmenters*/ + for (i=0; idone = 0; + tf->last_sample_cts = 0; + tf->next_sample_dts = 0; + tf->FragmentLength = 0; + tf->SampleNum = 0; + if (tf->is_ref_track) tfref = tf; + } + goto restart_fragmentation_pass; + } if (dash_mode) { char buffer[1000]; - u32 size; u32 h, m; Double s; /*flush last segment*/ if (!switch_segment) { + u64 idx_start_range, idx_end_range; + if (max_segment_duration * 1000 <= SegmentDuration) { max_segment_duration = SegmentDuration; max_segment_duration /= 1000; } - if (!split_at_rap) { - assert(max_segment_duration <= dash_duration_sec); - } - gf_isom_close_segment(output, fragments_per_sidx, ref_track_id, ref_track_cur_dur, daisy_chain_sidx, 1); + gf_isom_close_segment(output, subsegs_per_sidx, ref_track_id, ref_track_first_dts, ref_track_next_cts, daisy_chain_sidx, 1, &idx_start_range, &idx_end_range); nb_segments++; + if (!seg_rad_name) { - file_size = end_range = gf_isom_get_file_size(output); - fprintf(mpd_segs, " \n", start_range, end_range); - - if (dash_ctx) { - char szKey[100], szVal[4046]; - sprintf(szKey, "UrlInfo%d", gf_cfg_get_key_count(dash_ctx, "URLs") + 1 ); - sprintf(szVal, "", start_range, end_range); - gf_cfg_set_key(dash_ctx, "URLs", szKey, szVal); + file_size = gf_isom_get_file_size(output); + end_range = file_size - 1; + if (!single_segment_mode) { + fprintf(mpd_segs, " \n", start_range, end_range, idx_start_range, idx_end_range); + if (dash_ctx) { + char szKey[100], szVal[4046]; + sprintf(szKey, "UrlInfo%d", gf_cfg_get_key_count(dash_ctx, "URLs") + 1 ); + sprintf(szVal, "", start_range, end_range, idx_start_range, idx_end_range); + gf_cfg_set_key(dash_ctx, "URLs", szKey, szVal); + } } } else { file_size += gf_isom_get_file_size(output); } } - if (use_url_template) { - sprintf(SegName, "%s_seg$Index$.%s", seg_rad_name, seg_ext); - fprintf(mpd_segs, " \n", SegName, cur_seg-1); - } period_duration += file_duration; h = (u32) (period_duration/3600); m = (u32) (period_duration-h*60)/60; s = period_duration - h*3600 - m*60; bandwidth = (u32) (file_size * 8 / file_duration); - fprintf(mpd, "\n"); - fprintf(mpd, " \n"); - fprintf(mpd, " Media Presentation Description for file %s generated with GPAC \n", gf_isom_get_filename(input)); - fprintf(mpd, " \n"); - fprintf(mpd, " \n", h, m, s); - fprintf(mpd, " \n"); + if (strlen(szComponents)) fprintf(mpd, "%s", szComponents); if (dash_ctx) { Double seg_dur; @@ -1191,34 +1505,43 @@ GF_Err gf_media_fragment_file(GF_ISOFile *input, char *output_file, Double max_d } } - h = (u32) (max_segment_duration / 3600); - m = (u32) (max_segment_duration - h*60)/60; - s = max_segment_duration - h*3600 - m*60; - if (m) { - fprintf(mpd, " \n"); + } else if (single_segment_mode) { + fprintf(mpd, " %s\n", gf_isom_get_filename(output) ); + fprintf(mpd, " \n", index_start_range, index_end_range); } else { - fprintf(mpd, " %s.mp4\n", output_file ); + } + fprintf(mpd, " \n", (u32) (max_segment_duration*1000)); + //if (!sample_descs) + { + fprintf(mpd, " \n"); + } } - fprintf(mpd, ">\n"); - fprintf(mpd, " \n"); - gf_f64_seek(mpd_segs, 0, SEEK_END); - size = (u32) gf_f64_tell(mpd_segs); gf_f64_seek(mpd_segs, 0, SEEK_SET); while (!feof(mpd_segs)) { u32 r = fread(buffer, 1, 100, mpd_segs); - fwrite(buffer, 1, r, mpd); + gf_fwrite(buffer, 1, r, mpd); } - fprintf(mpd, " \n"); - fprintf(mpd, " \n"); - fprintf(mpd, " \n"); - fprintf(mpd, ""); + if (!use_url_template && !single_segment_mode) { + fprintf(mpd, " \n"); + } + + fprintf(mpd, " \n"); } /*store context*/ @@ -1259,12 +1582,62 @@ err_exit: return e; } + +GF_Err gf_media_mpd_start(char *mpd_name, char *title, Bool use_url_template, Bool single_segment, char *dash_ctx, char *init_segment, Double period_duration) +{ + u32 h, m; + Double s; + FILE *mpd = fopen(mpd_name, "wt"); + if (!mpd) return GF_IO_ERR; + + h = (u32) (period_duration/3600); + m = (u32) (period_duration-h*60)/60; + s = period_duration - h*3600 - m*60; + + /*TODO what should we put for minBufferTime */ + fprintf(mpd, "\n", + single_segment ? "urn:mpeg:dash:profile:isoff-on-demand:2011" : "urn:mpeg:dash:profile:full:2011", + h, m, s); + fprintf(mpd, " \n"); + if (title) + fprintf(mpd, " Media Presentation Description for file %s generated with GPAC \n", title); + fprintf(mpd, " \n"); + fprintf(mpd, " \n", h, m, s); + fprintf(mpd, " \n"); + + if (init_segment) { + if (use_url_template) { + fprintf(mpd, " \n", init_segment); + } else if (0 && !single_segment) { + fprintf(mpd, " \n"); + fprintf(mpd, " \n", init_segment); + fprintf(mpd, " \n"); + } + } + + fclose(mpd); + return GF_OK; +} + +GF_Err gf_media_mpd_end(char *mpd_name) +{ + FILE *mpd = fopen(mpd_name, "a+t"); + if (!mpd_name) return GF_IO_ERR; + + fprintf(mpd, " \n"); + fprintf(mpd, " \n"); + fprintf(mpd, ""); + + fclose(mpd); + return GF_OK; +} + GF_EXPORT GF_Err gf_media_import_chapters(GF_ISOFile *file, char *chap_file, Double import_fps) { int readen=0; GF_Err e; - u32 state, unicode_type, offset; + u32 state, offset; u32 cur_chap; u64 ts; u32 i, h, m, s, ms, fr, fps; @@ -1284,21 +1657,17 @@ GF_Err gf_media_import_chapters(GF_ISOFile *file, char *chap_file, Double import e = GF_NOT_SUPPORTED; goto err_exit; } - unicode_type = 2; offset = 2; } else if ((line[0]==(char)(0xFE)) && (line[1]==(char)(0xFF))) { if (!line[2] && !line[3]){ e = GF_NOT_SUPPORTED; goto err_exit; } - unicode_type = 1; offset = 2; } else if ((line[0]==(char)(0xEF)) && (line[1]==(char)(0xBB)) && (line[2]==(char)(0xBF))) { /*we handle UTF8 as asci*/ - unicode_type = 0; offset = 3; } else { - unicode_type = 0; offset = 0; } gf_f64_seek(f, offset, SEEK_SET); diff --git a/src/media_tools/m2ts_mux.c b/src/media_tools/m2ts_mux.c index f60671e..2c82391 100644 --- a/src/media_tools/m2ts_mux.c +++ b/src/media_tools/m2ts_mux.c @@ -1015,7 +1015,9 @@ static GFINLINE u64 gf_m2ts_get_pcr(GF_M2TS_Mux_Program *program) void gf_m2ts_stream_update_data_following(GF_M2TS_Mux_Stream *stream) { + Bool ignore_next=0; stream->next_payload_size = 0; + stream->next_pck_flags = 0; stream->copy_from_next_packets = 0; if (stream->program->mux->one_au_per_pes) return; @@ -1050,6 +1052,24 @@ void gf_m2ts_stream_update_data_following(GF_M2TS_Mux_Stream *stream) stream->next_pck_flags = stream->pck_first->flags; } } + /*consider we don't have the next AU if: + 1- we are asked to start new PES at RAP, just consider we don't have the next AU*/ + if (stream->start_pes_at_rap && (stream->next_pck_flags & GF_ESI_DATA_AU_RAP) ) { + ignore_next=1; + stream->program->force_pat_pmt_state = 1; + } + /*if we have a RAP about to start on a stream in this program, force all other streams to stop merging cuming data in their current PES*/ + else if (stream->program->force_pat_pmt_state) { + ignore_next=1; + } + + if (ignore_next) { + stream->next_payload_size = 0; + stream->next_pck_cts = 0; + stream->next_pck_dts = 0; + stream->next_pck_flags = 0; + } + if (stream->next_payload_size) { stream->next_payload_size += stream->reframe_overhead; @@ -1060,36 +1080,80 @@ void gf_m2ts_stream_update_data_following(GF_M2TS_Mux_Stream *stream) Bool gf_m2ts_stream_compute_pes_length(GF_M2TS_Mux_Stream *stream, u32 payload_length) { - stream->copy_from_next_packets = 0; - stream->next_payload_size = 0; - - gf_m2ts_stream_update_data_following(stream); - assert(stream->pes_data_remain==0); stream->pes_data_len = stream->curr_pck.data_len - stream->pck_offset; -// stream->next_payload_size = 0; + stream->copy_from_next_packets = 0; /*if we have next payload ready, compute transmitted size*/ if (stream->next_payload_size) { u32 pck_size = stream->curr_pck.data_len - stream->pck_offset; u32 ts_bytes = payload_length; - /*flushing end of previous PES, let's put the entire next AU in it*/ - if (pck_size<=ts_bytes) { + /*finish this AU*/ + while (ts_bytes < pck_size) { + ts_bytes += 184; + } + + /*current AU started in PES, don't start new AU - if enough data + still to be sent from current AU, don't send the complete AU + in order to avoid padding*/ + if (stream->prevent_two_au_start_in_pes && !stream->pck_offset) { + if (ts_bytes>184) + ts_bytes -= 184; + else + ts_bytes = pck_size; + } + /*try to fit in part of the next AU*/ + else if (stream->next_payload_size) { + /*how much more TS packets do we need to send next AU ?*/ while (ts_bytes < pck_size + stream->next_payload_size) { ts_bytes += 184; } - } - /*needs several TS packets to send the PES, don't attempt to stick in the entire next AU*/ - else { - while (ts_bytes < pck_size) { - ts_bytes += 184; + /*don't end next AU in next PES if we don't want to start 2 AUs in one PES*/ + if (stream->prevent_two_au_start_in_pes && (ts_bytes>pck_size + stream->next_payload_size)) { + if (ts_bytes>184) + ts_bytes -= 184; + else + ts_bytes = pck_size + stream->next_payload_size; } } + /*that's how much bytes we copy from the following AUs*/ - stream->copy_from_next_packets = ts_bytes - pck_size; + if (ts_bytes >= pck_size) { + stream->copy_from_next_packets = ts_bytes - pck_size; + } else { + u32 skipped = pck_size-ts_bytes; + if (stream->pes_data_len > skipped) + stream->pes_data_len -= skipped; + } + + if (stream->min_bytes_copy_from_next && stream->copy_from_next_packets) { + /*if we don't have enough space in the PES to store begining of new AU, don't copy it and ask + to recompute header (we might no longer have DTS/CTS signaled)*/ + if (stream->copy_from_next_packets < stream->min_bytes_copy_from_next) { + stream->copy_from_next_packets = 0; + stream->next_payload_size = 0; + stream->next_pck_flags = 0; + return 0; + } + /*if what will remain after copying next AU is less than the minimum safety copy only copy next AU and + realign n+2 AU start with PES*/ + if ((stream->copy_from_next_packets > stream->next_payload_size) + && (stream->copy_from_next_packets - stream->next_payload_size < stream->min_bytes_copy_from_next) + ) { + stream->copy_from_next_packets = stream->next_payload_size; + } + } + + if (stream->pck_offset && !stream->copy_from_next_packets && stream->next_payload_size) { + stream->copy_from_next_packets = 0; + stream->next_payload_size = 0; + stream->next_pck_flags = 0; + return 0; + } if (stream->ifce->caps & GF_ESI_STREAM_IS_OVER) { +#if 0 while (stream->copy_from_next_packets > stream->next_payload_size) { if (stream->copy_from_next_packets < 184) { stream->copy_from_next_packets = 0; @@ -1097,8 +1161,11 @@ Bool gf_m2ts_stream_compute_pes_length(GF_M2TS_Mux_Stream *stream, u32 payload_l } stream->copy_from_next_packets -= 184; } +#endif + stream->pes_data_len += stream->next_payload_size; + } else { + stream->pes_data_len += stream->copy_from_next_packets; } - stream->pes_data_len += stream->copy_from_next_packets; } stream->pes_data_remain = stream->pes_data_len; return 1; @@ -1110,14 +1177,16 @@ static u32 gf_m2ts_stream_get_pes_header_length(GF_M2TS_Mux_Stream *stream) u32 hdr_len, flags; flags = stream->pck_offset ? stream->next_pck_flags : stream->curr_pck.flags; + /*not done with the current pes*/ if (stream->pes_data_remain) return 0; - /*not the AU start*/ - if ( !(flags & GF_ESI_DATA_AU_START) ) - return 0; - hdr_len = 9; - if (flags & GF_ESI_DATA_HAS_CTS) hdr_len += 5; - if (flags & GF_ESI_DATA_HAS_DTS) hdr_len += 5; + /*signal timing only if AU start in the PES*/ + if ( flags & GF_ESI_DATA_AU_START) { + if (flags & GF_ESI_DATA_HAS_CTS) hdr_len += 5; + if (flags & GF_ESI_DATA_HAS_DTS) hdr_len += 5; + } else { + hdr_len = hdr_len; + } return hdr_len; } @@ -1136,6 +1205,11 @@ u32 gf_m2ts_stream_add_pes_header(GF_BitStream *bs, GF_M2TS_Mux_Stream *stream, use_dts = (stream->next_pck_flags & GF_ESI_DATA_HAS_DTS) ? 1 : 0; dts = stream->next_pck_dts; cts = stream->next_pck_cts; + } + /*we already sent the begining of the AU*/ + else if (stream->pck_offset) { + use_pts = use_dts = 0; + dts = cts = 0; } else { use_pts = (stream->curr_pck.flags & GF_ESI_DATA_HAS_CTS) ? 1 : 0; use_dts = (stream->curr_pck.flags & GF_ESI_DATA_HAS_DTS) ? 1 : 0; @@ -1143,6 +1217,7 @@ u32 gf_m2ts_stream_add_pes_header(GF_BitStream *bs, GF_M2TS_Mux_Stream *stream, cts = stream->curr_pck.cts; } + /*PES packet length: number of bytes in the PES packet following the last byte of the field "pes packet length"*/ assert(stream->pes_data_len); pes_len = stream->pes_data_len + 3; // 3 = header size if (use_pts) pes_len += 5; @@ -1198,35 +1273,60 @@ u32 gf_m2ts_stream_add_pes_header(GF_BitStream *bs, GF_M2TS_Mux_Stream *stream, void gf_m2ts_mux_pes_get_next_packet(GF_M2TS_Mux_Stream *stream, u8 *packet) { GF_BitStream *bs; - Bool needs_pcr; + Bool needs_pcr, first_pass; u32 adaptation_field_control, payload_length, payload_to_copy, padding_length, hdr_len, pos, copy_next; assert(stream->pid); bs = gf_bs_new(packet, 188, GF_BITSTREAM_WRITE); hdr_len = gf_m2ts_stream_get_pes_header_length(stream); - - adaptation_field_control = GF_M2TS_ADAPTATION_NONE; - payload_length = 184 - hdr_len; - payload_to_copy = padding_length = 0; - needs_pcr = (hdr_len && stream->pcr_priority ) ? 1 : 0; - - if (needs_pcr) { - /*AF headers + PCR*/ - payload_length -= 8; - adaptation_field_control = GF_M2TS_ADAPTATION_AND_PAYLOAD; - } - - if (hdr_len) { - assert(!stream->pes_data_remain); - gf_m2ts_stream_compute_pes_length(stream, payload_length); - assert(stream->pes_data_remain==stream->pes_data_len); + + /*we may need two pass in case we first compute hdr len and TS payload size by considering + we concatenate next au start in this PES but finally couldn't do it when computing PES len + and AU alignment constraint of the stream*/ + first_pass = 1; + while (1) { + if (hdr_len) { + if (first_pass) + gf_m2ts_stream_update_data_following(stream); + hdr_len = gf_m2ts_stream_get_pes_header_length(stream); + } + + adaptation_field_control = GF_M2TS_ADAPTATION_NONE; + payload_length = 184 - hdr_len; + payload_to_copy = padding_length = 0; + needs_pcr = (hdr_len && stream->pcr_priority ) ? 1 : 0; + + /*if we forced inserting PAT/PMT before new RAP, also insert PCR here*/ + if (stream->program->force_pat_pmt_state == 3) { + if( stream == stream->program->pcr) { + stream->program->force_pat_pmt_state = 0; + needs_pcr = 1; + } + } + + if (needs_pcr) { + /*AF headers + PCR*/ + payload_length -= 8; + adaptation_field_control = GF_M2TS_ADAPTATION_AND_PAYLOAD; + } + + if (hdr_len) { + assert(!stream->pes_data_remain); + if (! gf_m2ts_stream_compute_pes_length(stream, payload_length)) { + first_pass = 0; + continue; + } + + assert(stream->pes_data_remain==stream->pes_data_len); + } + break; } copy_next = stream->copy_from_next_packets; payload_to_copy = stream->curr_pck.data_len - stream->pck_offset; /*end of PES packet*/ - if (payload_to_copy > stream->pes_data_remain) { + if (payload_to_copy >= stream->pes_data_remain) { payload_to_copy = stream->pes_data_remain; copy_next = 0; } @@ -1252,7 +1352,8 @@ void gf_m2ts_mux_pes_get_next_packet(GF_M2TS_Mux_Stream *stream, u8 *packet) if (payload_length < payload_to_copy + copy_next) { padding_length = 10; payload_length -= padding_length; - payload_to_copy = payload_length; + if (payload_to_copy > payload_length) + payload_to_copy = payload_length; } else { padding_length = payload_length - payload_to_copy - copy_next; payload_length -= padding_length; @@ -1293,6 +1394,10 @@ void gf_m2ts_mux_pes_get_next_packet(GF_M2TS_Mux_Stream *stream, u8 *packet) } is_rap = (hdr_len && (stream->curr_pck.flags & GF_ESI_DATA_AU_RAP) ) ? 1 : 0; gf_m2ts_add_adaptation(bs, stream->pid, needs_pcr, pcr, is_rap, padding_length); + + if (padding_length) + stream->program->mux->tot_pes_pad_bytes += padding_length; + } if (hdr_len) gf_m2ts_stream_add_pes_header(bs, stream, payload_length); @@ -1300,6 +1405,7 @@ void gf_m2ts_mux_pes_get_next_packet(GF_M2TS_Mux_Stream *stream, u8 *packet) pos = (u32) gf_bs_get_position(bs); gf_bs_del(bs); + assert(stream->curr_pck.data_len - stream->pck_offset >= payload_to_copy); memcpy(packet+pos, stream->curr_pck.data + stream->pck_offset, payload_to_copy); stream->pck_offset += payload_to_copy; assert(stream->pes_data_remain >= payload_to_copy); @@ -1313,12 +1419,12 @@ void gf_m2ts_mux_pes_get_next_packet(GF_M2TS_Mux_Stream *stream, u8 *packet) if (stream->discard_data) gf_free(stream->curr_pck.data); stream->curr_pck.data = NULL; stream->curr_pck.data_len = 0; + stream->pck_offset = 0; GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[MPEG2-TS Muxer] Done sending PES (%d bytes) from PID %d at stream time %d:%d (DTS "LLD" - PCR "LLD")\n", stream->curr_pck.data_len, stream->pid, stream->time.sec, stream->time.nanosec, stream->curr_pck.dts, gf_m2ts_get_pcr(stream->program)/300)); #ifndef GPAC_DISABLE_LOG - if ((gf_log_get_level() >= GF_LOG_INFO) - && (gf_log_get_tools() & GF_LOG_CONTAINER) + if (gf_log_tool_level_on(GF_LOG_CONTAINER, GF_LOG_INFO) && gf_m2ts_time_less(&stream->program->mux->time, &stream->time) ) { s32 drift; @@ -1342,7 +1448,17 @@ void gf_m2ts_mux_pes_get_next_packet(GF_M2TS_Mux_Stream *stream, u8 *packet) while (1) { u32 remain = 0; Bool res = stream->process(stream->program->mux, stream); - assert(res); + if (!res) { + GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[MPEG2-TS Muxer] Not enough data to fill current PES (PID %d) - filling with 0xFF\n", stream->pid) ); + memset(packet+pos, 0xFF, copy_next); + + if (stream->copy_from_next_packets > copy_next) { + stream->copy_from_next_packets -= copy_next; + } else { + stream->copy_from_next_packets = 0; + } + break; + } if (copy_next > stream->curr_pck.data_len) { remain = copy_next - stream->curr_pck.data_len; copy_next = stream->curr_pck.data_len; @@ -1360,16 +1476,22 @@ void gf_m2ts_mux_pes_get_next_packet(GF_M2TS_Mux_Stream *stream, u8 *packet) } if (stream->pck_offset == stream->curr_pck.data_len) { + assert(!remain || (remain>=stream->min_bytes_copy_from_next)); /*PES has been sent, discard internal buffer*/ if (stream->discard_data) gf_free(stream->curr_pck.data); stream->curr_pck.data = NULL; stream->curr_pck.data_len = 0; + stream->pck_offset = 0; } if (!remain) break; pos += copy_next; copy_next = remain; } } + else if (stream->program->force_pat_pmt_state==1) { + stream->program->force_pat_pmt_state = 2; + stream->program->mux->force_pat = 1; + } } stream->bytes_since_last_time += 188; } @@ -1478,12 +1600,18 @@ GF_M2TS_Mux_Stream *gf_m2ts_program_stream_add(GF_M2TS_Mux_Program *program, str case GF_STREAM_VISUAL: /*just pick first valid stream_id in visual range*/ stream->mpeg2_stream_id = 0xE0; + /*for video streams, prevent sending two frames start in one PES. This will + introduce more overhead at very low bitrates where such cases happen, but will ensure proper timing + of each frame*/ + stream->prevent_two_au_start_in_pes = 1; switch (ifce->object_type_indication) { case GPAC_OTI_VIDEO_MPEG4_PART2: stream->mpeg2_stream_type = GF_M2TS_VIDEO_MPEG4; break; case GPAC_OTI_VIDEO_AVC: stream->mpeg2_stream_type = GF_M2TS_VIDEO_H264; + /*make sure we send AU delim NALU in same PES as first VCL NAL: 6 bytes (AU delim) + 4 byte start code + first nal header*/ + stream->min_bytes_copy_from_next = 11; break; case GPAC_OTI_VIDEO_MPEG1: stream->mpeg2_stream_type = GF_M2TS_VIDEO_MPEG1; @@ -1600,7 +1728,7 @@ GF_M2TS_Mux_Program *gf_m2ts_mux_program_add(GF_M2TS_Mux *muxer, u32 program_num program->pmt->program = program; muxer->pat->table_needs_update = 1; program->pmt->process = gf_m2ts_stream_process_pmt; - program->pmt->refresh_rate_ms = pmt_refresh_rate; + program->pmt->refresh_rate_ms = pmt_refresh_rate ? pmt_refresh_rate : (u32) -1; return program; } @@ -1611,7 +1739,7 @@ GF_M2TS_Mux *gf_m2ts_mux_new(u32 mux_rate, u32 pat_refresh_rate, Bool real_time) GF_SAFEALLOC(muxer, GF_M2TS_Mux); muxer->pat = gf_m2ts_stream_new(GF_M2TS_PID_PAT); muxer->pat->process = gf_m2ts_stream_process_pat; - muxer->pat->refresh_rate_ms = pat_refresh_rate; + muxer->pat->refresh_rate_ms = pat_refresh_rate ? pat_refresh_rate : (u32) -1; muxer->real_time = real_time; muxer->bit_rate = mux_rate; muxer->init_pcr_value = 0; @@ -1816,9 +1944,10 @@ const char *gf_m2ts_mux_process(GF_M2TS_Mux *muxer, u32 *status) /*PAT*/ res = muxer->pat->process(muxer, muxer->pat); - if (res && gf_m2ts_time_less_or_equal(&muxer->pat->time, &time) ) { + if ((res && gf_m2ts_time_less_or_equal(&muxer->pat->time, &time)) || muxer->force_pat) { time = muxer->pat->time; stream_to_process = muxer->pat; + muxer->force_pat = 0; /*force sending the PAT regardless of other streams*/ goto send_pck; } @@ -1827,9 +1956,11 @@ const char *gf_m2ts_mux_process(GF_M2TS_Mux *muxer, u32 *status) program = muxer->programs; while (program) { res = program->pmt->process(muxer, program->pmt); - if (res && gf_m2ts_time_less_or_equal(&program->pmt->time, &time) ) { + if ((res && gf_m2ts_time_less_or_equal(&program->pmt->time, &time)) || (program->force_pat_pmt_state==2)) { time = program->pmt->time; stream_to_process = program->pmt; + if (program->force_pat_pmt_state==2) + program->force_pat_pmt_state = 3; /*force sending the PMT regardless of other streams*/ goto send_pck; } @@ -1918,8 +2049,7 @@ send_pck: *status = GF_M2TS_STATE_DATA; #ifndef GPAC_DISABLE_LOG - if ((gf_log_get_level() >= GF_LOG_DEBUG) - && (gf_log_get_tools() & GF_LOG_CONTAINER) + if (gf_log_tool_level_on(GF_LOG_CONTAINER, GF_LOG_DEBUG) && muxer->fixed_rate ) { s32 drift; diff --git a/src/media_tools/m3u8.c b/src/media_tools/m3u8.c index 3916a46..17ce129 100644 --- a/src/media_tools/m3u8.c +++ b/src/media_tools/m3u8.c @@ -30,7 +30,7 @@ #include #include -/*#define MYLOG(xx) GF_LOG( GF_LOG_INFO, GF_LOG_CONTAINER, xx )*/ +/*#define MYLOG(xx) GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, xx )*/ //#define MYLOG(xx) printf xx #define MYLOG(xx) @@ -128,13 +128,16 @@ gf_free(pl); return result; }*/ -PlaylistElement * playlist_element_new(PlaylistElementType elementType, const char * url, const char * title, const char *codecs, int durationInfo) { +PlaylistElement * playlist_element_new(PlaylistElementType elementType, const char * url, const char * title, const char *codecs, int durationInfo, u64 byteRangeStart, u64 byteRangeEnd) { PlaylistElement * e = gf_malloc(sizeof(PlaylistElement)); bzero(e, sizeof(PlaylistElement)); assert( url ); if (e == NULL) return NULL; e->durationInfo = durationInfo; + e->byteRangeStart = byteRangeStart; + e->byteRangeEnd = byteRangeEnd; + e->title = (title ? gf_strdup(title) : NULL); e->codecs = (codecs ? gf_strdup(codecs) : NULL); assert( url); @@ -320,6 +323,7 @@ typedef struct _s_accumulated_attributes { int currentMediaSequence; Bool isVariantPlaylist; Bool isPlaylistEnded; + u64 byteRangeStart, byteRangeEnd; } s_accumulated_attributes; static Bool safe_start_equals(const char * attribute, const char * line) { @@ -336,6 +340,7 @@ static Bool safe_start_equals(const char * attribute, const char * line) { static char ** extractAttributes(const char * name, const char * line, const int numberOfAttributes) { int sz, i, currentAttribute, start; char ** ret; + u8 quote=0; int len = strlen(line); start = strlen(name); if (len <= start) @@ -345,7 +350,7 @@ static char ** extractAttributes(const char * name, const char * line, const int ret = gf_calloc((numberOfAttributes + 1 ), sizeof(char*)); currentAttribute = 0; for (i = start ; i <= len ; i++) { - if (line[i] == '\0' || line[i] == ',') { + if (line[i] == '\0' || (!quote && line[i] == ',') || (line[i] == quote) ) { u32 spaces = 0; sz = 1 + i - start; while (line[start+spaces] == ' ') spaces++; @@ -357,6 +362,10 @@ static char ** extractAttributes(const char * name, const char * line, const int return ret; } } + if ((line[i] == '\'') || (line[i] == '"')) { + if (quote) quote = 0; + else quote = line[i]; + } } if (currentAttribute == 0) { gf_free(ret); @@ -450,6 +459,22 @@ static char ** parseAttributes(const char * line, s_accumulated_attributes * att } return ret; } + ret = extractAttributes("#EXT-X-BYTERANGE:", line, 1); + if (ret) { + /* #EXT-X-BYTERANGE: */ + if (ret[0]) { + u64 begin, size; + if (sscanf(ret[0], LLU"@"LLU, &size, &begin) == 2) { + if (size) { + attributes->byteRangeStart = begin; + attributes->byteRangeEnd = begin + size - 1; + } else { + GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER,("[M3U8] Invalid byte range %s\n", ret[0])); + } + } + } + return ret; + } return NULL; } @@ -462,7 +487,7 @@ GF_Err parse_root_playlist(const char * file, VariantPlaylist ** playlist, const GF_Err parse_sub_playlist(const char * file, VariantPlaylist ** playlist, const char * baseURL, Program * in_program, PlaylistElement *sub_playlist) { - int readen, readPointer, len, i, currentLineNumber; + int len, i, currentLineNumber; FILE * f; VariantPlaylist * pl; char currentLine[M3U8_BUF_SIZE]; @@ -470,7 +495,7 @@ GF_Err parse_sub_playlist(const char * file, VariantPlaylist ** playlist, const s_accumulated_attributes attribs; f = gf_f64_open(file, "rt"); if (!f) { - GF_LOG( GF_LOG_ERROR, GF_LOG_CONTAINER,("[M3U8] Cannot Open m3u8 file %s for reading\n", file)); + GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER,("[M3U8] Cannot Open m3u8 file %s for reading\n", file)); return GF_SERVICE_ERROR; } if (*playlist == NULL) { @@ -481,8 +506,6 @@ GF_Err parse_sub_playlist(const char * file, VariantPlaylist ** playlist, const } } pl = *playlist; - readen=0; - readPointer = 0; currentLineNumber = 0; bzero(&attribs, sizeof(s_accumulated_attributes)); attribs.bandwidth = 0; @@ -509,7 +532,7 @@ GF_Err parse_sub_playlist(const char * file, VariantPlaylist ** playlist, const if (len < 7 || strncmp("#EXTM3U", currentLine, 7)!=0) { fclose(f); variant_playlist_del(pl); - GF_LOG( GF_LOG_ERROR, GF_LOG_CONTAINER, ("Failed to parse M3U8 File, it should start with #EXTM3U, but was : %s\n", currentLine)); + GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("Failed to parse M3U8 File, it should start with #EXTM3U, but was : %s\n", currentLine)); return GF_STREAM_NOT_FOUND; } continue; @@ -609,7 +632,8 @@ GF_Err parse_sub_playlist(const char * file, VariantPlaylist ** playlist, const fullURL, attribs.title, attribs.codecs, - attribs.durationInSeconds); + attribs.durationInSeconds, + attribs.byteRangeStart, attribs.byteRangeEnd); if (currentPlayList == NULL) { /* OUT of memory */ variant_playlist_del(*playlist); @@ -634,7 +658,8 @@ GF_Err parse_sub_playlist(const char * file, VariantPlaylist ** playlist, const baseURL, attribs.title, attribs.codecs, - attribs.durationInSeconds); + attribs.durationInSeconds, + attribs.byteRangeStart, attribs.byteRangeEnd); if (currentPlayList == NULL) { /* OUT of memory */ variant_playlist_del(*playlist); @@ -652,7 +677,8 @@ GF_Err parse_sub_playlist(const char * file, VariantPlaylist ** playlist, const fullURL, attribs.title, attribs.codecs, - attribs.durationInSeconds); + attribs.durationInSeconds, + attribs.byteRangeStart, attribs.byteRangeEnd); if (subElement == NULL) { variant_playlist_del(*playlist); playlist_element_del(currentPlayList); @@ -662,6 +688,7 @@ GF_Err parse_sub_playlist(const char * file, VariantPlaylist ** playlist, const } gf_list_add(currentPlayList->element.playlist.elements, subElement); gf_list_add(program->bitrates, currentPlayList); + currentPlayList->element.playlist.computed_duration += subElement->durationInfo; assert( program ); assert( program->bitrates); assert( currentPlayList); @@ -672,7 +699,8 @@ GF_Err parse_sub_playlist(const char * file, VariantPlaylist ** playlist, const fullURL, attribs.title, attribs.codecs, - attribs.durationInSeconds); + attribs.durationInSeconds, + attribs.byteRangeStart, attribs.byteRangeEnd); if (currentPlayList->elementType != TYPE_PLAYLIST) { currentPlayList->elementType = TYPE_PLAYLIST; if (!currentPlayList->element.playlist.elements) @@ -686,6 +714,7 @@ GF_Err parse_sub_playlist(const char * file, VariantPlaylist ** playlist, const return GF_OUT_OF_MEM; } gf_list_add(currentPlayList->element.playlist.elements, subElement); + currentPlayList->element.playlist.computed_duration += subElement->durationInfo; } } @@ -695,8 +724,13 @@ GF_Err parse_sub_playlist(const char * file, VariantPlaylist ** playlist, const currentPlayList->element.playlist.target_duration = attribs.targetDurationInSeconds; currentPlayList->durationInfo = attribs.targetDurationInSeconds; } - if (attribs.durationInSeconds) { - currentPlayList->durationInfo = attribs.durationInSeconds; + if (attribs.durationInSeconds) { + if (currentPlayList->durationInfo == 0) { + /* we set the playlist duration info as the duration of a segment, only if it's not set + There are cases of playlist with the last segment with a duration different from the others + (example: Apple bipbop test)*/ + currentPlayList->durationInfo = attribs.durationInSeconds; + } } currentPlayList->element.playlist.mediaSequenceMin = attribs.minMediaSequence; currentPlayList->element.playlist.mediaSequenceMax = attribs.currentMediaSequence++; @@ -723,5 +757,18 @@ GF_Err parse_sub_playlist(const char * file, VariantPlaylist ** playlist, const } } fclose(f); + + for (i=0; i < (int) gf_list_count(pl->programs); i++) { + u32 j; + Program *prog = gf_list_get(pl->programs, i); + prog->computed_duration = 0; + for (j=0; jbitrates); j++) { + PlaylistElement *ple = gf_list_get(prog->bitrates, j); + if (ple->elementType==TYPE_PLAYLIST) { + if (ple->element.playlist.computed_duration > prog->computed_duration) + prog->computed_duration = ple->element.playlist.computed_duration; + } + } + } return GF_OK; } diff --git a/src/media_tools/media_export.c b/src/media_tools/media_export.c index c907da8..f1374a0 100644 --- a/src/media_tools/media_export.c +++ b/src/media_tools/media_export.c @@ -41,7 +41,7 @@ static GF_Err gf_export_message(GF_MediaExporter *dumper, GF_Err e, char *format if (dumper->flags & GF_EXPORT_PROBE_ONLY) return e; #ifndef GPAC_DISABLE_LOG - if (gf_log_get_level() && (gf_log_get_tools() & GF_LOG_AUTHOR)) { + if (gf_log_tool_level_on(GF_LOG_AUTHOR, e ? GF_LOG_ERROR : GF_LOG_WARNING)) { va_list args; char szMsg[1024]; va_start(args, format); @@ -89,8 +89,8 @@ static GF_Err gf_dump_to_ogg(GF_MediaExporter *dumper, char *szName, u32 track) if (op.b_o_s) { ogg_stream_pageout(&os, &og); - fwrite(og.header, 1, og.header_len, out); - fwrite(og.body, 1, og.body_len, out); + gf_fwrite(og.header, 1, og.header_len, out); + gf_fwrite(og.body, 1, og.body_len, out); op.b_o_s = 0; if (esd->decoderConfig->objectTypeIndication==GPAC_OTI_MEDIA_THEORA) { @@ -116,8 +116,8 @@ static GF_Err gf_dump_to_ogg(GF_MediaExporter *dumper, char *szName, u32 track) gf_odf_desc_del((GF_Descriptor *)esd); while (ogg_stream_pageout(&os, &og)>0) { - fwrite(og.header, 1, og.header_len, out); - fwrite(og.body, 1, og.body_len, out); + gf_fwrite(og.header, 1, og.header_len, out); + gf_fwrite(og.body, 1, og.body_len, out); } op.granulepos = -1; @@ -157,15 +157,15 @@ static GF_Err gf_dump_to_ogg(GF_MediaExporter *dumper, char *szName, u32 track) if (dumper->flags & GF_EXPORT_DO_ABORT) break; while (ogg_stream_pageout(&os, &og)>0) { - fwrite(og.header, 1, og.header_len, out); - fwrite(og.body, 1, og.body_len, out); + gf_fwrite(og.header, 1, og.header_len, out); + gf_fwrite(og.body, 1, og.body_len, out); } } if (samp) gf_isom_sample_del(&samp); while (ogg_stream_flush(&os, &og)>0) { - fwrite(og.header, 1, og.header_len, out); - fwrite(og.body, 1, og.body_len, out); + gf_fwrite(og.header, 1, og.header_len, out); + gf_fwrite(og.body, 1, og.body_len, out); } ogg_stream_clear(&os); fclose(out); @@ -182,7 +182,10 @@ GF_Err gf_export_hint(GF_MediaExporter *dumper) FILE *out; u32 track, i, size, m_stype, sn, count; - track = gf_isom_get_track_by_id(dumper->file, dumper->trackID); + if (!(track = gf_isom_get_track_by_id(dumper->file, dumper->trackID))) { + GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("Wrong track ID %d for file %s \n", dumper->trackID, gf_isom_get_filename(dumper->file))); + return GF_BAD_PARAM; + } m_stype = gf_isom_get_media_subtype(dumper->file, track, 1); e = gf_isom_reset_hint_reader(dumper->file, track, dumper->sample_num ? dumper->sample_num : 1, 0, 0, 0); @@ -204,7 +207,7 @@ GF_Err gf_export_hint(GF_MediaExporter *dumper) if (e) return gf_export_message(dumper, e, "Error fetching hint packet %d", i); sprintf(szName, "%s_pck_%04d.%s", dumper->out_name, i, gf_4cc_to_str(m_stype)); out = gf_f64_open(szName, "wb"); - fwrite(pck, size, 1, out); + gf_fwrite(pck, size, 1, out); fclose(out); gf_free(pck); i++; @@ -243,7 +246,10 @@ GF_Err gf_media_export_samples(GF_MediaExporter *dumper) GF_BitStream *bs; u32 track, i, di, count, m_type, m_stype, dsi_size, is_mj2k; - track = gf_isom_get_track_by_id(dumper->file, dumper->trackID); + if (!(track = gf_isom_get_track_by_id(dumper->file, dumper->trackID))) { + GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("Wrong track ID %d for file %s \n", dumper->trackID, gf_isom_get_filename(dumper->file))); + return GF_BAD_PARAM; + } m_type = gf_isom_get_media_type(dumper->file, track); m_stype = gf_isom_get_media_subtype(dumper->file, track, 1); dsi_size = 0; @@ -382,7 +388,7 @@ GF_Err gf_media_export_samples(GF_MediaExporter *dumper) gf_export_message(dumper, GF_OK, "Extracting \'%s\' Track (type '%s') - Compressor %s sample%s", szEXT, gf_4cc_to_str(m_type), udesc ? udesc->compressor_name : "Unknown", szNum); break; } - if (udesc->extension_buf) gf_free(udesc->extension_buf); + if (udesc && udesc->extension_buf) gf_free(udesc->extension_buf); if (udesc) gf_free(udesc); } if (dumper->flags & GF_EXPORT_PROBE_ONLY) return GF_OK; @@ -566,7 +572,7 @@ GF_Err gf_media_export_native(GF_MediaExporter *dumper) GF_GenericSampleDescription *udesc; char szName[1000], szEXT[5], GUID[16]; FILE *out; - u32 *qcp_rates; + unsigned int *qcp_rates, rt_cnt; /*contains constants*/ GF_AVCConfig *avccfg; GF_M4ADecSpecInfo a_cfg; GF_BitStream *bs; @@ -575,12 +581,16 @@ GF_Err gf_media_export_native(GF_MediaExporter *dumper) u32 aac_type, is_aac; char *dsi; QCPRateTable rtable[8]; - u32 rt_cnt; dsi_size = 0; dsi = NULL; avccfg = NULL; - track = gf_isom_get_track_by_id(dumper->file, dumper->trackID); + + if (!(track = gf_isom_get_track_by_id(dumper->file, dumper->trackID))) { + GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("Wrong track ID %d for file %s \n", dumper->trackID, gf_isom_get_filename(dumper->file))); + return GF_BAD_PARAM; + } + m_type = gf_isom_get_media_type(dumper->file, track); m_stype = gf_isom_get_media_subtype(dumper->file, track, 1); has_qcp_pad = 0; @@ -869,10 +879,10 @@ GF_Err gf_media_export_native(GF_MediaExporter *dumper) } if (qcp_type==1) { - qcp_rates = (u32 *)GF_QCELP_RATE_TO_SIZE; + qcp_rates = (unsigned int*)GF_QCELP_RATE_TO_SIZE; rt_cnt = GF_QCELP_RATE_TO_SIZE_NB; } else { - qcp_rates = (u32 *)GF_SMV_EVRC_RATE_TO_SIZE; + qcp_rates = (unsigned int*)GF_SMV_EVRC_RATE_TO_SIZE; rt_cnt = GF_SMV_EVRC_RATE_TO_SIZE_NB; } @@ -961,7 +971,7 @@ GF_Err gf_media_export_native(GF_MediaExporter *dumper) while (remain) { nal_size = 0; if (remainnal_unit_size){ - GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("Corrupted NAL Unit: header size %d - bytes left %d\n", avccfg->nal_unit_size, remain) ); + GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("Sample %d (size %d): Corrupted NAL Unit: header size %d - bytes left %d\n", i+1, samp->dataLength, avccfg->nal_unit_size, remain) ); break; } for (j=0; jnal_unit_size; j++) { @@ -972,7 +982,7 @@ GF_Err gf_media_export_native(GF_MediaExporter *dumper) } gf_bs_write_u32(bs, 1); if (remain < nal_size) { - GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("Corrupted NAL Unit: size %d - bytes left %d\n", nal_size, remain) ); + GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("Sample %d (size %d): Corrupted NAL Unit: size %d - bytes left %d\n", i+1, samp->dataLength, nal_size, remain) ); nal_size = remain; } gf_bs_write_data(bs, ptr, nal_size); @@ -1073,7 +1083,7 @@ static GF_Err gf_media_export_avi_track(GF_MediaExporter *dumper) max_size = size; } AVI_read_frame(in, frame, (int*)&key); - if ((u32) size>4) fwrite(frame, 1, size, fout); + if ((u32) size>4) gf_fwrite(frame, 1, size, fout); gf_set_progress("AVI Extract", i+1, num_samples); } gf_free(frame); @@ -1119,7 +1129,7 @@ static GF_Err gf_media_export_avi_track(GF_MediaExporter *dumper) size = AVI_read_audio(in, frame, max_size, (int*)&continuous); if (!size) break; num_samples += size; - fwrite(frame, 1, size, fout); + gf_fwrite(frame, 1, size, fout); gf_set_progress("AVI Extract", num_samples, tot_size); } @@ -1141,7 +1151,10 @@ GF_Err gf_media_export_nhnt(GF_MediaExporter *dumper) Bool has_b_frames; u32 track, i, di, count, pos; - track = gf_isom_get_track_by_id(dumper->file, dumper->trackID); + if (!(track = gf_isom_get_track_by_id(dumper->file, dumper->trackID))) { + GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("Wrong track ID %d for file %s \n", dumper->trackID, gf_isom_get_filename(dumper->file))); + return GF_BAD_PARAM; + } esd = gf_isom_get_esd(dumper->file, track, 1); if (!esd) return gf_export_message(dumper, GF_NON_COMPLIANT_BITSTREAM, "Invalid MPEG-4 stream in track ID %d", dumper->trackID); @@ -1171,7 +1184,7 @@ GF_Err gf_media_export_nhnt(GF_MediaExporter *dumper) if (esd->decoderConfig->decoderSpecificInfo && esd->decoderConfig->decoderSpecificInfo->data) { sprintf(szName, "%s.info", dumper->out_name); out_inf = gf_f64_open(szName, "wb"); - if (out_inf) fwrite(esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength, 1, out_inf); + if (out_inf) gf_fwrite(esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength, 1, out_inf); fclose(out_inf); } @@ -1204,7 +1217,7 @@ GF_Err gf_media_export_nhnt(GF_MediaExporter *dumper) for (i=0; ifile, track, i+1, &di); if (!samp) break; - fwrite(samp->data, samp->dataLength, 1, out_med); + gf_fwrite(samp->data, samp->dataLength, 1, out_med); /*dump nhnt info*/ gf_bs_write_u24(bs, samp->dataLength); @@ -1402,7 +1415,10 @@ GF_Err gf_media_export_isom(GF_MediaExporter *dumper) u32 track; u8 mode; - track = gf_isom_get_track_by_id(dumper->file, dumper->trackID); + if (!(track = gf_isom_get_track_by_id(dumper->file, dumper->trackID))) { + GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("Wrong track ID %d for file %s \n", dumper->trackID, gf_isom_get_filename(dumper->file))); + return GF_BAD_PARAM; + } if (gf_isom_get_media_type(dumper->file, dumper->trackID)==GF_ISOM_MEDIA_OD) { return gf_export_message(dumper, GF_BAD_PARAM, "Cannot extract OD track, result is meaningless"); } @@ -1465,7 +1481,10 @@ GF_Err gf_media_export_avi(GF_MediaExporter *dumper) GF_M4VDecSpecInfo dsi; Double FPS; - track = gf_isom_get_track_by_id(dumper->file, dumper->trackID); + if (!(track = gf_isom_get_track_by_id(dumper->file, dumper->trackID))) { + GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("Wrong track ID %d for file %s \n", dumper->trackID, gf_isom_get_filename(dumper->file))); + return GF_BAD_PARAM; + } esd = gf_isom_get_esd(dumper->file, track, 1); if (!esd) return gf_export_message(dumper, GF_NON_COMPLIANT_BITSTREAM, "Invalid MPEG-4 stream in track ID %d", dumper->trackID); @@ -1575,9 +1594,12 @@ GF_Err gf_media_export_nhml(GF_MediaExporter *dumper, Bool dims_doc) u32 w, h; Bool uncompress; u32 track, i, di, count, pos, mstype; - const char *szRootName, *szSampleName; + const char *szRootName; - track = gf_isom_get_track_by_id(dumper->file, dumper->trackID); + if (!(track = gf_isom_get_track_by_id(dumper->file, dumper->trackID))) { + GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("Wrong track ID %d for file %s \n", dumper->trackID, gf_isom_get_filename(dumper->file))); + return GF_BAD_PARAM; + } if (!track) return gf_export_message(dumper, GF_BAD_PARAM, "Invalid track ID %d", dumper->trackID); if (dumper->flags & GF_EXPORT_PROBE_ONLY) { @@ -1590,7 +1612,6 @@ GF_Err gf_media_export_nhml(GF_MediaExporter *dumper, Bool dims_doc) if (dims_doc) { sprintf(szName, "%s.dml", dumper->out_name); szRootName = "DIMSStream"; - szSampleName = "DIMSUnit"; } else { sprintf(szMedia, "%s.media", dumper->out_name); med = gf_f64_open(szMedia, "wb"); @@ -1601,7 +1622,6 @@ GF_Err gf_media_export_nhml(GF_MediaExporter *dumper, Bool dims_doc) sprintf(szName, "%s.nhml", dumper->out_name); szRootName = "NHNTStream"; - szSampleName = "NHNTSample"; } nhml = gf_f64_open(szName, "wt"); if (!nhml) { @@ -1620,7 +1640,7 @@ GF_Err gf_media_export_nhml(GF_MediaExporter *dumper, Bool dims_doc) if (esd->decoderConfig->decoderSpecificInfo && esd->decoderConfig->decoderSpecificInfo->data) { sprintf(szName, "%s.info", dumper->out_name); inf = gf_f64_open(szName, "wb"); - if (inf) fwrite(esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength, 1, inf); + if (inf) gf_fwrite(esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength, 1, inf); fclose(inf); fprintf(nhml, "specificInfoFile=\"%s\" ", szName); } @@ -1653,7 +1673,7 @@ GF_Err gf_media_export_nhml(GF_MediaExporter *dumper, Bool dims_doc) if (sdesc->extension_buf) { sprintf(szName, "%s.info", dumper->out_name); inf = gf_f64_open(szName, "wb"); - if (inf) fwrite(sdesc->extension_buf, sdesc->extension_buf_size, 1, inf); + if (inf) gf_fwrite(sdesc->extension_buf, sdesc->extension_buf_size, 1, inf); fclose(inf); fprintf(nhml, "specificInfoFile=\"%s\" ", szName); gf_free(sdesc->extension_buf); @@ -1699,7 +1719,7 @@ GF_Err gf_media_export_nhml(GF_MediaExporter *dumper, Bool dims_doc) if (!samp) break; if (med) - fwrite(samp->data, samp->dataLength, 1, med); + gf_fwrite(samp->data, samp->dataLength, 1, med); if (dims_doc) { GF_BitStream *bs = gf_bs_new(samp->data, samp->dataLength, GF_BITSTREAM_READ); @@ -1754,7 +1774,7 @@ GF_Err gf_media_export_nhml(GF_MediaExporter *dumper, Bool dims_doc) inflateEnd(&d_stream); } } else { - fwrite(samp->data+pos+3, size-1, 1, nhml); + gf_fwrite(samp->data+pos+3, size-1, 1, nhml); } fprintf(nhml, "\n"); @@ -1877,7 +1897,7 @@ GF_Err gf_media_export_saf(GF_MediaExporter *dumper) while (1) { gf_saf_mux_for_time(mux, (u32) -1, 0, &data, &size); if (!data) break; - fwrite(data, size, 1, saf_f); + gf_fwrite(data, size, 1, saf_f); gf_free(data); } gf_set_progress("SAF Export", samp_done, tot_samp); @@ -1885,7 +1905,7 @@ GF_Err gf_media_export_saf(GF_MediaExporter *dumper) } gf_saf_mux_for_time(mux, (u32) -1, 1, &data, &size); if (data) { - fwrite(data, size, 1, saf_f); + gf_fwrite(data, size, 1, saf_f); gf_free(data); } fclose(saf_f); @@ -1905,12 +1925,12 @@ void m2ts_export_dump(GF_M2TS_Demuxer *ts, u32 evt_type, void *par) if (evt_type == GF_M2TS_EVT_PES_PCK) { FILE *dst = (FILE*)ts->user; GF_M2TS_PES_PCK *pck = (GF_M2TS_PES_PCK *)par; - fwrite(pck->data, pck->data_len, 1, dst); + gf_fwrite(pck->data, pck->data_len, 1, dst); } else if (evt_type == GF_M2TS_EVT_SL_PCK) { FILE *dst = (FILE*)ts->user; GF_M2TS_SL_PCK *pck = (GF_M2TS_SL_PCK *)par; - fwrite(pck->data + 5, pck->data_len - 5, 1, dst); + gf_fwrite(pck->data + 5, pck->data_len - 5, 1, dst); } } diff --git a/src/media_tools/media_import.c b/src/media_tools/media_import.c index b76406e..ca904df 100644 --- a/src/media_tools/media_import.c +++ b/src/media_tools/media_import.c @@ -41,7 +41,7 @@ GF_Err gf_import_message(GF_MediaImporter *import, GF_Err e, char *format, ...) { #ifndef GPAC_DISABLE_LOG - if (gf_log_get_level() && (gf_log_get_tools() & GF_LOG_AUTHOR)) { + if (gf_log_tool_level_on(GF_LOG_AUTHOR, e ? GF_LOG_ERROR : GF_LOG_INFO)) { va_list args; char szMsg[1024]; va_start(args, format); @@ -301,8 +301,8 @@ GF_Err gf_import_mp3(GF_MediaImporter *import) u16 sr; u32 nb_chan; FILE *in; - u32 hdr, size, max_size, track, di, duration; - u64 done, tot_size, offset; + u32 hdr, size, max_size, track, di; + u64 done, tot_size, offset, duration; GF_ISOSample *samp; in = gf_f64_open(import->in_name, "rb"); @@ -371,7 +371,8 @@ GF_Err gf_import_mp3(GF_MediaImporter *import) samp = gf_isom_sample_new(); samp->IsRAP = 1; - duration = import->duration*sr; + duration = import->duration; + duration *= sr; duration /= 1000; max_size = 0; @@ -499,8 +500,8 @@ GF_Err gf_import_aac_adts(GF_MediaImporter *import) ADTSHeader hdr; GF_M4ADecSpecInfo acfg; FILE *in; - u64 offset, tot_size, done; - u32 max_size, track, di, duration, prof, i; + u64 offset, tot_size, done, duration; + u32 max_size, track, di, i; GF_ISOSample *samp; in = gf_f64_open(import->in_name, "rb"); @@ -630,7 +631,6 @@ GF_Err gf_import_aac_adts(GF_MediaImporter *import) if (0 && hdr.is_mp2) acfg.audioPL = 0xFE; gf_bs_align(dsi); - prof = hdr.profile; e = GF_OK; destroy_esd = 0; @@ -687,7 +687,8 @@ GF_Err gf_import_aac_adts(GF_MediaImporter *import) if (e) goto exit; samp->DTS+=dts_inc; - duration = import->duration*sr; + duration = import->duration; + duration *= sr; duration /= 1000; tot_size = gf_bs_get_size(bs); @@ -742,9 +743,9 @@ static GF_Err gf_import_cmp(GF_MediaImporter *import, Bool mpeg12) Double FPS; FILE *mdia; GF_ISOSample *samp; - Bool is_vfr, enable_vfr, erase_pl, has_cts_offset, is_packed, destroy_esd, do_vfr, forced_packed; - u32 nb_samp, i, timescale, max_size, track, di, PL, max_b, nbI, nbP, nbB, nbNotCoded, dts_inc, ref_frame, b_frames, duration; - u64 pos, tot_size, done_size, samp_offset; + Bool is_vfr, erase_pl, has_cts_offset, is_packed, destroy_esd, do_vfr, forced_packed; + u32 nb_samp, i, timescale, max_size, track, di, PL, max_b, nbI, nbP, nbB, nbNotCoded, dts_inc, ref_frame, b_frames; + u64 pos, tot_size, done_size, samp_offset, duration; GF_M4VDecSpecInfo dsi; GF_M4VParser *vparse; GF_BitStream *bs; @@ -769,11 +770,11 @@ static GF_Err gf_import_cmp(GF_MediaImporter *import, Bool mpeg12) if (import->video_fps) FPS = (Double) import->video_fps; get_video_timing(FPS, ×cale, &dts_inc); - duration = (u32) (import->duration*FPS); + duration = (u64) (import->duration*FPS); is_packed = 0; nbNotCoded = nbI = nbP = nbB = max_b = 0; - enable_vfr = is_vfr = erase_pl = 0; + is_vfr = erase_pl = 0; if (import->flags & GF_IMPORT_PROBE_ONLY) { import->tk_info[0].track_num = 1; @@ -1022,11 +1023,11 @@ static GF_Err gf_import_avi_video(GF_MediaImporter *import) u32 i, num_samples, timescale, track, di, PL, max_b, nb_f, ref_frame, b_frames; u64 samp_offset, size, max_size; u32 nbI, nbP, nbB, nbDummy, nbNotCoded, dts_inc, cur_samp; - Bool is_vfr, enable_vfr, erase_pl; + Bool is_vfr, erase_pl; GF_M4VDecSpecInfo dsi; GF_M4VParser *vparse; s32 key; - u32 duration; + u64 duration; Bool destroy_esd, is_packed, is_init, has_cts_offset; char *comp, *frame; avi_t *in; @@ -1110,7 +1111,7 @@ static GF_Err gf_import_avi_video(GF_MediaImporter *import) FPS = AVI_frame_rate(in); if (import->video_fps) FPS = (Double) import->video_fps; get_video_timing(FPS, ×cale, &dts_inc); - duration = (u32) (import->duration*FPS); + duration = (u64) (import->duration*FPS); e = GF_OK; max_size = 0; @@ -1124,7 +1125,6 @@ static GF_Err gf_import_avi_video(GF_MediaImporter *import) is_packed = 0; nbDummy = nbNotCoded = nbI = nbP = nbB = max_b = 0; - enable_vfr = 0; has_cts_offset = 0; cur_samp = b_frames = ref_frame = 0; @@ -1378,7 +1378,8 @@ GF_Err gf_import_avi_audio(GF_MediaImporter *import) { GF_Err e; FILE *test; - u32 hdr, di, track, i, tot_size, duration; + u64 duration; + u32 hdr, di, track, i, tot_size; s64 offset; s32 size, max_size, done; u16 sampleRate; @@ -1535,8 +1536,8 @@ exit: GF_Err gf_import_isomedia(GF_MediaImporter *import) { GF_Err e; - u64 offset, sampDTS; - u32 track, di, trackID, track_in, i, num_samples, mtype, stype, w, h, duration, sr, sbr_sr, ch, mstype; + u64 offset, sampDTS, duration; + u32 track, di, trackID, track_in, i, num_samples, mtype, stype, w, h, sr, sbr_sr, ch, mstype; s32 trans_x, trans_y; s16 layer; u8 bps; @@ -1746,7 +1747,7 @@ GF_Err gf_import_isomedia(GF_MediaImporter *import) break; } - duration = (u32) (((Double)import->duration * gf_isom_get_media_timescale(import->orig, track_in)) / 1000); + duration = (u64) (((Double)import->duration * gf_isom_get_media_timescale(import->orig, track_in)) / 1000); num_samples = gf_isom_get_sample_count(import->orig, track_in); for (i=0; iflags & GF_IMPORT_USE_DATAREF) @@ -1883,7 +1884,8 @@ GF_Err gf_import_mpeg_ps_video(GF_MediaImporter *import) if (import->video_fps) FPS = (Double) import->video_fps; get_video_timing(FPS, ×cale, &dts_inc); - duration = import->duration*timescale; + duration = import->duration; + duration *= timescale; duration /= 1000; destroy_esd = 0; @@ -1963,8 +1965,8 @@ GF_Err gf_import_mpeg_ps_audio(GF_MediaImporter *import) GF_Err e; mpeg2ps_t *ps; char *buf; - u32 track, di, streamID, mtype, sr, nb_ch, nb_streams, buf_len, frames, hdr, duration, last_pos; - u64 file_size; + u32 track, di, streamID, mtype, sr, nb_ch, nb_streams, buf_len, frames, hdr, last_pos; + u64 file_size, duration; Bool destroy_esd; GF_ISOSample *samp; @@ -2041,7 +2043,7 @@ GF_Err gf_import_mpeg_ps_audio(GF_MediaImporter *import) gf_import_message(import, GF_OK, "%s Audio import - sample rate %d - %d channel%s", (mtype==GPAC_OTI_AUDIO_MPEG1) ? "MPEG-1" : "MPEG-2", sr, nb_ch, (nb_ch>1) ? "s" : ""); - duration = (u32) ((Double)import->duration/1000.0 * sr); + duration = (u64) ((Double)import->duration/1000.0 * sr); samp = gf_isom_sample_new(); samp->IsRAP = 1; @@ -2083,10 +2085,10 @@ GF_Err gf_import_nhnt(GF_MediaImporter *import) { GF_Err e; Bool destroy_esd, next_is_start; - u32 track, di, mtype, max_size, duration, count, w, h, sig; + u32 track, di, mtype, max_size, count, w, h, sig; GF_ISOSample *samp; s64 media_size, media_done; - u64 offset; + u64 offset, duration; GF_BitStream *bs; FILE *nhnt, *mdia, *info; char *ext, szName[1000], szMedia[1000], szNhnt[1000]; @@ -2234,7 +2236,7 @@ GF_Err gf_import_nhnt(GF_MediaImporter *import) gf_import_message(import, GF_OK, "NHNT import - Stream Type %s - ObjectTypeIndication 0x%02x", gf_odf_stream_type_name(import->esd->decoderConfig->streamType), import->esd->decoderConfig->objectTypeIndication); - duration = (u32) ( ((Double) import->duration)/ 1000 * import->esd->slConfig->timestampResolution); + duration = (u64) ( ((Double) import->duration)/ 1000 * import->esd->slConfig->timestampResolution); samp = gf_isom_sample_new(); samp->data = (char*)gf_malloc(sizeof(char) * 1024); @@ -2528,10 +2530,11 @@ GF_Err gf_import_nhml_dims(GF_MediaImporter *import, Bool dims_doc) GF_Err e; GF_DIMSDescription dims; Bool destroy_esd, inRootOD, do_compress, use_dict, is_dims; - u32 i, track, tkID, di, mtype, max_size, duration, count, streamType, oti, timescale, specInfoSize, dts_inc, par_den, par_num; + u32 i, track, tkID, di, mtype, max_size, count, streamType, oti, timescale, specInfoSize, dts_inc, par_den, par_num; GF_ISOSample *samp; GF_XMLAttribute *att; s64 media_size, media_done, offset; + u64 duration; FILE *nhml, *mdia, *info; char *dictionary; char *ext, szName[1000], szMedia[1000], szMediaTemp[1000], szInfo[1000], szXmlFrom[1000], szXmlTo[1000], *specInfo; @@ -2803,7 +2806,7 @@ GF_Err gf_import_nhml_dims(GF_MediaImporter *import, Bool dims_doc) gf_isom_set_audio_info(import->dest, track, di, sdesc.samplerate, sdesc.nb_channels, (u8) sdesc.bits_per_sample); } - duration = (u32) ( ((Double) import->duration)/ 1000 * timescale); + duration = (u64) ( ((Double) import->duration)/ 1000 * timescale); samp = gf_isom_sample_new(); samp->data = (char*)gf_malloc(sizeof(char) * 1024); @@ -3029,12 +3032,12 @@ exit: GF_Err gf_import_amr_evrc_smv(GF_MediaImporter *import) { GF_Err e; - u32 track, trackID, di, max_size, duration, sample_rate, block_size, i, readen; + u32 track, trackID, di, sample_rate, block_size, i, readen; GF_ISOSample *samp; char magic[20], *msg; Bool delete_esd, update_gpp_cfg; u32 media_done, mtype, oti, nb_frames; - u64 offset, media_size; + u64 offset, media_size, duration; GF_3GPConfig gpp_cfg; FILE *mdia; msg = NULL; @@ -3151,13 +3154,13 @@ GF_Err gf_import_amr_evrc_smv(GF_MediaImporter *import) if (e) goto exit; } gf_isom_set_audio_info(import->dest, track, di, sample_rate, 1, 16); - duration = import->duration * sample_rate; + duration = import->duration; + duration *= sample_rate; duration /= 1000; samp = gf_isom_sample_new(); samp->data = (char*)gf_malloc(sizeof(char) * 200); samp->IsRAP = 1; - max_size = 200; offset = gf_f64_tell(mdia); gf_f64_seek(mdia, 0, SEEK_END); media_size = gf_f64_tell(mdia) - offset; @@ -3178,9 +3181,9 @@ GF_Err gf_import_amr_evrc_smv(GF_MediaImporter *import) /*update mode set (same mechanism for both AMR and AMR-WB*/ gpp_cfg.AMR_mode_set |= (1<dataLength = GF_AMR_WB_FRAME_SIZE[ft]; + samp->dataLength = (u32)GF_AMR_WB_FRAME_SIZE[ft]; } else { - samp->dataLength = GF_AMR_FRAME_SIZE[ft]; + samp->dataLength = (u32)GF_AMR_FRAME_SIZE[ft]; } samp->data[0] = toc; break; @@ -3189,7 +3192,7 @@ GF_Err gf_import_amr_evrc_smv(GF_MediaImporter *import) for (i=0; idataLength = GF_SMV_EVRC_RATE_TO_SIZE[2*i+1] - 1; + samp->dataLength = (u32)GF_SMV_EVRC_RATE_TO_SIZE[2*i+1] - 1; break; } } @@ -3254,8 +3257,8 @@ static const char *QCP_SMV_GUID = "\x75\x2B\x7C\x8D\x97\xA7\x46\xED\x98\x5E\xD5\ GF_Err gf_import_qcp(GF_MediaImporter *import) { GF_Err e; - u32 track, trackID, di, i, nb_pck, duration, riff_size, chunk_size, major, minor, version, avg_bps, pck_size, block_size, bps, samplerate, vrat_rate_flag, size_in_packets, nb_frames; - u64 max_size; + u32 track, trackID, di, i, nb_pck, riff_size, chunk_size, pck_size, block_size, bps, samplerate, vrat_rate_flag, size_in_packets, nb_frames; + u64 max_size, duration; GF_BitStream *bs; GF_ISOSample *samp; char magic[12], GUID[16], name[81], fmt[162]; @@ -3308,17 +3311,17 @@ GF_Err gf_import_qcp(GF_MediaImporter *import) } chunk_size = gf_bs_read_u32_le(bs); has_pad = (chunk_size%2) ? 1 : 0; - major = gf_bs_read_u8(bs); - minor = gf_bs_read_u8(bs); + /*major = */gf_bs_read_u8(bs); + /*minor = */gf_bs_read_u8(bs); chunk_size -= 2; /*codec info*/ gf_bs_read_data(bs, GUID, 16); - version = gf_bs_read_u16_le(bs); + /*version = */gf_bs_read_u16_le(bs); chunk_size -= 18; gf_bs_read_data(bs, name, 80); name[80]=0; chunk_size -= 80; - avg_bps = gf_bs_read_u16_le(bs); + /*avg_bps = */gf_bs_read_u16_le(bs); pck_size = gf_bs_read_u16_le(bs); block_size = gf_bs_read_u16_le(bs); samplerate = gf_bs_read_u16_le(bs); @@ -3417,7 +3420,8 @@ GF_Err gf_import_qcp(GF_MediaImporter *import) } gf_isom_set_audio_info(import->dest, track, di, samplerate, 1, (u8) bps); - duration = import->duration * samplerate; + duration = import->duration; + duration *= samplerate; duration /= 1000; samp = gf_isom_sample_new(); @@ -3571,8 +3575,8 @@ static void h263_get_pic_size(GF_BitStream *bs, u32 fmt, u32 *w, u32 *h) GF_Err gf_import_h263(GF_MediaImporter *import) { GF_Err e; - u32 track, trackID, di, max_size, timescale, duration, w, h, fmt, nb_samp, dts_inc; - u64 offset, media_size, media_done; + u32 track, trackID, di, max_size, timescale, w, h, fmt, nb_samp, dts_inc; + u64 offset, media_size, media_done, duration; GF_ISOSample *samp; char *samp_data; GF_3GPConfig gpp_cfg; @@ -3648,7 +3652,7 @@ GF_Err gf_import_h263(GF_MediaImporter *import) samp = gf_isom_sample_new(); - duration = (u32) ( ((Double)import->duration) * timescale / 1000.0); + duration = (u64) ( ((Double)import->duration) * timescale / 1000.0); media_size = gf_bs_get_size(bs); nb_samp = 0; media_done = 0; @@ -3739,8 +3743,9 @@ GF_Err gf_media_avc_rewrite_samples(GF_ISOFile *file, u32 track, u32 prev_size, GF_Err gf_import_h264(GF_MediaImporter *import) { u64 nal_start, nal_end, total_size; - u32 nal_size, track, trackID, di, cur_samp, nb_i, nb_idr, nb_p, nb_b, nb_sp, nb_si, nb_sei, max_w, max_h, duration, max_total_delay; - s32 idx; + u32 nal_size, track, trackID, di, cur_samp, nb_i, nb_idr, nb_p, nb_b, nb_sp, nb_si, nb_sei, max_w, max_h, max_total_delay; + s32 idx, sei_recovery_frame_count; + u64 duration; u8 nal_type; GF_Err e; FILE *mdia; @@ -3749,10 +3754,11 @@ GF_Err gf_import_h264(GF_MediaImporter *import) GF_AVCConfig *avccfg, *svccfg, *dstcfg; GF_BitStream *bs; GF_BitStream *sample_data; - Bool flush_sample, sample_is_rap, first_nal, slice_is_ref, has_cts_offset, detect_fps, is_paff, set_subsamples; - u32 ref_frame, pred_frame, timescale, copy_size, size_length, dts_inc; + Bool flush_sample, sample_is_rap, sample_has_islice, first_nal, slice_is_ref, has_cts_offset, detect_fps, is_paff, set_subsamples, slice_force_ref; + u32 ref_frame, timescale, copy_size, size_length, dts_inc; s32 last_poc, max_last_poc, max_last_b_poc, poc_diff, prev_last_poc, min_poc, poc_shift; Bool first_avc; + Bool use_opengop_gdr = 0; u32 last_svc_sps; u32 prev_nalu_prefix_size, res_prev_nalu_prefix; u8 priority_prev_nalu_prefix; @@ -3787,6 +3793,7 @@ GF_Err gf_import_h264(GF_MediaImporter *import) poc_diff = 0; + restart_import: memset(&avc, 0, sizeof(AVCState)); @@ -3798,7 +3805,8 @@ restart_import: buffer = (char*)gf_malloc(sizeof(char) * max_size); sample_data = NULL; first_avc = 1; - last_svc_sps = 0, + last_svc_sps = 0; + sei_recovery_frame_count = -1; bs = gf_bs_from_file(mdia, GF_BITSTREAM_READ); if (!AVC_IsStartCode(bs)) { @@ -3833,16 +3841,17 @@ restart_import: sample_data = NULL; sample_is_rap = 0; + sample_has_islice = 0; cur_samp = 0; is_paff = 0; total_size = gf_bs_get_size(bs); nal_start = gf_bs_get_position(bs); - duration = (u32) ( ((Double)import->duration) * timescale / 1000.0); + duration = (u64) ( ((Double)import->duration) * timescale / 1000.0); nb_i = nb_idr = nb_p = nb_b = nb_sp = nb_si = nb_sei = 0; max_w = max_h = 0; first_nal = 1; - ref_frame = pred_frame = 0; + ref_frame = 0; last_poc = max_last_poc = max_last_b_poc = prev_last_poc = 0; max_total_delay = 0; @@ -4047,7 +4056,9 @@ restart_import: copy_size = nal_size; switch (avc.s_info.slice_type) { case GF_AVC_TYPE_P: case GF_AVC_TYPE2_P: nb_p++; break; - case GF_AVC_TYPE_I: case GF_AVC_TYPE2_I: nb_i++; break; + case GF_AVC_TYPE_I: case GF_AVC_TYPE2_I: nb_i++; + sample_has_islice = 1; + break; case GF_AVC_TYPE_B: case GF_AVC_TYPE2_B: nb_b++; break; case GF_AVC_TYPE_SP: case GF_AVC_TYPE2_SP: nb_sp++; break; case GF_AVC_TYPE_SI: case GF_AVC_TYPE2_SI: nb_si++; break; @@ -4106,6 +4117,15 @@ restart_import: GF_ISOSample *samp = gf_isom_sample_new(); samp->DTS = (u64)dts_inc*cur_samp; samp->IsRAP = sample_is_rap; + if (!sample_is_rap) { + if (sample_has_islice && (import->flags & GF_IMPORT_FORCE_SYNC) && (sei_recovery_frame_count==0)) { + samp->IsRAP = 1; + if (!use_opengop_gdr) { + use_opengop_gdr = 1; + GF_LOG(GF_LOG_WARNING, GF_LOG_CODING, ("[AVC Import] Forcing non-IDR samples with I slices to be marked as sync points - resulting file will not be ISO conformant\n")); + } + } + } gf_bs_get_content(sample_data, &samp->data, &samp->dataLength); gf_bs_del(sample_data); sample_data = NULL; @@ -4147,13 +4167,32 @@ restart_import: e = gf_isom_add_sample(import->dest, track, di, samp); if (e) goto exit; - gf_isom_sample_del(&samp); cur_samp++; + + /*write sampleGroups info*/ + if (!samp->IsRAP && (sei_recovery_frame_count>=0)) { + /*generic GDR*/ + if (sei_recovery_frame_count) { + if (!use_opengop_gdr) use_opengop_gdr = 1; + e = gf_isom_set_sample_roll_group(import->dest, track, cur_samp, (s16) sei_recovery_frame_count); + } + /*open-GOP*/ + else if (sample_has_islice) { + if (!use_opengop_gdr) use_opengop_gdr = 2; + e = gf_isom_set_sample_rap_group(import->dest, track, cur_samp, 0); + } + if (e) goto exit; + } + + gf_isom_sample_del(&samp); gf_set_progress("Importing AVC-H264", (u32) (nal_start/1024), (u32) (total_size/1024) ); first_nal = 1; if (min_poc > last_poc) min_poc = last_poc; + + sample_has_islice = 0; + sei_recovery_frame_count = -1; } if (copy_size) { @@ -4246,16 +4285,22 @@ restart_import: if (!is_paff && avc.s_info.bottom_field_flag) is_paff = 1; + slice_is_ref = (avc.s_info.nal_unit_type==GF_AVC_NALU_IDR_SLICE); + if (slice_is_ref) nb_idr++; + slice_force_ref = 0; + + /*we only indicate TRUE IDRs for sync samples (cf AVC file format spec). + SEI recovery should be used to build sampleToGroup & RollRecovery tables*/ if (first_nal) { first_nal = 0; - /*we only indicate TRUE IDRs for sync samples (cf AVC file format spec). - SEI recovery should be used to build sampleToGroup & RollRecovery tables*/ - avc.sei.recovery_point.valid = 0; - + if (avc.sei.recovery_point.valid) { + sei_recovery_frame_count = avc.sei.recovery_point.frame_cnt; + avc.sei.recovery_point.valid = 0; + if ((import->flags & GF_IMPORT_FORCE_SYNC) && (sei_recovery_frame_count==0)) + slice_force_ref = 1; + } sample_is_rap = AVC_SliceIsIDR(&avc); } - slice_is_ref = (avc.s_info.nal_unit_type==GF_AVC_NALU_IDR_SLICE); - if (slice_is_ref) nb_idr++; if (avc.s_info.pocDTS = (u64)dts_inc*cur_samp; samp->IsRAP = sample_is_rap; + if (!sample_is_rap && sample_has_islice && (import->flags & GF_IMPORT_FORCE_SYNC)) { + samp->IsRAP = 1; + } /*we store the frame order (based on the POC) as the CTS offset and update the whole table at the end*/ samp->CTS_Offset = last_poc - poc_shift; gf_bs_get_content(sample_data, &samp->data, &samp->dataLength); @@ -4361,6 +4415,7 @@ restart_import: cur_samp++; } + /*recompute all CTS offsets*/ if (has_cts_offset) { u32 i, last_cts_samp; @@ -4483,6 +4538,11 @@ restart_import: } } + if (use_opengop_gdr==2) { + gf_import_message(import, GF_OK, "OpenGOP detected - adjusting file brand"); + gf_isom_modify_alternate_brand(import->dest, GF_4CC('i', 's', 'o', '6'), 1); + } + /*rewrite ESD*/ if (import->esd) { if (!import->esd->slConfig) import->esd->slConfig = (GF_SLConfig*) gf_odf_desc_new(GF_ODF_SLC_TAG); @@ -4570,8 +4630,8 @@ GF_Err gf_import_ogg_video(GF_MediaImporter *import) { GF_Err e; ogg_sync_state oy; - u32 di, track, duration; - u64 tot_size, done; + u32 di, track; + u64 tot_size, done, duration; u32 w, h, fps_num, fps_den, keyframe_freq_force, theora_kgs, flag, dts_inc, timescale; Double FPS; Bool destroy_esd, go; @@ -4761,7 +4821,7 @@ GF_Err gf_import_ogg_video(GF_MediaImporter *import) Double d = import->duration; d *= import->esd->slConfig->timestampResolution; d /= 1000; - duration = (u32) d; + duration = (u64) d; } } @@ -4822,8 +4882,8 @@ GF_Err gf_import_ogg_audio(GF_MediaImporter *import) GF_Err e; ogg_sync_state oy; - u32 di, track, duration; - u64 done, tot_size; + u32 di, track; + u64 done, tot_size, duration; s32 block_size; GF_ISOSample *samp; Bool destroy_esd, go; @@ -4856,7 +4916,8 @@ GF_Err gf_import_ogg_audio(GF_MediaImporter *import) destroy_esd = 0; samp = gf_isom_sample_new(); /*avoids gcc warnings*/ - track = num_headers = duration = 0; + track = num_headers = 0; + duration = 0; ogg_sync_init(&oy); @@ -4945,7 +5006,7 @@ GF_Err gf_import_ogg_audio(GF_MediaImporter *import) Double d = import->duration; d *= vp.sample_rate; d /= 1000; - duration = (u32) d; + duration = (u64) d; } } continue; @@ -5253,6 +5314,9 @@ typedef struct #endif Bool force_next_au_start; Bool stream_setup; + u32 nb_video, nb_video_configured; + u32 nb_audio, nb_audio_configured; + } GF_TSImport; #ifndef GPAC_DISABLE_MPEG2TS @@ -5295,7 +5359,6 @@ static void m2ts_set_track_mpeg4_probe_info(GF_M2TS_ES *es, GF_ESD *esd, tk_info->type = GF_ISOM_MEDIA_ESM; break; } - if (es) tk_info->mpeg4_es_id = es->mpeg4_es_id; } } @@ -5373,13 +5436,16 @@ static void m2ts_set_track_mpeg4_creation_info(GF_MediaImporter *import, u32 *mt } -static void m2ts_create_track(GF_TSImport *tsimp, u32 mtype, u32 stype, u32 oti, Bool is_in_iod) +static void m2ts_create_track(GF_TSImport *tsimp, u32 mtype, u32 stype, u32 oti, u32 mpeg4_es_id, Bool is_in_iod) { GF_MediaImporter *import= (GF_MediaImporter *)tsimp->import; if (mtype != GF_ISOM_MEDIA_ESM) { u32 di; Bool destroy_esd = 0; - tsimp->track = gf_isom_new_track(import->dest, (import->esd?import->esd->ESID:import->trackID), mtype, 90000); + if (import->esd) mpeg4_es_id = import->esd->ESID; + else if (!mpeg4_es_id) mpeg4_es_id = import->trackID; + + tsimp->track = gf_isom_new_track(import->dest, mpeg4_es_id, mtype, 90000); if (!tsimp->track) { tsimp->track = gf_isom_new_track(import->dest, 0, mtype, 90000); if (!tsimp->track) { @@ -5412,6 +5478,43 @@ static void m2ts_create_track(GF_TSImport *tsimp, u32 mtype, u32 stype, u32 oti, } } +/*rewrite last AVC sample currently stored in Annex-B format to ISO format (rewrite start code)*/ +void m2ts_rewrite_avc_sample(GF_MediaImporter *import, GF_TSImport *tsimp) +{ + GF_Err e; + u32 sc_pos, start; + char *data; + GF_BitStream *bs; + GF_ISOSample *samp; + u32 count = gf_isom_get_sample_count(import->dest, tsimp->track); + if (!count || !tsimp->avccfg) return; + + samp = gf_isom_get_sample(import->dest, tsimp->track, count, NULL); + sc_pos = 1; + start = 0; + bs = gf_bs_new(samp->data, samp->dataLength, GF_BITSTREAM_WRITE); + data = samp->data; + while (1) { + if (!samp->data[start+sc_pos] && !samp->data[start+sc_pos+1] && !samp->data[start+sc_pos+2] && (samp->data[start+sc_pos+3]==1)) { + gf_bs_seek(bs, start); + gf_bs_write_u32(bs, (u32) sc_pos-start-4); + start = sc_pos; + } + sc_pos++; + if (start+sc_pos>=samp->dataLength) break; + } + gf_bs_seek(bs, start); + gf_bs_write_u32(bs, samp->dataLength-start-4); + + gf_bs_del(bs); + + e = gf_isom_update_sample(import->dest, tsimp->track, count, samp, 1); + if (e) { + GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG-2 TS Import] Error rewriting AVC NALUs: %s\n", gf_error_to_string(e) )); + } + gf_isom_sample_del(&samp); +} + void on_m2ts_import_data(GF_M2TS_Demuxer *ts, u32 evt_type, void *par) { GF_Err e; @@ -5423,7 +5526,6 @@ void on_m2ts_import_data(GF_M2TS_Demuxer *ts, u32 evt_type, void *par) GF_MediaImporter *import= (GF_MediaImporter *)tsimp->import; GF_M2TS_ES *es = NULL; GF_M2TS_PES *pes = NULL; - GF_M2TS_SECTION_ES *ses = NULL; switch (evt_type) { case GF_M2TS_EVT_PAT_FOUND: @@ -5465,13 +5567,15 @@ void on_m2ts_import_data(GF_M2TS_Demuxer *ts, u32 evt_type, void *par) es = (GF_M2TS_ES *)gf_list_get(prog->streams, i); if (es->pid == prog->pmt_pid) continue; if (es->flags & GF_M2TS_ES_IS_SECTION) { - ses = (GF_M2TS_SECTION_ES *)es; + //ses = (GF_M2TS_SECTION_ES *)es; } else { pes = (GF_M2TS_PES *)es; + gf_m2ts_set_pes_framing(pes, GF_M2TS_PES_FRAMING_DEFAULT); } idx = import->nb_tracks; import->tk_info[idx].track_num = es->pid; import->tk_info[idx].prog_num = prog->number; + import->tk_info[idx].mpeg4_es_id = es->mpeg4_es_id; switch (es->stream_type) { case GF_M2TS_VIDEO_MPEG1: @@ -5479,36 +5583,42 @@ void on_m2ts_import_data(GF_M2TS_Demuxer *ts, u32 evt_type, void *par) import->tk_info[idx].type = GF_ISOM_MEDIA_VISUAL; import->tk_info[idx].lang = pes->lang; import->nb_tracks++; + tsimp->nb_video++; break; case GF_M2TS_VIDEO_MPEG2: import->tk_info[idx].media_type = GF_4CC('M','P','G','2'); import->tk_info[idx].type = GF_ISOM_MEDIA_VISUAL; import->tk_info[idx].lang = pes->lang; import->nb_tracks++; + tsimp->nb_video++; break; case GF_M2TS_VIDEO_MPEG4: import->tk_info[idx].media_type = GF_4CC('M','P','4','V'); import->tk_info[idx].type = GF_ISOM_MEDIA_VISUAL; import->tk_info[idx].lang = pes->lang; import->nb_tracks++; + tsimp->nb_video++; break; case GF_M2TS_VIDEO_H264: import->tk_info[idx].media_type = GF_4CC('H','2','6','4'); import->tk_info[idx].type = GF_ISOM_MEDIA_VISUAL; import->tk_info[idx].lang = pes->lang; import->nb_tracks++; + tsimp->nb_video++; break; case GF_M2TS_AUDIO_MPEG1: import->tk_info[idx].media_type = GF_4CC('M','P','G','1'); import->tk_info[idx].type = GF_ISOM_MEDIA_AUDIO; import->tk_info[idx].lang = pes->lang; import->nb_tracks++; + tsimp->nb_audio++; break; case GF_M2TS_AUDIO_MPEG2: import->tk_info[idx].media_type = GF_4CC('M','P','G','2'); import->tk_info[idx].type = GF_ISOM_MEDIA_AUDIO; import->tk_info[idx].lang = pes->lang; import->nb_tracks++; + tsimp->nb_audio++; break; case GF_M2TS_AUDIO_AAC: case GF_M2TS_AUDIO_LATM_AAC: @@ -5516,18 +5626,21 @@ void on_m2ts_import_data(GF_M2TS_Demuxer *ts, u32 evt_type, void *par) import->tk_info[idx].type = GF_ISOM_MEDIA_AUDIO; import->tk_info[idx].lang = pes->lang; import->nb_tracks++; + tsimp->nb_audio++; break; case GF_M2TS_AUDIO_AC3: import->tk_info[idx].media_type = GF_4CC('D','A','C','3'); import->tk_info[idx].type = GF_ISOM_MEDIA_AUDIO; import->tk_info[idx].lang = pes->lang; import->nb_tracks++; + tsimp->nb_audio++; break; case GF_M2TS_AUDIO_EC3: import->tk_info[idx].media_type = GF_4CC('D','E','C','3'); import->tk_info[idx].type = GF_ISOM_MEDIA_AUDIO; import->tk_info[idx].lang = pes->lang; import->nb_tracks++; + tsimp->nb_audio++; break; case GF_M2TS_SYSTEMS_MPEG4_PES: case GF_M2TS_SYSTEMS_MPEG4_SECTIONS: @@ -5564,7 +5677,7 @@ void on_m2ts_import_data(GF_M2TS_Demuxer *ts, u32 evt_type, void *par) if (es->pid == prog->pmt_pid) continue; if (es->pid == import->trackID) found = 1; if (es->flags & GF_M2TS_ES_IS_SECTION) { - ses = (GF_M2TS_SECTION_ES *)es; + //ses = (GF_M2TS_SECTION_ES *)es; } else { pes = (GF_M2TS_PES *)es; } @@ -5588,7 +5701,7 @@ void on_m2ts_import_data(GF_M2TS_Demuxer *ts, u32 evt_type, void *par) es = ts->ess[import->trackID]; /* import->trackID == pid */ if (es->flags & GF_M2TS_ES_IS_SECTION) { - ses = (GF_M2TS_SECTION_ES *)es; + //ses = (GF_M2TS_SECTION_ES *)es; } else { pes = (GF_M2TS_PES *)es; gf_m2ts_set_pes_framing(pes, GF_M2TS_PES_FRAMING_DEFAULT); @@ -5643,7 +5756,7 @@ void on_m2ts_import_data(GF_M2TS_Demuxer *ts, u32 evt_type, void *par) } break; } - m2ts_create_track(tsimp, mtype, stype, oti, is_in_iod); + m2ts_create_track(tsimp, mtype, stype, oti, es->mpeg4_es_id, is_in_iod); } break; case GF_M2TS_EVT_AAC_CFG: @@ -5666,16 +5779,39 @@ void on_m2ts_import_data(GF_M2TS_Demuxer *ts, u32 evt_type, void *par) { GF_M2TS_PES_PCK *pck = (GF_M2TS_PES_PCK *)par; is_au_start = (pck->flags & GF_M2TS_PES_PCK_AU_START); - + if (import->flags & GF_IMPORT_PROBE_ONLY) { for (i=0; inb_tracks; i++) { if (import->tk_info[i].track_num == pck->stream->pid) { - if (pck->stream->aud_sr) { + if (pck->stream->aud_sr && ! import->tk_info[i].audio_info.sample_rate) { + import->tk_info[i].audio_info.sample_rate = pck->stream->aud_sr; + import->tk_info[i].audio_info.nb_channels = pck->stream->aud_nb_ch; + if ((pck->stream->stream_type==GF_M2TS_AUDIO_AAC) || (pck->stream->stream_type==GF_M2TS_AUDIO_LATM_AAC)) { + sprintf(import->tk_info[i].szCodecProfile, "mp4a.40.%02x", (u8) pck->stream->aud_obj_type); + } import->tk_info[i].audio_info.sample_rate = pck->stream->aud_sr; import->tk_info[i].audio_info.nb_channels = pck->stream->aud_nb_ch; + tsimp->nb_audio_configured++; } else { - import->tk_info[i].video_info.width = pck->stream->vid_w; - import->tk_info[i].video_info.height = pck->stream->vid_h; + /*unpack AVC config*/ + if ((pck->stream->stream_type==GF_M2TS_VIDEO_H264) && !pck->data[0] && !pck->data[1]) { + u32 nal_type = pck->data[4] & 0x1F; + if (nal_type == GF_AVC_NALU_SEQ_PARAM) { + sprintf(import->tk_info[i].szCodecProfile, "avc1.%02x%02x%02x", (u8) pck->data[5], (u8) pck->data[6], (u8) pck->data[7]); + } + } + if (pck->stream->vid_w && ! import->tk_info[i].video_info.width ) { + import->tk_info[i].video_info.width = pck->stream->vid_w; + import->tk_info[i].video_info.height = pck->stream->vid_h; + tsimp->nb_video_configured++; + } + } + /*consider we are done if not using 4 on 2*/ + if (!ts->has_4on2 + && (tsimp->nb_video_configured == tsimp->nb_video) + && (tsimp->nb_audio_configured == tsimp->nb_audio) + ) { + import->flags |= GF_IMPORT_DO_ABORT; } break; } @@ -5699,9 +5835,10 @@ void on_m2ts_import_data(GF_M2TS_Demuxer *ts, u32 evt_type, void *par) } if (pck->stream->pid != import->trackID) return; - if (tsimp->avccfg) { + /*avc data for the current sample is stored in annex-B, as we don't know the size of each nal + when called back (depending on PES packetization, the end of the nal could be in following pes)*/ + if (tsimp->avccfg && !pck->data[0] && !pck->data[1]) { GF_AVCConfigSlot *slc; - GF_BitStream *bs; s32 idx; Bool add_sps, is_subseq = 0; u32 nal_type = pck->data[4] & 0x1F; @@ -5774,11 +5911,6 @@ void on_m2ts_import_data(GF_M2TS_Demuxer *ts, u32 evt_type, void *par) break; } - /*rewrite nalu header*/ - bs = gf_bs_new(pck->data, pck->data_len, GF_BITSTREAM_WRITE); - gf_bs_write_u32(bs, pck->data_len - 4); - gf_bs_del(bs); - if (tsimp->force_next_au_start) { is_au_start = 1; tsimp->force_next_au_start = 0; @@ -5787,7 +5919,12 @@ void on_m2ts_import_data(GF_M2TS_Demuxer *ts, u32 evt_type, void *par) if (!is_au_start) { e = gf_isom_append_sample_data(import->dest, tsimp->track, (char*)pck->data, pck->data_len); if (e) { - GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG-2 TS Import] Error appending sample data\n")); + if (!gf_isom_get_sample_count(import->dest, tsimp->track)) { + GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[MPEG-2 TS Import] missed begining of sample data\n")); + e = GF_OK; + } else { + GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG-2 TS Import] Error appending sample data\n")); + } } if (pck->flags & GF_M2TS_PES_PCK_I_FRAME) tsimp->nb_i++; if (pck->flags & GF_M2TS_PES_PCK_P_FRAME) tsimp->nb_p++; @@ -5838,9 +5975,16 @@ void on_m2ts_import_data(GF_M2TS_Demuxer *ts, u32 evt_type, void *par) samp->IsRAP = (pck->flags & GF_M2TS_PES_PCK_RAP) ? 1 : 0; samp->data = pck->data; samp->dataLength = pck->data_len; - e = gf_isom_add_sample(import->dest, tsimp->track, 1, samp); + + if (samp->DTS && (samp->DTS==tsimp->last_dts)) { + e = gf_isom_append_sample_data(import->dest, tsimp->track, (char*)pck->data, pck->data_len); + } else { + + if (tsimp->avccfg) m2ts_rewrite_avc_sample(import, tsimp); + e = gf_isom_add_sample(import->dest, tsimp->track, 1, samp); + } if (e) { - GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG-2 TS Import] Error adding sample: %s\n", gf_error_to_string(e))); + GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG-2 TS Import] PID %d: Error adding sample: %s\n", pck->stream->pid, gf_error_to_string(e))); //import->flags |= GF_IMPORT_DO_ABORT; import->last_error = e; } @@ -5850,6 +5994,7 @@ void on_m2ts_import_data(GF_M2TS_Demuxer *ts, u32 evt_type, void *par) if (pck->flags & GF_M2TS_PES_PCK_I_FRAME) tsimp->nb_i++; if (pck->flags & GF_M2TS_PES_PCK_P_FRAME) tsimp->nb_p++; if (pck->flags & GF_M2TS_PES_PCK_B_FRAME) tsimp->nb_b++; + tsimp->last_dts = samp->DTS; } else { GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG-2 TS Import] negative time sample - skipping\n")); } @@ -5865,7 +6010,7 @@ void on_m2ts_import_data(GF_M2TS_Demuxer *ts, u32 evt_type, void *par) if (!sl_pck->stream->program->pmt_iod) return; if (sl_pck->stream->flags & GF_M2TS_ES_IS_SECTION) { - ses = (GF_M2TS_SECTION_ES *)sl_pck->stream; + //ses = (GF_M2TS_SECTION_ES *)sl_pck->stream; } else { pes = (GF_M2TS_PES *)sl_pck->stream; } @@ -5933,7 +6078,7 @@ void on_m2ts_import_data(GF_M2TS_Demuxer *ts, u32 evt_type, void *par) mtype = stype = oti = 0; import->esd = gf_m2ts_get_esd(sl_pck->stream); m2ts_set_track_mpeg4_creation_info(import, &mtype, &stype, &oti); - m2ts_create_track(tsimp, mtype, stype, oti, 0); + m2ts_create_track(tsimp, mtype, stype, oti, sl_pck->stream->mpeg4_es_id, 0); } if (import->esd) { @@ -5949,11 +6094,21 @@ void on_m2ts_import_data(GF_M2TS_Demuxer *ts, u32 evt_type, void *par) } else { if (!(sl_pck->stream->flags & GF_M2TS_ES_FIRST_DTS)) { sl_pck->stream->flags |= GF_M2TS_ES_FIRST_DTS; + + if (!hdr.compositionTimeStampFlag) { + hdr.compositionTimeStamp = sl_pck->stream->program->first_dts; + GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG-2 TS Import] PID %d First SL Access unit start flag set without any composition time stamp - defaulting to last CTS seen on program\n", sl_pck->stream->pid)); + } sl_pck->stream->first_dts = (hdr.decodingTimeStamp?hdr.decodingTimeStamp:hdr.compositionTimeStamp); if (!sl_pck->stream->program->first_dts || sl_pck->stream->program->first_dts > sl_pck->stream->first_dts) { sl_pck->stream->program->first_dts = sl_pck->stream->first_dts; } + } else { + if (!hdr.compositionTimeStampFlag) { + hdr.compositionTimeStamp = sl_pck->stream->first_dts + tsimp->last_dts+1; + GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG-2 TS Import] PID %d SL Access unit start flag set without any composition time stamp - defaulting to last CTS seen on stream + 1\n", sl_pck->stream->pid)); + } } samp = gf_isom_sample_new(); @@ -5971,8 +6126,9 @@ void on_m2ts_import_data(GF_M2TS_Demuxer *ts, u32 evt_type, void *par) samp->dataLength = sl_pck->data_len - hdr_len; e = gf_isom_add_sample(import->dest, tsimp->track, 1, samp); + /*if CTS was not specified, samples will simply be skipped*/ if (e) { - GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG-2 TS Import] Error adding sample\n")); + GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG-2 TS Import] PID %d Error adding sample\n", sl_pck->stream->pid)); } if (import->duration && (import->duration<=(samp->DTS+samp->CTS_Offset)/90)) { //import->flags |= GF_IMPORT_DO_ABORT; @@ -6074,32 +6230,37 @@ GF_Err gf_import_mpeg_ts(GF_MediaImporter *import) gf_isom_avc_config_update(import->dest, tsimp.track, 1, tsimp.avccfg); gf_isom_set_visual_info(import->dest, tsimp.track, 1, w, h); gf_isom_set_track_layout_info(import->dest, tsimp.track, w<<16, h<<16, 0, 0, 0); + + + m2ts_rewrite_avc_sample(import, &tsimp); + gf_odf_avc_cfg_del(tsimp.avccfg); } + if (tsimp.track) { + MP4T_RecomputeBitRate(import->dest, tsimp.track); + /* creation of the edit lists */ + if (es->first_dts != es->program->first_dts) { + u32 media_ts, moov_ts, offset; + u64 dur; + media_ts = gf_isom_get_media_timescale(import->dest, tsimp.track); + moov_ts = gf_isom_get_timescale(import->dest); + assert(es->program->first_dts <= es->first_dts); + offset = (u32)(es->first_dts - es->program->first_dts) * moov_ts / media_ts; + dur = gf_isom_get_media_duration(import->dest, tsimp.track) * moov_ts / media_ts; + gf_isom_set_edit_segment(import->dest, tsimp.track, 0, offset, 0, GF_ISOM_EDIT_EMPTY); + gf_isom_set_edit_segment(import->dest, tsimp.track, offset, dur, 0, GF_ISOM_EDIT_NORMAL); + gf_import_message(import, GF_OK, "Timeline offset: %d ms", offset); + } - MP4T_RecomputeBitRate(import->dest, tsimp.track); - /* creation of the edit lists */ - if (es->first_dts != es->program->first_dts) { - u32 media_ts, moov_ts, offset; - u64 dur; - media_ts = gf_isom_get_media_timescale(import->dest, tsimp.track); - moov_ts = gf_isom_get_timescale(import->dest); - assert(es->program->first_dts < es->first_dts); - offset = (u32)(es->first_dts - es->program->first_dts) * moov_ts / media_ts; - dur = gf_isom_get_media_duration(import->dest, tsimp.track) * moov_ts / media_ts; - gf_isom_set_edit_segment(import->dest, tsimp.track, 0, offset, 0, GF_ISOM_EDIT_EMPTY); - gf_isom_set_edit_segment(import->dest, tsimp.track, offset, dur, 0, GF_ISOM_EDIT_NORMAL); - gf_import_message(import, GF_OK, "Timeline offset: %d ms", offset); - } + if (tsimp.nb_p) { + gf_import_message(import, GF_OK, "Import results: %d VOPs (%d Is - %d Ps - %d Bs)", gf_isom_get_sample_count(import->dest, tsimp.track), tsimp.nb_i, tsimp.nb_p, tsimp.nb_b); + } - if (tsimp.nb_p) { - gf_import_message(import, GF_OK, "Import results: %d VOPs (%d Is - %d Ps - %d Bs)", gf_isom_get_sample_count(import->dest, tsimp.track), tsimp.nb_i, tsimp.nb_p, tsimp.nb_b); + if (es->program->pmt_iod) + gf_isom_set_brand_info(import->dest, GF_ISOM_BRAND_MP42, 1); } - - if (es->program->pmt_iod) - gf_isom_set_brand_info(import->dest, GF_ISOM_BRAND_MP42, 1); } gf_m2ts_demux_del(ts); @@ -6111,7 +6272,7 @@ GF_Err gf_import_mpeg_ts(GF_MediaImporter *import) GF_Err gf_import_vobsub(GF_MediaImporter *import) { - static u8 null_subpic[] = { 0x00, 0x09, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0xFF }; + static const u8 null_subpic[] = { 0x00, 0x09, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0xFF }; char filename[GF_MAX_PATH]; FILE *file = NULL; int version; @@ -6376,8 +6537,8 @@ GF_Err gf_import_ac3(GF_MediaImporter *import) u32 nb_chan; FILE *in; GF_BitStream *bs; - u32 max_size, track, di, duration; - u64 tot_size, done; + u32 max_size, track, di; + u64 tot_size, done, duration; GF_ISOSample *samp; in = gf_f64_open(import->in_name, "rb"); @@ -6450,7 +6611,8 @@ GF_Err gf_import_ac3(GF_MediaImporter *import) samp = gf_isom_sample_new(); samp->IsRAP = 1; - duration = import->duration*sr; + duration = import->duration; + duration *= sr; duration /= 1000; max_size = 0; @@ -6728,6 +6890,7 @@ GF_Err gf_media_change_pl(GF_ISOFile *file, u32 track, u32 profile, u32 level) if (level) slc->data[3] = level; } e = gf_isom_avc_config_update(file, track, 1, avcc); + assert (e == GF_OK); gf_odf_avc_cfg_del(avcc); return GF_OK; } diff --git a/src/media_tools/mpd.c b/src/media_tools/mpd.c index 61abb0b..c9b36b0 100644 --- a/src/media_tools/mpd.c +++ b/src/media_tools/mpd.c @@ -1,7 +1,7 @@ /* * GPAC - Multimedia Framework C SDK * -* Authors: Cyril Concolato +* Authors: Cyril Concolato - Jean Le Feuvre * Copyright (c) Telecom ParisTech 2010- * All rights reserved * @@ -28,6 +28,57 @@ #include #include + +static Bool gf_mpd_parse_bool(char *attr) +{ + if (!strcmp(attr, "true")) return 1; + return 0; +} + +static char *gf_mpd_parse_string(char *attr) +{ + return gf_strdup(attr); +} + +static char *gf_mpd_parse_text_content(GF_XMLNode *child) +{ + u32 child_index = 0; + while (1) { + child = gf_list_get(child->content, child_index); + if (!child) { + break; + } else if (child->type == GF_XML_TEXT_TYPE) { + return gf_mpd_parse_string(child->name); + } + child_index++; + } + return NULL; +} + +static u32 gf_mpd_parse_int(char *attr) +{ + return atoi(attr); +} + +static Double gf_mpd_parse_double(char *attr) +{ + return atof(attr); +} + +static GF_MPD_Fractional *gf_mpd_parse_frac(char *attr) +{ + GF_MPD_Fractional *res; + GF_SAFEALLOC(res, GF_MPD_Fractional); + sscanf(attr, "%d:%d", &res->num, &res->den); + return res; +} + +static u64 gf_mpd_parse_date(char *attr) +{ + fprintf(stdout, "error: mpd parse date not implemented\n"); + return 0; +} + static u32 gf_mpd_parse_duration(char *duration) { u32 i; if (!duration) return 0; @@ -76,373 +127,533 @@ static u32 gf_mpd_parse_duration(char *duration) { } } -static GF_Err gf_mpd_parse_rep_cp(GF_XMLNode *root, GF_MPD_Representation *rep) -{ - u32 att_index; - GF_XMLAttribute *att; - att_index = 0; - while (1) { - att = gf_list_get(root->attributes, att_index); - if (!att) { - break; - } else if (!strcmp(att->name, "SchemeInformation")) { - rep->content_protection_type = gf_strdup(att->value); - } else if (!strcmp(att->name, "schemeIdUri")) { - rep->content_protection_uri = gf_strdup(att->value); - } - att_index++; - } - return GF_OK; -} -static GF_Err gf_mpd_parse_rep_trickmode(GF_XMLNode *root, GF_MPD_Representation *rep) +static GF_MPD_ByteRange *gf_mpd_parse_byte_range(char *attr) { - u32 att_index; - GF_XMLAttribute *att; - att_index = 0; - while (1) { - att = gf_list_get(root->attributes, att_index); - if (!att) { - break; - } else if (!strcmp(att->name, "alternatePlayoutRate")) { - rep->alternatePlayoutRate = atof(att->value); - } - att_index++; - } - - return GF_OK; + GF_MPD_ByteRange *br; + GF_SAFEALLOC(br, GF_MPD_ByteRange); + sscanf(attr, LLD"-"LLD, &br->start_range, &br->end_range); + return br; } -static GF_Err gf_mpd_parse_rep_initseg(GF_XMLNode *root, GF_MPD_Representation *rep, const char * baseURL) +static GF_Err gf_mpd_parse_location(GF_MPD *mpd, GF_XMLNode *child) { - u32 att_index; - GF_XMLAttribute *att; - - att_index = 0; - while (1) { - att = gf_list_get(root->attributes, att_index); - if (!att) { - break; - } else if (!strcmp(att->name, "sourceURL")) { - GF_URL_Info info; - GF_Err e; - gf_dm_url_info_init(&info); - e = gf_dm_get_url_info(att->value, &info, baseURL); - assert( e == GF_OK ); - assert( info.canonicalRepresentation); - rep->init_url = gf_strdup(info.canonicalRepresentation); - gf_dm_url_info_del(&info); - } else if (!strcmp(att->name, "range")) { - rep->init_use_range = 1; - sscanf(att->value, "%d-%d", &rep->init_byterange_start, &rep->init_byterange_end); - } - att_index++; - } + char *str = gf_mpd_parse_text_content(child); + if (str) return gf_list_add(mpd->locations, str); return GF_OK; } -static GF_Err gf_mpd_parse_rep_urltemplate(GF_XMLNode *root, GF_MPD_Representation *rep) +static GF_Err gf_mpd_parse_metrics(GF_MPD *mpd, GF_XMLNode *child) { - u32 att_index; - GF_XMLAttribute *att; - - att_index = 0; - while (1) { - att = gf_list_get(root->attributes, att_index); - if (!att) { - break; - } else if (!strcmp(att->name, "sourceURL")) { - rep->url_template = gf_strdup(att->value); - } else if (!strcmp(att->name, "id")) { - rep->id = gf_strdup(att->value); - } else if (!strcmp(att->name, "startIndex")) { - rep->startIndex = atoi(att->value)-1; - } else if (!strcmp(att->name, "endIndex")) { - rep->endIndex = atoi(att->value)-1; - } - att_index++; - } + fprintf(stdout, "mpd metrics not implemented yet\n"); return GF_OK; } -static GF_Err gf_mpd_parse_rep_urlelt(GF_XMLNode *root, GF_MPD_SegmentInfo *seg, const char * baseURL) +GF_Err gf_mpd_parse_base_url(GF_List *container, GF_XMLNode *node) { - u32 att_index; + u32 i; + GF_Err e; GF_XMLAttribute *att; + GF_MPD_BaseURL *url; + GF_SAFEALLOC(url, GF_MPD_BaseURL); + if (! url) return GF_OUT_OF_MEM; + e = gf_list_add(container, url); - att_index = 0; - while (1) { - att = gf_list_get(root->attributes, att_index); - if (!att) { - break; - } else if (!strcmp(att->name, "sourceURL")) { - GF_URL_Info info; - GF_Err e; - gf_dm_url_info_init(&info); - e = gf_dm_get_url_info(att->value, &info, baseURL); - assert( e == GF_OK ); - assert( info.canonicalRepresentation); - seg->url = gf_strdup(info.canonicalRepresentation); - gf_dm_url_info_del(&info); - } else if (!strcmp(att->name, "range")) { - seg->use_byterange = 1; - sscanf(att->value, "%d-%d", &seg->byterange_start, &seg->byterange_end); - } - att_index++; + i = 0; + while ( (att = gf_list_enum(node->attributes, &i))) { + if (!strcmp(att->name, "serviceLocation")) url->service_location = gf_mpd_parse_string(att->value); + else if (!strcmp(att->name, "byteRange")) url->byte_range = gf_mpd_parse_byte_range(att->value); } + url->URL = gf_mpd_parse_text_content(node); return GF_OK; } -static GF_Err gf_mpd_parse_rep_segmentinfo(GF_XMLNode *root, GF_MPD_Representation *rep) +static GF_Err gf_mpd_parse_program_info(GF_MPD *mpd, GF_XMLNode *root) { + GF_MPD_ProgramInfo *info; u32 att_index, child_index; - u32 nb_urlelements; GF_XMLAttribute *att; GF_XMLNode *child; + GF_SAFEALLOC(info, GF_MPD_ProgramInfo); + if (!info) return GF_OUT_OF_MEM; + att_index = 0; while (1) { att = gf_list_get(root->attributes, att_index); if (!att) { break; - } else if (!strcmp(att->name, "duration")) { - rep->default_segment_duration = gf_mpd_parse_duration(att->value); - } else if (!strcmp(att->name, "baseURL")) { - rep->default_base_url = gf_strdup(att->value); + } else if (!strcmp(att->name, "moreInformationURL")) { + info->more_info_url = gf_mpd_parse_string(att->value); + } else if (!strcmp(att->name, "lang")) { + info->lang = gf_mpd_parse_string(att->value); } att_index++; } child_index = 0; - nb_urlelements = 0; while (1) { child = gf_list_get(root->content, child_index); if (!child) { break; } else if (child->type == GF_XML_NODE_TYPE) { - if (!strcmp(child->name, "InitialisationSegmentURL")) { - gf_mpd_parse_rep_initseg(child, rep, rep->default_base_url); - } else if (!strcmp(child->name, "BaseURL")) { - /*ISO/IEC 23001-6 (19/07/2011), 5.5.11.2: "If [InitialisationSegmentURL@sourceURL] - not present, then any BaseURL element is mapped to the sourceURL"*/ - att_index = 0; - while (att = gf_list_get(child->content, att_index)) { - if (att->value) { - rep->init_url = strdup(att->value); - } - att_index++; + if (!strcmp(child->name, "Title")) { + GF_XMLNode *data_node = gf_list_get(child->content, 0); + if (data_node && data_node->type == GF_XML_TEXT_TYPE) { + info->title = gf_strdup(data_node->name); + } + } else if (!strcmp(child->name, "Source")) { + GF_XMLNode *data_node = gf_list_get(child->content, 0); + if (data_node && data_node->type == GF_XML_TEXT_TYPE) { + info->source = gf_strdup(data_node->name); + } + } else if (!strcmp(child->name, "Copyright")) { + GF_XMLNode *data_node = gf_list_get(child->content, 0); + if (data_node && data_node->type == GF_XML_TEXT_TYPE) { + info->copyright = gf_strdup(data_node->name); } - } else if (!strcmp(child->name, "UrlTemplate")) { - gf_mpd_parse_rep_urltemplate(child, rep); - } else if (!strcmp(child->name, "Url")) { - nb_urlelements++; } } child_index++; } + return gf_list_add(mpd->program_infos, info); +} - /* We assume that URL Template has precedence over URL elements */ - if (rep->url_template) { - /* TODO: expand the template to create segment urls*/ - } else if (nb_urlelements) { - u32 urlelt_index = 0; - assert( !rep->segments ); - rep->segments = gf_list_new(); - - child_index = 0; - while (1) { - child = gf_list_get(root->content, child_index); - if (!child) { - break; - } else if (child->type == GF_XML_NODE_TYPE) { - if (!strcmp(child->name, "Url")) { - GF_MPD_SegmentInfo *seg_info; - GF_SAFEALLOC(seg_info, GF_MPD_SegmentInfo); - if (!seg_info) return GF_OUT_OF_MEM; - seg_info->byterange_end = 0; - seg_info->byterange_start = 0; - seg_info->use_byterange = 0; - seg_info->url = NULL; - gf_mpd_parse_rep_urlelt(child, seg_info, rep->default_base_url); - gf_list_add(rep->segments, seg_info); - urlelt_index++; - } +static GF_MPD_URL *gf_mpd_parse_url(GF_XMLNode *root) +{ + u32 i; + GF_MPD_URL *url; + GF_XMLAttribute *att; + + GF_SAFEALLOC(url, GF_MPD_URL); + if (!url) return NULL; + + i = 0; + while ( (att = gf_list_enum(root->attributes, &i)) ) { + if (!strcmp(att->name, "sourceURL")) url->sourceURL = gf_mpd_parse_string(att->value); + else if (!strcmp(att->name, "range")) url->byte_range = gf_mpd_parse_byte_range(att->value); + } + return url; +} + +static void gf_mpd_parse_segment_base_generic(GF_MPD_SegmentBase *seg, GF_XMLNode *root) +{ + GF_XMLAttribute *att; + GF_XMLNode *child; + u32 i = 0; + + /*setup some defaults*/ + seg->timescale = 1; + + while ( (att = gf_list_enum(root->attributes, &i)) ) { + if (!strcmp(att->name, "timescale")) seg->timescale = gf_mpd_parse_int(att->value); + else if (!strcmp(att->name, "presentationTimeOffset")) seg->presentation_time_offset = gf_mpd_parse_int(att->value); + else if (!strcmp(att->name, "indexRange")) seg->index_range = gf_mpd_parse_int(att->value); + else if (!strcmp(att->name, "indexRangeExact")) seg->index_range_exact = gf_mpd_parse_bool(att->value); + } + + i = 0; + while ( (child = gf_list_enum(root->content, &i))) { + if (child->type != GF_XML_NODE_TYPE) continue; + if (!strcmp(child->name, "Initialization")) seg->initialization_segment = gf_mpd_parse_url(child); + else if (!strcmp(child->name, "RepresentationIndex")) seg->representation_index = gf_mpd_parse_url(child); + } +} + +static GF_MPD_SegmentTimeline *gf_mpd_parse_segment_timeline(GF_XMLNode *root) +{ + u32 i, j; + GF_XMLAttribute *att; + GF_XMLNode *child; + GF_MPD_SegmentTimeline *seg; + GF_SAFEALLOC(seg, GF_MPD_SegmentTimeline); + if (!seg) return NULL; + seg->entries = gf_list_new(); + + i = 0; + while ( (child = gf_list_enum(root->content, &i))) { + if (child->type != GF_XML_NODE_TYPE) continue; + if (!strcmp(child->name, "S")) { + GF_MPD_SegmentTimelineEntry *segent; + GF_SAFEALLOC(segent, GF_MPD_SegmentTimelineEntry); + gf_list_add(seg->entries, segent); + + j = 0; + while ( (att = gf_list_enum(child->attributes, &j)) ) { + if (!strcmp(att->name, "t")) segent->start_time = gf_mpd_parse_int(att->value); + else if (!strcmp(att->name, "d")) segent->duration = gf_mpd_parse_int(att->value); + else if (!strcmp(att->name, "r")) segent->repeat_count = gf_mpd_parse_int(att->value); } - child_index++; } } + return seg; +} + +static GF_MPD_SegmentBase *gf_mpd_parse_segment_base(GF_XMLNode *root) +{ + GF_MPD_SegmentBase *seg; + GF_SAFEALLOC(seg, GF_MPD_SegmentBase); + if (!seg) return NULL; + gf_mpd_parse_segment_base_generic(seg, root); + return seg; +} + +void gf_mpd_parse_multiple_segment_base(GF_MPD_MultipleSegmentBase *seg, GF_XMLNode *root) +{ + u32 i; + GF_XMLAttribute *att; + GF_XMLNode *child; + + gf_mpd_parse_segment_base_generic((GF_MPD_SegmentBase*)seg, root); + seg->start_number = (u32) -1; + + i = 0; + while ( (att = gf_list_enum(root->attributes, &i)) ) { + if (!strcmp(att->name, "duration")) seg->duration = gf_mpd_parse_int(att->value); + else if (!strcmp(att->name, "startNumber")) seg->start_number = gf_mpd_parse_int(att->value); + } + + i = 0; + while ( (child = gf_list_enum(root->content, &i))) { + if (child->type != GF_XML_NODE_TYPE) continue; + if (!strcmp(child->name, "SegmentTimeline")) seg->segment_timeline = gf_mpd_parse_segment_timeline(child); + else if (!strcmp(child->name, "BitstreamSwitching")) seg->bitstream_switching_url = gf_mpd_parse_url(child); + } +} + +static void gf_mpd_parse_segment_url(GF_List *container, GF_XMLNode *root) +{ + u32 i; + GF_MPD_SegmentURL *seg; + GF_XMLAttribute *att; + + GF_SAFEALLOC(seg, GF_MPD_SegmentURL); + if (!seg) return; + gf_list_add(container, seg); + + i = 0; + while ( (att = gf_list_enum(root->attributes, &i)) ) { + if (!strcmp(att->name, "media")) seg->media = gf_mpd_parse_string(att->value); + else if (!strcmp(att->name, "index")) seg->index = gf_mpd_parse_string(att->value); + else if (!strcmp(att->name, "mediaRange")) seg->media_range = gf_mpd_parse_byte_range(att->value); + else if (!strcmp(att->name, "indexRange")) seg->index_range = gf_mpd_parse_byte_range(att->value); + } +} + +static GF_MPD_SegmentList *gf_mpd_parse_segment_list(GF_XMLNode *root) +{ + u32 i; + GF_MPD_SegmentList *seg; + GF_XMLAttribute *att; + GF_XMLNode *child; + + GF_SAFEALLOC(seg, GF_MPD_SegmentList); + if (!seg) return NULL; + seg->segment_URLs = gf_list_new(); + + i = 0; + while ( (att = gf_list_enum(root->attributes, &i)) ) { + if (strstr(att->name, "href") || strstr(att->name, "actuate")) { + } + } + gf_mpd_parse_multiple_segment_base((GF_MPD_MultipleSegmentBase *)seg, root); + + i = 0; + while ( (child = gf_list_enum(root->content, &i))) { + if (child->type != GF_XML_NODE_TYPE) continue; + if (!strcmp(child->name, "SegmentURL")) gf_mpd_parse_segment_url(seg->segment_URLs, child); + } + if (!gf_list_count(seg->segment_URLs)) { + gf_list_del(seg->segment_URLs); + seg->segment_URLs = NULL; + } + return seg; +} + +static GF_MPD_SegmentTemplate *gf_mpd_parse_segment_template(GF_XMLNode *root) +{ + u32 i; + GF_MPD_SegmentTemplate *seg; + GF_XMLAttribute *att; + + GF_SAFEALLOC(seg, GF_MPD_SegmentTemplate); + if (!seg) return NULL; + + i = 0; + while ( (att = gf_list_enum(root->attributes, &i)) ) { + if (!strcmp(att->name, "media")) seg->media = gf_mpd_parse_string(att->value); + else if (!strcmp(att->name, "index")) seg->index = gf_mpd_parse_string(att->value); + else if (!strcmp(att->name, "initialization")) seg->initialization = gf_mpd_parse_string(att->value); + else if (!strcmp(att->name, "bitstreamSwitching")) seg->bitstream_switching = gf_mpd_parse_string(att->value); + } + gf_mpd_parse_multiple_segment_base((GF_MPD_MultipleSegmentBase *)seg, root); + return seg; +} + +static GF_Err gf_mpd_parse_content_component(GF_List *container, GF_XMLNode *root) +{ return GF_OK; } -static GF_Err gf_mpd_parse_representation(GF_XMLNode *root, GF_MPD_Representation *rep) +static GF_Err gf_mpd_parse_descriptor(GF_List *container, GF_XMLNode *root) +{ + return GF_OK; +} + +static void gf_mpd_parse_common_representation(GF_MPD_CommonAttributes *com, GF_XMLNode *root) { - u32 att_index, child_index; GF_XMLAttribute *att; GF_XMLNode *child; + u32 i = 0; - att_index = 0; - while (1) { - att = gf_list_get(root->attributes, att_index); - if (!att) { - break; - } else if (!strcmp(att->name, "bandwidth")) { - rep->bandwidth = atoi(att->value); - } else if (!strcmp(att->name, "width")) { - rep->width = atoi(att->value); - } else if (!strcmp(att->name, "height")) { - rep->height = atoi(att->value); - } else if (!strcmp(att->name, "lang")) { - rep->lang = gf_strdup(att->value); - } else if (!strcmp(att->name, "mimeType")) { - rep->mime = gf_strdup(att->value); - } else if (!strcmp(att->name, "group")) { - rep->groupID = atoi(att->value); - } else if (!strcmp(att->name, "startWithRAP")) { - if (!strcmp(att->value, "true")) rep->startWithRap = 1; - } else if (!strcmp(att->name, "qualityRanking")) { - rep->qualityRanking = atoi(att->value); + /*setup some default*/ + com->max_playout_rate = 1.0; + + while ( (att = gf_list_enum(root->attributes, &i)) ) { + if (!strcmp(att->name, "profiles")) com->profiles = gf_mpd_parse_string(att->value); + else if (!strcmp(att->name, "width")) com->width = gf_mpd_parse_int(att->value); + else if (!strcmp(att->name, "height")) com->height = gf_mpd_parse_int(att->value); + else if (!strcmp(att->name, "sar")) com->sar = gf_mpd_parse_frac(att->value); + else if (!strcmp(att->name, "frameRate")) com->framerate = gf_mpd_parse_frac(att->value); + else if (!strcmp(att->name, "audioSamplingRate")) com->samplerate = gf_mpd_parse_int(att->value); + else if (!strcmp(att->name, "mimeType")) com->mime_type = gf_mpd_parse_string(att->value); + else if (!strcmp(att->name, "segmentProfiles")) com->segmentProfiles = gf_mpd_parse_string(att->value); + else if (!strcmp(att->name, "codecs")) com->codecs = gf_mpd_parse_string(att->value); + else if (!strcmp(att->name, "maximumSAPPeriod")) com->maximum_sap_period = gf_mpd_parse_int(att->value); + else if (!strcmp(att->name, "startWithSAP")) { + if (!strcmp(att->value, "false")) com->starts_with_sap = 0; + else com->starts_with_sap = gf_mpd_parse_int(att->value); + } + else if (!strcmp(att->name, "maxPlayoutRate")) com->max_playout_rate = gf_mpd_parse_double(att->value); + else if (!strcmp(att->name, "codingDependency")) com->coding_dependency = gf_mpd_parse_bool(att->value); + else if (!strcmp(att->name, "scanType")) { + if (!strcmp(att->value, "progressive")) com->scan_type = GF_MPD_SCANTYPE_PROGRESSIVE; + else if (!strcmp(att->value, "interlaced")) com->scan_type = GF_MPD_SCANTYPE_INTERLACED; } - att_index++; } - child_index = 0; - while (1) { - child = gf_list_get(root->content, child_index); - if (!child) { - break; - } else if (child->type == GF_XML_NODE_TYPE) { - if (!strcmp(child->name, "ContentProtection")) { - gf_mpd_parse_rep_cp(child, rep); - } else if (!strcmp(child->name, "TrickMode")) { - gf_mpd_parse_rep_trickmode(child, rep); - } else if (!strcmp(child->name, "SegmentInfo")) { - gf_mpd_parse_rep_segmentinfo(child, rep); - } + i = 0; + while ( (child = gf_list_enum(root->content, &i))) { + if (child->type != GF_XML_NODE_TYPE) continue; + if (!strcmp(child->name, "FramePacking")) { + gf_mpd_parse_content_component(com->frame_packing, child); + } + else if (!strcmp(child->name, "AudioChannelConfiguration")) { + gf_mpd_parse_content_component(com->audio_channels, child); + } + else if (!strcmp(child->name, "ContentProtection")) { + gf_mpd_parse_content_component(com->content_protection, child); } - child_index++; } - return GF_OK; } -static GF_Err gf_mpd_parse_segment_info_default(GF_XMLNode *root, GF_MPD_Period *period) +static void gf_mpd_init_common_attributes(GF_MPD_CommonAttributes *com) { - u32 att_index; + com->audio_channels = gf_list_new(); + com->content_protection = gf_list_new(); + com->frame_packing = gf_list_new(); +} +static GF_Err gf_mpd_parse_representation(GF_List *container, GF_XMLNode *root) +{ + u32 i; + GF_MPD_Representation *rep; GF_XMLAttribute *att; + GF_XMLNode *child; + GF_Err e; - att_index = 0; - while (1) { - att = gf_list_get(root->attributes, att_index); - if (!att) { - break; - } else if (!strcmp(att->name, "duration")) { - period->default_segment_duration = gf_mpd_parse_duration(att->value); - } else if (!strcmp(att->name, "baseURL")) { - period->default_base_url = gf_strdup(att->value); - } else if (!strcmp(att->name, "sourceUrlTemplatePeriod")) { - period->url_template = gf_strdup(att->value); - } - att_index++; + GF_SAFEALLOC(rep, GF_MPD_Representation); + if (!rep) return GF_OUT_OF_MEM; + gf_mpd_init_common_attributes((GF_MPD_CommonAttributes *)rep); + rep->base_URLs = gf_list_new(); + rep->sub_representations = gf_list_new(); + e = gf_list_add(container, rep); + if (e) return e; + + i = 0; + while ( (att = gf_list_enum(root->attributes, &i)) ) { + if (!strcmp(att->name, "id")) rep->id = gf_mpd_parse_string(att->value); + else if (!strcmp(att->name, "bandwidth")) rep->bandwidth = gf_mpd_parse_int(att->value); + else if (!strcmp(att->name, "qualityRanking")) rep->quality_ranking = gf_mpd_parse_int(att->value); + else if (!strcmp(att->name, "dependencyId")) rep->dependency_id = gf_mpd_parse_string(att->value); + else if (!strcmp(att->name, "mediaStreamStructureId")) rep->media_stream_structure_id = gf_mpd_parse_string(att->value); } + gf_mpd_parse_common_representation((GF_MPD_CommonAttributes*)rep, root); + i = 0; + while ( (child = gf_list_enum(root->content, &i))) { + if (child->type != GF_XML_NODE_TYPE) continue; + if (!strcmp(child->name, "BaseURL")) { + e = gf_mpd_parse_base_url(rep->base_URLs, child); + if (e) return e; + } + else if (!strcmp(child->name, "SegmentBase")) { + rep->segment_base = gf_mpd_parse_segment_base(child); + } + else if (!strcmp(child->name, "SegmentList")) { + rep->segment_list = gf_mpd_parse_segment_list(child); + } + else if (!strcmp(child->name, "SegmentTemplate")) { + rep->segment_template = gf_mpd_parse_segment_template(child); + } + else if (!strcmp(child->name, "SubRepresentation")) { +/*TODO + e = gf_mpd_parse_subrepresentation(rep->sub_representations, child); + if (e) return e; +*/ + } + } return GF_OK; } -static GF_Err gf_mpd_parse_period(GF_XMLNode *root, GF_MPD_Period *period, const char *default_base_url) +static GF_Err gf_mpd_parse_adaptation_set(GF_List *container, GF_XMLNode *root) { - u32 att_index, child_index; + u32 i; + GF_MPD_AdaptationSet *set; GF_XMLAttribute *att; GF_XMLNode *child; + GF_Err e; - att_index = 0; - while (1) { - att = gf_list_get(root->attributes, att_index); - if (!att) { - break; - } else if (!strcmp(att->name, "start")) { - } else if (!strcmp(att->name, "segmentAlignmentFlag")) { - if (!strcmp(att->value, "true")) { - period->segment_alignment_flag = 1; - } - } else if (!strcmp(att->name, "bitstreamSwitchingFlag")) { - if (!strcmp(att->value, "true")) { - period->segment_alignment_flag = 1; - } + GF_SAFEALLOC(set, GF_MPD_AdaptationSet); + if (!set) return GF_OUT_OF_MEM; + gf_mpd_init_common_attributes((GF_MPD_CommonAttributes *)set); + set->accessibility = gf_list_new(); + set->role = gf_list_new(); + set->rating = gf_list_new(); + set->viewpoint = gf_list_new(); + set->content_component = gf_list_new(); + set->base_URLs = gf_list_new(); + set->representations = gf_list_new(); + /*assign default ID and group*/ + set->group = -1; + + e = gf_list_add(container, set); + if (e) return e; + + i = 0; + while ( (att = gf_list_enum(root->attributes, &i)) ) { + if (strstr(att->name, "href") || strstr(att->name, "actuate")) { + } + else if (!strcmp(att->name, "id")) set->id = gf_mpd_parse_int(att->value); + else if (!strcmp(att->name, "group")) set->group = gf_mpd_parse_int(att->value); + else if (!strcmp(att->name, "lang")) set->lang = gf_mpd_parse_string(att->value); + else if (!strcmp(att->name, "contentType")) set->content_type = gf_mpd_parse_string(att->value); + else if (!strcmp(att->name, "par")) set->par = gf_mpd_parse_frac(att->value); + else if (!strcmp(att->name, "minBandwidth")) set->min_bandwidth = gf_mpd_parse_int(att->value); + else if (!strcmp(att->name, "maxBandwidth")) set->max_bandwidth = gf_mpd_parse_int(att->value); + else if (!strcmp(att->name, "minWidth")) set->min_width = gf_mpd_parse_int(att->value); + else if (!strcmp(att->name, "maxWidth")) set->max_width = gf_mpd_parse_int(att->value); + else if (!strcmp(att->name, "minHeight")) set->min_height = gf_mpd_parse_int(att->value); + else if (!strcmp(att->name, "maxHeight")) set->max_height = gf_mpd_parse_int(att->value); + else if (!strcmp(att->name, "minFrameRate")) set->min_framerate = gf_mpd_parse_int(att->value); + else if (!strcmp(att->name, "maxFrameRate")) set->max_framerate = gf_mpd_parse_int(att->value); + else if (!strcmp(att->name, "segmentAlignment")) set->segment_alignment = gf_mpd_parse_bool(att->value); + else if (!strcmp(att->name, "bitstreamSwitching")) set->bitstream_switching = gf_mpd_parse_bool(att->value); + else if (!strcmp(att->name, "subsegmentAlignment")) set->subsegment_alignment = gf_mpd_parse_bool(att->value); + else if (!strcmp(att->name, "subsegmentStartsWithSAP")) { + if (!strcmp(att->value, "false")) set->subsegment_starts_with_sap = 0; + else set->subsegment_starts_with_sap = gf_mpd_parse_int(att->value); } - att_index++; } + gf_mpd_parse_common_representation((GF_MPD_CommonAttributes*)set, root); - child_index = 0; - while (1) { - child = gf_list_get(root->content, child_index); - if (!child) { - break; - } else if (child->type == GF_XML_NODE_TYPE) { - if (!strcmp(child->name, "SegmentInfoDefault")) { - gf_mpd_parse_segment_info_default(child, period); - } else if (!strcmp(child->name, "Representation")) { - GF_MPD_Representation *rep; - GF_SAFEALLOC(rep, GF_MPD_Representation); - if (!rep) - return GF_OUT_OF_MEM; - if (default_base_url) { - rep->default_base_url = gf_strdup(default_base_url); - } - gf_mpd_parse_representation(child, rep); - gf_list_add(period->representations, rep); - } + i = 0; + while ( (child = gf_list_enum(root->content, &i))) { + if (child->type != GF_XML_NODE_TYPE) continue; + if (!strcmp(child->name, "Accessibility")) { + e = gf_mpd_parse_descriptor(set->accessibility, child); + if (e) return e; + } + else if (!strcmp(child->name, "Role")) { + e = gf_mpd_parse_descriptor(set->role, child); + if (e) return e; + } + else if (!strcmp(child->name, "Rating")) { + e = gf_mpd_parse_descriptor(set->rating, child); + if (e) return e; + } + else if (!strcmp(child->name, "Viewpoint")) { + e = gf_mpd_parse_descriptor(set->viewpoint, child); + if (e) return e; + } + else if (!strcmp(child->name, "BaseURL")) { + e = gf_mpd_parse_base_url(set->base_URLs, child); + if (e) return e; + } + else if (!strcmp(child->name, "ContentComponent")) { + e = gf_mpd_parse_content_component(set->content_component, child); + if (e) return e; + } + else if (!strcmp(child->name, "SegmentBase")) { + set->segment_base = gf_mpd_parse_segment_base(child); + } + else if (!strcmp(child->name, "SegmentList")) { + set->segment_list = gf_mpd_parse_segment_list(child); + } + else if (!strcmp(child->name, "SegmentTemplate")) { + set->segment_template = gf_mpd_parse_segment_template(child); + } + else if (!strcmp(child->name, "Representation")) { + e = gf_mpd_parse_representation(set->representations, child); + if (e) return e; } - child_index++; } - return GF_OK; } -static GF_Err gf_mpd_parse_program_info(GF_XMLNode *root, GF_MPD *mpd) +static GF_Err gf_mpd_parse_period(GF_MPD *mpd, GF_XMLNode *root) { - u32 att_index, child_index; + u32 i; + GF_MPD_Period *period; GF_XMLAttribute *att; GF_XMLNode *child; + GF_Err e; - att_index = 0; - while (1) { - att = gf_list_get(root->attributes, att_index); - if (!att) { - break; - } else if (!strcmp(att->name, "moreInformationURL")) { - mpd->more_info_url = gf_strdup(att->value); - } - att_index++; + GF_SAFEALLOC(period, GF_MPD_Period); + if (!period) return GF_OUT_OF_MEM; + period->adaptation_sets = gf_list_new(); + period->base_URLs = gf_list_new(); + period->subsets = gf_list_new(); + e = gf_list_add(mpd->periods, period); + if (e) return e; + + i = 0; + while ( (att = gf_list_enum(root->attributes, &i)) ) { + if (strstr(att->name, "href") || strstr(att->name, "actuate")) { + } + else if (!strcmp(att->name, "id")) period->ID = gf_mpd_parse_string(att->value); + else if (!strcmp(att->name, "start")) period->start = gf_mpd_parse_duration(att->value); + else if (!strcmp(att->name, "duration")) period->duration = gf_mpd_parse_duration(att->value); + else if (!strcmp(att->name, "bitstreamSwitching")) period->bitstream_switching = gf_mpd_parse_bool(att->value); } - child_index = 0; - while (1) { - child = gf_list_get(root->content, child_index); - if (!child) { - break; - } else if (child->type == GF_XML_NODE_TYPE) { - if (!strcmp(child->name, "Title")) { - GF_XMLNode *data_node = gf_list_get(child->content, 0); - if (data_node && data_node->type == GF_XML_TEXT_TYPE) { - mpd->title = gf_strdup(data_node->name); - } - } else if (!strcmp(child->name, "Source")) { - GF_XMLNode *data_node = gf_list_get(child->content, 0); - if (data_node && data_node->type == GF_XML_TEXT_TYPE) { - mpd->source = gf_strdup(data_node->name); - } - } else if (!strcmp(child->name, "Copyright")) { - GF_XMLNode *data_node = gf_list_get(child->content, 0); - if (data_node && data_node->type == GF_XML_TEXT_TYPE) { - mpd->copyright = gf_strdup(data_node->name); - } - } + i = 0; + while ( (child = gf_list_enum(root->content, &i))) { + if (child->type != GF_XML_NODE_TYPE) continue; + if (!strcmp(child->name, "BaseURL")) { + e = gf_mpd_parse_base_url(period->base_URLs, child); + if (e) return e; + } + else if (!strcmp(child->name, "SegmentBase")) { + period->segment_base = gf_mpd_parse_segment_base(child); + } + else if (!strcmp(child->name, "SegmentList")) { + period->segment_list = gf_mpd_parse_segment_list(child); + } + else if (!strcmp(child->name, "SegmentTemplate")) { + period->segment_template = gf_mpd_parse_segment_template(child); + } + else if (!strcmp(child->name, "AdaptationSet")) { + e = gf_mpd_parse_adaptation_set(period->adaptation_sets, child); + if (e) return e; + } + else if (!strcmp(child->name, "SubSet")) { } - child_index++; } return GF_OK; } + GF_MPD *gf_mpd_new() { GF_MPD *mpd; @@ -450,65 +661,186 @@ GF_MPD *gf_mpd_new() return mpd; } +static void gf_mpd_del_list(GF_List *list, void (*__destructor)(void *), Bool reset_only) +{ + if (!list) return; + while (gf_list_count(list)) { + void *item = gf_list_last(list); + gf_list_rem_last(list); + if (item && __destructor) __destructor(item); + } + if (!reset_only) gf_list_del(list); +} + +void gf_mpd_base_url_free(void *_item) +{ + GF_MPD_BaseURL *base_url = (GF_MPD_BaseURL *)_item; + if (base_url->service_location) gf_free(base_url->service_location); + if (base_url->URL) gf_free(base_url->URL); + gf_free(base_url); +} + +void gf_mpd_url_free(void *_item) +{ + GF_MPD_URL *ptr = (GF_MPD_URL*)_item; + if (ptr->sourceURL) gf_free(ptr->sourceURL); + gf_free(ptr); +} +void gf_mpd_string_free(void *_item) +{ + gf_free( (char *) _item ); +} + +void gf_mpd_prog_info_free(void *_item) +{ + GF_MPD_ProgramInfo *ptr = (GF_MPD_ProgramInfo *)_item; + if (ptr->lang) gf_free(ptr->lang); + if (ptr->title) gf_free(ptr->title); + if (ptr->source) gf_free(ptr->source); + if (ptr->copyright) gf_free(ptr->copyright); + if (ptr->more_info_url) gf_free(ptr->more_info_url); + gf_free(ptr); +} +void gf_mpd_segment_url_free(void *_item) +{ + GF_MPD_SegmentURL *ptr = (GF_MPD_SegmentURL*)_item; + if (ptr->index) gf_free(ptr->index); + if (ptr->index_range) gf_free(ptr->index_range); + if (ptr->media) gf_free(ptr->media); + if (ptr->media_range) gf_free(ptr->media_range); + gf_free(ptr); +} +void gf_mpd_segment_base_free(void *_item) +{ + GF_MPD_SegmentBase *ptr = (GF_MPD_SegmentBase *)_item; + if (ptr->initialization_segment) gf_mpd_url_free(ptr->initialization_segment); + if (ptr->representation_index) gf_mpd_url_free(ptr->representation_index); + gf_free(ptr); +} + +void gf_mpd_segment_entry_free(void *_item) +{ + gf_free(_item); +} +void gf_mpd_segment_timeline_free(void *_item) +{ + GF_MPD_SegmentTimeline *ptr = (GF_MPD_SegmentTimeline *)_item; + gf_mpd_del_list(ptr->entries, gf_mpd_segment_entry_free, 0); + gf_free(ptr); +} + +void gf_mpd_segment_list_free(void *_item) +{ + GF_MPD_SegmentList *ptr = (GF_MPD_SegmentList *)_item; + if (ptr->initialization_segment) gf_mpd_url_free(ptr->initialization_segment); + if (ptr->bitstream_switching_url) gf_mpd_url_free(ptr->bitstream_switching_url); + if (ptr->representation_index) gf_mpd_url_free(ptr->representation_index); + if (ptr->segment_timeline) gf_mpd_segment_timeline_free(ptr->segment_timeline); + gf_mpd_del_list(ptr->segment_URLs, gf_mpd_segment_url_free, 0); + gf_free(ptr); +} +void gf_mpd_segment_template_free(void *_item) +{ + GF_MPD_SegmentTemplate *ptr = (GF_MPD_SegmentTemplate *)_item; + if (ptr->initialization_segment) gf_mpd_url_free(ptr->initialization_segment); + if (ptr->bitstream_switching_url) gf_mpd_url_free(ptr->bitstream_switching_url); + if (ptr->representation_index) gf_mpd_url_free(ptr->representation_index); + if (ptr->segment_timeline) gf_mpd_segment_timeline_free(ptr->segment_timeline); + if (ptr->index) gf_free(ptr->index); + if (ptr->media) gf_free(ptr->media); + if (ptr->initialization) gf_free(ptr->initialization); + if (ptr->bitstream_switching) gf_free(ptr->bitstream_switching); + gf_free(ptr); +} +void gf_mpd_descriptor_free(void *item) +{ + fprintf(stdout, "error: descriptor not implemented\n"); + gf_free(item); +} + +void gf_mpd_content_component_free(void *item) +{ + fprintf(stdout, "error: content component not implemented\n"); + gf_free(item); +} + +void gf_mpd_common_attributes_free(GF_MPD_CommonAttributes *ptr) +{ + if (ptr->profiles) gf_free(ptr->profiles); + if (ptr->sar) gf_free(ptr->sar); + if (ptr->framerate) gf_free(ptr->framerate); + if (ptr->mime_type) gf_free(ptr->mime_type); + if (ptr->segmentProfiles) gf_free(ptr->segmentProfiles); + if (ptr->codecs) gf_free(ptr->codecs); + gf_mpd_del_list(ptr->frame_packing, gf_mpd_descriptor_free, 0); + gf_mpd_del_list(ptr->audio_channels, gf_mpd_descriptor_free, 0); + gf_mpd_del_list(ptr->content_protection, gf_mpd_descriptor_free, 0); +} + +void gf_mpd_representation_free(void *_item) +{ + GF_MPD_Representation *ptr = (GF_MPD_Representation *)_item; + gf_mpd_common_attributes_free((GF_MPD_CommonAttributes *)ptr); + if (ptr->id) gf_free(ptr->id); + if (ptr->dependency_id) gf_free(ptr->dependency_id); + if (ptr->media_stream_structure_id) gf_free(ptr->media_stream_structure_id); + + gf_mpd_del_list(ptr->base_URLs, gf_mpd_base_url_free, 0); + gf_mpd_del_list(ptr->sub_representations, NULL/*TODO*/, 0); + if (ptr->segment_base) gf_mpd_segment_base_free(ptr->segment_base); + if (ptr->segment_list) gf_mpd_segment_list_free(ptr->segment_list); + if (ptr->segment_template) gf_mpd_segment_template_free(ptr->segment_template); + gf_free(ptr); +} + +void gf_mpd_adaptation_set_free(void *_item) +{ + GF_MPD_AdaptationSet *ptr = (GF_MPD_AdaptationSet *)_item; + gf_mpd_common_attributes_free((GF_MPD_CommonAttributes *)ptr); + if (ptr->lang) gf_free(ptr->lang); + if (ptr->content_type) gf_free(ptr->content_type); + if (ptr->par) gf_free(ptr->par); + gf_mpd_del_list(ptr->accessibility, gf_mpd_descriptor_free, 0); + gf_mpd_del_list(ptr->role, gf_mpd_descriptor_free, 0); + gf_mpd_del_list(ptr->rating, gf_mpd_descriptor_free, 0); + gf_mpd_del_list(ptr->viewpoint, gf_mpd_descriptor_free, 0); + gf_mpd_del_list(ptr->content_component, gf_mpd_content_component_free, 0); + if (ptr->segment_base) gf_mpd_segment_base_free(ptr->segment_base); + if (ptr->segment_list) gf_mpd_segment_list_free(ptr->segment_list); + if (ptr->segment_template) gf_mpd_segment_template_free(ptr->segment_template); + gf_mpd_del_list(ptr->base_URLs, gf_mpd_base_url_free, 0); + gf_mpd_del_list(ptr->representations, gf_mpd_representation_free, 0); + gf_free(ptr); +} +void gf_mpd_period_free(void *_item) +{ + GF_MPD_Period *ptr = (GF_MPD_Period *)_item; + if (ptr->ID) gf_free(ptr->ID); + if (ptr->segment_base) gf_mpd_segment_base_free(ptr->segment_base); + if (ptr->segment_list) gf_mpd_segment_list_free(ptr->segment_list); + if (ptr->segment_template) gf_mpd_segment_template_free(ptr->segment_template); + + gf_mpd_del_list(ptr->base_URLs, gf_mpd_base_url_free, 0); + gf_mpd_del_list(ptr->adaptation_sets, gf_mpd_adaptation_set_free, 0); + gf_mpd_del_list(ptr->subsets, NULL/*TODO*/, 0); + gf_free(ptr); +} + void gf_mpd_del(GF_MPD *mpd) { - while (gf_list_count(mpd->periods)) { - GF_MPD_Period *period = gf_list_get(mpd->periods, 0); - gf_list_rem(mpd->periods, 0); - while (gf_list_count(period->representations)) { - GF_MPD_Representation *rep = gf_list_get(period->representations, 0); - gf_list_rem(period->representations, 0); - - while (gf_list_count(rep->segments)) { - GF_MPD_SegmentInfo *seg = gf_list_get(rep->segments, 0); - gf_list_rem(rep->segments, 0); - if (seg->url) gf_free(seg->url); - gf_free(seg); - } - if (rep->content_protection_type) gf_free(rep->content_protection_type); - rep->content_protection_type = NULL; - if (rep->content_protection_uri) gf_free(rep->content_protection_uri); - rep->content_protection_uri = NULL; - if (rep->default_base_url) gf_free(rep->default_base_url); - rep->default_base_url = NULL; - if (rep->id) gf_free(rep->id); - rep->id = NULL; - if (rep->init_url) gf_free(rep->init_url); - rep->init_url = NULL; - if (rep->lang) gf_free(rep->lang); - rep->lang = NULL; - if (rep->mime) gf_free(rep->mime); - rep->mime = NULL; - if (rep->url_template) gf_free(rep->url_template); - rep->url_template = NULL; - if (rep->segments) gf_list_del(rep->segments); - rep->segments = NULL; - gf_free(rep); - } - gf_list_del(period->representations); - period->representations = NULL; - if (period->default_base_url) gf_free(period->default_base_url); - if (period->url_template) gf_free(period->url_template); - - gf_free(period); - } - gf_list_del(mpd->periods); - mpd->periods = NULL; - if (mpd->base_url) gf_free(mpd->base_url); - mpd->base_url = NULL; - if (mpd->title) gf_free(mpd->title); - mpd->title = NULL; - if (mpd->source) gf_free(mpd->source); - mpd->source = NULL; - if (mpd->copyright) gf_free(mpd->copyright); - mpd->copyright = NULL; - if (mpd->more_info_url) gf_free(mpd->more_info_url); - mpd->more_info_url = NULL; + gf_mpd_del_list(mpd->program_infos, gf_mpd_prog_info_free, 0); + gf_mpd_del_list(mpd->base_URLs, gf_mpd_base_url_free, 0); + gf_mpd_del_list(mpd->locations, gf_mpd_string_free, 0); + gf_mpd_del_list(mpd->metrics, NULL/*TODO*/, 0); + gf_mpd_del_list(mpd->periods, gf_mpd_period_free, 0); + if (mpd->profiles) gf_free(mpd->profiles); + if (mpd->ID) gf_free(mpd->ID); gf_free(mpd); } GF_Err gf_mpd_init_from_dom(GF_XMLNode *root, GF_MPD *mpd, const char *default_base_url) { + GF_Err e; u32 att_index, child_index; GF_XMLAttribute *att; GF_XMLNode *child; @@ -517,6 +849,12 @@ GF_Err gf_mpd_init_from_dom(GF_XMLNode *root, GF_MPD *mpd, const char *default_b assert( !mpd->periods ); mpd->periods = gf_list_new(); + mpd->program_infos = gf_list_new(); + mpd->base_URLs = gf_list_new(); + mpd->locations = gf_list_new(); + mpd->metrics = gf_list_new(); + /*setup some defaults*/ + mpd->type = GF_MPD_TYPE_STATIC; att_index = 0; child_index = gf_list_count(root->attributes); @@ -524,20 +862,33 @@ GF_Err gf_mpd_init_from_dom(GF_XMLNode *root, GF_MPD *mpd, const char *default_b att = gf_list_get(root->attributes, att_index); if (!att) { continue; + } + + if (!strcmp(att->name, "id")) { + mpd->ID = gf_mpd_parse_string(att->value); + } else if (!strcmp(att->name, "profiles")) { + mpd->profiles = gf_mpd_parse_string(att->value); } else if (!strcmp(att->name, "type")) { - if (!strcmp(att->value, "OnDemand")) mpd->type = GF_MPD_TYPE_ON_DEMAND; - else if (!strcmp(att->value, "Live")) mpd->type = GF_MPD_TYPE_LIVE; + if (!strcmp(att->value, "static")) mpd->type = GF_MPD_TYPE_STATIC; + else if (!strcmp(att->value, "dynamic")) mpd->type = GF_MPD_TYPE_DYNAMIC; } else if (!strcmp(att->name, "availabilityStartTime")) { + mpd->availabilityStartTime = gf_mpd_parse_date(att->value); } else if (!strcmp(att->name, "availabilityEndTime")) { + mpd->availabilityEndTime = gf_mpd_parse_date(att->value); } else if (!strcmp(att->name, "mediaPresentationDuration")) { - mpd->duration = gf_mpd_parse_duration(att->value); - } else if (!strcmp(att->name, "minimumUpdatePeriodMPD")) { - mpd->min_update_time = gf_mpd_parse_duration(att->value); + mpd->media_presentation_duration = gf_mpd_parse_duration(att->value); + } else if (!strcmp(att->name, "minimumUpdatePeriod")) { + mpd->minimum_update_period = gf_mpd_parse_duration(att->value); } else if (!strcmp(att->name, "minBufferTime")) { mpd->min_buffer_time = gf_mpd_parse_duration(att->value); } else if (!strcmp(att->name, "timeShiftBufferDepth")) { mpd->time_shift_buffer_depth = gf_mpd_parse_duration(att->value); - } else if (!strcmp(att->name, "baseURL")) { + } else if (!strcmp(att->name, "suggestedPresentationDelay")) { + mpd->suggested_presentaton_delay = gf_mpd_parse_duration(att->value); + } else if (!strcmp(att->name, "maxSegmentDuration")) { + mpd->max_segment_duration = gf_mpd_parse_duration(att->value); + } else if (!strcmp(att->name, "maxSubsegmentDuration")) { + mpd->max_subsegment_duration = gf_mpd_parse_duration(att->value); } } @@ -548,15 +899,20 @@ GF_Err gf_mpd_init_from_dom(GF_XMLNode *root, GF_MPD *mpd, const char *default_b break; } else if (child->type == GF_XML_NODE_TYPE) { if (!strcmp(child->name, "ProgramInformation")) { - gf_mpd_parse_program_info(child, mpd); + e = gf_mpd_parse_program_info(mpd, child); + if (e) return e; + } else if (!strcmp(child->name, "Location")) { + e = gf_mpd_parse_location(mpd, child); + if (e) return e; } else if (!strcmp(child->name, "Period")) { - GF_MPD_Period *period; - GF_SAFEALLOC(period, GF_MPD_Period); - if (!period) - return GF_OUT_OF_MEM; - period->representations = gf_list_new(); - gf_mpd_parse_period(child, period, default_base_url); - gf_list_add(mpd->periods, period); + e = gf_mpd_parse_period(mpd, child); + if (e) return e; + } else if (!strcmp(child->name, "Metrics")) { + e = gf_mpd_parse_metrics(mpd, child); + if (e) return e; + } else if (!strcmp(child->name, "BaseURL")) { + e = gf_mpd_parse_base_url(mpd->base_URLs, child); + if (e) return e; } } child_index++; @@ -564,18 +920,21 @@ GF_Err gf_mpd_init_from_dom(GF_XMLNode *root, GF_MPD *mpd, const char *default_b return GF_OK; } -GF_Err gf_m3u8_to_mpd(GF_ClientService *service, const char *m3u8_file, const char *base_url, +GF_Err gf_m3u8_to_mpd(const char *m3u8_file, const char *base_url, const char *mpd_file, - u32 reload_count, char *mimeTypeForM3U8Segments) + u32 reload_count, char *mimeTypeForM3U8Segments, GF_ClientService *service, Bool do_import, Bool use_mpd_templates) { GF_Err e; - u32 i, count; + char *sep, *template_base, *template_ext; + u32 i, count, j, count2, k, count3, template_width, template_idx_start; Double update_interval; VariantPlaylist * pl = NULL; + Bool use_template; Program *prog; - PlaylistElement *pe, *the_pe; + PlaylistElement *pe, *the_pe, *elt; FILE *fmpd; Bool is_end; + u32 max_dur = 0; e = parse_root_playlist(m3u8_file, &pl, base_url); if (e) { @@ -589,12 +948,13 @@ GF_Err gf_m3u8_to_mpd(GF_ClientService *service, const char *m3u8_file, const ch mpd_file = m3u8_file; } the_pe = NULL; - is_end = !pl->playlistNeedsRefresh; + pe = NULL; i=0; assert( pl ); assert( pl->programs ); while ((prog = gf_list_enum(pl->programs, &i))) { u32 j=0; + while (NULL != (pe = gf_list_enum(prog->bitrates, &j))) { Bool found = 0; u32 k; @@ -614,10 +974,13 @@ GF_Err gf_m3u8_to_mpd(GF_ClientService *service, const char *m3u8_file, const ch if (found) continue; the_pe = pe; - suburl = gf_url_concatenate(base_url, pe->url); - if (!strcmp(base_url, suburl)) { - gf_free(suburl); - GF_LOG(GF_LOG_DEBUG, GF_LOG_MODULE, ("[MPD Generator] Not downloading, programs are identical for %s...\n", pe->url)); + suburl = NULL; + if (strcmp(base_url, pe->url)) + suburl = gf_url_concatenate(base_url, pe->url); + + if (!suburl || !strcmp(base_url, suburl)) { + if (suburl) gf_free(suburl); + GF_LOG(GF_LOG_DEBUG, GF_LOG_MODULE, ("[MPD Generator] Not downloading, programs are identical for %s...\n", pe->url)); continue; } if (service) { @@ -633,16 +996,22 @@ GF_Err gf_m3u8_to_mpd(GF_ClientService *service, const char *m3u8_file, const ch gf_term_download_del(sess); gf_free(suburl); } else { /* for use in MP4Box */ - extern GF_Err gf_dm_wget(const char *url, const char *filename); - e = gf_dm_wget(suburl, "tmp.m3u8"); - if (e==GF_OK) { - e = parse_sub_playlist("tmp.m3u8", &pl, suburl, prog, pe); + if (strstr(suburl, "://") && !strstr(suburl, "://") ) { + e = gf_dm_wget(suburl, "tmp.m3u8"); + if (e==GF_OK) { + e = parse_sub_playlist("tmp.m3u8", &pl, suburl, prog, pe); + } + gf_delete_file("tmp.m3u8"); + } else { + e = parse_sub_playlist(suburl, &pl, suburl, prog, pe); } - gf_delete_file("tmp.m3u8"); } } + if (max_dur < (u32) prog->computed_duration) + max_dur = (u32) prog->computed_duration; } + is_end = !pl->playlistNeedsRefresh; assert(the_pe); update_interval = 0; @@ -675,8 +1044,16 @@ GF_Err gf_m3u8_to_mpd(GF_ClientService *service, const char *m3u8_file, const ch variant_playlist_del(pl); return GF_IO_ERR; } - fprintf(fmpd, "\n"); fprintf(fmpd, " \n"); @@ -686,67 +1063,334 @@ GF_Err gf_m3u8_to_mpd(GF_ClientService *service, const char *m3u8_file, const ch title = the_pe->url; fprintf(fmpd, " %s\n", title ); } - fprintf(fmpd, " Generated from URL %s\n", base_url ); - fprintf(fmpd, " Generated by GPAC %s from %s\n", GPAC_FULL_VERSION, base_url); + fprintf(fmpd, " Generated from URL %s\n", base_url ); + fprintf(fmpd, " Generated by GPAC %s from %s\n", GPAC_FULL_VERSION, base_url); fprintf(fmpd, " \n"); - fprintf(fmpd, " \n"); + fprintf(fmpd, " \n"); + count = gf_list_count(pl->programs); + /*check if we use templates*/ + template_base = NULL; + template_ext = NULL; + use_template = use_mpd_templates; + template_width = 0; + template_idx_start = 0; for (i=0; iprograms, i); + prog = gf_list_get(pl->programs, i); count2 = gf_list_count(prog->bitrates); for (j = 0; jbitrates, j); - if (pe->elementType == TYPE_PLAYLIST) { - u32 k, count3; - char *base_url = gf_strdup(pe->url); - char *sep = strrchr(base_url, '/'); - if (sep) *(sep+1) = 0; - if (pe->codecs && (pe->codecs[0] = '\"')) { - u32 len = strlen(pe->codecs); - strncpy(pe->codecs, pe->codecs+1, len-1); - pe->codecs[len-2] = 0; - } - /* SOUCHAY : if mime-type is still unknown, do not try to add codec information since it would be wrong */ - if (!strcmp(M3U8_UNKOWN_MIME_TYPE, mimeTypeForM3U8Segments)){ - fprintf(fmpd, " \n", mimeTypeForM3U8Segments, pe->bandwidth); + pe = gf_list_get(prog->bitrates, j); + if (pe->elementType != TYPE_PLAYLIST) + continue; + + count3 = gf_list_count(pe->element.playlist.elements); + if (!count3) continue; + + if (!template_base && use_template) { + char *sub_url; + elt = gf_list_get(pe->element.playlist.elements, 0); + sub_url = strrchr(elt->url, '/'); + if (!sub_url) { + sub_url = elt->url; } else { - fprintf(fmpd, " \n", mimeTypeForM3U8Segments, (pe->codecs ? ";codecs=":""), (pe->codecs ? pe->codecs:""), pe->bandwidth); + sub_url ++; + } + template_base = gf_strdup(sub_url); + template_ext = strrchr(template_base, '.'); + k=0; + while (1) { + if (strchr("0123456789", template_base[k])) { + if (template_ext) { + template_ext[0] = 0; + template_width = strlen(template_base + k); + template_idx_start = atoi(template_base + k); + template_ext[0] = '.'; + } + template_base[k] = 0; + break; + } + k++; + if (!template_base[k]) { + use_template = 0; + break; + } } - fprintf(fmpd, "\n \n", pe->durationInfo, base_url); - count3 = gf_list_count(pe->element.playlist.elements); - update_interval = (count3 - 1) * pe->durationInfo * 1000; + } + if (!template_ext) template_ext=""; + + if (use_template) { for (k=0; kelement.playlist.elements, k); + char szURL[GF_MAX_PATH], *sub_url; + elt = gf_list_get(pe->element.playlist.elements, k); + + if (template_width==2) sprintf(szURL, "%s%02d%s", template_base, template_idx_start + k, template_ext); + else if (template_width==3) sprintf(szURL, "%s%03d%s", template_base, template_idx_start + k, template_ext); + else if (template_width==4) sprintf(szURL, "%s%04d%s", template_base, template_idx_start + k, template_ext); + else if (template_width==5) sprintf(szURL, "%s%05d%s", template_base, template_idx_start + k, template_ext); + else if (template_width==6) sprintf(szURL, "%s%06d%s", template_base, template_idx_start + k, template_ext); + else sprintf(szURL, "%s%d%s", template_base, template_idx_start + k, template_ext); - /*remove protocol scheme and try to find the common part in baseURL and segment URL - this avoids copying the entire url*/ - src_url = strstr(base_url, "://"); - if (src_url) src_url += 3; - else src_url = base_url; + sub_url = strrchr(elt->url, '/'); + if (!sub_url) sub_url = elt->url; + else sub_url ++; + if (strcmp(szURL, sub_url)) { + use_template = 0; + break; + } + } + } - seg_url = strstr(elt->url, "://"); - if (seg_url) seg_url += 3; - else seg_url = elt->url; + } + } + + fprintf(fmpd, " \n"); + + /*if we use templates, put the SegmentTemplate element at the adaptationSet level*/ + if (use_template) { + fprintf(fmpd, " durationInfo); + if (template_width>1) { + fprintf(fmpd, " media=\"%s$%%0%ddNumber$%s\"", template_base, template_width, template_ext); + } else { + fprintf(fmpd, " media=\"%s$Number$%s\"", template_base, template_ext); + } + fprintf(fmpd, " startNumber=\"%d\"", template_idx_start); + fprintf(fmpd, "/>\n"); + } - while (src_url[cmp] == seg_url[cmp]) cmp++; - fprintf(fmpd, " \n", cmp ? (seg_url + cmp) : elt->url); + if (do_import) { +#ifndef GPAC_DISABLE_MEDIA_IMPORT + GF_Err e; + GF_MediaImporter import; + char *tmp_file = NULL; + elt = gf_list_get(pe->element.playlist.elements, 0); + memset(&import, 0, sizeof(GF_MediaImporter)); + import.trackID = 0; + import.flags = GF_IMPORT_PROBE_ONLY; + + if (strstr(elt->url, "://") && !strstr(elt->url, "file://")) { + tmp_file = strrchr(elt->url, '/'); + if (!tmp_file) tmp_file = strrchr(elt->url, '\\'); + if (tmp_file) { + e = gf_dm_wget(elt->url, tmp_file); + if (e==GF_OK) { + import.in_name = tmp_file; + e = gf_media_import(&import); } - fprintf(fmpd, " \n"); - fprintf(fmpd, " \n"); - gf_free(base_url); - } else if (pe->elementType == TYPE_STREAM) { + } + } else { + import.in_name = elt->url; + e = gf_media_import(&import); + } + + if (import.nb_tracks>1) { + for (k=0; k\n"); + } + } + if (tmp_file) + gf_delete_file(tmp_file); +#endif + } + + /*check if we use templates*/ + count = gf_list_count(pl->programs); + for (i=0; iprograms, i); + count2 = gf_list_count(prog->bitrates); + for (j = 0; jbitrates, j); + + if (pe->elementType == TYPE_STREAM) { fprintf(stdout, "NOT SUPPORTED: M3U8 Stream\n"); + } else if (pe->elementType != TYPE_PLAYLIST) { + fprintf(stdout, "NOT SUPPORTED: M3U8 unknown type\n"); + } + + count3 = gf_list_count(pe->element.playlist.elements); + if (!count3) continue; + + base_url = gf_strdup(pe->url); + sep = strrchr(base_url, '/'); + + if (pe->codecs && (pe->codecs[0] = '\"')) { + u32 len = strlen(pe->codecs); + strncpy(pe->codecs, pe->codecs+1, len-1); + pe->codecs[len-2] = 0; + } + +#ifndef GPAC_DISABLE_MEDIA_IMPORT + width = height = samplerate = num_channels = 0; + if (do_import) { + GF_Err e; + GF_MediaImporter import; + char *tmp_file = NULL; + elt = gf_list_get(pe->element.playlist.elements, 0); + memset(&import, 0, sizeof(GF_MediaImporter)); + import.trackID = 0; + import.flags = GF_IMPORT_PROBE_ONLY; + + if (strstr(elt->url, "://") && !strstr(elt->url, "file://")) { + tmp_file = strrchr(elt->url, '/'); + if (!tmp_file) tmp_file = strrchr(elt->url, '\\'); + if (tmp_file) { + e = gf_dm_wget(elt->url, tmp_file); + if (e==GF_OK) { + import.in_name = tmp_file; + } + } + } else { + import.in_name = elt->url; + } + e = gf_media_import(&import); + + if (!pe->bandwidth && pe->durationInfo) { + u64 pos = 0; + Double bw; + FILE *t = gf_f64_open(import.in_name, "rb"); + if (t) { + gf_f64_seek(t, 0, SEEK_END); + pos = gf_f64_tell(t); + fclose(t); + } + bw = (Double) pos; + bw *= 8; + bw /= pe->durationInfo; + pe->bandwidth = (u32) bw; + } + + if (tmp_file) + gf_delete_file(tmp_file); + + if (!pe->codecs) { + char szCodecs[1024]; + szCodecs[0] = 0; + for (k=0; kcodecs = gf_strdup(szCodecs); + } + for (k=0; kbandwidth); + /* SOUCHAY : if mime-type is still unknown, do not try to add codec information since it would be wrong */ + if (!strcmp(M3U8_UNKOWN_MIME_TYPE, mimeTypeForM3U8Segments)){ + fprintf(fmpd, " mimeType=\"%s\"", mimeTypeForM3U8Segments); + } else { + fprintf(fmpd, " mimeType=\"%s\"", mimeTypeForM3U8Segments); } + if (pe->codecs) + fprintf(fmpd, " codecs=\"%s\"", pe->codecs); + if (width && height) { + fprintf(fmpd, " width=\"%d\" height=\"%d\"", width, height); + } + if (samplerate) + fprintf(fmpd, " audioSamplingRate=\"%d\"", samplerate); + + + if (use_template) { + if (sep) { + /*keep final '/' */ + sep[1] = 0; + fprintf(fmpd, ">\n %s\n \n", base_url); + } else + fprintf(fmpd, "/>\n"); + + continue; + } + + fprintf(fmpd, ">\n"); + + byte_range_media_file = NULL; + elt = gf_list_get(pe->element.playlist.elements, 0); + if (elt && (elt->byteRangeEnd || elt->byteRangeStart)) { + byte_range_media_file = elt->url; + fprintf(fmpd, " %s\n", byte_range_media_file); + } else if (sep) { + sep[1] = 0; + fprintf(fmpd, " %s\n", base_url); + } + + fprintf(fmpd, " \n", pe->durationInfo); + update_interval = (count3 - 1) * pe->durationInfo * 1000; + for (k=0; kelement.playlist.elements, k); + + /*remove protocol scheme and try to find the common part in baseURL and segment URL - this avoids copying the entire url*/ + src_url = strstr(base_url, "://"); + if (src_url) src_url += 3; + else src_url = base_url; + + seg_url = strstr(elt->url, "://"); + if (seg_url) seg_url += 3; + else seg_url = elt->url; + + while (src_url[cmp] == seg_url[cmp]) cmp++; + + if (byte_range_media_file) { + fprintf(fmpd, " byteRangeStart, elt->byteRangeEnd); + if (strcmp(elt->url, byte_range_media_file) )fprintf(fmpd, " media=\"%s\"", elt->url); + fprintf(fmpd, "/>\n"); + } else { + fprintf(fmpd, " \n", cmp ? (seg_url + cmp) : elt->url); + } + } + fprintf(fmpd, " \n"); + fprintf(fmpd, " \n"); + gf_free(base_url); } } + + if (template_base) { + gf_free(template_base); + template_base = NULL; + } + + fprintf(fmpd, " \n"); fprintf(fmpd, " \n"); fprintf(fmpd, ""); fclose(fmpd); variant_playlist_del(pl); return GF_OK; } - diff --git a/src/media_tools/mpegts.c b/src/media_tools/mpegts.c index eb052cd..22b243c 100644 --- a/src/media_tools/mpegts.c +++ b/src/media_tools/mpegts.c @@ -24,8 +24,6 @@ #include -#include - #ifndef GPAC_DISABLE_MPEG2TS @@ -33,9 +31,12 @@ #include #include #include -#include #include +#ifdef GPAC_CONFIG_LINUX +#include +#endif + #define DUMP_MPE_IP_DATAGRAMS //#define FORCE_DISABLE_MPEG4SL_OVER_MPEG2TS @@ -69,20 +70,12 @@ const char *gf_m2ts_get_stream_name(u32 streamType) } } -static u32 gf_m2ts_reframe_default(GF_M2TS_Demuxer *ts, GF_M2TS_PES *pes, u64 DTS, u64 PTS, unsigned char *data, u32 data_len) +static u32 gf_m2ts_reframe_default(GF_M2TS_Demuxer *ts, GF_M2TS_PES *pes, Bool same_pts, unsigned char *data, u32 data_len) { GF_M2TS_PES_PCK pck; pck.flags = 0; if (pes->rap) pck.flags |= GF_M2TS_PES_PCK_RAP; - - if (PTS) { - pes->PTS = PTS; - /*backup DTS for start detection*/ - PTS = pes->DTS; - if (DTS) pes->DTS = DTS; - else pes->DTS = PTS; - if (!PTS || (PTS != pes->DTS)) pck.flags = GF_M2TS_PES_PCK_AU_START; - } + if (!same_pts) pck.flags |= GF_M2TS_PES_PCK_AU_START; pck.DTS = pes->DTS; pck.PTS = pes->PTS; pck.data = data; @@ -93,8 +86,28 @@ static u32 gf_m2ts_reframe_default(GF_M2TS_Demuxer *ts, GF_M2TS_PES *pes, u64 DT return 0; } -static u32 gf_m2ts_reframe_avc_h264(GF_M2TS_Demuxer *ts, GF_M2TS_PES *pes, u64 DTS, u64 PTS, unsigned char *data, u32 data_len) +static u32 gf_m2ts_reframe_reset(GF_M2TS_Demuxer *ts, GF_M2TS_PES *pes, Bool same_pts, unsigned char *data, u32 data_len) { + if (pes->data) { + gf_free(pes->data); + pes->data = NULL; + } + pes->data_len = 0; + if (pes->prev_data) { + gf_free(pes->prev_data); + pes->prev_data = NULL; + } + pes->prev_data_len = 0; + pes->pes_len = 0; + pes->prev_PTS = 0; + pes->reframe = NULL; + pes->cc = -1; + return 0; +} + +static u32 gf_m2ts_reframe_avc_h264(GF_M2TS_Demuxer *ts, GF_M2TS_PES *pes, Bool same_pts, unsigned char *data, u32 data_len) +{ + Bool au_start_in_pes=0; Bool force_new_au=0; Bool start_code_found = 0; Bool short_start_code = 0; @@ -103,12 +116,8 @@ static u32 gf_m2ts_reframe_avc_h264(GF_M2TS_Demuxer *ts, GF_M2TS_PES *pes, u64 D GF_M2TS_PES_PCK pck; - if (PTS) { - if (pes->PTS != PTS) force_new_au = 1; - pes->PTS = PTS; - if (DTS) pes->DTS = DTS; - else pes->DTS = PTS; - } + if (!same_pts) + force_new_au = 1; /*dispatch frame*/ pck.stream = pes; @@ -191,8 +200,15 @@ static u32 gf_m2ts_reframe_avc_h264(GF_M2TS_Demuxer *ts, GF_M2TS_PES *pes, u64 D #endif /*check AU start type*/ if (nal_type==GF_AVC_NALU_ACCESS_UNIT) { + if (au_start_in_pes) { + /*FIXME - we should check the AVC framerate to update the timing ...*/ + pck.DTS += 3000; + pck.PTS += 3000; +// GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[MPEG-2 TS] PID%d: Two AVC AUs start in this PES packet - cannot recompute non-first AU timing\n", pes->pid)); + } pck.flags = GF_M2TS_PES_PCK_AU_START; force_new_au = 0; + au_start_in_pes = 1; } else if (nal_type==GF_AVC_NALU_IDR_SLICE) { pck.flags = GF_M2TS_PES_PCK_RAP; } @@ -237,7 +253,7 @@ static u32 gf_m2ts_reframe_avc_h264(GF_M2TS_Demuxer *ts, GF_M2TS_PES *pes, u64 D } if (force_new_au) { pck.flags |= GF_M2TS_PES_PCK_AU_START; - force_new_au = 0; + //force_new_au = 0; } ts->on_event(ts, GF_M2TS_EVT_PES_PCK, &pck); } @@ -245,17 +261,12 @@ static u32 gf_m2ts_reframe_avc_h264(GF_M2TS_Demuxer *ts, GF_M2TS_PES *pes, u64 D return 0; } -static u32 gf_m2ts_reframe_mpeg_video(GF_M2TS_Demuxer *ts, GF_M2TS_PES *pes, u64 DTS, u64 PTS, unsigned char *data, u32 data_len) +static u32 gf_m2ts_reframe_mpeg_video(GF_M2TS_Demuxer *ts, GF_M2TS_PES *pes, Bool same_pts, unsigned char *data, u32 data_len) { u32 sc_pos = 0; u32 to_send = data_len; GF_M2TS_PES_PCK pck; - if (PTS) { - pes->PTS = PTS; - if (DTS) pes->DTS = DTS; - else pes->DTS = PTS; - } /*dispatch frame*/ pck.stream = pes; pck.DTS = pes->DTS; @@ -350,27 +361,37 @@ typedef struct u32 profile, sr_idx, nb_ch, frame_size; } ADTSHeader; -static u32 gf_m2ts_reframe_aac_adts(GF_M2TS_Demuxer *ts, GF_M2TS_PES *pes, u64 DTS, u64 PTS, unsigned char *data, u32 data_len) +static u32 gf_m2ts_reframe_aac_adts(GF_M2TS_Demuxer *ts, GF_M2TS_PES *pes, Bool same_pts, unsigned char *data, u32 data_len) { ADTSHeader hdr; u32 sc_pos = 0; u32 start = 0; + u32 hdr_size = 0; + u64 PTS; Bool first = 1; - u32 remain; GF_M2TS_PES_PCK pck; - if (PTS) { - pes->PTS = PTS; - if (DTS) pes->DTS = DTS; - else pes->DTS = PTS; - } /*dispatch frame*/ + PTS = pes->PTS; pck.stream = pes; pck.DTS = pes->DTS; - pck.PTS = pes->PTS; + pck.PTS = PTS; pck.flags = 0; - remain = pes->frame_state; + + if (pes->frame_state && (data[pes->frame_state]==0xFF) && ((data[pes->frame_state+1] & 0xF0) == 0xF0)) { + assert(pes->frame_state<=data_len); + /*dispatch frame*/ + pck.stream = pes; + pck.DTS = PTS; + pck.PTS = PTS; + pck.flags = 0; + pck.data = data; + pck.data_len = pes->frame_state; + ts->on_event(ts, GF_M2TS_EVT_PES_PCK, &pck); + first = 0; + start = sc_pos = pes->frame_state; + } pes->frame_state = 0; /*fixme - we need to test this with more ADTS sources were PES framing is on any boundaries*/ @@ -387,13 +408,25 @@ static u32 gf_m2ts_reframe_aac_adts(GF_M2TS_Demuxer *ts, GF_M2TS_PES *pes, u64 D if (start < sc_pos) { /*dispatch frame*/ pck.stream = pes; - pck.DTS = pes->PTS; - pck.PTS = pes->PTS; + pck.DTS = PTS; + pck.PTS = PTS; pck.flags = 0; pck.data = data+start; pck.data_len = sc_pos-start; ts->on_event(ts, GF_M2TS_EVT_PES_PCK, &pck); - remain = 0; + if (pes->frame_state == pck.data_len) { + /*consider we are sync*/ + first = 0; + } else { + first = 1; + } + pes->frame_state = 0; + } + /*not enough data to parse the frame header*/ + if (sc_pos + 7 >= data_len) { + pes->frame_state = 0; + pes->prev_PTS = PTS; + return data_len-sc_pos; } bs = gf_bs_new(data + sc_pos + 1, 9, GF_BITSTREAM_READ); @@ -417,7 +450,7 @@ static u32 gf_m2ts_reframe_aac_adts(GF_M2TS_Demuxer *ts, GF_M2TS_PES *pes, u64 D /*make sure we are sync if we have more data following*/ if (sc_pos + hdr.frame_size < data_len) { - if ((data[sc_pos + hdr.frame_size]!=0xFF) || ((data[sc_pos+hdr.frame_size+1] & 0xF0) != 0xF0)) { + if ((hdr.frame_size < hdr_size) || (data[sc_pos + hdr.frame_size]!=0xFF) || ((data[sc_pos+hdr.frame_size+1] & 0xF0) != 0xF0)) { sc_pos++; continue; } @@ -439,19 +472,25 @@ static u32 gf_m2ts_reframe_aac_adts(GF_M2TS_Demuxer *ts, GF_M2TS_PES *pes, u64 D gf_free(pck.data); pes->aud_sr = cfg.base_sr; pes->aud_nb_ch = cfg.nb_chan; + pes->aud_obj_type = hdr.profile; } /*dispatch frame*/ pck.stream = pes; - pck.DTS = pes->PTS; - pck.PTS = pes->PTS; + if (first && pes->prev_PTS) { + pck.DTS = pck.PTS = pes->prev_PTS; + } else { + pck.DTS = pck.PTS = PTS; + } pck.flags = GF_M2TS_PES_PCK_AU_START | GF_M2TS_PES_PCK_RAP; pck.data = data + sc_pos + hdr_size; pck.data_len = hdr.frame_size - hdr_size; if (pck.data_len > data_len - sc_pos - hdr_size) { + assert(pck.data_len - (data_len - sc_pos - hdr_size) > 0); /*remember how much we have to send*/ pes->frame_state = pck.data_len - (data_len - sc_pos - hdr_size); + assert((s32) pes->frame_state > 0); pck.data_len = data_len - sc_pos - hdr_size; } @@ -459,10 +498,13 @@ static u32 gf_m2ts_reframe_aac_adts(GF_M2TS_Demuxer *ts, GF_M2TS_PES *pes, u64 D sc_pos += pck.data_len + hdr_size; start = sc_pos; - /*update PTS in case we don't get any update*/ - if (pes->aud_sr) { + if (first && pes->prev_PTS) { + pes->prev_PTS = 0; + } + /*update PTS in case we don't get any update*/ + else if (pes->aud_sr) { size = 1024*90000/pes->aud_sr; - pes->PTS += size; + PTS += size; } first = 0; } @@ -470,21 +512,16 @@ static u32 gf_m2ts_reframe_aac_adts(GF_M2TS_Demuxer *ts, GF_M2TS_PES *pes, u64 D return 0; } -static u32 gf_m2ts_reframe_aac_latm(GF_M2TS_Demuxer *ts, GF_M2TS_PES *pes, u64 DTS, u64 PTS, unsigned char *data, u32 data_len) +static u32 gf_m2ts_reframe_aac_latm(GF_M2TS_Demuxer *ts, GF_M2TS_PES *pes, Bool same_pts, unsigned char *data, u32 data_len) { u32 sc_pos = 0; u32 start = 0; GF_M2TS_PES_PCK pck; - if (PTS) { - pes->PTS = PTS; - if (DTS) pes->DTS = DTS; - else pes->DTS = PTS; - } /*dispatch frame*/ pck.stream = pes; pck.DTS = pes->DTS; - pck.PTS = pes->PTS; + pck.PTS = pes->PTS; pck.flags = 0; /*fixme - we need to test this with more LATM sources were PES framing is on any boundaries*/ @@ -519,18 +556,18 @@ static u32 gf_m2ts_reframe_aac_latm(GF_M2TS_Demuxer *ts, GF_M2TS_PES *pes, u64 D amux_versionA = 0; if (amux_version) amux_versionA = gf_bs_read_int(bs, 1); if (!amux_versionA) { - u32 i, allStreamsSameTimeFraming, numSubFrames, numProgram; + u32 i, allStreamsSameTimeFraming, numProgram; if (amux_version) latm_get_value(bs); allStreamsSameTimeFraming = gf_bs_read_int(bs, 1); - numSubFrames = gf_bs_read_int(bs, 6); + /*numSubFrames = */gf_bs_read_int(bs, 6); numProgram = gf_bs_read_int(bs, 4); for (i=0; i<=numProgram; i++) { u32 j, num_lay; num_lay = gf_bs_read_int(bs, 3); for (j=0;j<=num_lay; j++) { GF_M4ADecSpecInfo cfg; - u32 frameLengthType, latmBufferFullness; + u32 frameLengthType; Bool same_cfg = 0; if (i || j) same_cfg = gf_bs_read_int(bs, 1); @@ -542,6 +579,8 @@ static u32 gf_m2ts_reframe_aac_latm(GF_M2TS_Demuxer *ts, GF_M2TS_PES *pes, u64 D pck.stream = pes; pes->aud_sr = cfg.base_sr; pes->aud_nb_ch = cfg.nb_chan; + pes->aud_obj_type = cfg.base_object_type; + gf_m4a_write_config(&cfg, &pck.data, &pck.data_len); ts->on_event(ts, GF_M2TS_EVT_AAC_CFG, &pck); gf_free(pck.data); @@ -549,7 +588,7 @@ static u32 gf_m2ts_reframe_aac_latm(GF_M2TS_Demuxer *ts, GF_M2TS_PES *pes, u64 D } frameLengthType = gf_bs_read_int(bs, 3); if (!frameLengthType) { - latmBufferFullness = gf_bs_read_int(bs, 8); + /*latmBufferFullness = */gf_bs_read_int(bs, 8); if (!allStreamsSameTimeFraming) { } } else { @@ -612,18 +651,20 @@ static u32 gf_m2ts_reframe_aac_latm(GF_M2TS_Demuxer *ts, GF_M2TS_PES *pes, u64 D #ifndef GPAC_DISABLE_AV_PARSERS -static u32 gf_m2ts_reframe_mpeg_audio(GF_M2TS_Demuxer *ts, GF_M2TS_PES *pes, u64 DTS, u64 PTS, unsigned char *data, u32 data_len) +static u32 gf_m2ts_reframe_mpeg_audio(GF_M2TS_Demuxer *ts, GF_M2TS_PES *pes, Bool same_pts, unsigned char *data, u32 data_len) { GF_M2TS_PES_PCK pck; u32 pos, frame_size, remain; + u64 PTS; pck.flags = GF_M2TS_PES_PCK_RAP; pck.stream = pes; remain = pes->frame_state; + PTS = pes->PTS; if (remain) { /*dispatch end of prev frame*/ - pck.DTS = pck.PTS = pes->PTS; + pck.DTS = pck.PTS = PTS; pck.data = data; pck.data_len = (remain>data_len) ? data_len : remain; ts->on_event(ts, GF_M2TS_EVT_PES_PCK, &pck); @@ -644,23 +685,21 @@ static u32 gf_m2ts_reframe_mpeg_audio(GF_M2TS_Demuxer *ts, GF_M2TS_PES *pes, u64 } assert((pes->frame_state & 0xffe00000) == 0xffe00000); - if (!pes->PTS) { + if (!pes->aud_sr || !pes->aud_nb_ch) { pes->aud_sr = gf_mp3_sampling_rate(pes->frame_state); pes->aud_nb_ch = gf_mp3_num_channels(pes->frame_state); } - /*we may get a PTS for either the previous or the current frame*/ - if (PTS>=pes->PTS) pes->PTS = PTS; pck.flags = GF_M2TS_PES_PCK_RAP | GF_M2TS_PES_PCK_AU_START; frame_size = gf_mp3_frame_size(pes->frame_state); while (frame_size <= data_len) { /*dispatch frame*/ - pck.DTS = pck.PTS = pes->PTS; + pck.DTS = pck.PTS = PTS; pck.data = data; pck.data_len = frame_size; ts->on_event(ts, GF_M2TS_EVT_PES_PCK, &pck); - pes->PTS += gf_mp3_window_size(pes->frame_state)*90000/gf_mp3_sampling_rate(pes->frame_state); + PTS += gf_mp3_window_size(pes->frame_state)*90000/gf_mp3_sampling_rate(pes->frame_state); /*move frame*/ data += frame_size; data_len -= frame_size; @@ -679,12 +718,12 @@ static u32 gf_m2ts_reframe_mpeg_audio(GF_M2TS_Demuxer *ts, GF_M2TS_PES *pes, u64 frame_size = gf_mp3_frame_size(pes->frame_state); } if (data_len) { - pck.DTS = pck.PTS = pes->PTS; + pck.DTS = pck.PTS = PTS; pck.data = data; pck.data_len = data_len; ts->on_event(ts, GF_M2TS_EVT_PES_PCK, &pck); /*update PTS in case we don't get any update*/ - pes->PTS += gf_mp3_window_size(pes->frame_state)*90000/gf_mp3_sampling_rate(pes->frame_state); + PTS += gf_mp3_window_size(pes->frame_state)*90000/gf_mp3_sampling_rate(pes->frame_state); pes->frame_state = frame_size - data_len; } else { pes->frame_state = 0; @@ -804,14 +843,27 @@ static void gf_m2ts_section_complete(GF_M2TS_Demuxer *ts, GF_M2TS_SectionFilter pck.data_len = sec->length; pck.data = sec->section; pck.stream = (GF_M2TS_ES *)ses; - ts->on_event(ts, GF_M2TS_EVT_AIT_FOUND, &pck); - } else if (ts->on_mpe_event && ((ses && (ses->flags & GF_M2TS_EVT_DVB_MPE)) || (sec->section[0]==GF_M2TS_TABLE_ID_INT)) ) { + //ts->on_event(ts, GF_M2TS_EVT_AIT_FOUND, &pck); + on_ait_section(ts, GF_M2TS_EVT_AIT_FOUND, &pck); + } else if ((ts->on_event && (sec->section[0]==GF_M2TS_TABLE_ID_DSM_CC_ENCAPSULATED_DATA || sec->section[0]==GF_M2TS_TABLE_ID_DSM_CC_UN_MESSAGE || + sec->section[0]==GF_M2TS_TABLE_ID_DSM_CC_DOWNLOAD_DATA_MESSAGE || sec->section[0]==GF_M2TS_TABLE_ID_DSM_CC_STREAM_DESCRIPTION || sec->section[0]==GF_M2TS_TABLE_ID_DSM_CC_PRIVATE)) ) { + GF_M2TS_SL_PCK pck; + pck.data_len = sec->length; + pck.data = sec->section; + pck.stream = (GF_M2TS_ES *)ses; + on_dsmcc_section(ts,GF_M2TS_EVT_DSMCC_FOUND,&pck); + //ts->on_event(ts, GF_M2TS_EVT_DSMCC_FOUND, &pck); + } +#ifdef DUMP_MPE_IP_DATAGRAMS + else if (ts->on_mpe_event && ((ses && (ses->flags & GF_M2TS_EVT_DVB_MPE)) || (sec->section[0]==GF_M2TS_TABLE_ID_INT)) ) { GF_M2TS_SL_PCK pck; pck.data_len = sec->length; pck.data = sec->section; pck.stream = (GF_M2TS_ES *)ses; ts->on_mpe_event(ts, GF_M2TS_EVT_DVB_MPE, &pck); - } else if (ts->on_event) { + } +#endif + else if (ts->on_event) { GF_M2TS_SL_PCK pck; pck.data_len = sec->length; pck.data = sec->section; @@ -988,6 +1040,8 @@ static Bool gf_m2ts_is_long_section(u8 table_id) case GF_M2TS_TABLE_ID_SIT: case GF_M2TS_TABLE_ID_DSM_CC_PRIVATE: case GF_M2TS_TABLE_ID_MPE_FEC: + case GF_M2TS_TABLE_ID_DSM_CC_DOWNLOAD_DATA_MESSAGE: + case GF_M2TS_TABLE_ID_DSM_CC_UN_MESSAGE: return 1; default: if (table_id >= GF_M2TS_TABLE_ID_EIT_SCHEDULE_MIN && table_id <= GF_M2TS_TABLE_ID_EIT_SCHEDULE_MAX) @@ -1103,7 +1157,7 @@ aggregated_section: static void gf_m2ts_process_sdt(GF_M2TS_Demuxer *ts, GF_M2TS_SECTION_ES *ses, GF_List *sections, u8 table_id, u16 ex_table_id, u8 version_number, u8 last_section_number, u32 status) { - u32 orig_net_id, pos, evt_type; + u32 pos, evt_type; u32 nb_sections; u32 data_size; unsigned char *data; @@ -1134,7 +1188,7 @@ static void gf_m2ts_process_sdt(GF_M2TS_Demuxer *ts, GF_M2TS_SECTION_ES *ses, GF data = section->data; data_size = section->data_size; - orig_net_id = (data[0] << 8) | data[1]; + //orig_net_id = (data[0] << 8) | data[1]; pos = 3; while (pos < data_size) { GF_M2TS_SDT *sdt; @@ -1377,17 +1431,19 @@ static void gf_m2ts_process_pmt(GF_M2TS_Demuxer *ts, GF_M2TS_SECTION_ES *pmt, GF while (info_length > first_loop_len) { #ifndef FORCE_DISABLE_MPEG4SL_OVER_MPEG2TS if (tag == GF_M2TS_MPEG4_IOD_DESCRIPTOR) { - u8 scope, label; u32 size; GF_BitStream *iod_bs; - scope = data[6]; - label = data[7]; iod_bs = gf_bs_new(data+8, len-2, GF_BITSTREAM_READ); if (pmt->program->pmt_iod) gf_odf_desc_del((GF_Descriptor *)pmt->program->pmt_iod); gf_odf_parse_descriptor(iod_bs , (GF_Descriptor **) &pmt->program->pmt_iod, &size); /*remember program number for service/program selection*/ if (pmt->program->pmt_iod) pmt->program->pmt_iod->ServiceID = pmt->program->number; gf_bs_del(iod_bs ); + /*if empty IOD (freebox case), discard it and use dynamic declaration of object*/ + if (!gf_list_count(pmt->program->pmt_iod->ESDescriptors)) { + gf_odf_desc_del((GF_Descriptor *)pmt->program->pmt_iod); + pmt->program->pmt_iod = NULL; + } } else { #else { @@ -1405,8 +1461,7 @@ static void gf_m2ts_process_pmt(GF_M2TS_Demuxer *ts, GF_M2TS_SECTION_ES *pmt, GF /* count de number of program related PMT received */ for(i=0;iprograms);i++){ GF_M2TS_Program *prog = (GF_M2TS_Program *)gf_list_get(ts->programs,i); - if(prog->pmt_pid == pmt->pid){ - ts->nb_prog_pmt_received++; + if(prog->pmt_pid == pmt->pid){ break; } } @@ -1424,7 +1479,7 @@ static void gf_m2ts_process_pmt(GF_M2TS_Demuxer *ts, GF_M2TS_SECTION_ES *pmt, GF reg_desc_format = 0; switch (stream_type) { - printf("stream_type :%d \n",stream_type); + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("stream_type :%d \n",stream_type)); /* PES */ case GF_M2TS_VIDEO_MPEG1: case GF_M2TS_VIDEO_MPEG2: @@ -1466,24 +1521,34 @@ static void gf_m2ts_process_pmt(GF_M2TS_Demuxer *ts, GF_M2TS_SECTION_ES *pmt, GF break; - case GF_M2TS_PRIVATE_SECTION: - GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("AIT section found on pid %d\n", pid)); + /*to refine with generic private section redispatching to AIT or other afterwards*/ es = gf_ait_section_new(pmt->program->number); ses = (GF_M2TS_SECTION_ES *)es; ses->sec = gf_m2ts_section_filter_new(NULL, 0); break; + case GF_M2TS_13818_6_ANNEX_A: + case GF_M2TS_13818_6_ANNEX_B: + case GF_M2TS_13818_6_ANNEX_C: case GF_M2TS_13818_6_ANNEX_D: + case GF_M2TS_PRIVATE_SECTION: GF_SAFEALLOC(ses, GF_M2TS_SECTION_ES); es = (GF_M2TS_ES *)ses; - es->flags |= GF_M2TS_ES_IS_SECTION; - printf("stream type DSM CC user private section: pid = %d \n", pid); + es->flags |= GF_M2TS_ES_IS_SECTION; + es->pid = pid; + es->service_id = pmt->program->number; + if(stream_type == GF_M2TS_PRIVATE_SECTION){ + GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("AIT section found on pid %d\n", pid)); + }else{ + GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("stream type DSM CC user private section: pid = %d \n", pid)); + } /* NULL means: trigger the call to on_event with DVB_GENERAL type and the raw section as payload */ ses->sec = gf_m2ts_section_filter_new(NULL, 1); + //ses->sec->service_id = pmt->program->number; break; case GF_M2TS_MPE_SECTIONS: - printf("stream type MPE found : pid = %d \n", pid); + GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("stream type MPE found : pid = %d \n", pid)); #ifdef DUMP_MPE_IP_DATAGRAMS es = gf_dvb_mpe_section_new(); if (es->flags & GF_M2TS_ES_IS_SECTION) { @@ -1561,7 +1626,7 @@ static void gf_m2ts_process_pmt(GF_M2TS_Demuxer *ts, GF_M2TS_SECTION_ES *pmt, GF case GF_M2TS_DVB_STREAM_IDENTIFIER_DESCRIPTOR: { es->component_tag = data[2]; - printf("Component Tag: %d on Program %d\n", es->component_tag, es->program->number); + GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("Component Tag: %d on Program %d\n", es->component_tag, es->program->number)); } break; case GF_M2TS_DVB_TELETEXT_DESCRIPTOR: @@ -1620,11 +1685,9 @@ static void gf_m2ts_process_pmt(GF_M2TS_Demuxer *ts, GF_M2TS_SECTION_ES *pmt, GF nb_es++; } } + - if (nb_es) { - if(ts->nb_prog_pmt_received == gf_list_count(ts->programs)){ - ts->all_prog_pmt_received = 1; - } + if (nb_es) { evt_type = (status&GF_M2TS_TABLE_FOUND) ? GF_M2TS_EVT_PMT_FOUND : GF_M2TS_EVT_PMT_UPDATE; if (ts->on_event) ts->on_event(ts, evt_type, pmt->program); } else { @@ -1770,7 +1833,7 @@ static GFINLINE u64 gf_m2ts_get_pts(unsigned char *data) static void gf_m2ts_pes_header(GF_M2TS_PES *pes, unsigned char *data, u32 data_size, GF_M2TS_PESHeader *pesh) { - u32 has_pts, has_dts, te; + u32 has_pts, has_dts; u32 len_check; memset(pesh, 0, sizeof(GF_M2TS_PESHeader)); @@ -1788,7 +1851,6 @@ static void gf_m2ts_pes_header(GF_M2TS_PES *pes, unsigned char *data, u32 data_s copyright = gf_bs_read_int(bs,1); original = gf_bs_read_int(bs,1); */ - te = data[4]; has_pts = (data[4]&0x80); has_dts = has_pts ? (data[4]&0x40) : 0; /* @@ -1830,10 +1892,16 @@ static void gf_m2ts_flush_pes(GF_M2TS_Demuxer *ts, GF_M2TS_PES *pes, GF_M2TS_Hea u32 stream_id = pes->data[3] | 0x100; if ((stream_id >= 0x1c0 && stream_id <= 0x1df) || (stream_id >= 0x1e0 && stream_id <= 0x1ef) || - (stream_id == 0x1bd)) { + (stream_id == 0x1bd) || + /*SL-packetized*/ + ((u8) pes->data[3]==0xfa) + ) { + Bool same_pts = 0; /*OK read header*/ gf_m2ts_pes_header(pes, pes->data+3, pes->data_len-3, &pesh); + + /*send PES timing*/ { GF_M2TS_PES_PCK pck; memset(&pck, 0, sizeof(GF_M2TS_PES_PCK)); @@ -1844,20 +1912,69 @@ static void gf_m2ts_flush_pes(GF_M2TS_Demuxer *ts, GF_M2TS_PES *pes, GF_M2TS_Hea pes->pes_end_packet_number = ts->pck_number; if (ts->on_event) ts->on_event(ts, GF_M2TS_EVT_PES_TIMING, &pck); } - GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[MPEG-2 TS] PID %d Got PES header PTS %d\n", pes->pid, pesh.PTS)); + + if (pesh.PTS) { + + if (pesh.PTS==pes->PTS) { + same_pts = 1; + GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[MPEG-2 TS] PID %d - same PTS "LLU" for two consecutive PES packets \n", pes->pid, pes->PTS) ); + } +#ifndef GPAC_DISABLE_LOG + /*FIXME - this test should only be done for non bi-directionnally coded media + else if (pesh.PTS < pes->PTS) { + GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[MPEG-2 TS] PID %d - PTS "LLU" less than previous packet PTS "LLU"\n", pes->pid, pesh.PTS, pes->PTS) ); + } + */ +#endif + + pes->PTS = pesh.PTS; + if (!pesh.DTS) pesh.DTS = pesh.PTS; + +#ifndef GPAC_DISABLE_LOG + if (pesh.DTS==pes->DTS) { + GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[MPEG-2 TS] PID %d - same DTS "LLU" for two consecutive PES packets \n", pes->pid, pes->DTS) ); + } if (pesh.DTSDTS) { + GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[MPEG-2 TS] PID %d - DTS "LLU" less than previous DTS "LLU"\n", pes->pid, pesh.DTS, pes->DTS) ); + } +#endif + pes->DTS = pesh.DTS; + } + /*no PTSs were coded, same time*/ + else if (!pesh.hdr_data_len) + same_pts = 1; + + /*3-byte start-code + 6 bytes header + hdr extensions*/ len = 9 + pesh.hdr_data_len; - if (pes->reframe) { + + if ((u8) pes->data[3]==0xfa) { + GF_M2TS_SL_PCK sl_pck; + + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[MPEG-2 TS] SL Packet in PES for %d - ES ID %d\n", pes->pid, pes->mpeg4_es_id)); + + if (pes->data_len > len) { + sl_pck.data = pes->data + len; + sl_pck.data_len = pes->data_len - len; + sl_pck.stream = (GF_M2TS_ES *)pes; + if (ts->on_event) ts->on_event(ts, GF_M2TS_EVT_SL_PCK, &sl_pck); + } else { + GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG-2 TS] Bad SL Packet size: (%d indicated < %d header)\n", pes->pid, pes->data_len, len)); + } + } else if (pes->reframe) { u32 remain; u32 offset = len; + if (pesh.pck_len && (pesh.pck_len-3-pesh.hdr_data_len != pes->data_len-len)) { + GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[MPEG-2 TS] PES payload size %d but received %d bytes\n", (u32) ( pesh.pck_len-3-pesh.hdr_data_len), pes->data_len-len)); + } + if (pes->prev_data_len) { assert(pes->prev_data_len < len); offset = len - pes->prev_data_len; memcpy(pes->data + offset, pes->prev_data, pes->prev_data_len); } - remain = pes->reframe(ts, pes, pesh.DTS, pesh.PTS, pes->data+offset, pes->data_len-offset); + remain = pes->reframe(ts, pes, same_pts, pes->data+offset, pes->data_len-offset); if (pes->prev_data) gf_free(pes->prev_data); pes->prev_data = NULL; @@ -1868,25 +1985,6 @@ static void gf_m2ts_flush_pes(GF_M2TS_Demuxer *ts, GF_M2TS_PES *pes, GF_M2TS_Hea pes->prev_data_len = remain; } } - } - /*SL-packetized stream*/ - else if ((u8) pes->data[3]==0xfa) { - GF_M2TS_SL_PCK sl_pck; - /*read header*/ - gf_m2ts_pes_header(pes, pes->data+3, pes->data_len-3, &pesh); - - /*3-byte start-code + 6 bytes header + hdr extensions*/ - len = 9 + pesh.hdr_data_len; - - GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[MPEG-2 TS] SL Packet in PES for %d - ES ID %d\n", pes->pid, pes->mpeg4_es_id)); - if (pes->data_len > len) { - sl_pck.data = pes->data + len; - sl_pck.data_len = pes->data_len - len; - sl_pck.stream = (GF_M2TS_ES *)pes; - if (ts->on_event) ts->on_event(ts, GF_M2TS_EVT_SL_PCK, &sl_pck); - } else { - GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG-2 TS] Bad SL Packet size: (%d indicated < %d header)\n", pes->pid, pes->data_len, len)); - } } else { GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[MPEG-2 TS] PES %d: unknown stream ID %08X\n", pes->pid, stream_id)); } @@ -1914,15 +2012,21 @@ static void gf_m2ts_process_pes(GF_M2TS_Demuxer *ts, GF_M2TS_PES *pes, GF_M2TS_H pes->cc = hdr->continuity_counter; if (disc) { - GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG-2 TS] PES %d: Packet discontinuity (%d expected - got %d- - trashing PES packet\n", pes->pid, expect_cc, hdr->continuity_counter)); - if (pes->data) { - gf_free(pes->data); - pes->data = NULL; + if (pes->flags & GF_M2TS_ES_IGNORE_NEXT_DISCONTINUITY) { + pes->flags &= ~GF_M2TS_ES_IGNORE_NEXT_DISCONTINUITY; + disc = 0; + } + if (disc) { + GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG-2 TS] PES %d: Packet discontinuity (%d expected - got %d- - trashing PES packet\n", pes->pid, expect_cc, hdr->continuity_counter)); + if (pes->data) { + gf_free(pes->data); + pes->data = NULL; + } + pes->data_len = 0; + pes->pes_len = 0; + pes->cc = -1; + return; } - pes->data_len = 0; - pes->pes_len = 0; - pes->cc = -1; - return; } if (!pes->reframe) return; @@ -2029,7 +2133,7 @@ static void gf_m2ts_process_packet(GF_M2TS_Demuxer *ts, unsigned char *data) hdr.continuity_counter = data[3] & 0xf; if (hdr.error) { - GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG-2 TS] TS Packet has error (PID could be %d)\n", hdr.pid)); + GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[MPEG-2 TS] TS Packet has error (PID could be %d)\n", hdr.pid)); return; } //#if DEBUG_TS_PACKET @@ -2049,15 +2153,16 @@ static void gf_m2ts_process_packet(GF_M2TS_Demuxer *ts, unsigned char *data) } paf = ⁡ memset(paf, 0, sizeof(GF_M2TS_AdaptationField)); - gf_m2ts_get_adaptation_field(ts, paf, data+5, af_size, hdr.pid); + assert(af_size>=0 && af_size<=182); + if (af_size) gf_m2ts_get_adaptation_field(ts, paf, data+5, af_size, hdr.pid); pos += 1+af_size; payload_size = 183 - af_size; break; /*adaptation only - still process in cas of PCR*/ case 2: af_size = data[4]; - if (af_size>183) { - //error + if (af_size != 183) { + GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[MPEG-2 TS] Non conformant bitstream: AF size is %d when it must be 183 for AF type 2\n", af_size)); return; } paf = ⁡ @@ -2214,6 +2319,16 @@ GF_ESD *gf_m2ts_get_esd(GF_M2TS_ES *es) return esd; } +void gf_m2ts_set_segment_switch(GF_M2TS_Demuxer *ts) +{ + u32 i; + for (i=0; iess[i]; + if (!es) continue; + es->flags |= GF_M2TS_ES_IGNORE_NEXT_DISCONTINUITY; + } +} + void gf_m2ts_reset_parsers(GF_M2TS_Demuxer *ts) { u32 i; @@ -2274,8 +2389,6 @@ void gf_m2ts_reset_parsers(GF_M2TS_Demuxer *ts) static void gf_m2ts_process_section_discard(GF_M2TS_Demuxer *ts, GF_M2TS_SECTION_ES *es, GF_List *sections, u8 table_id, u16 ex_table_id, u8 version_number, u8 last_section_number, u32 status) { - u32 res; - res = 0; } GF_Err gf_m2ts_set_pes_framing(GF_M2TS_PES *pes, u32 mode) @@ -2300,18 +2413,7 @@ GF_Err gf_m2ts_set_pes_framing(GF_M2TS_PES *pes, u32 mode) pes->reframe = gf_m2ts_reframe_default; break; case GF_M2TS_PES_FRAMING_SKIP: - if (pes->data) { - gf_free(pes->data); - pes->data = NULL; - } - pes->data_len = 0; - if (pes->prev_data) { - gf_free(pes->prev_data); - pes->prev_data = NULL; - } - pes->prev_data_len = 0; - pes->pes_len = 0; - pes->reframe = NULL; + pes->reframe = gf_m2ts_reframe_reset; break; case GF_M2TS_PES_FRAMING_SKIP_NO_RESET: pes->reframe = NULL; @@ -2353,6 +2455,7 @@ GF_Err gf_m2ts_set_pes_framing(GF_M2TS_PES *pes, u32 mode) GF_M2TS_Demuxer *gf_m2ts_demux_new() { GF_M2TS_Demuxer *ts; + GF_SAFEALLOC(ts, GF_M2TS_Demuxer); ts->programs = gf_list_new(); ts->SDTs = gf_list_new(); @@ -2372,10 +2475,35 @@ GF_M2TS_Demuxer *gf_m2ts_demux_new() ts->requested_pids = gf_list_new(); ts->demux_and_play = 0; ts->nb_prog_pmt_received = 0; + ts->ChannelAppList = gf_list_new(); return ts; } +void gf_m2ts_demux_dmscc_init(GF_M2TS_Demuxer *ts){ + + char* temp_dir; + u32 length; + GF_Err e; + + ts->dsmcc_controler = gf_list_new(); + ts->process_dmscc = 1; + + temp_dir = gf_get_default_cache_directory(); + length = strlen(temp_dir); + if(temp_dir[length-1] == GF_PATH_SEPARATOR){ + temp_dir[length-1] = 0; + } + + ts->dsmcc_root_dir = (char*)gf_calloc(strlen(temp_dir)+strlen("CarouselData")+2,sizeof(char)); + sprintf(ts->dsmcc_root_dir,"%s%cCarouselData",temp_dir,GF_PATH_SEPARATOR); + e = gf_mkdir(ts->dsmcc_root_dir); + if(e){ + GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[Process DSMCC] Error during the creation of the directory %s \n",ts->dsmcc_root_dir)); + } + +} + void gf_m2ts_demux_del(GF_M2TS_Demuxer *ts) { u32 i; @@ -2413,6 +2541,23 @@ void gf_m2ts_demux_del(GF_M2TS_Demuxer *ts) gf_dvb_mpe_shutdown(ts); #endif + if(gf_list_count(ts->dsmcc_controler)){ + GF_M2TS_DSMCC_OVERLORD* dsmcc_overlord = (GF_M2TS_DSMCC_OVERLORD*)gf_list_get(ts->dsmcc_controler,0); + gf_cleanup_dir(dsmcc_overlord->root_dir); + gf_rmdir(dsmcc_overlord->root_dir); + gf_m2ts_delete_dsmcc_overlord(dsmcc_overlord); + if(ts->dsmcc_root_dir){ + gf_free(ts->dsmcc_root_dir); + } + } + + while(gf_list_count(ts->ChannelAppList)){ + GF_M2TS_CHANNEL_APPLICATION_INFO* ChanAppInfo = (GF_M2TS_CHANNEL_APPLICATION_INFO*)gf_list_get(ts->ChannelAppList,0); + gf_m2ts_delete_channel_application_info(ChanAppInfo); + gf_list_rem(ts->ChannelAppList,0); + } + gf_list_del(ts->ChannelAppList); + gf_free(ts); } @@ -2437,8 +2582,6 @@ static u32 TSDemux_DemuxRun(void *_p) //u32 i; GF_M2TS_Demuxer *ts = _p; - ts->run_state = 1; - gf_m2ts_reset_parsers(ts); #ifdef GPAC_HAS_LINUX_DVB @@ -2483,6 +2626,11 @@ static u32 TSDemux_DemuxRun(void *_p) } } else if (ts->file) { u32 pos = 0; + + if (ts->segment_switch) { + ts->segment_switch = 0; + goto next_segment_setup; + } if (ts->start_range && ts->duration) { Double perc = ts->start_range / (1000 * ts->duration); pos = (u32) (s64) (perc * ts->file_size); @@ -2496,6 +2644,7 @@ static u32 TSDemux_DemuxRun(void *_p) gf_f64_seek(ts->file, pos, SEEK_SET); restart_file: + gf_f64_seek(ts->file, ts->start_byterange, SEEK_SET); while (ts->run_state && !feof(ts->file)) { /*m2ts chunks by chunks*/ @@ -2516,9 +2665,12 @@ restart_file: ts->nb_pck++; //fprintf(stderr, "TS packet #%d\r", ts->nb_pck); + if (ts->end_byterange && (gf_f64_tell(ts->file)>=ts->end_byterange)) + break; + //gf_sleep(0); /*if asked to regulate, wait until we get a play request*/ - while (ts->run_state && !ts->nb_playing && ts->file_regulate) { + while (ts->run_state && !ts->nb_playing && (ts->file_regulate==1)) { gf_sleep(50); continue; } @@ -2531,11 +2683,15 @@ restart_file: } if (feof(ts->file)) GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[M2TSDemux] EOS reached\n")); +next_segment_setup: if (ts->run_state && ts->query_next) { - const char *next_url = ts->query_next(ts->udta_query); + const char *next_url = NULL; + ts->query_next(ts->query_udta, 0, &next_url, &ts->start_byterange, &ts->end_byterange); if (next_url) { fclose(ts->file); ts->file = gf_f64_open(next_url, "rb"); + gf_m2ts_set_segment_switch(ts); + if (ts->file) goto restart_file; GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[M2TSDemux] Cannot open next file %s\n", next_url)); } @@ -2837,6 +2993,12 @@ static GF_Err TSDemux_SetupFile(GF_M2TS_Demuxer *ts, char *url) ts->end_range = ts->start_range = 0; ts->nb_playing = 0; + ts->start_byterange = ts->end_byterange = 0; + if (ts->query_next) { + ts->query_next(ts->query_udta, 1, NULL, &ts->start_byterange, &ts->end_byterange); + } + + return TSDemux_DemuxPlay(ts); } @@ -2858,7 +3020,7 @@ GF_Err TSDemux_Demux_Setup(GF_M2TS_Demuxer *ts, const char *url, Bool loop) if(loop == 1){ ts->loop_demux = 1; - printf("Loop Mode activated \n"); + GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("Loop Mode activated \n")); } if (!strnicmp(url, "udp://", 6) @@ -2900,6 +3062,9 @@ GF_Err TSDemux_CloseDemux(GF_M2TS_Demuxer *ts) GF_Err TSDemux_DemuxPlay(GF_M2TS_Demuxer *ts){ + /*set the state variable outside the TS thread. If inside, we may get called for shutdown before the TS thread has started + and we would overwrite the run_state when entering the TS thread, which would make the thread run forever and the stop() wait forever*/ + ts->run_state = 1; if(ts->th){ /*start playing for tune-in*/ return gf_th_run(ts->th, TSDemux_DemuxRun, ts); diff --git a/src/media_tools/text_import.c b/src/media_tools/text_import.c index 00d4ce3..3d4801f 100644 --- a/src/media_tools/text_import.c +++ b/src/media_tools/text_import.c @@ -1472,7 +1472,6 @@ static GF_Err gf_text_import_texml(GF_MediaImporter *import) } } else if (!strcmp(sub->name, "sharedStyles")) { - u32 idx = 0; GF_XMLNode *style, *ftable; u32 m=0; while ((style=(GF_XMLNode*)gf_list_enum(sub->content, &m))) { @@ -1503,7 +1502,10 @@ static GF_Err gf_text_import_texml(GF_MediaImporter *import) if (!strcmp(css_style, "font-table")) { u32 z; styles[nb_styles].fontID = atoi(css_val); - for (z=0; zname, "sampleData")) { - Bool is_utf16 = 0; GF_XMLNode *sub; u16 start, end; u32 styleID; @@ -1550,7 +1551,7 @@ static GF_Err gf_text_import_texml(GF_MediaImporter *import) k=0; while ((att=(GF_XMLAttribute *)gf_list_enum(desc->attributes, &k))) { - if (!strcmp(att->name, "targetEncoding") && !strcmp(att->value, "utf16")) is_utf16 = 1; + if (!strcmp(att->name, "targetEncoding") && !strcmp(att->value, "utf16")) ;//is_utf16 = 1; else if (!strcmp(att->name, "scrollDelay")) gf_isom_text_set_scroll_delay(samp, atoi(att->value) ); else if (!strcmp(att->name, "highlightColor")) gf_isom_text_set_highlight_color_argb(samp, tx3g_get_color(import, att->value)); } diff --git a/src/media_tools/vobsub.c b/src/media_tools/vobsub.c index b215c1b..f24cca9 100644 --- a/src/media_tools/vobsub.c +++ b/src/media_tools/vobsub.c @@ -633,7 +633,7 @@ GF_Err vobsub_packetize_subpicture(FILE *fsub, u64 pts, char *data, u32 dataSize } /* Write packet into file */ - if (fwrite(buf, sizeof(buf), 1, fsub) != 1) { + if (gf_fwrite(buf, sizeof(buf), 1, fsub) != 1) { return GF_IO_ERR; } diff --git a/src/odf/desc_private.c b/src/odf/desc_private.c index 85aef08..cb3059b 100644 --- a/src/odf/desc_private.c +++ b/src/odf/desc_private.c @@ -451,6 +451,11 @@ GF_Err gf_odf_size_descriptor(GF_Descriptor *desc, u32 *outSize) #endif /*GPAC_MINIMAL_ODF*/ default: + /*don't write out l descriptors*/ + if ((desc->tag>=GF_ODF_MUXINFO_TAG) && (desc->tag<=GF_ODF_LASER_CFG_TAG)) { + *outSize = 0; + return GF_OK; + } return gf_odf_size_default((GF_DefaultDescriptor *)desc, outSize); } return GF_OK; @@ -544,6 +549,9 @@ GF_Err gf_odf_write_descriptor(GF_BitStream *bs, GF_Descriptor *desc) return gf_odf_write_ipmp_tool(bs, (GF_IPMP_Tool *)desc); #endif /*GPAC_MINIMAL_ODF*/ default: + /*don't write out internal descriptors*/ + if ((desc->tag>=GF_ODF_MUXINFO_TAG) && (desc->tag<=GF_ODF_LASER_CFG_TAG)) + return GF_OK; return gf_odf_write_default(bs, (GF_DefaultDescriptor *)desc); } return GF_OK; diff --git a/src/odf/descriptors.c b/src/odf/descriptors.c index 1c7968a..16cbcbd 100644 --- a/src/odf/descriptors.c +++ b/src/odf/descriptors.c @@ -325,7 +325,6 @@ GF_EXPORT GF_BIFSConfig *gf_odf_get_bifs_config(GF_DefaultDescriptor *dsi, u8 oti) { Bool hasSize, cmd_stream; - GF_Err e; GF_BitStream *bs; GF_BIFSConfig *cfg; @@ -341,7 +340,6 @@ GF_BIFSConfig *gf_odf_get_bifs_config(GF_DefaultDescriptor *dsi, u8 oti) bs = gf_bs_new(dsi->data, dsi->dataLength, GF_BITSTREAM_READ); cfg = (GF_BIFSConfig *) gf_odf_desc_new(GF_ODF_BIFS_CFG_TAG); - e = GF_OK; if (oti==2) { /*3D Mesh Coding*/ gf_bs_read_int(bs, 1); @@ -363,7 +361,9 @@ GF_BIFSConfig *gf_odf_get_bifs_config(GF_DefaultDescriptor *dsi, u8 oti) if (gf_bs_read_int(bs, 1) == 0) break; } gf_bs_align(bs); - if (gf_bs_get_size(bs) != gf_bs_get_position(bs)) e = GF_NOT_SUPPORTED; + if (gf_bs_get_size(bs) != gf_bs_get_position(bs)) { + GF_LOG(GF_LOG_WARNING, GF_LOG_CODEC, ("[ODF] Reading bifs config: shift in sizes (not supported)\n")); + } } else { cfg->pixelMetrics = gf_bs_read_int(bs, 1); hasSize = gf_bs_read_int(bs, 1); @@ -372,7 +372,8 @@ GF_BIFSConfig *gf_odf_get_bifs_config(GF_DefaultDescriptor *dsi, u8 oti) cfg->pixelHeight = gf_bs_read_int(bs, 16); } gf_bs_align(bs); - if (gf_bs_get_size(bs) != gf_bs_get_position(bs)) e = GF_ODF_INVALID_DESCRIPTOR; + if (gf_bs_get_size(bs) != gf_bs_get_position(bs)) + GF_LOG(GF_LOG_WARNING, GF_LOG_CODEC, ("[ODF] Reading bifs config: shift in sizes (invalid descriptor)\n")); } gf_bs_del(bs); return cfg; diff --git a/src/odf/odf_code.c b/src/odf/odf_code.c index 9ffd547..059079b 100644 --- a/src/odf/odf_code.c +++ b/src/odf/odf_code.c @@ -472,14 +472,14 @@ GF_Err AddDescriptorToIOD(GF_InitialObjectDescriptor *iod, GF_Descriptor *desc) GF_Err gf_odf_read_iod(GF_BitStream *bs, GF_InitialObjectDescriptor *iod, u32 DescSize) { GF_Err e; - u32 reserved, urlflag, read; + u32 urlflag, read; u32 tmp_size, nbBytes = 0; if (! iod) return GF_BAD_PARAM; iod->objectDescriptorID = gf_bs_read_int(bs, 10); urlflag = gf_bs_read_int(bs, 1); iod->inlineProfileFlag = gf_bs_read_int(bs, 1); - reserved = gf_bs_read_int(bs, 4); + /*reserved = */gf_bs_read_int(bs, 4); nbBytes += 2; if (urlflag) { @@ -649,13 +649,13 @@ GF_Err AddDescriptorToOD(GF_ObjectDescriptor *od, GF_Descriptor *desc) GF_Err gf_odf_read_od(GF_BitStream *bs, GF_ObjectDescriptor *od, u32 DescSize) { GF_Err e; - u32 reserved, urlflag; + u32 urlflag; u32 tmpSize, nbBytes = 0; if (! od) return GF_BAD_PARAM; od->objectDescriptorID = gf_bs_read_int(bs, 10); urlflag = gf_bs_read_int(bs, 1); - reserved = gf_bs_read_int(bs, 5); + /*reserved = */gf_bs_read_int(bs, 5); nbBytes += 2; if (urlflag) { @@ -812,14 +812,14 @@ GF_Err AddDescriptorToIsomIOD(GF_IsomInitialObjectDescriptor *iod, GF_Descriptor GF_Err gf_odf_read_isom_iod(GF_BitStream *bs, GF_IsomInitialObjectDescriptor *iod, u32 DescSize) { u32 nbBytes = 0, tmpSize; - u32 reserved, urlflag; + u32 urlflag; GF_Err e; if (! iod) return GF_BAD_PARAM; iod->objectDescriptorID = gf_bs_read_int(bs, 10); urlflag = gf_bs_read_int(bs, 1); iod->inlineProfileFlag = gf_bs_read_int(bs, 1); - reserved = gf_bs_read_int(bs, 4); + /*reserved = */gf_bs_read_int(bs, 4); nbBytes += 2; if (urlflag) { @@ -1000,13 +1000,13 @@ GF_Err AddDescriptorToIsomOD(GF_IsomObjectDescriptor *od, GF_Descriptor *desc) GF_Err gf_odf_read_isom_od(GF_BitStream *bs, GF_IsomObjectDescriptor *od, u32 DescSize) { GF_Err e; - u32 reserved, urlflag; + u32 urlflag; u32 tmpSize, nbBytes = 0; if (! od) return GF_BAD_PARAM; od->objectDescriptorID = gf_bs_read_int(bs, 10); urlflag = gf_bs_read_int(bs, 1); - reserved = gf_bs_read_int(bs, 5); + /*reserved = */gf_bs_read_int(bs, 5); nbBytes += 2; if (urlflag) { @@ -1119,13 +1119,13 @@ GF_Err gf_odf_del_dcd(GF_DecoderConfig *dcd) GF_Err gf_odf_read_dcd(GF_BitStream *bs, GF_DecoderConfig *dcd, u32 DescSize) { GF_Err e; - u32 reserved, tmp_size, nbBytes = 0; + u32 /*reserved, */tmp_size, nbBytes = 0; if (! dcd) return GF_BAD_PARAM; dcd->objectTypeIndication = gf_bs_read_int(bs, 8); dcd->streamType = gf_bs_read_int(bs, 6); dcd->upstream = gf_bs_read_int(bs, 1); - reserved = gf_bs_read_int(bs, 1); + /*reserved = */gf_bs_read_int(bs, 1); dcd->bufferSizeDB = gf_bs_read_int(bs, 24); dcd->maxBitrate = gf_bs_read_int(bs, 32); dcd->avgBitrate = gf_bs_read_int(bs, 32); @@ -1189,7 +1189,6 @@ GF_Err gf_odf_size_dcd(GF_DecoderConfig *dcd, u32 *outSize) e = gf_odf_size_descriptor_list(dcd->profileLevelIndicationIndexDescriptor, outSize); if (e) return e; return GF_OK; - } GF_Err gf_odf_write_dcd(GF_BitStream *bs, GF_DecoderConfig *dcd) @@ -1885,7 +1884,7 @@ GF_Err gf_odf_del_cc_name(GF_CC_Name *cnd) GF_Err gf_odf_read_cc_name(GF_BitStream *bs, GF_CC_Name *cnd, u32 DescSize) { GF_Err e; - u32 i, aligned, count, len, nbBytes = 0; + u32 i, count, len, nbBytes = 0; if (!cnd) return GF_BAD_PARAM; count = gf_bs_read_int(bs, 8); @@ -1896,7 +1895,7 @@ GF_Err gf_odf_read_cc_name(GF_BitStream *bs, GF_CC_Name *cnd, u32 DescSize) memset(tmp , 0, sizeof(GF_ContentCreatorInfo)); tmp->langCode = gf_bs_read_int(bs, 24); tmp->isUTF8 = gf_bs_read_int(bs, 1); - aligned = gf_bs_read_int(bs, 7); + /*aligned = */gf_bs_read_int(bs, 7); nbBytes += 4; e = OD_ReadUTF8String(bs, & tmp->contentCreatorName, tmp->isUTF8, &len); @@ -1975,7 +1974,7 @@ GF_Err gf_odf_del_ci(GF_CIDesc *cid) GF_Err gf_odf_read_ci(GF_BitStream *bs, GF_CIDesc *cid, u32 DescSize) { - u32 reserved, nbBytes = 0; + u32 nbBytes = 0; if (! cid) return GF_BAD_PARAM; cid->compatibility = gf_bs_read_int(bs, 2); //MUST BE NULL @@ -1984,7 +1983,7 @@ GF_Err gf_odf_read_ci(GF_BitStream *bs, GF_CIDesc *cid, u32 DescSize) cid->contentTypeFlag = gf_bs_read_int(bs, 1); cid->contentIdentifierFlag = gf_bs_read_int(bs, 1); cid->protectedContent = gf_bs_read_int(bs, 1); - reserved = gf_bs_read_int(bs, 3); + /*reserved = */gf_bs_read_int(bs, 3); nbBytes += 1; if (cid->contentTypeFlag) { @@ -2099,12 +2098,12 @@ GF_Err gf_odf_read_exp_text(GF_BitStream *bs, GF_ExpandedTextual *etd, u32 DescS { GF_Err e; u32 nbBytes = 0; - u32 i, aligned, len, nonLen, count; + u32 i, len, nonLen, count; if (!etd) return GF_BAD_PARAM; etd->langCode = gf_bs_read_int(bs, 24); etd->isUTF8 = gf_bs_read_int(bs, 1); - aligned = gf_bs_read_int(bs, 7); + /*aligned = */gf_bs_read_int(bs, 7); count = gf_bs_read_int(bs, 8); nbBytes += 5; @@ -2588,12 +2587,12 @@ GF_Err gf_odf_del_kw(GF_KeyWord *kwd) GF_Err gf_odf_read_kw(GF_BitStream *bs, GF_KeyWord *kwd, u32 DescSize) { GF_Err e; - u32 nbBytes = 0, aligned, i, kwcount, len; + u32 nbBytes = 0, i, kwcount, len; if (!kwd) return GF_BAD_PARAM; kwd->languageCode = gf_bs_read_int(bs, 24); kwd->isUTF8 = gf_bs_read_int(bs, 1); - aligned = gf_bs_read_int(bs, 7); + /*aligned = */gf_bs_read_int(bs, 7); kwcount = gf_bs_read_int(bs, 8); nbBytes += 5; @@ -2632,7 +2631,9 @@ GF_Err gf_odf_write_kw(GF_BitStream *bs, GF_KeyWord *kwd) if (!kwd) return GF_BAD_PARAM; e = gf_odf_size_descriptor((GF_Descriptor *)kwd, &size); + assert(e == GF_OK); e = gf_odf_write_base_descriptor(bs, kwd->tag, size); + assert(e == GF_OK); gf_bs_write_int(bs, kwd->languageCode, 24); gf_bs_write_int(bs, kwd->isUTF8, 1); @@ -2727,7 +2728,7 @@ GF_Err gf_odf_read_oci_name(GF_BitStream *bs, GF_OCICreators *ocn, u32 DescSize) { GF_Err e; u32 nbBytes = 0; - u32 i, aligned, count, len; + u32 i, count, len; if (!ocn) return GF_BAD_PARAM; count = gf_bs_read_int(bs, 8); @@ -2737,7 +2738,7 @@ GF_Err gf_odf_read_oci_name(GF_BitStream *bs, GF_OCICreators *ocn, u32 DescSize) if (! tmp) return GF_OUT_OF_MEM; tmp->langCode = gf_bs_read_int(bs, 24); tmp->isUTF8 = gf_bs_read_int(bs, 1); - aligned = gf_bs_read_int(bs, 7); + /*aligned = */gf_bs_read_int(bs, 7); nbBytes += 4; e = OD_ReadUTF8String(bs, & tmp->OCICreatorName, tmp->isUTF8, &len); if (e) return e; @@ -2986,12 +2987,11 @@ GF_Err gf_odf_read_short_text(GF_BitStream *bs, GF_ShortTextual *std, u32 DescSi { GF_Err e; u32 nbBytes = 0, len; - u8 aligned; if (!std) return GF_BAD_PARAM; std->langCode = gf_bs_read_int(bs, 24); std->isUTF8 = gf_bs_read_int(bs, 1); - aligned = gf_bs_read_int(bs, 7); + /*aligned = */gf_bs_read_int(bs, 7); nbBytes += 4; e = OD_ReadUTF8String(bs, & std->eventName, std->isUTF8, &len); diff --git a/src/odf/odf_command.c b/src/odf/odf_command.c index 2b62167..a24d02b 100644 --- a/src/odf/odf_command.c +++ b/src/odf/odf_command.c @@ -172,7 +172,9 @@ GF_Err gf_odf_write_od_remove(GF_BitStream *bs, GF_ODRemove *odRem) if (! odRem) return GF_BAD_PARAM; e = gf_odf_size_od_remove(odRem, &size); + assert(e == GF_OK); e = gf_odf_write_base_descriptor(bs, odRem->tag, size); + assert(e == GF_OK); for (i = 0; i < odRem->NbODs; i++) { gf_bs_write_int(bs, odRem->OD_ID[i], 10); @@ -431,11 +433,11 @@ GF_Err gf_odf_del_esd_remove(GF_ESDRemove *ESDRemove) GF_Err gf_odf_read_esd_remove(GF_BitStream *bs, GF_ESDRemove *esdRem, u32 gf_odf_size_command) { - u32 i = 0, aligned, nbBits; + u32 i = 0; if (! esdRem) return GF_BAD_PARAM; esdRem->ODID = gf_bs_read_int(bs, 10); - aligned = gf_bs_read_int(bs, 6); //aligned + /*aligned = */gf_bs_read_int(bs, 6); //aligned //we have gf_odf_size_command - 2 bytes left, and this is our ES_ID[1...255] //this works because OD commands are aligned @@ -452,7 +454,7 @@ GF_Err gf_odf_read_esd_remove(GF_BitStream *bs, GF_ESDRemove *esdRem, u32 gf_odf esdRem->ES_ID[i] = gf_bs_read_int(bs, 16); } //OD commands are aligned (but we should already be aligned.... - nbBits = gf_bs_align(bs); + /*nbBits = */gf_bs_align(bs); return GF_OK; } @@ -569,7 +571,9 @@ GF_Err gf_odf_del_ipmp_update(GF_IPMPUpdate *IPMPDUpdate) while (gf_list_count(IPMPDUpdate->IPMPDescList)) { GF_Descriptor *tmp = (GF_Descriptor*)gf_list_get(IPMPDUpdate->IPMPDescList, 0); e = gf_odf_delete_descriptor(tmp); + assert(e == GF_OK); e = gf_list_rem(IPMPDUpdate->IPMPDescList, 0); + assert(e == GF_OK); } gf_list_del(IPMPDUpdate->IPMPDescList); gf_free(IPMPDUpdate); diff --git a/src/odf/odf_parse.c b/src/odf/odf_parse.c index 4363404..2050b0d 100644 --- a/src/odf/odf_parse.c +++ b/src/odf/odf_parse.c @@ -374,7 +374,6 @@ GF_Err gf_odf_set_field(GF_Descriptor *desc, char *fieldName, char *val) break; case GF_ODF_BIFS_CFG_TAG: { - s32 notused; GF_BIFSConfig *bcd = (GF_BIFSConfig*)desc; if (!stricmp(val, "auto")) return GF_OK; if (!stricmp(fieldName, "nodeIDbits")) ret += sscanf(val, "%hu", &bcd->nodeIDbits); @@ -385,8 +384,8 @@ GF_Err gf_odf_set_field(GF_Descriptor *desc, char *fieldName, char *val) else if (!stricmp(fieldName, "pixelMetric") || !stricmp(fieldName, "pixelMetrics")) GET_BOOL(bcd->pixelMetrics) else if (!stricmp(fieldName, "pixelWidth")) ret += sscanf(val, "%hu", &bcd->pixelWidth); else if (!stricmp(fieldName, "pixelHeight")) ret += sscanf(val, "%hu", &bcd->pixelHeight); - else if (!stricmp(fieldName, "use3DMeshCoding")) GET_BOOL(notused) - else if (!stricmp(fieldName, "usePredictiveMFField")) GET_BOOL(notused) + else if (!stricmp(fieldName, "use3DMeshCoding")) ret = 1; + else if (!stricmp(fieldName, "usePredictiveMFField")) ret = 1; else if (!stricmp(fieldName, "randomAccess")) GET_BOOL(bcd->randomAccess) else if (!stricmp(fieldName, "useNames")) GET_BOOL(bcd->useNames) } @@ -397,7 +396,7 @@ GF_Err gf_odf_set_field(GF_Descriptor *desc, char *fieldName, char *val) if (!stricmp(fieldName, "fileName") || !stricmp(fieldName, "url")) GET_STRING(mi->file_name) else if (!stricmp(fieldName, "streamFormat")) GET_STRING(mi->streamFormat) else if (!stricmp(fieldName, "GroupID")) ret += sscanf(val, "%u", &mi->GroupID); - else if (!stricmp(fieldName, "startTime")) ret += sscanf(val, "%u", &mi->startTime); + else if (!stricmp(fieldName, "startTime")) ret += sscanf(val, "%d", &mi->startTime); else if (!stricmp(fieldName, "duration")) ret += sscanf(val, "%u", &mi->duration); else if (!stricmp(fieldName, "carouselPeriod")) { ret += sscanf(val, "%u", &mi->carousel_period_plus_one); diff --git a/src/odf/slc.c b/src/odf/slc.c index 3bae3d1..b0bd1e6 100644 --- a/src/odf/slc.c +++ b/src/odf/slc.c @@ -165,7 +165,7 @@ static u32 GetTSbytesLen(GF_SLConfig *sl) GF_Err gf_odf_read_slc(GF_BitStream *bs, GF_SLConfig *sl, u32 DescSize) { GF_Err e; - u32 reserved, nbBytes = 0; + u32 nbBytes = 0; if (!sl) return GF_BAD_PARAM; @@ -217,7 +217,7 @@ GF_Err gf_odf_read_slc(GF_BitStream *bs, GF_SLConfig *sl, u32 DescSize) sl->packetSeqNumLength = gf_bs_read_int(bs, 5); if (sl->packetSeqNumLength > 16) return GF_ODF_INVALID_DESCRIPTOR; - reserved = gf_bs_read_int(bs, 2); + /*reserved = */gf_bs_read_int(bs, 2); nbBytes += 15; } @@ -262,7 +262,9 @@ GF_Err gf_odf_write_slc(GF_BitStream *bs, GF_SLConfig *sl) if (! sl) return GF_BAD_PARAM; e = gf_odf_size_descriptor((GF_Descriptor *)sl, &size); + assert(e == GF_OK); e = gf_odf_write_base_descriptor(bs, sl->tag, size); + assert(e == GF_OK); gf_bs_write_int(bs, sl->predefined, 8); if (! sl->predefined) { diff --git a/src/scene_manager/encode_isom.c b/src/scene_manager/encode_isom.c index ab80aad..306eba2 100644 --- a/src/scene_manager/encode_isom.c +++ b/src/scene_manager/encode_isom.c @@ -297,14 +297,12 @@ static GF_Err gf_sm_import_specials(GF_SceneManager *ctx) { GF_Err e; u32 i, j, n, m, k; - GF_ESD *esd; GF_AUContext *au; GF_StreamContext *sc; i=0; while ((sc = (GF_StreamContext*)gf_list_enum(ctx->streams, &i))) { if (sc->streamType != GF_STREAM_OD) continue; - esd = NULL; j=0; while ((au = (GF_AUContext *)gf_list_enum(sc->AUs, &j))) { GF_ODCom *com; @@ -348,7 +346,6 @@ static GF_Err gf_sm_import_specials(GF_SceneManager *ctx) static GF_ESD *gf_sm_locate_esd(GF_SceneManager *ctx, u16 ES_ID) { u32 i, j, n, m, k; - GF_ESD *esd; GF_AUContext *au; GF_StreamContext *sc; if (!ES_ID) return NULL; @@ -356,7 +353,6 @@ static GF_ESD *gf_sm_locate_esd(GF_SceneManager *ctx, u16 ES_ID) i=0; while ((sc = (GF_StreamContext*)gf_list_enum(ctx->streams, &i))) { if (sc->streamType != GF_STREAM_OD) continue; - esd = NULL; j=0; while ((au = (GF_AUContext *)gf_list_enum(sc->AUs, &j))) { GF_ODCom *com; @@ -397,7 +393,7 @@ static GF_ESD *gf_sm_locate_esd(GF_SceneManager *ctx, u16 ES_ID) static GF_Err gf_sm_encode_scene(GF_SceneManager *ctx, GF_ISOFile *mp4, GF_SMEncodeOptions *opts, u32 scene_type) { char *data; - Bool is_in_iod, delete_desc, first_scene_id; + Bool is_in_iod, delete_desc; u32 i, j, di, rate, init_offset, data_len, count, track, rap_delay, flags, rap_mode; u64 last_rap, dur, time_slice, avg_rate, prev_dts; GF_Err e; @@ -438,7 +434,6 @@ static GF_Err gf_sm_encode_scene(GF_SceneManager *ctx, GF_ISOFile *mp4, GF_SMEnc flags = opts ? opts->flags : 0; delete_desc = 0; - first_scene_id = 0; esd = NULL; @@ -585,15 +580,6 @@ force_scene_rap: if (sc && sc->timeScale) esd->slConfig->timestampResolution = sc->timeScale; if (!esd->slConfig->timestampResolution) esd->slConfig->timestampResolution = 1000; - /*force scene dependencies (we cannot encode in 2 different scene contexts)*/ - if (!esd->dependsOnESID) { - if (!first_scene_id) { - esd->dependsOnESID = 0; - first_scene_id = esd->ESID; - } else { - esd->dependsOnESID = first_scene_id; - } - } if (!esd->decoderConfig) esd->decoderConfig = (GF_DecoderConfig*)gf_odf_desc_new(GF_ODF_DCD_TAG); esd->decoderConfig->streamType = GF_STREAM_SCENE; @@ -1046,6 +1032,21 @@ static GF_Err gf_sm_encode_od(GF_SceneManager *ctx, GF_ISOFile *mp4, char *media GF_ESD *imp_esd; m=0; while ((imp_esd = (GF_ESD*)gf_list_enum(od->ESDescriptors, &m))) { + /*do not import scene and OD streams*/ + if (imp_esd->decoderConfig) { + switch (imp_esd->decoderConfig->streamType) { + case GF_STREAM_SCENE: + /*import AFX streams, but not others*/ + if (imp_esd->decoderConfig->objectTypeIndication==GPAC_OTI_SCENE_AFX) + break; + continue; + case GF_STREAM_OD: + continue; + default: + break; + } + } + switch (imp_esd->tag) { case GF_ODF_ESD_TAG: e = gf_sm_import_stream(ctx, mp4, imp_esd, au->timing_sec, mediaSource, au->flags & GF_SM_AU_RAP); diff --git a/src/scene_manager/loader_bt.c b/src/scene_manager/loader_bt.c index d991a14..de85f89 100644 --- a/src/scene_manager/loader_bt.c +++ b/src/scene_manager/loader_bt.c @@ -31,6 +31,75 @@ #include /*for key codes...*/ #include +#include +#include + + +void gf_sm_update_bitwrapper_buffer(GF_Node *node, const char *fileName) +{ + u32 data_size = 0; + char *data = NULL; + char *buffer; + M_BitWrapper *bw = (M_BitWrapper *)node; + + if (!bw->buffer.buffer) return; + buffer = bw->buffer.buffer; + if (!strnicmp(buffer, "file://", 7)) { + char *url = gf_url_concatenate(fileName, buffer+7); + if (url) { + FILE *f = fopen(url, "rb"); + if (f) { + fseek(f, 0, SEEK_END); + data_size = ftell(f); + fseek(f, 0, SEEK_SET); + data = gf_malloc(sizeof(char)*data_size); + if (data) { + size_t s = fread(data, 1, data_size, f); + assert(s == data_size); + } + fclose(f); + } + gf_free(url); + } + } else { + Bool base_64 = 0; + if (!strnicmp(buffer, "data:application/octet-string", 29)) { + char *sep = strchr(bw->buffer.buffer, ','); + base_64 = strstr(bw->buffer.buffer, ";base64") ? 1 : 0; + if (sep) buffer = sep+1; + } + + if (base_64) { + data_size = 2*strlen(buffer); + data = (char*)gf_malloc(sizeof(char)*data_size); + if (data) + data_size = gf_base64_decode(buffer, strlen(buffer), data, data_size); + } else { + u32 i, c; + char s[3]; + data_size = strlen(buffer) / 3; + data = (char*)gf_malloc(sizeof(char) * data_size); + if (data) { + s[2] = 0; + for (i=0; ibuffer.buffer); + bw->buffer.buffer = NULL; + bw->buffer_len = 0; + if (data) { + bw->buffer.buffer = data; + bw->buffer_len = data_size; + } + +} + #ifndef GPAC_DISABLE_LOADER_BT @@ -63,7 +132,7 @@ typedef struct GF_Err last_error; u32 line; - Bool done; + Bool done, in_com; u32 is_wrl; /*0: no unicode, 1: UTF-16BE, 2: UTF-16LE*/ u32 unicode_type; @@ -112,7 +181,7 @@ GF_Node *gf_bt_peek_node(GF_BTParser *parser, char *defID); static GF_Err gf_bt_report(GF_BTParser *parser, GF_Err e, char *format, ...) { #ifndef GPAC_DISABLE_LOG - if (gf_log_get_level() && (gf_log_get_tools() & GF_LOG_PARSER)) { + if (gf_log_tool_level_on(GF_LOG_PARSER, e ? GF_LOG_ERROR : GF_LOG_WARNING)) { char szMsg[2048]; va_list args; va_start(args, format); @@ -454,7 +523,7 @@ char *gf_bt_get_next(GF_BTParser *parser, Bool point_break) return parser->cur_buffer; } -char *gf_bt_get_string(GF_BTParser *parser) +char *gf_bt_get_string(GF_BTParser *parser, u8 string_delim) { char *res; s32 i, size; @@ -473,22 +542,23 @@ char *gf_bt_get_string(GF_BTParser *parser) if (gzeof(parser->gz_in)) return NULL; gf_bt_check_line(parser); } + if (!string_delim) string_delim = '"'; i=0; while (1) { - if (parser->line_buffer[parser->line_pos] == '\"') + if (parser->line_buffer[parser->line_pos] == string_delim) if ( !parser->line_pos || (parser->line_buffer[parser->line_pos-1] != '\\') ) break; BT_STR_CHECK_ALLOC if ((parser->line_buffer[parser->line_pos]=='/') && (parser->line_buffer[parser->line_pos+1]=='/') && (parser->line_buffer[parser->line_pos-1]!=':') ) { /*this looks like a comment*/ - if (!strstr(&parser->line_buffer[parser->line_pos], "\"")) { + if (!strchr(&parser->line_buffer[parser->line_pos], string_delim)) { gf_bt_check_line(parser); continue; } } - if ((parser->line_buffer[parser->line_pos] != '\\') || (parser->line_buffer[parser->line_pos+1] != '"')) { + if ((parser->line_buffer[parser->line_pos] != '\\') || (parser->line_buffer[parser->line_pos+1] != string_delim)) { /*handle UTF-8 - WARNING: if parser is in unicode string is already utf8 multibyte chars*/ if (!parser->unicode_type && parser->line_buffer[parser->line_pos] & 0x80) { char c = parser->line_buffer[parser->line_pos]; @@ -773,6 +843,7 @@ static void gf_bt_update_timenode(GF_BTParser *parser, GF_Node *node) } } + void gf_bt_sffield(GF_BTParser *parser, GF_FieldInfo *info, GF_Node *n) { switch (info->fieldType) { @@ -854,8 +925,12 @@ void gf_bt_sffield(GF_BTParser *parser, GF_FieldInfo *info, GF_Node *n) if (parser->last_error) return; break; case GF_SG_VRML_SFSTRING: - if (gf_bt_check_code(parser, '\"') || gf_bt_check_code(parser, '\'')) { - char *str = gf_bt_get_string(parser); + { + u8 delim = 0; + if (gf_bt_check_code(parser, '\"')) delim = '\"'; + else if (gf_bt_check_code(parser, '\'')) delim = '\''; + if (delim) { + char *str = gf_bt_get_string(parser, delim); if (!str) goto err; if (((SFString *)info->far_ptr)->buffer) gf_free(((SFString *)info->far_ptr)->buffer); @@ -864,14 +939,23 @@ void gf_bt_sffield(GF_BTParser *parser, GF_FieldInfo *info, GF_Node *n) ((SFString *)info->far_ptr)->buffer = str; else gf_free(str); + + if (n && (n->sgprivate->tag==TAG_MPEG4_BitWrapper)) { + gf_sm_update_bitwrapper_buffer(n, parser->load->fileName); + } } else { goto err; } + } break; case GF_SG_VRML_SFURL: - if (gf_bt_check_code(parser, '\"') || gf_bt_check_code(parser, '\'')) { + { + u8 delim = 0; + if (gf_bt_check_code(parser, '\"')) delim = '\"'; + else if (gf_bt_check_code(parser, '\'')) delim = '\''; + if (delim) { SFURL *url = (SFURL *)info->far_ptr; - char *str = gf_bt_get_string(parser); + char *str = gf_bt_get_string(parser, delim); if (!str) goto err; if (url->url) gf_free(url->url); url->url = NULL; @@ -902,6 +986,7 @@ void gf_bt_sffield(GF_BTParser *parser, GF_FieldInfo *info, GF_Node *n) if (parser->last_error) return; ((SFURL *)info->far_ptr)->OD_ID = val; } + } break; case GF_SG_VRML_SFCOMMANDBUFFER: { @@ -967,7 +1052,7 @@ void gf_bt_sffield(GF_BTParser *parser, GF_FieldInfo *info, GF_Node *n) if (!gf_bt_check_code(parser, '\"')) { gf_bt_report(parser, GF_BAD_PARAM, "\" expected in Script"); } - sc->script_text = (unsigned char*)gf_bt_get_string(parser); + sc->script_text = (unsigned char*)gf_bt_get_string(parser, '\"'); } break; case GF_SG_VRML_SFATTRREF: @@ -1336,7 +1421,7 @@ GF_Node *gf_bt_sf_node(GF_BTParser *parser, char *node_name, GF_Node *parent, ch } /*we ignore 'description' for MPEG4 sensors*/ else if (!strcmp(str, "description")) { - char *str = gf_bt_get_string(parser); + char *str = gf_bt_get_string(parser, 0); gf_free(str); parser->last_error = GF_OK; continue; @@ -1380,7 +1465,6 @@ GF_Node *gf_bt_sf_node(GF_BTParser *parser, char *node_name, GF_Node *parent, ch if (is_script && parser->last_error) { u32 eType, fType; - GF_ScriptField *sf; eType = 0; if (!strcmp(str, "eventIn") || !strcmp(str, "inputOnly")) eType = GF_SG_SCRIPT_TYPE_EVENT_IN; else if (!strcmp(str, "eventOut") || !strcmp(str, "outputOnly")) eType = GF_SG_SCRIPT_TYPE_EVENT_OUT; @@ -1397,7 +1481,7 @@ GF_Node *gf_bt_sf_node(GF_BTParser *parser, char *node_name, GF_Node *parent, ch } parser->last_error = GF_OK; str = gf_bt_get_next(parser, 0); - sf = gf_sg_script_field_new(node, eType, fType, str); + gf_sg_script_field_new(node, eType, fType, str); parser->last_error = gf_node_get_field_by_name(node, str, &info); if (parser->parsing_proto && gf_bt_set_field_is(parser, &info, node)) continue; @@ -1768,12 +1852,10 @@ next_field: if (externProto) { SFURL *url; - u32 nb_urls; Bool has_urls = 0; if (gf_bt_check_code(parser, '[')) has_urls = 1; gf_sg_vrml_mf_reset(&proto->ExternProto, GF_SG_VRML_MFURL); - nb_urls = 0; do { str = gf_bt_get_next(parser, 0); gf_sg_vrml_mf_append(&proto->ExternProto, GF_SG_VRML_MFURL, (void **) &url); @@ -3173,14 +3255,17 @@ GF_Err gf_bt_loader_run_intern(GF_BTParser *parser, GF_Command *init_com, Bool i { char *str; GF_Node *node, *vrml_root_node; - Bool in_com, force_new_com; + Bool force_new_com; GF_Route *r; Bool has_id; char szDEFName[1000]; vrml_root_node = NULL; has_id = 0; - in_com = init_com ? 0 : 1; + + if (init_com) + parser->in_com = 0 ; + parser->cur_com = init_com; force_new_com = (parser->load->flags & GF_SM_LOAD_CONTEXT_READY) ? 1 : 0; @@ -3202,7 +3287,8 @@ GF_Err gf_bt_loader_run_intern(GF_BTParser *parser, GF_Command *init_com, Bool i } } - parser->stream_id = parser->load->force_es_id; + if (!parser->in_com) + parser->stream_id = parser->load->force_es_id; /*parse all top-level items*/ while (!parser->last_error) { @@ -3257,7 +3343,7 @@ GF_Err gf_bt_loader_run_intern(GF_BTParser *parser, GF_Command *init_com, Bool i is always RAP*/ if (!parser->au_time) parser->au_is_rap = 1; - in_com = 1; + parser->in_com = 1; if (!gf_bt_check_code(parser, '{')) { str = gf_bt_get_next(parser, 0); @@ -3316,7 +3402,10 @@ GF_Err gf_bt_loader_run_intern(GF_BTParser *parser, GF_Command *init_com, Bool i GF_StreamContext *prev = parser->od_es; parser->od_es = gf_sm_stream_new(parser->load->ctx, (u16) parser->stream_id, GF_STREAM_OD, 0); /*force new AU if stream changed*/ - if (parser->od_es != prev) parser->bifs_au = NULL; + if (parser->od_es != prev) { + parser->bifs_au = NULL; + parser->od_au = NULL; + } } if (!parser->od_es) parser->od_es = gf_sm_stream_new(parser->load->ctx, (u16) parser->stream_id, GF_STREAM_OD, 0); if (!parser->od_au) parser->od_au = gf_sm_stream_au_new(parser->od_es, parser->au_time, 0, parser->au_is_rap); @@ -3394,7 +3483,7 @@ GF_Err gf_bt_loader_run_intern(GF_BTParser *parser, GF_Command *init_com, Bool i /*if in command, check command end*/ else { /*check command end*/ - if (/*in_com && */gf_bt_check_code(parser, '}')) in_com = 0; + if (/*in_com && */gf_bt_check_code(parser, '}')) parser->in_com = 0; else if (strlen(str)) { gf_bt_report(parser, GF_BAD_PARAM, "%s: Unknown top-level element", str); } @@ -3416,6 +3505,7 @@ GF_Err gf_bt_loader_run_intern(GF_BTParser *parser, GF_Command *init_com, Bool i static GF_Err gf_sm_load_bt_initialize(GF_SceneLoader *load, const char *str, Bool input_only) { u32 size; + char *sep; gzFile gzInput; GF_Err e; unsigned char BOM[5]; @@ -3474,7 +3564,13 @@ static GF_Err gf_sm_load_bt_initialize(GF_SceneLoader *load, const char *str, Bo if (parser->gz_in) gzseek(parser->gz_in, 3, SEEK_CUR); } parser->initialized = 1; - + + if ( load->fileName ) + { + sep = strrchr(load->fileName, '.'); + if (sep && !strnicmp(sep, ".wrl", 4)) parser->is_wrl = 1; + } + if (input_only) return GF_OK; /*initalize default streams in the context*/ diff --git a/src/scene_manager/loader_isom.c b/src/scene_manager/loader_isom.c index 6461a1b..1d0c77c 100644 --- a/src/scene_manager/loader_isom.c +++ b/src/scene_manager/loader_isom.c @@ -139,7 +139,7 @@ static void UpdateODCommand(GF_ISOFile *mp4, GF_ODCom *com) static void mp4_report(GF_SceneLoader *load, GF_Err e, char *format, ...) { #ifndef GPAC_DISABLE_LOG - if (gf_log_get_level() && (gf_log_get_tools() & GF_LOG_PARSER)) { + if (gf_log_tool_level_on(GF_LOG_PARSER, e ? GF_LOG_ERROR : GF_LOG_WARNING)) { char szMsg[1024]; va_list args; va_start(args, format); @@ -333,7 +333,7 @@ static GF_Err gf_sm_isom_suspend(GF_SceneLoader *loader, Bool suspend) GF_Err gf_sm_load_init_isom(GF_SceneLoader *load) { - u32 i, track; + u32 i; GF_BIFSConfig *bc; GF_ESD *esd; GF_Err e; @@ -382,8 +382,6 @@ GF_Err gf_sm_load_init_isom(GF_SceneLoader *load) e = GF_OK; GF_LOG(GF_LOG_INFO, GF_LOG_PARSER, ("%s\n", scene_msg)); - track = i+1; - /*BIFS: update size & pixel metrics info*/ if (esd->decoderConfig->objectTypeIndication<=2) { bc = gf_odf_get_bifs_config(esd->decoderConfig->decoderSpecificInfo, esd->decoderConfig->objectTypeIndication); diff --git a/src/scene_manager/loader_qt.c b/src/scene_manager/loader_qt.c index c7dd13b..b84ac02 100644 --- a/src/scene_manager/loader_qt.c +++ b/src/scene_manager/loader_qt.c @@ -35,7 +35,7 @@ static GF_Err gf_qt_report(GF_SceneLoader *load, GF_Err e, char *format, ...) { #ifndef GPAC_DISABLE_LOG - if (gf_log_get_level() && (gf_log_get_tools() & GF_LOG_PARSER)) { + if (gf_log_tool_level_on(GF_LOG_PARSER, e ? GF_LOG_ERROR : GF_LOG_WARNING)) { char szMsg[1024]; va_list args; va_start(args, format); @@ -51,7 +51,7 @@ static GF_Err gf_qt_report(GF_SceneLoader *load, GF_Err e, char *format, ...) /*import cubic QTVR to mp4*/ GF_Err gf_sm_load_init_qt(GF_SceneLoader *load) { - u32 i, di, w, h, tk, pano_t, nb_samp; + u32 i, di, w, h, tk, nb_samp; Bool has_qtvr; GF_ISOSample *samp; GF_ISOFile *src; @@ -72,7 +72,6 @@ GF_Err gf_sm_load_init_qt(GF_SceneLoader *load) if (!src) return gf_qt_report(load, GF_URL_ERROR, "Opening file %s failed", load->fileName); w = h = tk = 0; - pano_t = 0; nb_samp = 0; has_qtvr = 0; @@ -172,7 +171,7 @@ GF_Err gf_sm_load_init_qt(GF_SceneLoader *load) samp = gf_isom_get_sample(src, tk, i+1, &di); img = gf_f64_open(mi->file_name, "wb"); - fwrite(samp->data, samp->dataLength, 1, img); + gf_fwrite(samp->data, samp->dataLength, 1, img); fclose(img); gf_isom_sample_del(&samp); } diff --git a/src/scene_manager/loader_svg.c b/src/scene_manager/loader_svg.c index 889b0bf..cedbab8 100644 --- a/src/scene_manager/loader_svg.c +++ b/src/scene_manager/loader_svg.c @@ -129,7 +129,7 @@ typedef struct static GF_Err svg_report(GF_SVG_Parser *parser, GF_Err e, char *format, ...) { #ifndef GPAC_DISABLE_LOG - if (gf_log_get_level() && (gf_log_get_tools() & GF_LOG_PARSER)) { + if (gf_log_tool_level_on(GF_LOG_PARSER, e ? GF_LOG_ERROR : GF_LOG_WARNING)) { char szMsg[2048]; va_list args; va_start(args, format); @@ -151,9 +151,9 @@ static void svg_progress(void *cbk, u64 done, u64 total) /*notify MediaEvent*/ if (parser->load && parser->load->is) { - parser->load->is->on_media_event(parser->load->is, GF_EVENT_MEDIA_DATA_PROGRESS); + parser->load->is->on_media_event(parser->load->is, GF_EVENT_MEDIA_PROGRESS); if (done == total) { - parser->load->is->on_media_event(parser->load->is, GF_EVENT_MEDIA_END_OF_DATA); + parser->load->is->on_media_event(parser->load->is, GF_EVENT_MEDIA_LOAD_DONE); } } gf_set_progress("SVG (Dynamic Attribute List) Parsing", done, total); @@ -357,9 +357,12 @@ static Bool svg_parse_animation(GF_SVG_Parser *parser, GF_SceneGraph *sg, SVG_De if (anim->resolve_stage==0) { /* Stage 0: parsing the animation attribute values for that we need to resolve the target first */ + + /* if we don't have a target, try to get it */ if (!anim->target) anim->target = (SVG_Element *) gf_sg_find_node_by_name(sg, anim->target_id + 1); + /* if now we have a target, create the xlink:href attribute on the animation element and set it to the found target */ if (anim->target) { XMLRI *iri; gf_node_get_attribute_by_tag((GF_Node *)anim->animation_elt, TAG_XLINK_ATT_href, 1, 0, &info); @@ -444,24 +447,28 @@ static Bool svg_parse_animation(GF_SVG_Parser *parser, GF_SceneGraph *sg, SVG_De if (!anim->target) return 0; if (anim->to) { + /* now that we have a target, if there is a to value to parse, create the attribute and parse it */ gf_node_get_attribute_by_tag((GF_Node *)anim->animation_elt, TAG_SVG_ATT_to, 1, 0, &info); gf_svg_parse_attribute((GF_Node *)anim->animation_elt, &info, anim->to, anim_value_type); if (anim_value_type==XMLRI_datatype) svg_post_process_href(parser, (XMLRI*)((SMIL_AnimateValue *)info.far_ptr)->value); } if (anim->from) { + /* now that we have a target, if there is a from value to parse, create the attribute and parse it */ gf_node_get_attribute_by_tag((GF_Node *)anim->animation_elt, TAG_SVG_ATT_from, 1, 0, &info); gf_svg_parse_attribute((GF_Node *)anim->animation_elt, &info, anim->from, anim_value_type); if (anim_value_type==XMLRI_datatype) svg_post_process_href(parser, (XMLRI*)((SMIL_AnimateValue *)info.far_ptr)->value); } if (anim->by) { + /* now that we have a target, if there is a by value to parse, create the attribute and parse it */ gf_node_get_attribute_by_tag((GF_Node *)anim->animation_elt, TAG_SVG_ATT_by, 1, 0, &info); gf_svg_parse_attribute((GF_Node *)anim->animation_elt, &info, anim->by, anim_value_type); if (anim_value_type==XMLRI_datatype) svg_post_process_href(parser, (XMLRI*)((SMIL_AnimateValue *)info.far_ptr)->value); } if (anim->values) { + /* now that we have a target, if there is a 'values' value to parse, create the attribute and parse it */ gf_node_get_attribute_by_tag((GF_Node *)anim->animation_elt, TAG_SVG_ATT_values, 1, 0, &info); gf_svg_parse_attribute((GF_Node *)anim->animation_elt, &info, anim->values, anim_value_type); if (anim_value_type==XMLRI_datatype) { @@ -621,26 +628,36 @@ static SVG_Element *svg_parse_element(GF_SVG_Parser *parser, const char *name, c GF_LOG(GF_LOG_DEBUG, GF_LOG_PARSER, ("[SVG Parsing] Parsing node %s\n", name)); *has_ns = 0; - /*parse all att for namespace*/ + /*parse all att to check for namespaces, to: + - add the prefixed namespaces (xmlns:xxxx='...') + - change the namespace for this element if no prefix (xmlns='...')*/ for (i=0; ivalue || !strlen(att->value)) continue; if (!strncmp(att->name, "xmlns", 5)) { + /* check if we have a prefix for this namespace */ char *qname = strchr(att->name, ':'); - if (qname) + if (qname) { qname++; - + } + /* Adds the namespace to the scene graph, either with a prefix or with NULL */ gf_sg_add_namespace(parser->load->scene_graph, att->value, qname); - if (!qname) - parser->current_ns = gf_sg_get_namespace_code_from_name(parser->load->scene_graph, att->value); + if (!qname) { + /* Only if there was no prefix, we change the namespace for the current element */ + parser->current_ns = gf_sg_get_namespace_code_from_name(parser->load->scene_graph, att->value); + } + /* Signal that when ending this element, namespaces will have to be removed */ *has_ns = 1; } + /* FIXME: This should be changed to reflect that xml:id has precedence over id if both are specified with different values */ else if (!stricmp(att->name, "id") || !stricmp(att->name, "xml:id")) { if (!ID) ID = att->value; } } + /* CHECK: overriding the element namespace with the parent one, if given ??? + This is wrong ??*/ xmlns = parser->current_ns; if (name_space) { xmlns = gf_sg_get_namespace_code(parser->load->scene_graph, (char *) name_space); @@ -661,6 +678,8 @@ static SVG_Element *svg_parse_element(GF_SVG_Parser *parser, const char *name, c #endif } + /* If this element has an ID, we look in the list of elements already created in advance (in case of reference) to see if it is there, + in which case we will reuse it*/ has_id = 0; count = gf_list_count(parser->peeked_nodes); if (count && ID) { @@ -676,6 +695,8 @@ static SVG_Element *svg_parse_element(GF_SVG_Parser *parser, const char *name, c } } + /* If the element was found in the list of elements already created, we do not need to create it, we reuse it. + Otherwise, we create it based on the tag */ if (!has_id) { /* Creates a node in the current scene graph */ elt = (SVG_Element*)gf_node_new(parser->load->scene_graph, tag); @@ -683,6 +704,7 @@ static SVG_Element *svg_parse_element(GF_SVG_Parser *parser, const char *name, c parser->last_error = GF_SG_UNKNOWN_NODE; return NULL; } + /* CHECK: Why isn't this code in the gf_node_new call ?? */ if (tag == TAG_DOMFullNode) { GF_DOMFullNode *d = (GF_DOMFullNode *)elt; d->name = gf_strdup(name); @@ -690,10 +712,12 @@ static SVG_Element *svg_parse_element(GF_SVG_Parser *parser, const char *name, c } } + /* We indicate that the element is used by its parent (reference counting for safe deleting) */ gf_node_register((GF_Node *)elt, (parent ? (GF_Node *)parent->node : NULL)); + /* We attach this element as the last child of its parent */ if (parent && elt) gf_node_list_add_child_last( & parent->node->children, (GF_Node*)elt, & parent->last_child); - + /* By default, all elements will need initialization for rendering, except some that will explicitly set it to 0 */ needs_init = 1; if (gf_svg_is_animation_tag(tag)) { @@ -722,6 +746,7 @@ static SVG_Element *svg_parse_element(GF_SVG_Parser *parser, const char *name, c } anim->resolve_stage = 1; } else if ((tag == TAG_SVG_script) || (tag==TAG_SVG_handler)) { + /* Scripts and handlers don't render and have no initialization phase */ needs_init = 0; } @@ -737,6 +762,8 @@ static SVG_Element *svg_parse_element(GF_SVG_Parser *parser, const char *name, c char *att_name = NULL; if (!att->value || !strlen(att->value)) continue; + /* first determine in which namespace is the attribute and store the result in ns, + then shift the char buffer to point to the local name of the attribute*/ ns = xmlns; att_name = strchr(att->name, ':'); if (att_name) { @@ -753,10 +780,16 @@ static SVG_Element *svg_parse_element(GF_SVG_Parser *parser, const char *name, c att_name = att->name; } + /* Begin of special cases of attributes */ + + /* CHECK: Shouldn't namespaces be checked here ? */ if (!stricmp(att_name, "style")) { gf_svg_parse_style((GF_Node *)elt, att->value); continue; } + + /* Some attributes of the animation elements cannot be parsed (into typed values) until the type of value is known, + we defer the parsing and store them temporarily as strings */ if (anim) { if (!stricmp(att_name, "to")) { anim->to = gf_strdup(att->value); @@ -780,27 +813,36 @@ static SVG_Element *svg_parse_element(GF_SVG_Parser *parser, const char *name, c } } + /* Special case for xlink:href attributes */ if ((ns == GF_XMLNS_XLINK) && !stricmp(att_name, "href") ) { + if (gf_svg_is_animation_tag(tag)) { + /* For xlink:href in animation elements, + we try to locate the target of the xlink:href to determine the type of values to be animated */ assert(anim); anim->target_id = gf_strdup(att->value); - /*may be NULL*/ + /*The target may be NULL, if it has not yet been parsed, we will try to resolve it later on */ anim->target = (SVG_Element *) gf_sg_find_node_by_name(parser->load->scene_graph, anim->target_id + 1); continue; } else { + /* For xlink:href attribute on elements other than animation elements, + we create the attribute, parse it and try to do some special process it */ GF_FieldInfo info; XMLRI *iri = NULL; if (gf_node_get_attribute_by_tag((GF_Node *)elt, TAG_XLINK_ATT_href, 1, 0, &info)==GF_OK) { gf_svg_parse_attribute((GF_Node *)elt, &info, att->value, 0); iri = info.far_ptr; + /* Embed script if needed or clean data URL with proper mime type */ svg_process_media_href(parser, (GF_Node *)elt, iri); - + /* extract data URL and store as file */ svg_post_process_href(parser, iri); continue; } } } + + /* For the XML Event handler element, we need to defer the parsing of some attributes */ if ((tag == TAG_SVG_handler) && (ns == GF_XMLNS_XMLEV)) { if (!stricmp(att_name, "event") ) { ev_event = att->value; @@ -814,6 +856,7 @@ static SVG_Element *svg_parse_element(GF_SVG_Parser *parser, const char *name, c /*laser specific stuff*/ if (ns == GF_XMLNS_LASER) { + /* CHECK: we should probably check the namespace of the attribute here */ if (!stricmp(att_name, "scale") ) { if (gf_node_get_attribute_by_tag((GF_Node *)elt, TAG_SVG_ATT_transform, 1, 1, &info)==GF_OK) { SVG_Point pt; @@ -834,6 +877,9 @@ static SVG_Element *svg_parse_element(GF_SVG_Parser *parser, const char *name, c } } + /* For all attributes of the form 'on...', like 'onclick' we create a listener for the event on the current element, + we connect the listener to a handler that contains the code in the 'on...' attribute. */ + /* CHECK: we should probably check the namespace of the attribute and of the element here */ if (!strncmp(att_name, "on", 2)) { u32 evtType = gf_dom_event_type_by_name(att_name + 2); if (evtType != GF_EVENT_UNKNOWN) { @@ -844,6 +890,9 @@ static SVG_Element *svg_parse_element(GF_SVG_Parser *parser, const char *name, c svg_report(parser, GF_OK, "Skipping unknown event handler %s on node %s", att->name, name); } + /* end of special cases of attributes */ + + /* General attribute creation and parsing */ if (gf_node_get_attribute_by_name((GF_Node *)elt, att_name, ns, 1, 0, &info)==GF_OK) { #ifndef SKIP_ATTS_PARSING GF_Err e = gf_svg_parse_attribute((GF_Node *)elt, &info, att->value, 0); @@ -857,6 +906,7 @@ static SVG_Element *svg_parse_element(GF_SVG_Parser *parser, const char *name, c the 'xml:id' attribute."*/ if (!node_name || (info.fieldIndex == TAG_XML_ATT_id)) { node_name = *(SVG_ID *)info.far_ptr; + /* Check if ID start with a digit, which is not a valid ID for a node according to XML (see http://www.w3.org/TR/xml/#id) */ if (isdigit(node_name[0])) { svg_report(parser, GF_BAD_PARAM, "Invalid value %s for node %s %s", node_name, name, att->name); node_name = NULL; @@ -869,7 +919,7 @@ static SVG_Element *svg_parse_element(GF_SVG_Parser *parser, const char *name, c case TAG_SVG_ATT_initialVisibility: case TAG_SVG_ATT_fullscreen: case TAG_SVG_ATT_requiredFonts: - /*switch to v2*/ + /*switch LASeR Configuration to v2 because these attributes are not part of v1*/ svg_lsr_set_v2(parser); break; } @@ -877,15 +927,18 @@ static SVG_Element *svg_parse_element(GF_SVG_Parser *parser, const char *name, c #endif continue; } + + /* all other attributes (??? failed to be created) should fall in this category */ svg_report(parser, GF_OK, "Skipping attribute %s on node %s", att->name, name); } - /*set root BEFORE processing events in order to have it setup for script init*/ - if ((tag == TAG_SVG_svg) && !parser->has_root) + /*set the root of the SVG tree BEFORE processing events in order to have it setup for script init (e.g. load events)*/ + if ((tag == TAG_SVG_svg) && !parser->has_root) { svg_init_root_element(parser, elt); + } + /* When a handler element specifies the event attribute, an implicit listener is defined */ if (ev_event) { - /* When the handler element specifies the event attribute, an implicit listener is defined */ GF_Node *node = (GF_Node *)elt; SVG_Element *listener; u32 type; @@ -906,22 +959,28 @@ static SVG_Element *svg_parse_element(GF_SVG_Parser *parser, const char *name, c ((XMLRI *)info.far_ptr)->target = node; if (ev_observer) { + /* An observer was specified, so it needs to be used */ gf_node_get_attribute_by_tag((GF_Node *)listener, TAG_XMLEV_ATT_observer, 1, 0, &info); gf_svg_parse_attribute((GF_Node *)elt, &info, (char*)ev_observer, 0); } else { - /* this listener listens with the parent of the handler as the event target */ + /* No observer specified, this listener listens with the parent of the handler as the event target */ gf_node_get_attribute_by_tag((GF_Node *)listener, TAG_XMLEV_ATT_target, 1, 0, &info); ((XMLRI *)info.far_ptr)->target = parent->node; } + /* if the target was found (already parsed), we are fine, otherwise we need to try to find it again, + we place the listener in the defered listener list */ if ( ((XMLRI *)info.far_ptr)->target) gf_node_dom_listener_add(((XMLRI *)info.far_ptr)->target, (GF_Node *) listener); else gf_list_add(parser->defered_listeners, listener); } - /* if the new element has an id, we try to resolve defered references */ + /* if the new element has an id, we try to resolve defered references (including animations, href and listeners (just above)*/ if (node_name) { - if (!has_id) gf_svg_parse_element_id((GF_Node *)elt, node_name, parser->command_depth ? 1 : 0); + if (!has_id) { + /* if the element was already created before this call, we don't need to get a numerical id, we have it already */ + gf_svg_parse_element_id((GF_Node *)elt, node_name, parser->command_depth ? 1 : 0); + } svg_resolved_refs(parser, parser->load->scene_graph, node_name); } @@ -942,18 +1001,22 @@ static SVG_Element *svg_parse_element(GF_SVG_Parser *parser, const char *name, c } #ifndef SKIP_INIT - if (needs_init) gf_node_init((GF_Node *)elt); + if (needs_init) { + /* For elements that need it, we initialize the rendering stack */ + gf_node_init((GF_Node *)elt); + } #endif if (parent && elt) { /*mark parent element as dirty (new child added) and invalidate parent graph for progressive rendering*/ gf_node_dirty_set((GF_Node *)parent->node, GF_SG_CHILD_DIRTY, 1); /*request scene redraw*/ - if (parser->load->scene_graph->NodeCallback) + if (parser->load->scene_graph->NodeCallback) { parser->load->scene_graph->NodeCallback(parser->load->scene_graph->userpriv, GF_SG_CALLBACK_MODIFIED, NULL, NULL); + } } - /*register listener element*/ + /*If we are in playback mode, we register (reference counting for safe deleting) the listener element with the element that uses it */ if ((parser->load->flags & GF_SM_LOAD_FOR_PLAYBACK) && elt && (tag==TAG_SVG_listener)) { GF_FieldInfo info; Bool post_pone = 0; @@ -1334,7 +1397,6 @@ static void svg_node_start(void *sax_cbck, const char *name, const char *name_sp u32 time, OTI, ST, i, ts_res; GF_ODUpdate *odU; GF_ObjectDescriptor *od; - Bool rap; SVG_SAFExternalStream*st; /*create a SAF stream*/ if (!parser->saf_es) { @@ -1348,13 +1410,12 @@ static void svg_node_start(void *sax_cbck, const char *name, const char *name_sp gf_list_add(parser->load->ctx->root_od->ESDescriptors, esd); } time = 0; - rap = 0; ts_res = 1000; OTI = ST = 0; for (i=0; iname, "time")) time = atoi(att->value); - else if (!strcmp(att->name, "rap")) rap = !strcmp(att->value, "yes") ? 1 : 0; + else if (!strcmp(att->name, "rap")) ;//rap = !strcmp(att->value, "yes") ? 1 : 0; else if (!strcmp(att->name, "url")) url = gf_strdup(att->value); else if (!strcmp(att->name, "streamID")) ID = att->value; else if (!strcmp(att->name, "objectTypeIndication")) OTI = atoi(att->value); @@ -1592,7 +1653,9 @@ static void svg_node_start(void *sax_cbck, const char *name, const char *name_sp static void svg_node_end(void *sax_cbck, const char *name, const char *name_space) { +#ifdef SKIP_UNKNOWN_NODES u32 ns; +#endif GF_SVG_Parser *parser = (GF_SVG_Parser *)sax_cbck; SVG_NodeStack *top = (SVG_NodeStack *)gf_list_last(parser->node_stack); @@ -1620,12 +1683,12 @@ static void svg_node_end(void *sax_cbck, const char *name, const char *name_spac return; } +#ifdef SKIP_UNKNOWN_NODES ns = parser->current_ns; if (name_space) ns = gf_sg_get_namespace_code(parser->load->scene_graph, (char *) name_space); /*only remove created nodes ... */ -#ifdef SKIP_UNKNOWN_NODES if (gf_xml_get_element_tag(name, ns) != TAG_UndefinedNode) #endif { diff --git a/src/scene_manager/loader_xmt.c b/src/scene_manager/loader_xmt.c index 4275e01..86c336b 100644 --- a/src/scene_manager/loader_xmt.c +++ b/src/scene_manager/loader_xmt.c @@ -30,6 +30,8 @@ #include #include +void gf_sm_update_bitwrapper_buffer(GF_Node *node, const char *fileName); + #ifndef GPAC_DISABLE_LOADER_XMT @@ -110,7 +112,7 @@ typedef struct u32 stream_id; Double au_time; Bool au_is_rap; - + Bool in_com; GF_List *script_to_load; } GF_XMTParser; @@ -137,7 +139,7 @@ typedef struct static GF_Err xmt_report(GF_XMTParser *parser, GF_Err e, char *format, ...) { #ifndef GPAC_DISABLE_LOG - if (gf_log_get_level() && (gf_log_get_tools() & GF_LOG_PARSER)) { + if (gf_log_tool_level_on(GF_LOG_PARSER, e ? GF_LOG_ERROR : GF_LOG_WARNING)) { char szMsg[2048]; va_list args; va_start(args, format); @@ -159,7 +161,7 @@ static Bool xmt_esid_available(GF_XMTParser *parser, u16 ESID) XMT_ESDLink *esdl; i=0; while ((esdl = (XMT_ESDLink *)gf_list_enum(parser->esd_links, &i))) { - if (esdl->esd->ESID == ESID) return 0; + if (esdl->ESID == ESID) return 0; } return 1; } @@ -170,7 +172,7 @@ static char *xmt_get_es_name(GF_XMTParser *parser, u16 ESID) XMT_ESDLink *esdl; i=0; while ((esdl = (XMT_ESDLink *)gf_list_enum(parser->esd_links, &i))) { - if (esdl->esd->ESID == ESID) return esdl->desc_name; + if (esdl->ESID == ESID) return esdl->desc_name; } return NULL; } @@ -904,6 +906,9 @@ static u32 xmt_parse_sf_field(GF_XMTParser *parser, GF_FieldInfo *info, GF_Node break; case GF_SG_VRML_SFSTRING: res = xmt_parse_string(parser, info->name, (SFString*)info->far_ptr, 0, a_value); + if (n && (n->sgprivate->tag==TAG_MPEG4_BitWrapper)) { + gf_sm_update_bitwrapper_buffer(n, parser->load->fileName); + } break; case GF_SG_VRML_SFSCRIPT: res = xmt_parse_script(parser, info->name, (SFScript *)info->far_ptr, 0, a_value); @@ -1400,7 +1405,7 @@ static GF_Node *xmt_parse_element(GF_XMTParser *parser, char *name, const char * { GF_Err e; GF_FieldInfo info; - u32 tag, i, count, ID; + u32 tag, i, ID; Bool register_def = 0; Bool is_script = 0; GF_Node *node; @@ -1642,7 +1647,6 @@ static GF_Node *xmt_parse_element(GF_XMTParser *parser, char *name, const char * ID = 0; register_def = 0; tag = 0; - count = 0; } } } else { @@ -1971,10 +1975,11 @@ static void xmt_parse_command(GF_XMTParser *parser, const char *name, const GF_X parser->state = XMT_STATE_ELEMENTS; return; } - - parser->stream_id = parser->load->force_es_id; + if (!parser->in_com) + parser->stream_id = parser->load->force_es_id; if (!strcmp(name, "par")) { + parser->in_com = 1; for (i=0; ivalue || !strlen(att->value)) continue; @@ -2416,7 +2421,7 @@ static void xmt_parse_command(GF_XMTParser *parser, const char *name, const GF_X else if (!strcmp(name, "IPMP_DescriptorRemove")) tag = GF_ODF_IPMP_REMOVE_TAG; stream = gf_sm_stream_find(parser->load->ctx, (u16) stream_id); - if (!stream || (stream->streamType!=GF_STREAM_OD)) stream_id = parser->base_od_id; + if (stream && (stream->streamType!=GF_STREAM_OD)) stream_id = parser->base_od_id; parser->od_es = gf_sm_stream_new(parser->load->ctx, (u16) stream_id, GF_STREAM_OD, 0); parser->od_au = gf_sm_stream_au_new(parser->od_es, 0, au_time, au_is_rap); parser->od_command = gf_odf_com_new(tag); @@ -2682,6 +2687,10 @@ static void xmt_node_end(void *sax_cbck, const char *name, const char *name_spac parser->od_command = NULL; } + else if (!strcmp(name, "par")) + parser->in_com = 1; + + } else if (parser->state == XMT_STATE_BODY_END) { /*end XMT-A*/ @@ -2964,9 +2973,15 @@ static GF_Err xmt_restore_context(GF_SceneLoader *load) switch (sc->streamType) { case GF_STREAM_SCENE: case GF_STREAM_PRIVATE_SCENE: - if (!parser->scene_es) parser->scene_es = sc; break; - case GF_STREAM_OD: if (!parser->od_es) parser->od_es = sc; break; - default: break; + if (!parser->scene_es) + parser->scene_es = sc; + break; + case GF_STREAM_OD: + if (!parser->od_es) + parser->od_es = sc; + break; + default: + break; } } /*scene creation - pick up a size*/ diff --git a/src/scene_manager/scene_dump.c b/src/scene_manager/scene_dump.c index 37a41d7..4f69376 100644 --- a/src/scene_manager/scene_dump.c +++ b/src/scene_manager/scene_dump.c @@ -29,6 +29,7 @@ #include #include #include +#include #ifndef __SYMBIAN32__ #include @@ -491,7 +492,7 @@ static void scene_dump_vrml_route_id(GF_SceneDumper *sdump, u32 routeID, char *r } -static void gf_dump_vrml_sffield(GF_SceneDumper *sdump, u32 type, void *ptr, Bool is_mf) +static void gf_dump_vrml_sffield(GF_SceneDumper *sdump, u32 type, void *ptr, Bool is_mf, GF_Node *node) { switch (type) { case GF_SG_VRML_SFBOOL: @@ -601,6 +602,22 @@ static void gf_dump_vrml_sffield(GF_SceneDumper *sdump, u32 type, void *ptr, Boo } /*dump in unicode*/ str = ((SFString *)ptr)->buffer; + + if (node && (gf_node_get_tag(node)==TAG_MPEG4_BitWrapper)) { + u32 bufsize = 50+ ((M_BitWrapper*)node)->buffer_len * 2; + str = gf_malloc(sizeof(char)* bufsize); + if (str) { + s32 res; + strcpy(str, "data:application/octet-string;base64,"); + res = gf_base64_encode(((M_BitWrapper*)node)->buffer.buffer, ((M_BitWrapper*)node)->buffer_len, str+37, bufsize-37); + if (res<0) { + gf_free(str); + str = NULL; + } else { + str[res+37] = 0; + } + } + } if (str && str[0]) { if (sdump->XMLDump) { scene_dump_utf_string(sdump, 1, str); @@ -614,6 +631,9 @@ static void gf_dump_vrml_sffield(GF_SceneDumper *sdump, u32 type, void *ptr, Boo } } } + if (node && (gf_node_get_tag(node)==TAG_MPEG4_BitWrapper)) { + if (str) gf_free(str); + } if (sdump->XMLDump) { if (is_mf) fprintf(sdump->trace, sdump->X3DDump ? "\"" : """); @@ -681,7 +701,7 @@ static void gf_dump_vrml_sffield(GF_SceneDumper *sdump, u32 type, void *ptr, Boo } -static void gf_dump_vrml_simple_field(GF_SceneDumper *sdump, GF_FieldInfo field) +static void gf_dump_vrml_simple_field(GF_SceneDumper *sdump, GF_FieldInfo field, GF_Node *parent) { GenMFField *mffield; u32 i, sf_type; @@ -710,7 +730,7 @@ static void gf_dump_vrml_simple_field(GF_SceneDumper *sdump, GF_FieldInfo field) } if (gf_sg_vrml_is_sf_field(field.fieldType)) { if (sdump->XMLDump) StartAttribute(sdump, "value"); - gf_dump_vrml_sffield(sdump, field.fieldType, field.far_ptr, 0); + gf_dump_vrml_sffield(sdump, field.fieldType, field.far_ptr, 0, parent); if (sdump->XMLDump) EndAttribute(sdump); } else { mffield = (GenMFField *) field.far_ptr; @@ -726,7 +746,7 @@ static void gf_dump_vrml_simple_field(GF_SceneDumper *sdump, GF_FieldInfo field) if (i) fprintf(sdump->trace, " "); gf_sg_vrml_mf_get_item(field.far_ptr, field.fieldType, &slot_ptr, i); /*this is to cope with single MFString which shall appear as SF in XMT*/ - gf_dump_vrml_sffield(sdump, sf_type, slot_ptr, 1); + gf_dump_vrml_sffield(sdump, sf_type, slot_ptr, 1, parent); } if (!sdump->XMLDump) { fprintf(sdump->trace, "]"); @@ -851,7 +871,7 @@ static void gf_dump_vrml_field(GF_SceneDumper *sdump, GF_Node *node, GF_FieldInf if (gf_sg_vrml_is_sf_field(field.fieldType)) { StartAttribute(sdump, field.name); - gf_dump_vrml_sffield(sdump, field.fieldType, field.far_ptr, 0); + gf_dump_vrml_sffield(sdump, field.fieldType, field.far_ptr, 0, node); EndAttribute(sdump); } else { mffield = (GenMFField *) field.far_ptr; @@ -876,7 +896,7 @@ static void gf_dump_vrml_field(GF_SceneDumper *sdump, GF_Node *node, GF_FieldInf for (i=0; icount; i++) { if (i) fprintf(sdump->trace, " "); gf_sg_vrml_mf_get_item(field.far_ptr, field.fieldType, &slot_ptr, i); - gf_dump_vrml_sffield(sdump, sf_type, slot_ptr, 1); + gf_dump_vrml_sffield(sdump, sf_type, slot_ptr, 1, node); } if (!sdump->XMLDump) fprintf(sdump->trace, "]"); @@ -1020,7 +1040,7 @@ static void gf_dump_vrml_dyn_field(GF_SceneDumper *sdump, GF_Node *node, GF_Fiel } else { fprintf(sdump->trace, " %s=\"", GetXMTFieldTypeValueName(field.fieldType)); } - gf_dump_vrml_sffield(sdump, field.fieldType, field.far_ptr, 0); + gf_dump_vrml_sffield(sdump, field.fieldType, field.far_ptr, 0, node); if (has_sublist) fprintf(sdump->trace, "\">\n"); else @@ -1036,7 +1056,7 @@ static void gf_dump_vrml_dyn_field(GF_SceneDumper *sdump, GF_Node *node, GF_Fiel if (field.fieldType == GF_SG_VRML_SFNODE) { gf_dump_vrml_node(sdump, field.far_ptr ? *(GF_Node **)field.far_ptr : NULL, 0, NULL); } else { - gf_dump_vrml_simple_field(sdump, field); + gf_dump_vrml_simple_field(sdump, field, node); } } fprintf(sdump->trace, "\n"); @@ -1066,7 +1086,7 @@ static void gf_dump_vrml_dyn_field(GF_SceneDumper *sdump, GF_Node *node, GF_Fiel if (i) fprintf(sdump->trace, " "); if (field.fieldType != GF_SG_VRML_MFNODE) { gf_sg_vrml_mf_get_item(field.far_ptr, field.fieldType, &slot_ptr, i); - gf_dump_vrml_sffield(sdump, sf_type, slot_ptr, (mffield->count>1) ? 1 : 0); + gf_dump_vrml_sffield(sdump, sf_type, slot_ptr, (mffield->count>1) ? 1 : 0, node); } } } @@ -1107,7 +1127,7 @@ static void gf_dump_vrml_dyn_field(GF_SceneDumper *sdump, GF_Node *node, GF_Fiel if (i) fprintf(sdump->trace, " "); if (field.fieldType != GF_SG_VRML_MFNODE) { gf_sg_vrml_mf_get_item(field.far_ptr, field.fieldType, &slot_ptr, i); - gf_dump_vrml_sffield(sdump, sf_type, slot_ptr, (mffield->count>1) ? 1 : 0); + gf_dump_vrml_sffield(sdump, sf_type, slot_ptr, (mffield->count>1) ? 1 : 0, node); } } if (has_sublist) @@ -1148,7 +1168,7 @@ static void gf_dump_vrml_proto_field(GF_SceneDumper *sdump, GF_Node *node, GF_Fi } else { fprintf(sdump->trace, " %s=\"", GetXMTFieldTypeValueName(field.fieldType)); } - gf_dump_vrml_sffield(sdump, field.fieldType, field.far_ptr, 0); + gf_dump_vrml_sffield(sdump, field.fieldType, field.far_ptr, 0, node); fprintf(sdump->trace, "\"/>\n"); } } else { @@ -1179,7 +1199,7 @@ static void gf_dump_vrml_proto_field(GF_SceneDumper *sdump, GF_Node *node, GF_Fi if (i) fprintf(sdump->trace, " "); if (field.fieldType != GF_SG_VRML_MFNODE) { gf_sg_vrml_mf_get_item(field.far_ptr, field.fieldType, &slot_ptr, i); - gf_dump_vrml_sffield(sdump, sf_type, slot_ptr, (mffield->count>1) ? 1 : 0); + gf_dump_vrml_sffield(sdump, sf_type, slot_ptr, (mffield->count>1) ? 1 : 0, node); } } fprintf(sdump->trace, "\"/>\n"); @@ -1630,7 +1650,7 @@ static GF_Err DumpMultipleIndexedReplace(GF_SceneDumper *sdump, GF_Command *com) } else { fprintf(sdump->trace, "%d BY ", inf->pos); } - gf_dump_vrml_simple_field(sdump, field); + gf_dump_vrml_simple_field(sdump, field, com->node); if (sdump->XMLDump) { fprintf(sdump->trace, "/>"); } else { @@ -1669,7 +1689,7 @@ static GF_Err DumpMultipleReplace(GF_SceneDumper *sdump, GF_Command *com) DUMP_IND(sdump); if (gf_sg_vrml_get_sf_type(info.fieldType) != GF_SG_VRML_SFNODE) { fprintf(sdump->trace, "node); fprintf(sdump->trace, "/>\n"); } else { fprintf(sdump->trace, ""); @@ -1834,7 +1854,7 @@ static GF_Err DumpIndexInsert(GF_SceneDumper *sdump, GF_Command *com) fprintf(sdump->trace, "\n"); } else { sffield.far_ptr = inf->field_ptr; - gf_dump_vrml_simple_field(sdump, sffield); + gf_dump_vrml_simple_field(sdump, sffield, com->node); if (sdump->XMLDump) fprintf(sdump->trace, "/>"); fprintf(sdump->trace, "\n"); } @@ -2006,7 +2026,7 @@ static GF_Err DumpFieldReplace(GF_SceneDumper *sdump, GF_Command *com) break; default: field.far_ptr = inf->field_ptr; - gf_dump_vrml_simple_field(sdump, field); + gf_dump_vrml_simple_field(sdump, field, com->node); if (sdump->XMLDump) fprintf(sdump->trace, "/>"); fprintf(sdump->trace, "\n"); } @@ -2060,7 +2080,7 @@ static GF_Err DumpIndexReplace(GF_SceneDumper *sdump, GF_Command *com) } else { field.fieldType = gf_sg_vrml_get_sf_type(field.fieldType); field.far_ptr = inf->field_ptr; - gf_dump_vrml_simple_field(sdump, field); + gf_dump_vrml_simple_field(sdump, field, com->node); fprintf(sdump->trace, sdump->XMLDump ? "/>\n" : "\n"); } return GF_OK; @@ -2189,7 +2209,7 @@ static GF_Err DumpXReplace(GF_SceneDumper *sdump, GF_Command *com) gf_dump_vrml_node(sdump, inf->new_node, 0, NULL); fprintf(sdump->trace, (sdump->XMLDump) ? "\n" : "\n"); } else { - gf_dump_vrml_simple_field(sdump, field); + gf_dump_vrml_simple_field(sdump, field, com->node); fprintf(sdump->trace, sdump->XMLDump ? "/>\n" : "\n"); } return GF_OK; @@ -2297,7 +2317,7 @@ static GF_Err DumpProtos(GF_SceneDumper *sdump, GF_List *protoList) fprintf(sdump->trace, "Name, proto->ID); if (proto->ExternProto.count) { fprintf(sdump->trace, " locations=\""); - gf_dump_vrml_sffield(sdump, GF_SG_VRML_SFURL, &proto->ExternProto.vals[0], 0); + gf_dump_vrml_sffield(sdump, GF_SG_VRML_SFURL, &proto->ExternProto.vals[0], 0, NULL); fprintf(sdump->trace, "\""); } fprintf(sdump->trace, ">\n"); @@ -2369,7 +2389,7 @@ static GF_Err DumpProtos(GF_SceneDumper *sdump, GF_List *protoList) if (proto->ExternProto.count) { if (!sdump->XMLDump) { fprintf(sdump->trace, " \""); - gf_dump_vrml_sffield(sdump, GF_SG_VRML_SFURL, &proto->ExternProto.vals[0], 0); + gf_dump_vrml_sffield(sdump, GF_SG_VRML_SFURL, &proto->ExternProto.vals[0], 0, NULL); fprintf(sdump->trace, "\"\n\n"); } else { fprintf(sdump->trace, "\n"); @@ -2710,6 +2730,10 @@ GF_Err gf_sm_dump_command_list(GF_SceneDumper *sdump, GF_List *comList, u32 inde EndElement(sdump, "Scene", 1); sdump->indent--; EndElement(sdump, "Replace", 1); + } else { + DUMP_IND(sdump); + fprintf(sdump->trace, "\nAT 0 {\n"); + sdump->indent++; } } #endif @@ -2813,6 +2837,7 @@ GF_Err gf_sm_dump_command_list(GF_SceneDumper *sdump, GF_List *comList, u32 inde } if (remain && !sdump->XMLDump) { + sdump->indent--; DUMP_IND(sdump); fprintf(sdump->trace, "}\n"); } @@ -2836,7 +2861,6 @@ void gf_dump_svg_element(GF_SceneDumper *sdump, GF_Node *n, GF_Node *parent, Boo GF_ChildNodeItem *list; char attName[100], *attValue, attID[100]; u32 i, count, nID; - Bool needs_cr; SVG_Element *svg = (SVG_Element *)n; GF_FieldInfo info; SVGAttribute *att; @@ -2994,7 +3018,6 @@ void gf_dump_svg_element(GF_SceneDumper *sdump, GF_Node *n, GF_Node *parent, Boo } if (tag==TAG_SVG_text || tag==TAG_SVG_textArea) sdump->in_text = 1; - needs_cr = 1; sdump->indent++; list = svg->children; while (list) { @@ -3281,6 +3304,7 @@ GF_Err gf_sm_dump(GF_SceneManager *ctx, char *rad_name, u32 dump_mode) } if (dumper->dump_mode==GF_SM_DUMP_SVG) break; } + first_bifs = (num_scene==1) ? 1 : 0; num_scene = (num_scene>1) ? 1 : 0; num_od = (num_od>1) ? 1 : 0; @@ -3307,7 +3331,6 @@ GF_Err gf_sm_dump(GF_SceneManager *ctx, char *rad_name, u32 dump_mode) time = dumper->LSRDump ? -1 : 0; first_par = 0; - first_bifs = 1; while (gf_list_count(sample_list)) { GF_AUContext *au = (GF_AUContext*)gf_list_get(sample_list, 0); @@ -3361,7 +3384,7 @@ GF_Err gf_sm_dump(GF_SceneManager *ctx, char *rad_name, u32 dump_mode) } else { fprintf(dumper->trace, " \n"); } - fprintf(dumper->trace, " \n", au->timing_sec, au->owner->ESID); + fprintf(dumper->trace, " \n", au->timing_sec, au->owner->ESID, (au->flags & GF_SM_AU_RAP) ? "yes" : "no"); } else if (au->timing_sec>time) { if (!first_par) { first_par = 1; diff --git a/src/scene_manager/scene_engine.c b/src/scene_manager/scene_engine.c index 30b4d23..3147182 100644 --- a/src/scene_manager/scene_engine.c +++ b/src/scene_manager/scene_engine.c @@ -72,7 +72,6 @@ struct __tag_scene_engine #ifndef GPAC_DISABLE_BIFS_ENC static GF_Err gf_sm_setup_bifsenc(GF_SceneEngine *seng, GF_StreamContext *sc, GF_ESD *esd) { - GF_Err e; char *data; u32 data_len; u32 nbb; @@ -81,7 +80,6 @@ static GF_Err gf_sm_setup_bifsenc(GF_SceneEngine *seng, GF_StreamContext *sc, GF if (!esd->decoderConfig || (esd->decoderConfig->streamType != GF_STREAM_SCENE)) return GF_BAD_PARAM; - e = GF_OK; if (!seng->bifsenc) seng->bifsenc = gf_bifs_encoder_new(seng->ctx->scene_graph); @@ -149,17 +147,14 @@ static GF_Err gf_sm_setup_bifsenc(GF_SceneEngine *seng, GF_StreamContext *sc, GF #ifndef GPAC_DISABLE_LASER static GF_Err gf_sm_setup_lsrenc(GF_SceneEngine *seng, GF_StreamContext *sc, GF_ESD *esd) { - GF_Err e; char *data; u32 data_len; GF_LASERConfig lsr_cfg; if (!esd->decoderConfig || (esd->decoderConfig->streamType != GF_STREAM_SCENE)) return GF_BAD_PARAM; - e = GF_OK; seng->lsrenc = gf_laser_encoder_new(seng->ctx->scene_graph); - /*inputctx is not properly setup, do it*/ if (!esd->decoderConfig->decoderSpecificInfo) { memset(&lsr_cfg, 0, sizeof(GF_LASERConfig)); @@ -195,7 +190,7 @@ static GF_Err gf_sm_live_setup(GF_SceneEngine *seng) GF_StreamContext *sc; GF_InitialObjectDescriptor *iod; GF_ESD *esd; - u32 i, j, count; + u32 i, j; e = GF_OK; @@ -225,7 +220,6 @@ static GF_Err gf_sm_live_setup(GF_SceneEngine *seng) } } - count = gf_list_count(seng->ctx->streams); i=0; while ((sc = (GF_StreamContext*)gf_list_enum(seng->ctx->streams, &i))) { @@ -302,23 +296,22 @@ static GF_Err gf_seng_encode_dims_au(GF_SceneEngine *seng, u16 ESID, GF_List *co u64 fsize; char *buffer = NULL; GF_BitStream *bs = NULL; - u32 offset; u8 dims_header; - Bool compress_dims; + Bool compress_dims; #ifdef DUMP_DIMS_LOG_WITH_TIME - u32 do_dump_with_time = 1; + u32 do_dump_with_time = 1; #endif u32 buffer_len; - char *cache_dir, *dump_name; + char *cache_dir, *dump_name; - if (!data) return GF_BAD_PARAM; + if (!data) return GF_BAD_PARAM; e = GF_OK; - if (!seng->dump_path) cache_dir = "C:\\Windows\\Temp"; - else cache_dir = seng->dump_path; + if (!seng->dump_path) cache_dir = gf_get_default_cache_directory(); + else cache_dir = seng->dump_path; - dump_name = "gpac_scene_engine_dump"; + dump_name = "gpac_scene_engine_dump"; compress_dims = 1; #ifdef DUMP_DIMS_LOG_WITH_TIME @@ -341,6 +334,7 @@ start: sprintf(rad_name, "%s%c%s-%s-%s%s", cache_dir, GF_PATH_SEPARATOR, date_str, time_str, "rap_", dump_name); #endif } + dumper = gf_sm_dumper_new(seng->ctx->scene_graph, rad_name, ' ', GF_SM_DUMP_SVG); if (!dumper) { GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[SceneEngine] Cannot create SVG dumper for %s.svg\n", rad_name)); @@ -436,23 +430,22 @@ start: GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[SceneEngine] Warning: DIMS Unit size too big !!!\n")); gf_bs_write_u16(bs, 0); /* internal GPAC hack to indicate that the size is larger than 65535 */ gf_bs_write_u32(bs, buffer_len+1); - offset = 6; } else { gf_bs_write_u16(bs, buffer_len+1); - offset = 2; } gf_bs_write_u8(bs, dims_header); gf_bs_write_data(bs, buffer, buffer_len); gf_free(buffer); - buffer = NULL; + buffer = NULL; - gf_bs_get_content(bs, data, size); + gf_bs_get_content(bs, data, size); gf_bs_del(bs); exit: - if (buffer) gf_free(buffer); - if (file) fclose(file); + if (!seng->dump_path) gf_free(cache_dir); + if (buffer) gf_free(buffer); + if (file) fclose(file); return e; } @@ -642,10 +635,9 @@ GF_EXPORT GF_Err gf_seng_encode_from_string(GF_SceneEngine *seng, u16 ESID, Bool disable_aggregation, char *auString, gf_seng_callback callback) { GF_StreamContext *sc; - u32 i, count; + u32 i; GF_Err e; - count = gf_list_count(seng->ctx->streams); i = 0; while ((sc = (GF_StreamContext*)gf_list_enum(seng->ctx->streams, &i))) { sc->current_au_count = gf_list_count(sc->AUs); @@ -654,11 +646,11 @@ GF_Err gf_seng_encode_from_string(GF_SceneEngine *seng, u16 ESID, Bool disable_a seng->loader.flags |= GF_SM_LOAD_CONTEXT_READY; seng->loader.force_es_id = ESID; - /* We need to create an empty AU for the parser to correctly parse a LASeR Command without SceneUnit */ - sc = gf_list_get(seng->ctx->streams, 0); - if (sc->objectType == GPAC_OTI_SCENE_DIMS) { - gf_seng_create_new_au(sc, 0); - } + /* We need to create an empty AU for the parser to correctly parse a LASeR Command without SceneUnit */ + sc = gf_list_get(seng->ctx->streams, 0); + if (sc->objectType == GPAC_OTI_SCENE_DIMS) { + gf_seng_create_new_au(sc, 0); + } e = gf_sm_load_string(&seng->loader, auString, 0); if (e) goto exit; @@ -753,7 +745,7 @@ GF_Err gf_seng_encode_from_file(GF_SceneEngine *seng, u16 ESID, Bool disable_agg { GF_Err e; GF_StreamContext *sc; - u32 i, count; + u32 i; Bool dims = 0; seng->loader.fileName = auFile; @@ -761,18 +753,17 @@ GF_Err gf_seng_encode_from_file(GF_SceneEngine *seng, u16 ESID, Bool disable_agg seng->loader.force_es_id = ESID; sc = NULL; - count = gf_list_count(seng->ctx->streams); i=0; while ((sc = (GF_StreamContext*)gf_list_enum(seng->ctx->streams, &i))) { sc->current_au_count = gf_list_count(sc->AUs); sc->disable_aggregation = disable_aggregation; } - /* We need to create an empty AU for the parser to correctly parse a LASeR Command without SceneUnit */ - sc = gf_list_get(seng->ctx->streams, 0); - if (sc->objectType == GPAC_OTI_SCENE_DIMS) { + /* We need to create an empty AU for the parser to correctly parse a LASeR Command without SceneUnit */ + sc = gf_list_get(seng->ctx->streams, 0); + if (sc->objectType == GPAC_OTI_SCENE_DIMS) { dims = 1; - gf_seng_create_new_au(sc, 0); - } + gf_seng_create_new_au(sc, 0); + } seng->loader.flags |= GF_SM_LOAD_CONTEXT_READY; if (dims) { diff --git a/src/scene_manager/scene_manager.c b/src/scene_manager/scene_manager.c index 2f041e0..9cc2752 100644 --- a/src/scene_manager/scene_manager.c +++ b/src/scene_manager/scene_manager.c @@ -388,7 +388,6 @@ GF_Err gf_sm_aggregate(GF_SceneManager *ctx, u16 ESID) for (i=0; istreams, i); if (ESID && (sc->ESID!=ESID)) continue; @@ -396,11 +395,9 @@ GF_Err gf_sm_aggregate(GF_SceneManager *ctx, u16 ESID) /*locate the AU in which our commands will be aggregated*/ carousel_au = NULL; carousel_commands = NULL; - self_carousel = 0; aggregate_on_stream = sc->aggregate_on_esid ? gf_sm_get_stream(ctx, sc->aggregate_on_esid) : NULL; if (aggregate_on_stream==sc) { carousel_commands = gf_list_new(); - self_carousel = 1; } else if (aggregate_on_stream) { if (!gf_list_count(aggregate_on_stream->AUs)) { carousel_au = gf_sm_stream_au_new(aggregate_on_stream, 0, 0, 1); @@ -415,17 +412,14 @@ GF_Err gf_sm_aggregate(GF_SceneManager *ctx, u16 ESID) #ifndef GPAC_DISABLE_VRML if (sc->streamType == GF_STREAM_SCENE) { Bool has_modif = 0; - Bool first_au=1; /*we check for each stream if it is a base stream (SceneReplace ...) - several streams may carry RAPs if inline nodes are used*/ Bool base_stream_found = 0; - u32 first_au_com_count = 0; /*in DIMS we use an empty initial AU with no commands to signal the RAP*/ if (sc->objectType == GPAC_OTI_SCENE_DIMS) base_stream_found = 1; /*apply all commands - this will also apply the SceneReplace*/ while (gf_list_count(sc->AUs)) { - Bool first_com=1; u32 count; au = (GF_AUContext *) gf_list_get(sc->AUs, 0); gf_list_rem(sc->AUs, 0); @@ -433,12 +427,10 @@ GF_Err gf_sm_aggregate(GF_SceneManager *ctx, u16 ESID) /*AU not aggregated*/ if (au->flags & GF_SM_AU_NOT_AGGREGATED) { gf_sm_au_del(sc, au); - first_au=0; continue; } count = gf_list_count(au->commands); - if (first_au && (au->flags & GF_SM_AU_CAROUSEL) ) first_au_com_count = count; for (j=0; jscene_graph, com, 0); break; } - first_com=0; } gf_sm_au_del(sc, au); - first_au=0; } /*and recreate scene replace*/ diff --git a/src/scene_manager/swf_bifs.c b/src/scene_manager/swf_bifs.c index a939f42..da983b8 100644 --- a/src/scene_manager/swf_bifs.c +++ b/src/scene_manager/swf_bifs.c @@ -1994,8 +1994,6 @@ Bool swf_bifs_action(SWFReader *read, SWFAction *act) GF_List *dst; MFURL url; SFURL sfurl; - MFString str; - SFString sfstr; Bool bval; GF_Node *n; Double time; @@ -2026,11 +2024,7 @@ Bool swf_bifs_action(SWFReader *read, SWFAction *act) sfurl.OD_ID = 0; sfurl.url = act->url; url.count = 1; url.vals = &sfurl; s2b_set_field(read, dst, n, "url", -1, GF_SG_VRML_MFURL, &url, 0); - - sfstr.buffer = act->target; - str.count = 1; str.vals = &act->target; s2b_set_field(read, dst, n, "parameter", -1, GF_SG_VRML_MFSTRING, &url, 0); - bval = 1; s2b_set_field(read, dst, n, "activate", -1, GF_SG_VRML_SFBOOL, &bval, 0); break; diff --git a/src/scene_manager/swf_parse.c b/src/scene_manager/swf_parse.c index ce9e972..8bebbb7 100644 --- a/src/scene_manager/swf_parse.c +++ b/src/scene_manager/swf_parse.c @@ -1228,7 +1228,7 @@ static GF_Err swf_place_obj(SWFReader *read, u32 revision) { GF_Err e; u32 shape_id; - u32 ID, bitsize, ratio; + u32 ID, bitsize; u32 clip_depth; GF_Matrix2D mat; GF_ColorMatrix cmat; @@ -1289,7 +1289,7 @@ static GF_Err swf_place_obj(SWFReader *read, u32 revision) swf_get_colormatrix(read, &cmat); swf_align(read); } - if (has_ratio) ratio = swf_get_16(read); + if (has_ratio) /*ratio = */swf_get_16(read); if (has_clip) clip_depth = swf_get_16(read); if (has_name) { @@ -1816,7 +1816,7 @@ static GF_Err swf_def_sound(SWFReader *read) u32 toread = read->size - tot_size; if (toread>alloc_size) toread = alloc_size; swf_read_data(read, frame, toread); - fwrite(frame, sizeof(char)*toread, 1, snd->output); + gf_fwrite(frame, sizeof(char)*toread, 1, snd->output); tot_size += toread; } @@ -1900,8 +1900,6 @@ static GF_Err swf_start_sound(SWFReader *read) static GF_Err swf_soundstream_hdr(SWFReader *read) { char szName[1024]; - u8 rec_mix; - u32 samplesperframe; SWFSound *snd; if (read->sound_stream) { @@ -1911,7 +1909,7 @@ static GF_Err swf_soundstream_hdr(SWFReader *read) GF_SAFEALLOC(snd, SWFSound); - rec_mix = swf_read_int(read, 8); + /*rec_mix = */swf_read_int(read, 8); /*0: uncompressed, 1: ADPCM, 2: MP3*/ snd->format = swf_read_int(read, 4); /*0: 5.5k, 1: 11k, 2: 2: 22k, 3: 44k*/ @@ -1921,7 +1919,7 @@ static GF_Err swf_soundstream_hdr(SWFReader *read) /*0: mono, 8 1: stereo*/ snd->stereo = swf_read_int(read, 1); /*samplesperframe hint*/ - samplesperframe = swf_read_int(read, 16); + swf_read_int(read, 16); switch (snd->format) { /*raw PCM*/ @@ -1959,14 +1957,14 @@ static GF_Err swf_soundstream_block(SWFReader *read) return swf_func_skip(read); #else unsigned char bytes[4]; - u32 hdr, alloc_size, size, tot_size, samplesPerFrame, delay; + u32 hdr, alloc_size, size, tot_size, samplesPerFrame; char *frame; /*note we're doing only MP3*/ if (!read->sound_stream) return swf_func_skip(read); samplesPerFrame = swf_get_16(read); - delay = swf_get_16(read); + /*delay = */swf_get_16(read); if (!read->sound_stream->is_setup) { @@ -2004,8 +2002,8 @@ static GF_Err swf_soundstream_block(SWFReader *read) if (tot_size + size >= read->size) size = read->size - tot_size; swf_read_data(read, frame, size-4); - fwrite(bytes, sizeof(char)*4, 1, read->sound_stream->output); - fwrite(frame, sizeof(char)*(size-4), 1, read->sound_stream->output); + gf_fwrite(bytes, sizeof(char)*4, 1, read->sound_stream->output); + gf_fwrite(frame, sizeof(char)*(size-4), 1, read->sound_stream->output); if (tot_size + size >= read->size) break; tot_size += size; } @@ -2061,7 +2059,7 @@ static GF_Err swf_def_bits_jpeg(SWFReader *read, u32 version) if (version==1 && read->jpeg_hdr_size) { /*remove JPEG EOI*/ - fwrite(read->jpeg_hdr, 1, read->jpeg_hdr_size-2, file); + gf_fwrite(read->jpeg_hdr, 1, read->jpeg_hdr_size-2, file); /*remove JPEG SOI*/ swf_get_16(read); size-=2; @@ -2069,7 +2067,7 @@ static GF_Err swf_def_bits_jpeg(SWFReader *read, u32 version) buf = gf_malloc(sizeof(u8)*size); swf_read_data(read, buf, size); if (version==1) { - fwrite(buf, 1, size, file); + gf_fwrite(buf, 1, size, file); } else { u32 i; for (i=0; isetup_image(read, ID, szName); } @@ -2319,7 +2317,7 @@ GF_Err swf_parse_sprite(SWFReader *read) void swf_report(SWFReader *read, GF_Err e, char *format, ...) { #ifndef GPAC_DISABLE_LOG - if (gf_log_get_level() && (gf_log_get_tools() & GF_LOG_PARSER)) { + if (gf_log_tool_level_on(GF_LOG_PARSER, e ? GF_LOG_ERROR : GF_LOG_WARNING)) { char szMsg[2048]; va_list args; va_start(args, format); @@ -2416,7 +2414,6 @@ GF_Err gf_sm_load_init_swf(GF_SceneLoader *load) GF_Err e; FILE *input; u8 sig[3]; - u8 version; if (!load->ctx || !load->scene_graph || !load->fileName) return GF_BAD_PARAM; input = gf_f64_open(load->fileName, "rb"); @@ -2460,7 +2457,7 @@ GF_Err gf_sm_load_init_swf(GF_SceneLoader *load) e = GF_URL_ERROR; goto exit; } - version = gf_bs_read_u8(read->bs); + /*version = */gf_bs_read_u8(read->bs); read->length = swf_get_32(read); /*if compressed decompress the whole file*/ diff --git a/src/scene_manager/text_to_bifs.c b/src/scene_manager/text_to_bifs.c index 365ef1d..1e70c4e 100644 --- a/src/scene_manager/text_to_bifs.c +++ b/src/scene_manager/text_to_bifs.c @@ -350,7 +350,6 @@ static GF_Err gf_text_import_sub_bifs(GF_SceneManager *ctx, GF_ESD *src, GF_MuxI SFString *sfstr; GF_CommandField *inf; Bool first_samp; - Double fps; char szLine[2048], szTime[30], szText[2048]; GF_StreamContext *sc = NULL; @@ -416,9 +415,6 @@ static GF_Err gf_text_import_sub_bifs(GF_SceneManager *ctx, GF_ESD *src, GF_MuxI com = NULL; inf = NULL; - fps = GF_IMPORT_DEFAULT_FPS; - if (mux->frame_rate) fps = mux->frame_rate; - line = 0; first_samp = 1; while (1) { diff --git a/src/scenegraph/dom_events.c b/src/scenegraph/dom_events.c index 6205318..a435a53 100644 --- a/src/scenegraph/dom_events.c +++ b/src/scenegraph/dom_events.c @@ -48,7 +48,7 @@ static void gf_dom_refresh_event_filter(GF_SceneGraph *sg) if (sg->nb_evts_smil) sg->dom_evt_filter |= GF_DOM_EVENT_SMIL; if (sg->nb_evts_laser) sg->dom_evt_filter |= GF_DOM_EVENT_LASER; if (sg->nb_evts_svg) sg->dom_evt_filter |= GF_DOM_EVENT_SVG; - if (sg->nb_evts_mae) sg->dom_evt_filter |= GF_DOM_EVENT_MEDIA_ACCESS; + if (sg->nb_evts_media) sg->dom_evt_filter |= GF_DOM_EVENT_MEDIA; /*for each graph until top, update event filter*/ par = sg->parent_scene; @@ -71,7 +71,7 @@ void gf_sg_unregister_event_type(GF_SceneGraph *sg, u32 type) if (sg->nb_evts_laser && (type & GF_DOM_EVENT_LASER)) sg->nb_evts_laser--; if (sg->nb_evts_text && (type & GF_DOM_EVENT_TEXT)) sg->nb_evts_text--; if (sg->nb_evts_svg && (type & GF_DOM_EVENT_SVG)) sg->nb_evts_svg--; - if (sg->nb_evts_mae && (type & GF_DOM_EVENT_MEDIA_ACCESS)) sg->nb_evts_mae--; + if (sg->nb_evts_media && (type & GF_DOM_EVENT_MEDIA)) sg->nb_evts_media--; gf_dom_refresh_event_filter(sg); } @@ -88,7 +88,7 @@ void gf_sg_register_event_type(GF_SceneGraph *sg, u32 type) if (type & GF_DOM_EVENT_SMIL) sg->nb_evts_smil++; if (type & GF_DOM_EVENT_LASER) sg->nb_evts_laser++; if (type & GF_DOM_EVENT_SVG) sg->nb_evts_svg++; - if (type & GF_DOM_EVENT_MEDIA_ACCESS) sg->nb_evts_mae++; + if (type & GF_DOM_EVENT_MEDIA) sg->nb_evts_media++; gf_dom_refresh_event_filter(sg); } @@ -526,12 +526,10 @@ Bool gf_dom_event_fire(GF_Node *node, GF_DOM_Event *event) GF_DOMHandler *gf_dom_listener_build_ex(GF_Node *node, u32 event_type, u32 event_parameter, GF_Node *handler, GF_Node **out_listener) { - u32 tag; SVG_Element *listener; GF_FieldInfo info; GF_ChildNodeItem *last = NULL; - tag = gf_node_get_tag(node); listener = (SVG_Element *) gf_node_new(node->sgprivate->scenegraph, TAG_SVG_listener); /*don't register the listener, this will be done when adding to the node events list*/ diff --git a/src/scenegraph/dom_smjs.c b/src/scenegraph/dom_smjs.c index 7204ff2..b0ee905 100644 --- a/src/scenegraph/dom_smjs.c +++ b/src/scenegraph/dom_smjs.c @@ -1306,7 +1306,6 @@ static JSBool dom_node_setProperty(JSContext *c, JSObject *obj, SMJS_PROP_SETTER { u32 tag; GF_Node *n; - GF_ParentNode *par; n = dom_get_node(c, obj); /*note an element - we don't support property setting on document yet*/ @@ -1315,7 +1314,6 @@ static JSBool dom_node_setProperty(JSContext *c, JSObject *obj, SMJS_PROP_SETTER if (!SMJS_ID_IS_INT(id)) return JS_TRUE; tag = n ? gf_node_get_tag(n) : 0; - par = (GF_ParentNode*)n; switch (SMJS_ID_TO_INT(id)) { /*"nodeValue"*/ @@ -1799,10 +1797,154 @@ exit: return JS_TRUE; } +static void gf_dom_add_handler_listener(GF_Node *n, u32 evtType, char *handlerCode) +{ + /*check if we're modifying an existing listener*/ + SVG_handlerElement *handler; + u32 i, count = gf_dom_listener_count(n); + for (i=0;itype != evtType)) continue; + + /* found a listener for this event, override the handler + TODO: FIX this, there may be a listener/handler already set with JS, why overriding ? */ + gf_node_get_attribute_by_tag(listen, TAG_XMLEV_ATT_handler, 0, 0, &info); + assert(info.far_ptr); + handler = (SVG_handlerElement *) ((XMLRI*)info.far_ptr)->target; + text = (GF_DOMText*)handler->children->node; + if (text->sgprivate->tag==TAG_DOMText) { + if (text->textContent) gf_free(text->textContent); + text->textContent = gf_strdup(handlerCode); + } + return; + } + /*nope, create a listener*/ + handler = gf_dom_listener_build(n, evtType, 0); + gf_dom_add_text_node((GF_Node*)handler, gf_strdup(handlerCode)); + return; +} + +static void gf_dom_full_set_attribute(GF_DOMFullNode *node, char *attribute_name, char *attribute_content) +{ + GF_DOMFullAttribute *prev = NULL; + GF_DOMFullAttribute *att = (GF_DOMFullAttribute*)node->attributes; + while (att) { + if ((att->tag==TAG_DOM_ATT_any) && !strcmp(att->name, attribute_name)) { + if (att->data) gf_free(att->data); + att->data = gf_strdup(attribute_content); + dom_node_changed((GF_Node *)node, 0, NULL); + return; + } + prev = att; + att = (GF_DOMFullAttribute *) att->next; + } + /*create new att*/ + GF_SAFEALLOC(att, GF_DOMFullAttribute); + att->name = gf_strdup(attribute_name); + att->data = gf_strdup(attribute_content); + if (prev) prev->next = (GF_DOMAttribute*) att; + else node->attributes = (GF_DOMAttribute*) att; + return; +} + +static void gf_svg_set_attribute(GF_Node *n, char * ns, char *name, char *val) +{ + GF_FieldInfo info; + u32 anim_value_type = 0; + u32 ns_code = 0; + if (ns) { + ns_code = gf_sg_get_namespace_code_from_name(n->sgprivate->scenegraph, ns); + } else { + ns_code = gf_xml_get_element_namespace(n); + } + + if (!strcmp(name, "attributeName")) { + if (gf_node_get_attribute_by_tag(n, TAG_SVG_ATT_attributeName, 0, 0, &info) == GF_OK) { + SMIL_AttributeName *attname = (SMIL_AttributeName *)info.far_ptr; + + /*parse the attribute name even if the target is not found, because a namespace could be specified and + only valid for the current node*/ + if (!attname->type) { + char *sep; + char *name = attname->name; + sep = strchr(name, ':'); + if (sep) { + sep[0] = 0; + attname->type = gf_sg_get_namespace_code(n->sgprivate->scenegraph, name); + sep[0] = ':'; + name = gf_strdup(sep+1); + gf_free(attname->name); + attname->name = name; + } + } + } + } + + if ((n->sgprivate->tag == TAG_SVG_animateTransform) && (strstr(name, "from") || strstr(name, "to")) ) { + if (gf_node_get_attribute_by_tag((GF_Node *)n, TAG_SVG_ATT_transform_type, 1, 0, &info) != GF_OK) { + GF_LOG(GF_LOG_WARNING, GF_LOG_SCRIPT, ("Cannot retrieve attribute 'type' from animateTransform\n")); + return; + } + + switch(*(SVG_TransformType *) info.far_ptr) { + case SVG_TRANSFORM_TRANSLATE: anim_value_type = SVG_Transform_Translate_datatype; break; + case SVG_TRANSFORM_SCALE: anim_value_type = SVG_Transform_Scale_datatype; break; + case SVG_TRANSFORM_ROTATE: anim_value_type = SVG_Transform_Rotate_datatype; break; + case SVG_TRANSFORM_SKEWX: anim_value_type = SVG_Transform_SkewX_datatype; break; + case SVG_TRANSFORM_SKEWY: anim_value_type = SVG_Transform_SkewY_datatype; break; + case SVG_TRANSFORM_MATRIX: anim_value_type = SVG_Transform_datatype; break; + default: + return; + } + } + + if (gf_node_get_attribute_by_name(n, name, ns_code, 1, 1, &info)==GF_OK) { + GF_Err e; + if (!strcmp(name, "from") || !strcmp(name, "to") || !strcmp(name, "values") ) { + GF_FieldInfo attType; + SMIL_AttributeName *attname; + if (gf_node_get_attribute_by_tag((GF_Node *)n, TAG_SVG_ATT_attributeName, 0, 0, &attType) != GF_OK) { + GF_LOG(GF_LOG_WARNING, GF_LOG_SCRIPT, ("Cannot retrieve attribute 'attributeName'\n")); + return; + } + + attname = (SMIL_AttributeName *)attType.far_ptr; + if (!attname->type && attname->name) { + GF_Node *anim_target = gf_smil_anim_get_target(n); + if (anim_target) { + gf_node_get_attribute_by_name((GF_Node *)anim_target, attname->name, attname->type, 0, 0, &attType); + attname->type = attType.fieldType; + } else { + GF_LOG(GF_LOG_ERROR, GF_LOG_SCRIPT, ("[DOM] Cannot find attribute 'type' on <%s> element and cannot find target of the animation to parse attribute %s\n", gf_node_get_class_name(n), attname->name)); + } + } + + anim_value_type = attname->type; + } + e = gf_svg_parse_attribute(n, &info, val, anim_value_type); + if (e != GF_OK) { + GF_LOG(GF_LOG_ERROR, GF_LOG_SCRIPT, ("[DOM] Error parsing attribute\n")); + } + + if (info.fieldType==SVG_ID_datatype) { + char *idname = *(SVG_String*)info.far_ptr; + gf_svg_parse_element_id(n, idname, 0); + } + if (info.fieldType==XMLRI_datatype) { + gf_node_dirty_set(n, GF_SG_SVG_XLINK_HREF_DIRTY, 0); + } + dom_node_changed(n, 0, &info); + return; + } +} static JSBool SMJS_FUNCTION(xml_element_set_attribute) { - u32 evtType, idx; + u32 idx; char *name, *val, *ns, *_val; char szVal[100]; SMJS_OBJ @@ -1811,14 +1953,16 @@ static JSBool SMJS_FUNCTION(xml_element_set_attribute) if (!n) return JS_TRUE; if ((argc < 2)) return JS_TRUE; - if (!JSVAL_CHECK_STRING(argv[0])) return JS_TRUE; + if (!JSVAL_CHECK_STRING(argv[0])) + return JS_TRUE; idx = 1; _val = name = ns = NULL; /*NS version*/ if (argc==3) { char *sep; - if (!JSVAL_CHECK_STRING(argv[1])) return JS_TRUE; + if (!JSVAL_CHECK_STRING(argv[1])) + return JS_TRUE; ns = js_get_utf8(c, argv[0]); gf_sg_add_namespace(n->sgprivate->scenegraph, ns, NULL); name = SMJS_CHARS(c, argv[1]); @@ -1848,59 +1992,21 @@ static JSBool SMJS_FUNCTION(xml_element_set_attribute) } else { goto exit; } - if (!name || !val) goto exit; + if (!name || !val) + goto exit; + /* For on* attribute (e.g. onclick), we create a couple listener/handler elements on setting the attribute */ if ((name[0]=='o') && (name[1]=='n')) { - evtType = gf_dom_event_type_by_name(name + 2); + u32 evtType = gf_dom_event_type_by_name(name + 2); if (evtType != GF_EVENT_UNKNOWN) { - /*check if we're modifying an existing listener*/ - SVG_handlerElement *handler; - u32 i, count = gf_dom_listener_count(n); - for (i=0;itype != evtType)) continue; - gf_node_get_attribute_by_tag(listen, TAG_XMLEV_ATT_handler, 0, 0, &info); - assert(info.far_ptr); - handler = (SVG_handlerElement *) ((XMLRI*)info.far_ptr)->target; - text = (GF_DOMText*)handler->children->node; - if (text->sgprivate->tag==TAG_DOMText) { - if (text->textContent) gf_free(text->textContent); - text->textContent = gf_strdup(val); - } - goto exit; - } - /*nope, create a listener*/ - handler = gf_dom_listener_build(n, evtType, 0); - gf_dom_add_text_node((GF_Node*)handler, gf_strdup(val) ); + gf_dom_add_handler_listener(n, evtType, val); goto exit; } } if (n->sgprivate->tag==TAG_DOMFullNode) { - GF_DOMFullAttribute *prev = NULL; - GF_DOMFullNode *node = (GF_DOMFullNode*)n; - GF_DOMFullAttribute *att = (GF_DOMFullAttribute*)node->attributes; - while (att) { - if ((att->tag==TAG_DOM_ATT_any) && !strcmp(att->name, name)) { - if (att->data) gf_free(att->data); - att->data = gf_strdup(val); - dom_node_changed(n, 0, NULL); - goto exit; - } - prev = att; - att = (GF_DOMFullAttribute *) att->next; - } - /*create new att*/ - GF_SAFEALLOC(att, GF_DOMFullAttribute); - att->name = gf_strdup(name); - att->data = gf_strdup(val); - if (prev) prev->next = (GF_DOMAttribute*) att; - else node->attributes = (GF_DOMAttribute*) att; + gf_dom_full_set_attribute((GF_DOMFullNode*)n, name, val); goto exit; } @@ -1909,80 +2015,7 @@ static JSBool SMJS_FUNCTION(xml_element_set_attribute) } if (n->sgprivate->tag<=GF_NODE_RANGE_LAST_SVG) { - GF_FieldInfo info; - u32 anim_value_type = 0; - u32 ns_code = 0; - if (ns) { - ns_code = gf_sg_get_namespace_code_from_name(n->sgprivate->scenegraph, ns); - } else { - ns_code = gf_xml_get_element_namespace(n); - } - if (!strcmp(name, "attributeName")) { - if (gf_node_get_attribute_by_tag(n, TAG_SVG_ATT_attributeName, 0, 0, &info) == GF_OK) { - SMIL_AttributeName *attname = (SMIL_AttributeName *)info.far_ptr; - - /*parse the attribute name even if the target is not found, because a namespace could be specified and - only valid for the current node*/ - if (!attname->type) { - char *sep; - char *name = attname->name; - sep = strchr(name, ':'); - if (sep) { - sep[0] = 0; - attname->type = gf_sg_get_namespace_code(n->sgprivate->scenegraph, name); - sep[0] = ':'; - name = gf_strdup(sep+1); - gf_free(attname->name); - attname->name = name; - } - } - } - } - - if ((n->sgprivate->tag == TAG_SVG_animateTransform) && (strstr(name, "from") || strstr(name, "to")) ) { - if (gf_node_get_attribute_by_tag((GF_Node *)n, TAG_SVG_ATT_transform_type, 1, 0, &info) != GF_OK) - goto exit; - - switch(*(SVG_TransformType *) info.far_ptr) { - case SVG_TRANSFORM_TRANSLATE: anim_value_type = SVG_Transform_Translate_datatype; break; - case SVG_TRANSFORM_SCALE: anim_value_type = SVG_Transform_Scale_datatype; break; - case SVG_TRANSFORM_ROTATE: anim_value_type = SVG_Transform_Rotate_datatype; break; - case SVG_TRANSFORM_SKEWX: anim_value_type = SVG_Transform_SkewX_datatype; break; - case SVG_TRANSFORM_SKEWY: anim_value_type = SVG_Transform_SkewY_datatype; break; - case SVG_TRANSFORM_MATRIX: anim_value_type = SVG_Transform_datatype; break; - default: - goto exit; - } - } - - if (gf_node_get_attribute_by_name(n, name, ns_code, 1, 1, &info)==GF_OK) { - if (!strcmp(name, "from") || !strcmp(name, "to") || !strcmp(name, "values") ) { - GF_FieldInfo attType; - SMIL_AttributeName *attname; - if (gf_node_get_attribute_by_tag((GF_Node *)n, TAG_SVG_ATT_attributeName, 0, 0, &attType) != GF_OK) - goto exit; - - attname = (SMIL_AttributeName *)attType.far_ptr; - if (!attname->type && attname->name) { - GF_Node *anim_target = gf_smil_anim_get_target(n); - gf_node_get_attribute_by_name((GF_Node *)anim_target, attname->name, attname->type, 0, 0, &attType); - attname->type = attType.fieldType; - } - - anim_value_type = attname->type; - } - gf_svg_parse_attribute(n, &info, val, anim_value_type); - - if (info.fieldType==SVG_ID_datatype) { - char *idname = *(SVG_String*)info.far_ptr; - gf_svg_parse_element_id(n, idname, 0); - } - if (info.fieldType==XMLRI_datatype) { - gf_node_dirty_set(n, GF_SG_SVG_XLINK_HREF_DIRTY, 0); - } - dom_node_changed(n, 0, &info); - goto exit; - } + gf_svg_set_attribute(n, ns, name, val); } exit: if (ns) gf_free(ns); @@ -2027,7 +2060,6 @@ static JSBool SMJS_FUNCTION(xml_element_elements_by_tag) static JSBool SMJS_FUNCTION(xml_element_set_id) { - const char *node_name; u32 node_id; char *name; Bool is_id; @@ -2047,7 +2079,7 @@ static JSBool SMJS_FUNCTION(xml_element_set_id) name = SMJS_CHARS(c, argv[0]); is_id = (JSVAL_TO_BOOLEAN(argv[1])==JS_TRUE) ? 1 : 0; } - node_name = gf_node_get_name_and_id(n, &node_id); + gf_node_get_name_and_id(n, &node_id); if (node_id && is_id) { /*we only support ONE ID per node*/ SMJS_FREE(c, name); @@ -2239,22 +2271,29 @@ static JSBool event_getProperty(JSContext *c, JSObject *obj, SMJS_PROP_GETTER, j case 42: *vp = INT_TO_JSVAL(evt->detail); return JS_TRUE; - /*MAE*/ + case 52:/*loaded*/ + if (!evt->media_event) return JS_TRUE; + *vp = INT_TO_JSVAL( evt->media_event->loaded_size); + return JS_TRUE; + case 53:/*total*/ + if (!evt->media_event) return JS_TRUE; + *vp = INT_TO_JSVAL( evt->media_event->total_size); + return JS_TRUE; case 54:/*bufferLevelValid*/ - if (!evt->mae) return JS_TRUE; - *vp = BOOLEAN_TO_JSVAL( evt->mae->bufferValid ? JS_TRUE : JS_FALSE); + if (!evt->media_event) return JS_TRUE; + *vp = BOOLEAN_TO_JSVAL( evt->media_event->bufferValid ? JS_TRUE : JS_FALSE); return JS_TRUE; case 55:/*bufferLevel*/ - if (!evt->mae) return JS_TRUE; - *vp = INT_TO_JSVAL( evt->mae->level); + if (!evt->media_event) return JS_TRUE; + *vp = INT_TO_JSVAL( evt->media_event->level); return JS_TRUE; case 56:/*bufferRemainingTime*/ - if (!evt->mae) return JS_TRUE; - *vp = JS_MAKE_DOUBLE(c, evt->mae->remaining_time); + if (!evt->media_event) return JS_TRUE; + *vp = JS_MAKE_DOUBLE(c, evt->media_event->remaining_time); return JS_TRUE; case 57:/*status*/ - if (!evt->mae) return JS_TRUE; - *vp = INT_TO_JSVAL( evt->mae->status); + if (!evt->media_event) return JS_TRUE; + *vp = INT_TO_JSVAL( evt->media_event->status); return JS_TRUE; /*VRML ones*/ @@ -2324,8 +2363,6 @@ typedef struct GF_List *node_stack; /*dom graph*/ GF_SceneGraph *document; - - Bool use_cache; } XMLHTTPContext; static void xml_http_append_send_header(XMLHTTPContext *ctx, char *hdr, char *val) @@ -2783,7 +2820,8 @@ static void xml_http_on_data(void *usr_cbk, GF_NETIO_Parameter *parameter) if (!strncmp(parameter->value, "application/xml", 15) || !strncmp(parameter->value, "text/xml", 8) || strstr(parameter->value, "+xml") - || !strncmp(parameter->value, "text/plain", 10) + || strstr(parameter->value, "/xml") +// || !strncmp(parameter->value, "text/plain", 10) ) { assert(!ctx->sax); ctx->sax = gf_xml_sax_new(xml_http_sax_start, xml_http_sax_end, xml_http_sax_text, ctx); @@ -2792,7 +2830,7 @@ static void xml_http_on_data(void *usr_cbk, GF_NETIO_Parameter *parameter) /*mark this doc as "nomade", and let it leave until all references to it are destroyed*/ ctx->document->reference_count = 1; } else { - GF_LOG(GF_LOG_ERROR, GF_LOG_SCRIPT, ("[XmlHttpRequest] content type %s not supported\n", parameter->value)); + GF_LOG(GF_LOG_INFO, GF_LOG_SCRIPT, ("[XmlHttpRequest] content type %s - ResponseXML not supported\n", parameter->value)); } return; case GF_NETIO_DATA_EXCHANGE: @@ -2816,23 +2854,18 @@ static void xml_http_on_data(void *usr_cbk, GF_NETIO_Parameter *parameter) return; case GF_NETIO_DATA_TRANSFERED: if (ctx->sax) { - if(ctx->use_cache ) { - const char *filename; - ctx->sax = gf_xml_sax_new(xml_http_sax_start, xml_http_sax_end, xml_http_sax_text, ctx); - filename = gf_dm_sess_get_cache_name(ctx->sess); - gf_xml_sax_parse_file(ctx->sax, filename, NULL); - } #if !USE_PROGRESSIVE_SAX - else { - gf_xml_sax_init(ctx->sax, ctx->data); - } + gf_xml_sax_init(ctx->sax, ctx->data); #endif } + /* No return, go till the end of the function */ break; case GF_NETIO_DISCONNECTED: return; case GF_NETIO_STATE_ERROR: ctx->ret_code = parameter->error; + /* No return, go till the end of the function */ + GF_LOG(GF_LOG_ERROR, GF_LOG_SCRIPT, ("[XmlHttpRequest] Download error: %s\n", gf_error_to_string(parameter->error))); break; } @@ -2845,6 +2878,8 @@ static void xml_http_on_data(void *usr_cbk, GF_NETIO_Parameter *parameter) /*error, complete reset*/ if (parameter->error) { xml_http_reset(ctx); + } else { + ctx->html_status = 200; } /*but stay in loaded mode*/ ctx->readyState = 4; @@ -2857,7 +2892,6 @@ static JSBool SMJS_FUNCTION(xml_http_send) GF_SceneGraph *scene; char *data = NULL; XMLHTTPContext *ctx; - GF_Err e; SMJS_OBJ SMJS_ARGS @@ -2892,20 +2926,19 @@ static JSBool SMJS_FUNCTION(xml_http_send) ctx->data = data ? gf_strdup(data) : NULL; SMJS_FREE(c, data); - ctx->use_cache = 0; if (!strncmp(ctx->url, "http://", 7)) { - ctx->sess = gf_dm_sess_new(par.dnld_man, ctx->url, (ctx->use_cache ? 0 : GF_NETIO_SESSION_NOT_CACHED), xml_http_on_data, ctx, &e); - + GF_Err e; + + ctx->sess = gf_dm_sess_new(par.dnld_man, ctx->url, ctx->async ? 0 : GF_NETIO_SESSION_NOT_THREADED, xml_http_on_data, ctx, &e); if (!ctx->sess) return JS_TRUE; - - /*just wait for destruction*/ - if (!ctx->async) { - while (ctx->sess) { - gf_sg_lock_javascript(ctx->c, 0); - gf_sleep(20); - gf_sg_lock_javascript(ctx->c, 1); - } + + /*start our download (whether the session is threaded or not)*/ + e = gf_dm_sess_process(ctx->sess); + if (e!=GF_OK) { + GF_LOG(GF_LOG_ERROR, GF_LOG_SCRIPT, ("[XmlHttpRequest] Error processing %s: %s\n", ctx->url, gf_error_to_string(e) )); } + /**/ + if (!ctx->async && ctx->sess) gf_dm_sess_del(ctx->sess); } else { u64 fsize; FILE * xmlf; @@ -3082,7 +3115,7 @@ static JSBool xml_http_getProperty(JSContext *c, JSObject *obj, SMJS_PROP_GETTER /*responseXML*/ case 3: if (ctx->readyState<3) return JS_TRUE; - if (ctx->data) { + if (ctx->data && ctx->document) { *vp = dom_document_construct(c, ctx->document); } else { *vp = JSVAL_VOID; @@ -3827,7 +3860,8 @@ void dom_js_pre_destroy(JSContext *c, GF_SceneGraph *sg, GF_Node *n) count = gf_list_count(dom_rt->handlers); for (i=0; ihandlers, i); - if (handler->js_context==c) { + /*if same context and same document, discard handler*/ + if ( (handler->js_context==c) && (!sg || (handler->sgprivate->scenegraph==sg)) ) { /*unprotect the function*/ gf_js_remove_root(handler->js_context, &(handler->js_fun_val), GF_JSGC_VAL); handler->js_fun_val=0; diff --git a/src/scenegraph/mpeg4_nodes.c b/src/scenegraph/mpeg4_nodes.c index 621cafb..df72149 100644 --- a/src/scenegraph/mpeg4_nodes.c +++ b/src/scenegraph/mpeg4_nodes.c @@ -24,7 +24,7 @@ /* - DO NOT MOFIFY - File generated on GMT Wed Jul 20 05:50:21 2011 + DO NOT MOFIFY - File generated on GMT Tue Nov 08 09:10:57 2011 BY MPEG4Gen for GPAC Version 0.4.6-DEV */ @@ -2776,14 +2776,14 @@ static Bool CompositeTexture2D_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType *QType = 13; *QT13_bits = 16; *b_min = FLT2FIX(0); - *b_max = FIX_MAX /*WARNING: modified to allow 16.16 fixed point version!!*/; + *b_max = FIX_MAX; return 1; case 4: *AType = 0; *QType = 13; *QT13_bits = 16; *b_min = FLT2FIX(0); - *b_max = FIX_MAX /*WARNING: modified to allow 16.16 fixed point version!!*/; + *b_max = FIX_MAX; return 1; case 7: *AType = 0; @@ -2969,14 +2969,14 @@ static Bool CompositeTexture3D_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType *QType = 13; *QT13_bits = 16; *b_min = FLT2FIX(0); - *b_max = FIX_MAX /*WARNING: modified to allow 16.16 fixed point version!!*/; + *b_max = FIX_MAX; return 1; case 4: *AType = 0; *QType = 13; *QT13_bits = 16; *b_min = FLT2FIX(0); - *b_max = FIX_MAX /*WARNING: modified to allow 16.16 fixed point version!!*/; + *b_max = FIX_MAX; return 1; default: return 0; @@ -30855,14 +30855,14 @@ static Bool SynthesizedTexture_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QType *QType = 13; *QT13_bits = 16; *b_min = FLT2FIX(0); - *b_max = FIX_MAX /*WARNING: modified to allow 16.16 fixed point version!!*/; + *b_max = FIX_MAX; return 1; case 3: *AType = 0; *QType = 13; *QT13_bits = 16; *b_min = FLT2FIX(0); - *b_max = FIX_MAX /*WARNING: modified to allow 16.16 fixed point version!!*/; + *b_max = FIX_MAX; return 1; case 5: *AType = 7; @@ -32120,20 +32120,20 @@ static Bool AdvancedAudioBuffer_get_aq_info(GF_Node *n, u32 FieldIndex, u8 *QTyp *QType = 13; *QT13_bits = 16; *b_min = FLT2FIX(0); - *b_max = FIX_MAX /*WARNING: modified to allow 16.16 fixed point version!!*/; + *b_max = FIX_MAX; return 1; case 11: *AType = 0; *QType = 13; *QT13_bits = 17; - *b_min = FLT2FIX(-65536); + *b_min = FIX_MIN; *b_max = FLT2FIX( 0); return 1; case 12: *AType = 0; *QType = 13; *QT13_bits = 17; - *b_min = FLT2FIX(-65536); + *b_min = FIX_MIN; *b_max = FLT2FIX( 0); return 1; case 13: diff --git a/src/scenegraph/smil_anim.c b/src/scenegraph/smil_anim.c index 4b8568f..c16f05b 100644 --- a/src/scenegraph/smil_anim.c +++ b/src/scenegraph/smil_anim.c @@ -145,12 +145,18 @@ static void gf_smil_anim_set(SMIL_Anim_RTI *rai) GF_FieldInfo to_info; SMILAnimationAttributesPointers *animp = rai->animp; - if (!animp->to || !animp->to->type) { + if (!animp->to) { GF_LOG(GF_LOG_ERROR, GF_LOG_SMIL, ("[SMIL Animation] Animation %s - set element without to attribute\n", gf_node_get_log_name((GF_Node *)rai->anim_elt))); return; } + if (!animp->to->type) { + GF_LOG(GF_LOG_ERROR, GF_LOG_SMIL, + ("[SMIL Animation] Animation %s - set element with an unparsed to attribute\n", + gf_node_get_log_name((GF_Node *)rai->anim_elt))); + return; + } if (rai->change_detection_mode) { /* if the set has been applied, unless next animations are additive we don't need @@ -618,7 +624,7 @@ static void gf_smil_anim_compute_interpolation_value(SMIL_Anim_RTI *rai, Fixed n } #ifndef GPAC_DISABLE_LOG - if (0 && (gf_log_get_level() >= GF_LOG_DEBUG) && (gf_log_get_tools() & GF_LOG_SMIL)) { + if (0 && gf_log_tool_level_on(GF_LOG_SMIL, GF_LOG_DEBUG)) { char *str; gf_log_lt(GF_LOG_DEBUG, GF_LOG_SMIL); str = gf_svg_dump_attribute(rai->anim_elt, &rai->interpolated_value); @@ -779,7 +785,7 @@ static void gf_smil_apply_additive(SMIL_Anim_RTI *rai) 1); #ifndef GPAC_DISABLE_LOG - if ((gf_log_get_level() >= GF_LOG_DEBUG) && (gf_log_get_tools() & GF_LOG_SMIL)) { + if (gf_log_tool_level_on(GF_LOG_SMIL, GF_LOG_DEBUG)) { char *str; gf_log_lt(GF_LOG_DEBUG, GF_LOG_SMIL); str = gf_svg_dump_attribute((GF_Node*)rai->anim_elt, &rai->owner->presentation_value); @@ -807,7 +813,7 @@ static void gf_smil_apply_additive(SMIL_Anim_RTI *rai) gf_svg_attributes_copy(&rai->owner->presentation_value, &rai->interpolated_value, 1); #ifndef GPAC_DISABLE_LOG - if ((gf_log_get_level() >= GF_LOG_DEBUG) && (gf_log_get_tools() & GF_LOG_SMIL)) { + if (gf_log_tool_level_on(GF_LOG_SMIL, GF_LOG_DEBUG)) { char *str; gf_log_lt(GF_LOG_DEBUG, GF_LOG_SMIL); str = gf_svg_dump_attribute((GF_Node*)rai->anim_elt, &rai->owner->presentation_value); @@ -904,7 +910,7 @@ static void gf_smil_anim_remove(SMIL_Timing_RTI *rti, Fixed normalized_simple_ti rai->anim_done = 1; #ifndef GPAC_DISABLE_LOG - if ((gf_log_get_level() >= GF_LOG_DEBUG) && (gf_log_get_tools() & GF_LOG_SMIL)) { + if (gf_log_tool_level_on(GF_LOG_SMIL, GF_LOG_DEBUG)) { char *str; gf_log_lt(GF_LOG_DEBUG, GF_LOG_SMIL); str = gf_svg_dump_attribute((GF_Node*)rai->anim_elt, &rai->owner->presentation_value); @@ -957,7 +963,7 @@ void gf_svg_apply_animations(GF_Node *node, SVGPropertiesPointers *render_svg_pr #ifndef GPAC_DISABLE_LOG u32 time=0; - if ((gf_log_get_level() >= GF_LOG_DEBUG) && (gf_log_get_tools() & GF_LOG_RTI)) { + if (gf_log_tool_level_on(GF_LOG_RTI, GF_LOG_DEBUG)) { time = gf_sys_clock(); } #endif @@ -1038,7 +1044,7 @@ void gf_svg_apply_animations(GF_Node *node, SVGPropertiesPointers *render_svg_pr #ifndef GPAC_DISABLE_LOG if (aa->presentation_value_changed) { - if ((gf_log_get_level() >= GF_LOG_DEBUG) && (gf_log_get_tools() & GF_LOG_SMIL)) { + if (gf_log_tool_level_on(GF_LOG_SMIL, GF_LOG_DEBUG)) { char *str; gf_log_lt(GF_LOG_DEBUG, GF_LOG_SMIL); str = gf_svg_dump_attribute(node, &aa->presentation_value); @@ -1066,7 +1072,7 @@ void gf_svg_apply_animations(GF_Node *node, SVGPropertiesPointers *render_svg_pr } #ifndef GPAC_DISABLE_LOG - if ((gf_log_get_level() >= GF_LOG_DEBUG) && (gf_log_get_tools() & GF_LOG_RTI)) { + if (gf_log_tool_level_on(GF_LOG_RTI, GF_LOG_DEBUG)) { time_spent_in_anim += gf_sys_clock() - time; } #endif @@ -1078,9 +1084,13 @@ GF_Node *gf_smil_anim_get_target(GF_Node *e) XLinkAttributesPointers *xlinkp = NULL; if (!gf_svg_is_animation_tag(e->sgprivate->tag)) return NULL; xlinkp = ((SVGTimedAnimBaseElement *)e)->xlinkp; - return xlinkp->href->target; + return (xlinkp && xlinkp->href) ? xlinkp->href->target : NULL; } +/* Attributes from the animation elements are not easy to use during runtime, + the runtime info is a set of easy to use structures. + This function initializes them (interpolation values ...) + Needs to be called after gf_smil_timing_init_runtime_info */ void gf_smil_anim_init_runtime_info(GF_Node *e) { u32 i; @@ -1097,6 +1107,7 @@ void gf_smil_anim_init_runtime_info(GF_Node *e) /* Filling animation structures to be independent of the SVG Element structure */ animp = ((SVGTimedAnimBaseElement *)e)->animp; timingp = ((SVGTimedAnimBaseElement *)e)->timingp; + if (!animp || !timingp) return; xlinkp = ((SVGTimedAnimBaseElement *)e)->xlinkp; target = xlinkp->href->target; @@ -1104,8 +1115,7 @@ void gf_smil_anim_init_runtime_info(GF_Node *e) memset(&target_attribute, 0, sizeof(GF_FieldInfo)); if (animp->attributeName && (animp->attributeName->name || animp->attributeName->tag)) { /* Filling the target_attribute structure with info on the animated attribute (type, pointer to data, ...) - NOTE: in the mode Dynamic Allocation of Attributes, this means that the animated - attribute is created with a default value, if it was not specified on the target element */ + NOTE: the animated attribute is created with a default value, if it was not specified on the target element */ if (animp->attributeName->tag) { gf_node_get_attribute_by_tag(target, animp->attributeName->tag, 1, 1, &target_attribute); } else { @@ -1421,7 +1431,8 @@ void gf_smil_anim_init_node(GF_Node *node) xlinkp->href = all_atts.xlink_href; xlinkp->type = all_atts.xlink_type; - /*perform init of default values*/ + /*perform init of default values + When the xlink:href attribute of animation is not set, the target defaults to the parent element */ if (!xlinkp->href) { GF_FieldInfo info; gf_node_get_attribute_by_tag((GF_Node *)node, TAG_XLINK_ATT_href, 1, 0, &info); @@ -1431,7 +1442,7 @@ void gf_smil_anim_init_node(GF_Node *node) } if (xlinkp->href->type == XMLRI_STRING) { if (!xlinkp->href->string) { - fprintf(stderr, "Error: IRI not initialized\n"); + GF_LOG(GF_LOG_ERROR, GF_LOG_SMIL,("Error: IRI not initialized\n")); return; } else { GF_Node *n; @@ -1446,14 +1457,21 @@ void gf_smil_anim_init_node(GF_Node *node) } } } - if (!xlinkp->href->target) return; + if (!xlinkp->href->target) { + GF_LOG(GF_LOG_WARNING, GF_LOG_SMIL,("Trying to initialize an animation when the target is not known\n")); + return; + } // We may not have an attribute name, when using an animateMotion element - if (node->sgprivate->tag != TAG_SVG_animateMotion && !all_atts.attributeName) return; + if (node->sgprivate->tag != TAG_SVG_animateMotion && !all_atts.attributeName) { + goto end_init; + } + /* if an attribute (to, from or by) is present but its type is not set + (e.g. it could not be determined before, the target was not known), we try to get the type from the target */ if ( (all_atts.to && (all_atts.to->type==0)) || (all_atts.from && (all_atts.from->type==0)) - || (all_atts.from && (all_atts.from->type==0)) + || (all_atts.by && (all_atts.by->type==0)) ) { GF_FieldInfo info; if (gf_node_get_attribute_by_name((GF_Node *)xlinkp->href->target, all_atts.attributeName->name, 0, 1, 1, &info)==GF_OK) { @@ -1469,11 +1487,10 @@ void gf_smil_anim_init_node(GF_Node *node) if (gf_node_get_attribute_by_tag((GF_Node *)node, tag, 0, 0, &info)==GF_OK) { SMIL_AnimateValue *attval = info.far_ptr; if (attval->type==0) { - SVG_String *string = attval->value; + SVG_String string = attval->value; attval->value = NULL; if (string) { - gf_svg_parse_attribute((GF_Node *)node, &info, * string, anim_value_type); - if (* string) gf_free(* string); + gf_svg_parse_attribute((GF_Node *)node, &info, string, anim_value_type); gf_free(string); } } @@ -1509,7 +1526,7 @@ void gf_smil_anim_init_node(GF_Node *node) e->animp->rotate = NULL; } - +end_init: gf_smil_timing_init_runtime_info(node); gf_smil_anim_init_runtime_info(node); gf_smil_anim_set_anim_runtime_in_timing(node); diff --git a/src/scenegraph/smil_timing.c b/src/scenegraph/smil_timing.c index 36f3f4e..aa525d9 100644 --- a/src/scenegraph/smil_timing.c +++ b/src/scenegraph/smil_timing.c @@ -307,7 +307,8 @@ static void gf_smil_mark_modified(SMIL_Timing_RTI *rti, Bool remove) /* Attributes from the timed elements are not easy to use during runtime, the runtime info is a set of easy to use structures. - This function initializes them (intervals, status ...). */ + This function initializes them (intervals, status ...) + and registers the element with the scenegraph */ GF_EXPORT void gf_smil_timing_init_runtime_info(GF_Node *timed_elt) { @@ -332,6 +333,7 @@ void gf_smil_timing_init_runtime_info(GF_Node *timed_elt) e->timingp->repeatDur = all_atts.repeatDur; e->timingp->restart = all_atts.restart; timingp = e->timingp; + if (!timingp) return; if (tag == TAG_SVG_audio || tag == TAG_SVG_video) { /* if the dur attribute is not set, then it should be set to media @@ -342,14 +344,13 @@ void gf_smil_timing_init_runtime_info(GF_Node *timed_elt) have a defined duration." TODO: Check if this should work with the animation element */ if (!e->timingp->dur) { - SVGAttribute *att = gf_xml_create_attribute((GF_Node *)e, TAG_SVG_ATT_dur); - e->timingp->dur = (SMIL_Duration *)att->data; + GF_FieldInfo info; + gf_node_get_attribute_by_tag((GF_Node *)e, TAG_SVG_ATT_dur, 1, 0, &info); + e->timingp->dur = (SMIL_Duration *)info.far_ptr; e->timingp->dur->type = SMIL_DURATION_MEDIA; } } - if (!timingp) return; - GF_SAFEALLOC(rti, SMIL_Timing_RTI) timingp->runtime = rti; rti->timed_elt = timed_elt; diff --git a/src/scenegraph/svg_attributes.c b/src/scenegraph/svg_attributes.c index 2f7eaa1..a6a7802 100644 --- a/src/scenegraph/svg_attributes.c +++ b/src/scenegraph/svg_attributes.c @@ -122,6 +122,7 @@ static const struct dom_event_def {u32 event; const char *name; u32 category; } { GF_EVENT_CPU, "cpu", GF_DOM_EVENT_LASER }, /*MediaAccess events*/ +#if 0 { GF_EVENT_MEDIA_BEGIN_SESSION_SETUP, "BeginSessionSetup", GF_DOM_EVENT_MEDIA_ACCESS }, { GF_EVENT_MEDIA_END_SESSION_SETUP, "EndSessionSetup", GF_DOM_EVENT_MEDIA_ACCESS }, { GF_EVENT_MEDIA_DATA_REQUEST, "DataRequest", GF_DOM_EVENT_MEDIA_ACCESS }, @@ -131,6 +132,34 @@ static const struct dom_event_def {u32 event; const char *name; u32 category; } { GF_EVENT_MEDIA_END_OF_DATA, "EndOfDataReception", GF_DOM_EVENT_MEDIA_ACCESS }, { GF_EVENT_MEDIA_STOP, "Stop", GF_DOM_EVENT_MEDIA_ACCESS }, { GF_EVENT_MEDIA_ERROR, "Error", GF_DOM_EVENT_MEDIA_ACCESS }, +#endif + + { GF_EVENT_MEDIA_SETUP_BEGIN, "setupbegin", GF_DOM_EVENT_MEDIA}, + { GF_EVENT_MEDIA_SETUP_DONE, "setupdone", GF_DOM_EVENT_MEDIA}, + + { GF_EVENT_MEDIA_LOAD_START, "loadstart", GF_DOM_EVENT_MEDIA }, + { GF_EVENT_MEDIA_LOAD_DONE, "loaddone", GF_DOM_EVENT_MEDIA }, + { GF_EVENT_MEDIA_PROGRESS, "progress", GF_DOM_EVENT_MEDIA }, + { GF_EVENT_MEDIA_SUSPEND, "suspend", GF_DOM_EVENT_MEDIA }, + { GF_EVENT_ABORT, "abort", GF_DOM_EVENT_MEDIA }, + { GF_EVENT_ERROR, "error", GF_DOM_EVENT_MEDIA }, + { GF_EVENT_MEDIA_EMPTIED, "emptied", GF_DOM_EVENT_MEDIA }, + { GF_EVENT_MEDIA_STALLED, "stalled", GF_DOM_EVENT_MEDIA }, + { GF_EVENT_MEDIA_LOADED_METADATA, "loadedmetadata", GF_DOM_EVENT_MEDIA }, + { GF_EVENT_MEDIA_LODADED_DATA, "loadeddata", GF_DOM_EVENT_MEDIA }, + { GF_EVENT_MEDIA_CANPLAY, "canplay", GF_DOM_EVENT_MEDIA }, + { GF_EVENT_MEDIA_CANPLAYTHROUGH, "canplaythrough", GF_DOM_EVENT_MEDIA }, + { GF_EVENT_MEDIA_PLAYING, "playing", GF_DOM_EVENT_MEDIA }, + { GF_EVENT_MEDIA_WAITING, "waiting", GF_DOM_EVENT_MEDIA }, + { GF_EVENT_MEDIA_SEEKING, "seeking", GF_DOM_EVENT_MEDIA }, + { GF_EVENT_MEDIA_SEEKED, "seeked", GF_DOM_EVENT_MEDIA }, + { GF_EVENT_MEDIA_ENDED, "ended", GF_DOM_EVENT_MEDIA }, + { GF_EVENT_MEDIA_DURATION_CHANGED, "durationchanged", GF_DOM_EVENT_MEDIA }, + { GF_EVENT_MEDIA_TIME_UPDATE, "timeupdate", GF_DOM_EVENT_MEDIA }, + { GF_EVENT_PLAY, "play", GF_DOM_EVENT_MEDIA }, + { GF_EVENT_PAUSE, "pause", GF_DOM_EVENT_MEDIA }, + { GF_EVENT_MEDIA_RATECHANGE, "ratechange", GF_DOM_EVENT_MEDIA }, + { GF_EVENT_MEDIA_VOLUME_CHANGED, "volumechange", GF_DOM_EVENT_MEDIA }, /*GPAC internals*/ { GF_EVENT_SCENE_ATTACHED, "gpac_scene_attached", GF_DOM_EVENT_DOM }, @@ -1297,6 +1326,9 @@ Bool gf_svg_parse_transformlist(GF_Matrix2D *mat, char *attribute_content) GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] Missing opening parenthesis in transform attribute: %s\n", attribute_content)); return 0; } + } else { + GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SVG Parsing] Unrecognized transofrm type in attribute %s\n", attribute_content)); + return 0; } /*for svgView parsing*/ if (str[i] == ')') i++; @@ -5323,7 +5355,7 @@ Bool gf_svg_attributes_equal(GF_FieldInfo *f1, GF_FieldInfo *f2) return 1; } default: - GF_LOG(GF_LOG_WARNING, GF_LOG_CODING|GF_LOG_INTERACT, ("[SVG Attributes] comparaison for field %s of type %s not supported\n", f1->name, gf_svg_attribute_type_to_string(f1->fieldType))); + GF_LOG(GF_LOG_WARNING, GF_LOG_INTERACT, ("[SVG Attributes] comparaison for field %s of type %s not supported\n", f1->name, gf_svg_attribute_type_to_string(f1->fieldType))); return 0; } } diff --git a/src/scenegraph/svg_smjs.c b/src/scenegraph/svg_smjs.c index c0b6b56..7a1bd54 100644 --- a/src/scenegraph/svg_smjs.c +++ b/src/scenegraph/svg_smjs.c @@ -65,9 +65,9 @@ jsval dom_node_get_sibling(JSContext *c, GF_Node *n, Bool is_prev, Bool elt_only -#define _ScriptMessage(_sg, _e, _msg) {\ +#define _ScriptMessage(_sg, _msg) {\ GF_JSAPIParam par; \ - par.info.e = _e; \ + par.info.e = GF_SCRIPT_INFO; \ par.info.msg = _msg; \ _sg->script_action(_sg->script_action_cbck, GF_JSAPI_OP_MESSAGE, NULL, &par);\ } @@ -188,7 +188,7 @@ static JSBool SMJS_FUNCTION(svg_echo) if (JSVAL_IS_STRING(argv[0])) { char *str = SMJS_CHARS_FROM_STRING(c, JS_ValueToString(c, argv[0]) ); - _ScriptMessage(sg, GF_SCRIPT_INFO, str); + _ScriptMessage(sg, str); SMJS_FREE(c, str); } return JS_TRUE; @@ -320,7 +320,6 @@ static JSBool svg_element_getProperty(JSContext *c, JSObject *obj, SMJS_PROP_GET if (!SMJS_ID_IS_INT(id)) return JS_TRUE; prop_id = SMJS_ID_TO_INT(id); - switch (prop_id) { case 0: /*id*/ { @@ -518,6 +517,9 @@ JSBool SMJS_FUNCTION_EXT(svg_udom_smil_time_insert, Bool is_end) } else { info.far_ptr = ((SVGTimedAnimBaseElement *)n)->timingp->begin; } + if (!info.far_ptr) { + return JS_FALSE; + } times = *((GF_List **)info.far_ptr); @@ -572,6 +574,8 @@ JSBool SMJS_FUNCTION(svg_udom_smil_pause) gf_smil_timing_pause(n); } else if (gf_svg_is_timing_tag(tag)) { ScriptAction(n->sgprivate->scenegraph, GF_JSAPI_OP_PAUSE_SVG, n, NULL); + } else if ((tag==TAG_SVG_svg) && (n->sgprivate->scenegraph->RootNode==n)) { + ScriptAction(n->sgprivate->scenegraph, GF_JSAPI_OP_PAUSE_SVG, n, NULL); } else { return JS_TRUE; } @@ -590,6 +594,49 @@ JSBool SMJS_FUNCTION(svg_udom_smil_resume) gf_smil_timing_resume(n); } else if (gf_svg_is_timing_tag(tag)) { ScriptAction(n->sgprivate->scenegraph, GF_JSAPI_OP_RESUME_SVG, n, NULL); + } else if ((tag==TAG_SVG_svg) && (n->sgprivate->scenegraph->RootNode==n)) { + ScriptAction(n->sgprivate->scenegraph, GF_JSAPI_OP_RESUME_SVG, n, NULL); + } else { + return JS_TRUE; + } + return JS_TRUE; +} + +JSBool SMJS_FUNCTION(svg_udom_smil_restart) +{ + u32 tag; + SMJS_OBJ + GF_Node *n = dom_get_element(c, obj); + + tag = gf_node_get_tag(n); + if ((tag==TAG_SVG_svg) && (n->sgprivate->scenegraph->RootNode==n)) { + ScriptAction(n->sgprivate->scenegraph, GF_JSAPI_OP_RESTART_SVG, n, NULL); + } else { + return JS_TRUE; + } + return JS_TRUE; +} + +JSBool SMJS_FUNCTION(svg_udom_smil_set_speed) +{ + u32 tag; + Double speed = 1.0; + SMJS_OBJ + SMJS_ARGS + GF_Node *n = dom_get_element(c, obj); + + if (argc && JSVAL_IS_NUMBER(argv[0]) ) { + jsdouble d; + JS_ValueToNumber(c, argv[0], &d); + speed = d; + } + + tag = gf_node_get_tag(n); + if ((tag==TAG_SVG_svg) && (n->sgprivate->scenegraph->RootNode==n)) { + GF_JSAPIParam par; + memset(&par, 0, sizeof(GF_JSAPIParam)); + par.val = FLT2FIX(speed); + ScriptAction(n->sgprivate->scenegraph, GF_JSAPI_OP_SET_SCENE_SPEED, n, &par); } else { return JS_TRUE; } @@ -1217,7 +1264,6 @@ static JSBool SMJS_FUNCTION_EXT(svg_get_bbox, Bool get_screen) { GF_JSAPIParam par; SMJS_OBJ - SMJS_ARGS GF_Node *n = dom_get_element(c, obj); if (!n || argc) return JS_TRUE; @@ -1254,7 +1300,6 @@ JSBool SMJS_FUNCTION(svg_udom_get_screen_ctm) { GF_JSAPIParam par; SMJS_OBJ - SMJS_ARGS GF_Node *n = dom_get_element(c, obj); if (!n || argc) return JS_TRUE; @@ -2187,6 +2232,8 @@ static void svg_init_js_api(GF_SceneGraph *scene) SMJS_FUNCTION_SPEC("endElement", svg_udom_smil_end, 0), SMJS_FUNCTION_SPEC("pauseElement", svg_udom_smil_pause, 0), SMJS_FUNCTION_SPEC("resumeElement", svg_udom_smil_resume, 0), + SMJS_FUNCTION_SPEC("restartElement", svg_udom_smil_restart, 0), + SMJS_FUNCTION_SPEC("setSpeed", svg_udom_smil_set_speed, 0), SMJS_FUNCTION_SPEC("getTotalLength", svg_path_get_total_length, 0), @@ -2557,7 +2604,7 @@ void JSScript_LoadSVG(GF_Node *node) if (!txt) return; ret = JS_EvaluateScript(svg_js->js_ctx, svg_js->global, txt->textContent, strlen(txt->textContent), 0, 0, &rval); if (ret==JS_FALSE) { - _ScriptMessage(node->sgprivate->scenegraph, GF_SCRIPT_ERROR, "SVG: Invalid script"); + GF_LOG(GF_LOG_ERROR, GF_LOG_SCRIPT, ("SVG: Invalid script\n") ); } gf_dom_listener_process_add(node->sgprivate->scenegraph); } @@ -2601,7 +2648,7 @@ static Bool svg_script_execute_handler(GF_Node *node, GF_DOM_Event *event, GF_No svg_js = node->sgprivate->scenegraph->svg_js; #ifndef GPAC_DISABLE_LOG - if ((gf_log_get_level() >= (GF_LOG_DEBUG)) && (gf_log_get_tools() & (GF_LOG_SCRIPT))) { + if (gf_log_tool_level_on(GF_LOG_SCRIPT, GF_LOG_DEBUG)) { char *content, *_content = NULL; if (utf8_script) { content = utf8_script; @@ -2703,7 +2750,7 @@ static Bool svg_script_execute_handler(GF_Node *node, GF_DOM_Event *event, GF_No #endif if (ret==JS_FALSE) { - _ScriptMessage(node->sgprivate->scenegraph, GF_SCRIPT_ERROR, "SVG: Invalid handler textContent"); + GF_LOG(GF_LOG_ERROR, GF_LOG_SCRIPT, ("SVG: Invalid handler textContent\n" )); return 0; } return 1; diff --git a/src/scenegraph/svg_types.c b/src/scenegraph/svg_types.c index 48d1e22..556a268 100644 --- a/src/scenegraph/svg_types.c +++ b/src/scenegraph/svg_types.c @@ -92,6 +92,14 @@ void gf_svg_node_del(GF_Node *node) if (p->sgprivate->tag==TAG_SVG_handler) { GF_Node *listener = p->sgprivate->UserPrivate; if (listener && (listener->sgprivate->tag==TAG_SVG_listener)) { + GF_FieldInfo info; + if (gf_node_get_attribute_by_tag(listener, TAG_XMLEV_ATT_handler, 0, 0, &info) == GF_OK) { + XMLRI *iri = (XMLRI *)info.far_ptr; + if (iri->target) { + assert(iri->target==p); + iri->target = NULL; + } + } gf_node_unregister(listener, NULL); // gf_svg_node_del(listener); } @@ -222,10 +230,9 @@ void gf_svg_reset_path(SVG_PathData d) GF_EXPORT void gf_svg_path_build(GF_Path *path, GF_List *commands, GF_List *points) { - u32 i, j, command_count, points_count; + u32 i, j, command_count; SVG_Point orig, ct_orig, ct_end, end, *tmp; command_count = gf_list_count(commands); - points_count = gf_list_count(points); orig.x = orig.y = ct_orig.x = ct_orig.y = 0; for (i=0, j=0; isgprivate->tag==TAG_MPEG4_InputSensor) { - GF_Command *com_o, *com_f; - u32 k = 0; - M_InputSensor *clone_is = (M_InputSensor *)node; - M_InputSensor *orig_is = (M_InputSensor *)orig; - while ( (com_o = (GF_Command *)gf_list_enum(orig_is->buffer.commandList, &k) ) ) { - com_f = gf_sg_vrml_command_clone(com_o, node->sgprivate->scenegraph, 1); - gf_list_add(clone_is->buffer.commandList, com_f); - } - } - /*init node before creating ISed routes so the eventIn handler are in place*/ if (node->sgprivate->tag == TAG_MPEG4_Conditional) BIFS_SetupConditionalClone(node, orig); else if (node->sgprivate->tag != TAG_ProtoNode) gf_node_init(node); diff --git a/src/scenegraph/vrml_route.c b/src/scenegraph/vrml_route.c index e410514..13a48ee 100644 --- a/src/scenegraph/vrml_route.c +++ b/src/scenegraph/vrml_route.c @@ -55,10 +55,9 @@ GF_Route *gf_sg_route_new(GF_SceneGraph *sg, GF_Node *fromNode, u32 fromField, G void gf_sg_route_del(GF_Route *r) { GF_SceneGraph *sg; - s32 ind; /*remove declared routes*/ - ind = gf_list_del_item(r->graph->Routes, r); + gf_list_del_item(r->graph->Routes, r); /*remove route from node - do this regardless of setup state since the route is registered upon creation*/ if (r->FromNode && r->FromNode->sgprivate->interact && r->FromNode->sgprivate->interact->routes) { gf_list_del_item(r->FromNode->sgprivate->interact->routes, r); diff --git a/src/scenegraph/vrml_smjs.c b/src/scenegraph/vrml_smjs.c index d7f05b1..ee4a0e1 100644 --- a/src/scenegraph/vrml_smjs.c +++ b/src/scenegraph/vrml_smjs.c @@ -157,11 +157,11 @@ typedef struct } GF_RouteToFunction; -#define _ScriptMessage(_c, _e, _msg) { \ +#define _ScriptMessage(_c, _msg) { \ GF_Node *_n = (GF_Node *) JS_GetContextPrivate(_c); \ if (_n->sgprivate->scenegraph->script_action) {\ GF_JSAPIParam par; \ - par.info.e = (_e); \ + par.info.e = GF_SCRIPT_INFO; \ par.info.msg = (_msg); \ _n->sgprivate->scenegraph->script_action(_n->sgprivate->scenegraph->script_action_cbck, GF_JSAPI_OP_MESSAGE, NULL, &par);\ } \ @@ -477,7 +477,7 @@ static JSBool SMJS_FUNCTION(JSPrint) SMJS_ARGS if (JSVAL_IS_STRING(argv[0])) { char *str = SMJS_CHARS(c, argv[0]); - _ScriptMessage(c, GF_SCRIPT_INFO, str); + _ScriptMessage(c, str); SMJS_FREE(c, str); } return JS_TRUE; @@ -2319,19 +2319,24 @@ static JSBool SMJS_FUNCTION(rot_multVec) static JSBool SMJS_FUNCTION(rot_setAxis) { SFVec3f v; - SFRotation r; - SMJS_ARGS + SFRotation *r; + GF_JSField *ptr; SMJS_OBJ + SMJS_ARGS if (argc<=0) return JS_FALSE; if (argc<=0 || !JSVAL_IS_OBJECT(argv[0]) || !JS_InstanceOf(c, JSVAL_TO_OBJECT(argv[0]), &js_rt->SFVec3fClass, NULL)) return JS_FALSE; - r = *(SFRotation *) ((GF_JSField *) JS_GetPrivate(c, obj))->field.far_ptr; + ptr = (GF_JSField *) JS_GetPrivate(c, obj); + r = (SFRotation *) ptr->field.far_ptr; + v = *(SFVec3f *) ((GF_JSField *) JS_GetPrivate(c, JSVAL_TO_OBJECT(argv[0])))->field.far_ptr; - r.x = v.x; - r.y = v.y; - r.z = v.z; + + r->x = v.x; + r->y = v.y; + r->z = v.z; + Script_FieldChanged(c, NULL, ptr, NULL); return JS_TRUE; } static JSBool SMJS_FUNCTION(rot_slerp) @@ -2993,7 +2998,6 @@ JSBool SMJS_FUNCTION(vrml_event_add_listener) GF_Node *node; GF_JSField *ptr; SMJS_OBJ - SMJS_ARGS if (! JS_InstanceOf(c, obj, &js_rt->SFNodeClass, NULL) ) return JS_FALSE; ptr = (GF_JSField *) JS_GetPrivate(c, obj); assert(ptr->field.fieldType==GF_SG_VRML_SFNODE); @@ -3006,7 +3010,6 @@ JSBool SMJS_FUNCTION(vrml_event_remove_listener) GF_Node *node; GF_JSField *ptr; SMJS_OBJ - SMJS_ARGS if (! JS_InstanceOf(c, obj, &js_rt->SFNodeClass, NULL) ) return JS_FALSE; ptr = (GF_JSField *) JS_GetPrivate(c, obj); assert(ptr->field.fieldType==GF_SG_VRML_SFNODE); @@ -3587,9 +3590,7 @@ void gf_sg_script_to_node_field(JSContext *c, jsval val, GF_FieldInfo *field, GF break; case GF_SG_VRML_MFCOLOR: if ( JSVAL_IS_OBJECT(item) && JS_InstanceOf(c, JSVAL_TO_OBJECT(item), &js_rt->SFColorClass, NULL) ) { - SFColor *col; from = (GF_JSField *) JS_GetPrivate(c, JSVAL_TO_OBJECT(item)); - col = (SFColor *)from->field.far_ptr; gf_sg_vrml_field_copy(& ((MFColor*)field->far_ptr)->vals[i], from->field.far_ptr, GF_SG_VRML_SFCOLOR); } break; @@ -4159,6 +4160,8 @@ static void JS_PreDestroy(GF_Node *node) dom_js_pre_destroy(priv->js_ctx, node->sgprivate->scenegraph, NULL); #endif + gf_sg_lock_javascript(priv->js_ctx, 0); + gf_sg_ecmascript_del(priv->js_ctx); #ifndef GPAC_DISABLE_SVG @@ -4169,8 +4172,6 @@ static void JS_PreDestroy(GF_Node *node) priv->js_ctx = NULL; - gf_sg_lock_javascript(priv->js_ctx, 0); - /*unregister script from parent scene (cf base_scenegraph::sg_reset) */ gf_list_del_item(node->sgprivate->scenegraph->scripts, node); } @@ -4522,6 +4523,8 @@ static void JSScript_NodeModified(GF_SceneGraph *sg, GF_Node *node, GF_FieldInfo jsf = JS_GetPrivate(priv->js_ctx, obj); if (jsf->node && (jsf->node==node)) { jsf->node = NULL; + /*Ivica patch*/ + node->sgprivate->interact->js_binding->node = NULL; } } return; @@ -4543,7 +4546,7 @@ static void JSScript_NodeModified(GF_SceneGraph *sg, GF_Node *node, GF_FieldInfo else { gf_sg_js_call_gc(sg->svg_js->js_ctx); - JS_ClearNewbornRoots(sg->svg_js->js_ctx); + //invalid with firefox>7: JS_ClearNewbornRoots(sg->svg_js->js_ctx); } #endif } @@ -4638,7 +4641,7 @@ void gf_sg_handle_dom_event_for_vrml(GF_Node *node, GF_DOM_Event *event, GF_Node #ifndef GPAC_DISABLE_SVG GF_ScriptPriv *priv; Bool prev_type; - JSBool ret = JS_FALSE; + //JSBool ret = JS_FALSE; GF_DOM_Event *prev_event = NULL; SVG_handlerElement *hdl; jsval rval; @@ -4658,19 +4661,26 @@ void gf_sg_handle_dom_event_for_vrml(GF_Node *node, GF_DOM_Event *event, GF_Node gf_sg_lock_javascript(priv->js_ctx, 1); + evt = gf_dom_new_event(priv->js_ctx); + if (!evt) { + gf_sg_lock_javascript(priv->js_ctx, 0); + GF_LOG(GF_LOG_ERROR, GF_LOG_SCRIPT, ("[DOM Events] Cannot create JavaScript dom event for event type %d\n", event->type)); + return; + } + prev_type = event->is_vrml; event->is_vrml = 1; JS_SetPrivate(priv->js_ctx, priv->event, event); - evt = gf_dom_new_event(priv->js_ctx); + JS_SetPrivate(priv->js_ctx, evt, event); argv[0] = OBJECT_TO_JSVAL(evt); if (hdl->js_fun_val) { jsval funval = (jsval ) hdl->js_fun_val; - ret = JS_CallFunctionValue(priv->js_ctx, hdl->evt_listen_obj ? (JSObject *)hdl->evt_listen_obj: priv->js_obj, funval, 1, argv, &rval); + /*ret = */JS_CallFunctionValue(priv->js_ctx, hdl->evt_listen_obj ? (JSObject *)hdl->evt_listen_obj: priv->js_obj, funval, 1, argv, &rval); } else { - ret = JS_CallFunctionName(priv->js_ctx, hdl->evt_listen_obj, "handleEvent", 1, argv, &rval); + /*ret = */JS_CallFunctionName(priv->js_ctx, hdl->evt_listen_obj, "handleEvent", 1, argv, &rval); } /*check any pending exception if outer-most event*/ @@ -4711,7 +4721,6 @@ void gf_sg_set_script_action(GF_SceneGraph *scene, gf_sg_script_action script_ac GF_Node *gf_sg_js_get_node(JSContext *c, JSObject *obj) { - JSBool has_p; #ifndef GPAC_DISABLE_VRML if (js_rt && JS_InstanceOf(c, obj, &js_rt->SFNodeClass, NULL) ) { GF_JSField *ptr = (GF_JSField *) JS_GetPrivate(c, obj); @@ -4720,9 +4729,11 @@ GF_Node *gf_sg_js_get_node(JSContext *c, JSObject *obj) #endif #ifndef GPAC_DISABLE_SVG - has_p = 0; - if (JS_HasProperty(c, obj, "namespaceURI", &has_p)) { - if (has_p==JS_TRUE) return dom_get_element(c, obj); + { + JSBool has_p = 0; + if (JS_HasProperty(c, obj, "namespaceURI", &has_p)) { + if (has_p==JS_TRUE) return dom_get_element(c, obj); + } } #endif return NULL; diff --git a/src/scenegraph/vrml_tools.c b/src/scenegraph/vrml_tools.c index e51db21..5c73e63 100644 --- a/src/scenegraph/vrml_tools.c +++ b/src/scenegraph/vrml_tools.c @@ -1120,13 +1120,13 @@ void VRML_FieldCopyCast(void *dest, u32 dst_field_type, void *orig, u32 ori_fiel ((SFString*)dest)->buffer = gf_strdup(tmp); } else { if ( ((SFString*)dest)->buffer) gf_free(((SFString*)dest)->buffer); - ((SFString*)dest)->buffer = gf_strdup(url->url); + ((SFString*)dest)->buffer = url->url ? gf_strdup(url->url) : NULL; } } /*for SFString to MFString cast*/ else if (ori_field_type == GF_SG_VRML_SFSTRING) { if ( ((SFString*)dest)->buffer) gf_free(((SFString*)dest)->buffer); - ((SFString*)dest)->buffer = gf_strdup(((SFString*)orig)->buffer); + ((SFString*)dest)->buffer = ((SFString*)orig)->buffer ? gf_strdup(((SFString*)orig)->buffer) : NULL; } return; case GF_SG_VRML_SFURL: @@ -1235,7 +1235,7 @@ void gf_sg_vrml_field_clone(void *dest, void *orig, u32 field_type, GF_SceneGrap SFCommandBuffer *cb_src = (SFCommandBuffer *)orig; cb_dst->bufferSize = cb_src->bufferSize; - if (cb_dst->bufferSize) { + if (cb_dst->bufferSize && !gf_list_count(cb_src->commandList) ) { cb_dst->buffer = (u8*)gf_realloc(cb_dst->buffer, sizeof(char)*cb_dst->bufferSize); memcpy(cb_dst->buffer, cb_src->buffer, sizeof(char)*cb_src->bufferSize); } else { diff --git a/src/scenegraph/xbl_process.c b/src/scenegraph/xbl_process.c index 0243410..0af4aa2 100644 --- a/src/scenegraph/xbl_process.c +++ b/src/scenegraph/xbl_process.c @@ -56,7 +56,7 @@ typedef struct static GF_Err xbl_parse_report(GF_XBL_Parser *parser, GF_Err e, char *format, ...) { #ifndef GPAC_DISABLE_LOG - if (gf_log_get_level() && (gf_log_get_tools() & GF_LOG_PARSER)) { + if (gf_log_tool_level_on(GF_LOG_PARSER, e ? GF_LOG_ERROR : GF_LOG_WARNING)) { char szMsg[2048]; va_list args; va_start(args, format); diff --git a/src/scenegraph/xml_ns.c b/src/scenegraph/xml_ns.c index 466dde6..42b3240 100644 --- a/src/scenegraph/xml_ns.c +++ b/src/scenegraph/xml_ns.c @@ -1131,7 +1131,7 @@ GF_Err gf_node_store_embedded_data(XMLRI *iri, const char *cache_dir, const char iri->string = NULL; return GF_IO_ERR; } - fwrite(data, data_size, 1, f); + gf_fwrite(data, data_size, 1, f); fclose(f); } gf_free(data); diff --git a/src/terminal/channel.c b/src/terminal/channel.c index 39c7d6a..7c833ed 100644 --- a/src/terminal/channel.c +++ b/src/terminal/channel.c @@ -345,13 +345,13 @@ static void Channel_UpdateBuffering(GF_Channel *ch, Bool update_info) { if (update_info && ch->MaxBuffer) gf_scene_buffering_info(ch->odm->parentscene ? ch->odm->parentscene : ch->odm->subscene); - gf_term_service_media_event(ch->odm, GF_EVENT_MEDIA_DATA_PROGRESS); + gf_term_service_media_event(ch->odm, GF_EVENT_MEDIA_PROGRESS); if (!Channel_NeedsBuffering(ch, 0)) { ch_buffer_off(ch); if (ch->MaxBuffer && update_info) gf_scene_buffering_info(ch->odm->parentscene ? ch->odm->parentscene : ch->odm->subscene); - gf_term_service_media_event(ch->odm, GF_EVENT_MEDIA_PLAYABLE); + gf_term_service_media_event(ch->odm, GF_EVENT_MEDIA_PLAYING); } } @@ -578,7 +578,7 @@ static void Channel_DispatchAU(GF_Channel *ch, u32 duration) } else { /*trigger the data progress every 500 ms*/ if (ch->last_au_time + 500 > time) { - gf_term_service_media_event(ch->odm, GF_EVENT_MEDIA_DATA_PROGRESS); + gf_term_service_media_event(ch->odm, GF_EVENT_MEDIA_PROGRESS); ch->last_au_time = time; } } @@ -659,7 +659,12 @@ static void gf_es_check_timing(GF_Channel *ch) /*if channel is not the OCR, shift all time stamps to match the current time at clock init*/ else if (!ch->IsClockInit ) { // ch->ts_offset += gf_clock_real_time(ch->clock); - if (ch->clock->clock_init) ch->IsClockInit = 1; + if (ch->clock->clock_init) { + ch->IsClockInit = 1; + if (ch->odm->flags & GF_ODM_INHERIT_TIMELINE) { +// ch->ts_offset += gf_clock_real_time(ch->clock) - ch->CTS; + } + } } /*deal with some broken DMB streams were the timestamps on BIFS/OD are not set (0) or completely out of sync of the OCR clock (usually audio). If the audio codec (BSAC ...) is not found, we force re-initializing of the clock @@ -675,11 +680,24 @@ static void gf_es_check_timing(GF_Channel *ch) void gf_es_dispatch_raw_media_au(GF_Channel *ch, char *payload, u32 payload_size, u32 cts) { + u32 now; GF_CompositionMemory *cb; GF_CMUnit *cu; if (!payload || !ch->odm->codec->CB) return; if (!ch->odm->codec->CB->no_allocation) return; + now = gf_clock_real_time(ch->clock); + if (cts + ch->MinBuffer < now) { + if (ch->MinBuffer && (ch->is_raw_channel==2)) { + ch->clock->clock_init = 0; + gf_clock_set_time(ch->clock, cts); + GF_LOG(GF_LOG_WARNING, GF_LOG_MEDIA, ("[ODM%d] Raw Frame dispatched at OTB %d but frame TS is %d ms - adjusting clock\n", ch->odm->OD->objectDescriptorID, now, cts)); + } else { + GF_LOG(GF_LOG_WARNING, GF_LOG_MEDIA, ("[ODM%d] Raw Frame dispatched at OTB %d but frame TS is %d ms - DROPPING\n", ch->odm->OD->objectDescriptorID, now, cts)); + } + return; + } + cb = ch->odm->codec->CB; cu = gf_cm_lock_input(cb, cts, 1); if (cu) { @@ -689,7 +707,7 @@ void gf_es_dispatch_raw_media_au(GF_Channel *ch, char *payload, u32 payload_size cu->data = payload; size = payload_size; cu->TS = cts; - GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("[ODM%d] Raw Frame dispatched to CB - TS %d ms\n", ch->odm->OD->objectDescriptorID, cu->TS)); + GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("[ODM%d] Raw Frame dispatched to CB - TS %d ms - OTB %d ms - OTB_drift %d ms\n", ch->odm->OD->objectDescriptorID, cu->TS, gf_clock_real_time(ch->clock), gf_clock_time(ch->clock) )); } gf_cm_unlock_input(cb, cu, size, 1); @@ -762,18 +780,30 @@ void gf_es_receive_sl_packet(GF_ClientService *serv, GF_Channel *ch, char *paylo /*channel is the OCR, re-initialize the clock with the proper OCR*/ if (gf_es_owns_clock(ch)) { u32 OCR_TS; + + /*timestamps of PCR stream haven been shifted - shift the OCR as well*/ + if (ch->seed_ts) { + u64 diff_ts; + Double scale = hdr.m2ts_pcr ? 27000000 : ch->esd->slConfig->OCRResolution; + scale /= ch->ts_res; + diff_ts = (u64) (ch->seed_ts * scale); + hdr.objectClockReference -= diff_ts; + } + /*if SL is mapped from network module(eg not coded), OCR=PCR shall be given in 27Mhz units*/ if (hdr.m2ts_pcr) { OCR_TS = (u32) ( hdr.objectClockReference / 27000); } else { OCR_TS = (u32) ( (s64) (hdr.objectClockReference) * ch->ocr_scale); } + OCR_TS += ch->ts_offset; ch->clock->clock_init = 0; + gf_clock_set_time(ch->clock, OCR_TS); /*many TS streams deployed with HLS have broken PCRs - we will check their consistency when receiving the first AU with DTS/CTS on this channel*/ ch->clock->probe_ocr = 1; - GF_LOG(GF_LOG_INFO, GF_LOG_SYNC, ("[SyncLayer] ES%d: initializing clock at STB %d from OCR TS %d (origial TS "LLD") - %d buffering - OTB %d\n", ch->esd->ESID, gf_term_get_time(ch->odm->term), OCR_TS, hdr.objectClockReference, ch->clock->Buffering, gf_clock_time(ch->clock) )); + GF_LOG(GF_LOG_INFO, GF_LOG_SYNC, ("[SyncLayer] ES%d: initializing clock at STB %d from OCR TS %d (original TS "LLD") - %d buffering - OTB %d\n", ch->esd->ESID, gf_term_get_time(ch->odm->term), OCR_TS, hdr.objectClockReference, ch->clock->Buffering, gf_clock_time(ch->clock) )); if (ch->clock->clock_init) ch->IsClockInit = 1; } @@ -784,7 +814,7 @@ void gf_es_receive_sl_packet(GF_ClientService *serv, GF_Channel *ch, char *paylo u32 ck; u32 OCR_TS = (u32) ( hdr.objectClockReference / 27000); ck = gf_clock_time(ch->clock); - GF_LOG(GF_LOG_DEBUG, GF_LOG_SYNC, ("[SyncLayer] ES%d: At OTB %d - OCR Discontinuity OCR: adjusting to %d (origial TS "LLD") - original clock %d\n", ch->esd->ESID, gf_clock_real_time(ch->clock), OCR_TS, hdr.objectClockReference, ck)); + GF_LOG(GF_LOG_DEBUG, GF_LOG_SYNC, ("[SyncLayer] ES%d: At OTB %d - OCR Discontinuity OCR: adjusting to %d (original TS "LLD") - original clock %d\n", ch->esd->ESID, gf_clock_real_time(ch->clock), OCR_TS, hdr.objectClockReference, ck)); // gf_clock_set_time(ch->clock, (u32) OCR_TS); } /*compute clock drift*/ @@ -797,7 +827,7 @@ void gf_es_receive_sl_packet(GF_ClientService *serv, GF_Channel *ch, char *paylo OCR_TS = (u32) ( (s64) (hdr.objectClockReference) * ch->ocr_scale); } ck = gf_clock_time(ch->clock); - GF_LOG(GF_LOG_DEBUG, GF_LOG_SYNC, ("[SyncLayer] ES%d: At OTB %d adjusting OCR to %d (origial TS "LLD") - diff %d\n", ch->esd->ESID, gf_clock_real_time(ch->clock), OCR_TS, hdr.objectClockReference, (s32) OCR_TS - (s32) ck)); + GF_LOG(GF_LOG_DEBUG, GF_LOG_SYNC, ("[SyncLayer] ES%d: At OTB %d adjusting OCR to %d (original TS "LLD") - diff %d\n", ch->esd->ESID, gf_clock_real_time(ch->clock), OCR_TS, hdr.objectClockReference, (s32) OCR_TS - (s32) ck)); // gf_clock_set_time(ch->clock, (u32) OCR_TS); } #else @@ -810,7 +840,7 @@ void gf_es_receive_sl_packet(GF_ClientService *serv, GF_Channel *ch, char *paylo OCR_TS = (u32) ( (s64) (hdr.objectClockReference) * ch->ocr_scale); } ck = gf_clock_time(ch->clock); - GF_LOG(GF_LOG_DEBUG, GF_LOG_SYNC, ("[SyncLayer] ES%d: At OTB %d got OCR %d (origial TS "LLD") - diff %d%s\n", ch->esd->ESID, gf_clock_real_time(ch->clock), OCR_TS, hdr.objectClockReference, (s32) OCR_TS - (s32) ck, (hdr.m2ts_pcr==2) ? " - PCR Discontinuity flag" : "" )); + GF_LOG(GF_LOG_DEBUG, GF_LOG_SYNC, ("[SyncLayer] ES%d: At OTB %d got OCR %d (original TS "LLD") - diff %d%s\n", ch->esd->ESID, gf_clock_real_time(ch->clock), OCR_TS, hdr.objectClockReference, (s32) OCR_TS - (s32) ck, (hdr.m2ts_pcr==2) ? " - PCR Discontinuity flag" : "" )); } #endif if (!payload_size) return; @@ -883,7 +913,7 @@ void gf_es_receive_sl_packet(GF_ClientService *serv, GF_Channel *ch, char *paylo GF_LOG(GF_LOG_WARNING, GF_LOG_SYNC, ("[SyncLayer] ES%d: missed end of AU (DTS %d)\n", ch->esd->ESID, ch->DTS)); } if (ch->codec_resilient) { - if (!ch->IsClockInit) gf_es_check_timing(ch); + if (!ch->IsClockInit && !ch->skip_time_check_for_pending) gf_es_check_timing(ch); Channel_DispatchAU(ch, 0); } else { gf_free(ch->buffer); @@ -892,6 +922,7 @@ void gf_es_receive_sl_packet(GF_ClientService *serv, GF_Channel *ch, char *paylo ch->len = ch->allocSize = 0; } } + ch->skip_time_check_for_pending = 0; AUSeqNum = hdr.AU_sequenceNumber; /*Get CTS */ if (ch->esd->slConfig->useTimestampsFlag) { @@ -928,7 +959,7 @@ void gf_es_receive_sl_packet(GF_ClientService *serv, GF_Channel *ch, char *paylo s32 diff_ts = ch->DTS; diff_ts -= ch->clock->init_time; if (ABS(diff_ts) > 10000) { - GF_LOG(GF_LOG_ERROR, GF_LOG_SYNC, ("[SyncLayer] ES%d: invalid clock reference detected - DTS %d OCR %d - using DTS as OCR\n", ch->DTS, ch->clock->init_time)); + GF_LOG(GF_LOG_ERROR, GF_LOG_SYNC, ("[SyncLayer] ES%d: invalid clock reference detected - DTS %d but OCR %d - using DTS as OCR\n", ch->esd->ESID, ch->DTS, ch->clock->init_time)); ch->clock->clock_init = 0; gf_clock_set_time(ch->clock, ch->DTS-1000); } @@ -941,7 +972,8 @@ void gf_es_receive_sl_packet(GF_ClientService *serv, GF_Channel *ch, char *paylo } } else { /*use CU duration*/ - if (!ch->IsClockInit) ch->DTS = ch->CTS = ch->ts_offset; + if (!ch->IsClockInit) + ch->DTS = ch->CTS = ch->ts_offset; if (!ch->esd->slConfig->AUSeqNumLength) { if (!ch->au_sn) { @@ -1137,6 +1169,8 @@ void gf_es_receive_sl_packet(GF_ClientService *serv, GF_Channel *ch, char *paylo } } + gf_es_lock(ch, 1); + if (hdr.paddingFlag && !EndAU) { /*to do - this shouldn't happen anyway */ @@ -1157,6 +1191,9 @@ void gf_es_receive_sl_packet(GF_ClientService *serv, GF_Channel *ch, char *paylo } if (EndAU) Channel_DispatchAU(ch, hdr.au_duration); + + + gf_es_lock(ch, 0); } @@ -1318,7 +1355,7 @@ void gf_es_drop_au(GF_Channel *ch) /*if we get under our limit, rebuffer EXCEPT WHEN EOS is signaled*/ if (!ch->IsEndOfStream && Channel_NeedsBuffering(ch, 1)) { ch_buffer_on(ch); - gf_term_service_media_event(ch->odm, GF_EVENT_MEDIA_NOT_PLAYABLE); + gf_term_service_media_event(ch->odm, GF_EVENT_MEDIA_WAITING); } /*unlock the channel*/ diff --git a/src/terminal/clock.c b/src/terminal/clock.c index aa3d644..8883847 100644 --- a/src/terminal/clock.c +++ b/src/terminal/clock.c @@ -164,8 +164,8 @@ void gf_clock_reset(GF_Clock *ck) void gf_clock_stop(GF_Clock *ck) { - ck->StartTime = gf_clock_time(ck); ck->clock_init = 0; + ck->StartTime = 0; } void gf_clock_set_time(GF_Clock *ck, u32 TS) @@ -202,7 +202,7 @@ void gf_clock_pause(GF_Clock *ck) void gf_clock_resume(GF_Clock *ck) { gf_mx_p(ck->mx); -// assert(ck->Paused); + assert(ck->Paused); ck->Paused -= 1; if (!ck->Paused) ck->StartTime += gf_term_get_time(ck->term) - ck->PauseTime; @@ -240,7 +240,7 @@ u32 gf_clock_ellapse_time(GF_Clock *ck) Bool gf_clock_is_started(GF_Clock *ck) { - if (/*!ck->StartTime || */ck->Buffering || ck->Paused) return 0; + if (!ck || ck->Buffering || ck->Paused) return 0; return 1; } diff --git a/src/terminal/decoder.c b/src/terminal/decoder.c index 38b3081..4441ae9 100644 --- a/src/terminal/decoder.c +++ b/src/terminal/decoder.c @@ -83,8 +83,7 @@ GF_Err gf_codec_add_channel(GF_Codec *codec, GF_Channel *ch) GF_Err e; GF_NetworkCommand com; GF_Channel *a_ch; - char *dsi; - u32 dsiSize, CUsize, i; + u32 CUsize, i; GF_CodecCapability cap; u32 min, max; @@ -92,22 +91,13 @@ GF_Err gf_codec_add_channel(GF_Codec *codec, GF_Channel *ch) /*only for valid codecs (eg not OCR)*/ if (codec->decio) { com.get_dsi.dsi = NULL; - dsi = NULL; - dsiSize = 0; if (ch->esd->decoderConfig->upstream) codec->flags |= GF_ESM_CODEC_HAS_UPSTREAM; - if (ch->esd->decoderConfig->decoderSpecificInfo) { - dsi = ch->esd->decoderConfig->decoderSpecificInfo->data; - dsiSize = ch->esd->decoderConfig->decoderSpecificInfo->dataLength; - } /*For objects declared in OD stream, override with network DSI if any*/ if (ch->service && !(ch->odm->flags & GF_ODM_NOT_IN_OD_STREAM) ) { com.command_type = GF_NET_CHAN_GET_DSI; com.base.on_channel = ch; e = gf_term_service_command(ch->service, &com); if (!e && com.get_dsi.dsi) { - dsi = com.get_dsi.dsi; - dsiSize = com.get_dsi.dsi_len; - if (ch->esd->decoderConfig->decoderSpecificInfo->data) gf_free(ch->esd->decoderConfig->decoderSpecificInfo->data); ch->esd->decoderConfig->decoderSpecificInfo->data = com.get_dsi.dsi; ch->esd->decoderConfig->decoderSpecificInfo->dataLength = com.get_dsi.dsi_len; @@ -144,9 +134,11 @@ GF_Err gf_codec_add_channel(GF_Codec *codec, GF_Channel *ch) case GF_STREAM_VISUAL: case GF_STREAM_AUDIO: cap.CapCode = GF_CODEC_BUFFER_MIN; + cap.cap.valueInt = 1; gf_codec_get_capability(codec, &cap); min = cap.cap.valueInt; cap.CapCode = GF_CODEC_BUFFER_MAX; + cap.cap.valueInt = 1; gf_codec_get_capability(codec, &cap); max = cap.cap.valueInt; break; @@ -193,6 +185,7 @@ GF_Err gf_codec_add_channel(GF_Codec *codec, GF_Channel *ch) com.base.on_channel = ch; com.cfg.priority = ch->esd->streamPriority; + assert( ch->clock ); com.cfg.sync_id = ch->clock->clockID; memcpy(&com.cfg.sl_config, ch->esd->slConfig, sizeof(GF_SLConfig)); /*get the frame duration if audio (used by some network stack)*/ @@ -234,6 +227,9 @@ GF_Err gf_codec_add_channel(GF_Codec *codec, GF_Channel *ch) codec->CB->Min = 0; codec->CB->odm = codec->odm; ch->is_raw_channel = 1; + if (gf_es_owns_clock(ch)) + ch->is_raw_channel = 2; + if (ch->is_pulling) { codec->process = gf_codec_process_raw_media_pull; } @@ -502,7 +498,6 @@ exit: /*special handling of decoders not using ESM*/ static GF_Err PrivateScene_Process(GF_Codec *codec, u32 TimeAvailable) { - Bool resume_clock; u32 now; GF_Channel *ch; GF_Scene *scene_locked; @@ -522,7 +517,6 @@ static GF_Err PrivateScene_Process(GF_Codec *codec, u32 TimeAvailable) ch = (GF_Channel*)gf_list_get(codec->inChannels, 0); if (!ch) return GF_OK; - resume_clock = 0; /*init channel clock*/ if (!ch->IsClockInit) { Bool started; @@ -697,7 +691,7 @@ static GF_Err MediaCodec_Process(GF_Codec *codec, u32 TimeAvailable) if (codec->CB->Capacity == 1) { /*a SHA signature is computed for each AU. This avoids decoding/recompositing when identical (for instance streaming a carousel)*/ u8 new_unit_signature[20]; - gf_sha1_csum(AU->data, AU->dataLength, new_unit_signature); + gf_sha1_csum((u8*)AU->data, AU->dataLength, new_unit_signature); if (!memcmp(codec->last_unit_signature, new_unit_signature, sizeof(new_unit_signature))) { codec->nb_repeted_frames++; gf_es_drop_au(ch); @@ -816,7 +810,7 @@ scalable_retry: unit_size = 0; e = UnlockCompositionUnit(codec, CU, unit_size); - GF_LOG(GF_LOG_DEBUG, GF_LOG_RTI|GF_LOG_CODEC, ("[%s] ODM%d at %d decoded packed frame TS %d in %d ms\n", codec->decio->module_name, codec->odm->OD->objectDescriptorID, gf_clock_real_time(ch->clock), AU->CTS, now)); + GF_LOG(GF_LOG_DEBUG, GF_LOG_CODEC, ("[%s] ODM%d at %d decoded packed frame TS %d in %d ms\n", codec->decio->module_name, codec->odm->OD->objectDescriptorID, gf_clock_real_time(ch->clock), AU->CTS, now)); if (ch->skip_sl) { if (codec->bytes_per_sec) { codec->cur_audio_bytes += unit_size; @@ -841,7 +835,7 @@ scalable_retry: processing a scalable stream*/ case GF_OK: if (unit_size) { - GF_LOG(GF_LOG_DEBUG, GF_LOG_RTI|GF_LOG_CODEC, ("[%s] ODM%d at %d decoded frame TS %d in %d ms (DTS %d) - %d in CB\n", codec->decio->module_name, codec->odm->OD->objectDescriptorID, gf_clock_real_time(ch->clock), AU->CTS, now, AU->DTS, codec->CB->UnitCount + 1)); + GF_LOG(GF_LOG_DEBUG, GF_LOG_CODEC, ("[%s] ODM%d at %d decoded frame TS %d in %d ms (DTS %d) - %d in CB\n", codec->decio->module_name, codec->odm->OD->objectDescriptorID, gf_clock_real_time(ch->clock), AU->CTS, now, AU->DTS, codec->CB->UnitCount + 1)); } /*if no size the decoder is not using the composition memory - if the object is in intitial buffering resume it!!*/ else if (codec->CB->Status == CB_BUFFER) { @@ -1012,7 +1006,7 @@ GF_Err gf_codec_get_capability(GF_Codec *codec, GF_CodecCapability *cap) if (codec->flags & GF_ESM_CODEC_IS_RAW_MEDIA) { GF_BitStream *bs; - u32 pf, w, h, stride, out_size, sr, nb_ch, bpp, ch_cfg; + u32 pf, w, h, stride=0, out_size, sr, nb_ch, bpp, ch_cfg; GF_Channel *ch = gf_list_get(codec->odm->channels, 0); if (!ch || !ch->esd->decoderConfig->decoderSpecificInfo || !ch->esd->decoderConfig->decoderSpecificInfo->data) return 0; bs = gf_bs_new(ch->esd->decoderConfig->decoderSpecificInfo->data, ch->esd->decoderConfig->decoderSpecificInfo->dataLength, GF_BITSTREAM_READ); @@ -1060,6 +1054,15 @@ GF_Err gf_codec_get_capability(GF_Codec *codec, GF_CodecCapability *cap) case GF_CODEC_CHANNEL_CONFIG: cap->cap.valueInt = ch_cfg; return GF_OK; + case GF_CODEC_PAR: + cap->cap.valueInt = 0; + return GF_OK; + case GF_CODEC_PADDING_BYTES: + cap->cap.valueInt = 0; + return GF_OK; + case GF_CODEC_RESILIENT: + cap->cap.valueInt = 1; + return GF_OK; } } return GF_BAD_PARAM; @@ -1120,18 +1123,9 @@ static GF_Err Codec_LoadModule(GF_Codec *codec, GF_ESD *esd, u32 PL) GF_BaseDecoder *ifce, *dec_ifce; u32 i, plugCount; u32 ifce_type; - char *cfg; - u32 cfg_size, dec_confidence; + u32 dec_confidence; GF_Terminal *term = codec->odm->term; - if (esd->decoderConfig->decoderSpecificInfo) { - cfg = esd->decoderConfig->decoderSpecificInfo->data; - cfg_size = esd->decoderConfig->decoderSpecificInfo->dataLength; - } else { - cfg = NULL; - cfg_size = 0; - } - switch (esd->decoderConfig->streamType) { case GF_STREAM_AUDIO: case GF_STREAM_VISUAL: diff --git a/src/terminal/input_sensor.c b/src/terminal/input_sensor.c index 0a427fe..a79f127 100644 --- a/src/terminal/input_sensor.c +++ b/src/terminal/input_sensor.c @@ -603,7 +603,7 @@ void gf_term_mouse_input(GF_Terminal *term, GF_EventMouse *event) gf_free(buf); } -void gf_term_keyboard_input(GF_Terminal *term, u32 key_code, u32 hw_code, Bool isKeyUp) +Bool gf_term_keyboard_input(GF_Terminal *term, u32 key_code, u32 hw_code, Bool isKeyUp) { u32 i; GF_BitStream *bs; @@ -618,7 +618,7 @@ void gf_term_keyboard_input(GF_Terminal *term, u32 key_code, u32 hw_code, Bool i GF_Codec *cod; s32 keyPressed, keyReleased, actionKeyPressed, actionKeyReleased; - if (!term || (!gf_list_count(term->input_streams) && !gf_list_count(term->x3d_sensors)) ) return; + if (!term || (!gf_list_count(term->input_streams) && !gf_list_count(term->x3d_sensors)) ) return 0; memset(&slh, 0, sizeof(GF_SLHeader)); slh.accessUnitStartFlag = slh.accessUnitEndFlag = 1; @@ -724,7 +724,7 @@ void gf_term_keyboard_input(GF_Terminal *term, u32 key_code, u32 hw_code, Bool i char szStr[10]; const unsigned short *ptr; if (gf_node_get_tag((GF_Node*)n) != TAG_X3D_KeySensor) continue; - if (!n->enabled) return; + if (!n->enabled) return 0; if (keyPressed) { if (n->keyPress.buffer) gf_free(n->keyPress.buffer); @@ -777,6 +777,8 @@ void gf_term_keyboard_input(GF_Terminal *term, u32 key_code, u32 hw_code, Bool i } } #endif + /*with KeySensor, we don't know if the key will be consumed or not, assume it is*/ + return 1; } void gf_term_string_input(GF_Terminal *term, u32 character) @@ -806,7 +808,7 @@ void gf_term_string_input(GF_Terminal *term, u32 character) ISPriv *is = (ISPriv *)cod->decio->privateStack; if (is->type==IS_StringSensor) { - GF_Channel *ch = (GF_Channel *)gf_list_get(cod->inChannels, 0); +// GF_Channel *ch = (GF_Channel *)gf_list_get(cod->inChannels, 0); is->enteredText[is->text_len] = character; is->text_len += 1; @@ -818,7 +820,8 @@ void gf_term_string_input(GF_Terminal *term, u32 character) gf_bs_get_content(bs, &buf, &buf_size); gf_bs_del(bs); - gf_es_receive_sl_packet(ch->service, ch, buf, buf_size, &slh, GF_OK); +// gf_es_receive_sl_packet(ch->service, ch, buf, buf_size, &slh, GF_OK); + IS_ProcessData((GF_SceneDecoder*)cod->decio, buf, buf_size, 0, 0, 0); gf_free(buf); } diff --git a/src/terminal/media_control.c b/src/terminal/media_control.c index d3485a4..d7284e7 100644 --- a/src/terminal/media_control.c +++ b/src/terminal/media_control.c @@ -346,7 +346,10 @@ void RenderMediaControl(GF_Node *node, void *rs, Bool is_destroy) /*OD not ready yet*/ if (!stack->ck) { stack->stream = NULL; - if (stack->control->url.count) gf_term_invalidate_compositor(stack->parent->root_od->term); + if (stack->control->url.count) { + stack->is_init = 0; + gf_term_invalidate_compositor(stack->parent->root_od->term); + } return; } gf_sg_vrml_field_copy(&stack->url, &stack->control->url, GF_SG_VRML_MFURL); @@ -385,7 +388,14 @@ void RenderMediaControl(GF_Node *node, void *rs, Bool is_destroy) stack->media_stop = stack->control->mediaStopTime; stack->is_init = 1; /*the object has already been started, and media start time is not 0, restart*/ - if (stack->stream->num_open && (stack->media_start > 0) ) mediacontrol_restart(odm); + if (stack->stream->num_open) { + if (stack->media_start > 0) { + mediacontrol_restart(odm); + } else if (stack->media_speed == 0) { + mediacontrol_pause(odm); + stack->paused = 1; + } + } return; } diff --git a/src/terminal/media_manager.c b/src/terminal/media_manager.c index 19506b3..86d7538 100644 --- a/src/terminal/media_manager.c +++ b/src/terminal/media_manager.c @@ -149,6 +149,8 @@ void gf_term_add_codec(GF_Terminal *term, GF_Codec *codec) } else if (term->flags & GF_TERM_SINGLE_THREAD) { threaded = 0; } + if (codec->flags & GF_ESM_CODEC_IS_RAW_MEDIA) + threaded = 0; if (threaded) { cd->thread = gf_th_new(cd->dec->decio->module_name); @@ -342,19 +344,6 @@ static u32 MM_SimulationStep_Decoder(GF_Terminal *term) return time_left; } -u32 MM_SimulationStep_Compositor(GF_Terminal *term, u32 time_left) -{ - u32 time_taken = gf_sys_clock(); - gf_sc_draw_frame(term->compositor); - time_taken = gf_sys_clock() - time_taken; - if (time_left>time_taken) - time_left -= time_taken; - else - time_left = 0; - - return time_left; -} - u32 MM_Loop(void *par) { GF_Terminal *term = (GF_Terminal *) par; @@ -371,8 +360,15 @@ u32 MM_Loop(void *par) if (do_codec) left = MM_SimulationStep_Decoder(term); else left = term->frame_duration; - if (do_scene) left = MM_SimulationStep_Compositor(term, left); - + if (do_scene) { + u32 time_taken = gf_sys_clock(); + gf_sc_draw_frame(term->compositor); + time_taken = gf_sys_clock() - time_taken; + if (left>time_taken) + left -= time_taken; + else + left = 0; + } if (do_regulate) gf_sleep(left); } @@ -551,7 +547,7 @@ void gf_term_set_threading(GF_Terminal *term, u32 mode) if (ce->flags & GF_MM_CE_THREADED) { /*wait for thread to die*/ - while (!(ce->flags & GF_MM_CE_DEAD)) gf_sleep(0); + while (!(ce->flags & GF_MM_CE_DEAD)) gf_sleep(1); ce->flags &= ~GF_MM_CE_DEAD; gf_th_del(ce->thread); ce->thread = NULL; @@ -619,23 +615,27 @@ void gf_term_set_priority(GF_Terminal *term, s32 Priority) } GF_EXPORT -GF_Err gf_term_process_step(GF_Terminal *term) +u32 gf_term_process_step(GF_Terminal *term) { - u32 left = 0; + u32 time_taken = gf_sys_clock(); if (term->flags & GF_TERM_NO_DECODER_THREAD) { - left = MM_SimulationStep_Decoder(term); - } else { - left = term->frame_duration; - } + MM_SimulationStep_Decoder(term); + } if (term->flags & GF_TERM_NO_COMPOSITOR_THREAD) { - left = MM_SimulationStep_Compositor(term, left); + gf_sc_draw_frame(term->compositor); } - if (term->user->init_flags & GF_TERM_NO_REGULATION) return GF_OK; + time_taken = gf_sys_clock() - time_taken; + if (time_taken > term->compositor->frame_duration) { + time_taken = 0; + } else { + time_taken = term->compositor->frame_duration - time_taken; + } + if (term->user->init_flags & GF_TERM_NO_REGULATION) return time_taken; - gf_sleep(left); - return GF_OK; + gf_sleep(time_taken); + return time_taken; } GF_EXPORT diff --git a/src/terminal/media_memory.c b/src/terminal/media_memory.c index 892c131..4c8741e 100644 --- a/src/terminal/media_memory.c +++ b/src/terminal/media_memory.c @@ -358,6 +358,8 @@ void gf_cm_unlock_input(GF_CompositionMemory *cb, GF_CMUnit *cu, u32 cu_size, Bo gf_clock_buffer_off(cb->odm->codec->ck); cb->odm->codec->ck->data_timeout = 0; GF_LOG(GF_LOG_DEBUG, GF_LOG_SYNC, ("[SyncLayer] ODM%d: buffering off at %d (nb buffering on clock: %d)\n", cb->odm->OD->objectDescriptorID, gf_term_get_time(cb->odm->term), cb->odm->codec->ck->Buffering)); + + gf_term_service_media_event(cb->odm->parentscene->root_od, GF_EVENT_MEDIA_CANPLAY); } /*since a new CU is here notify the compositor*/ @@ -551,14 +553,17 @@ void gf_cm_drop_output(GF_CompositionMemory *cb) /*this allows reuse of the CU*/ cb->output->RenderedLength = 0; cb->LastRenderedTS = cb->output->TS; - if (cb->odm->raw_frame_sema) { - cb->output->dataLength = 0; - gf_sema_notify(cb->odm->raw_frame_sema, 1); - } + + /*WARNING: in RAW mode, we (for the moment) only have one unit - setting output->dataLength to 0 means the input is available + for the raw channel - we have to make sure the output is completely reseted before releasing the sema*/ /*on visual streams (except raw oness), always keep the last AU*/ if (!cb->no_allocation && cb->output->dataLength && (cb->odm->codec->type == GF_STREAM_VISUAL) ) { if ( !cb->output->next->dataLength || (cb->Capacity == 1) ) { + if (cb->odm->raw_frame_sema) { + cb->output->dataLength = 0; + gf_sema_notify(cb->odm->raw_frame_sema, 1); + } return; } } @@ -572,6 +577,10 @@ void gf_cm_drop_output(GF_CompositionMemory *cb) if (!cb->HasSeenEOS && cb->UnitCount <= cb->Min) { cb->odm->codec->PriorityBoost = 1; } + + if (cb->odm->raw_frame_sema) { + gf_sema_notify(cb->odm->raw_frame_sema, 1); + } } void gf_cm_set_status(GF_CompositionMemory *cb, u32 Status) diff --git a/src/terminal/media_object.c b/src/terminal/media_object.c index 36367e7..d6dec1f 100644 --- a/src/terminal/media_object.c +++ b/src/terminal/media_object.c @@ -120,6 +120,9 @@ GF_MediaObject *gf_mo_register(GF_Node *node, MFURL *url, Bool lock_timelines, B case TAG_MPEG4_AnimationStream: obj_type = GF_MEDIA_OBJECT_UPDATES; break; + case TAG_MPEG4_BitWrapper: + obj_type = GF_MEDIA_OBJECT_SCENE; + break; case TAG_MPEG4_InputSensor: obj_type = GF_MEDIA_OBJECT_INTERACT; break; @@ -181,9 +184,6 @@ GF_MediaObject *gf_mo_register(GF_Node *node, MFURL *url, Bool lock_timelines, B scene = scene->root_od->parentscene; res = gf_scene_get_media_object_ex(scene, url, obj_type, lock_timelines, syncRef, force_new_res, node); - - if (res) { - } return res; } @@ -235,6 +235,36 @@ Bool gf_mo_get_visual_info(GF_MediaObject *mo, u32 *width, u32 *height, u32 *str cap.CapCode = GF_CODEC_PIXEL_FORMAT; gf_codec_get_capability(mo->odm->codec, &cap); *pixelFormat = cap.cap.valueInt; + + if (mo->odm && mo->odm->parentscene->is_dynamic_scene) { + const char *name = gf_node_get_name(gf_list_get(mo->nodes, 0)); + if (name && !strcmp(name, "DYN_VIDEO")) { + const char *opt; + u32 r, g, b, a; + M_Background2D *back = (M_Background2D *) gf_sg_find_node_by_name(mo->odm->parentscene->graph, "DYN_BACK"); + if (back) { + switch (cap.cap.valueInt) { + case GF_PIXEL_ARGB: + case GF_PIXEL_RGBA: + case GF_PIXEL_YUVA: + opt = gf_cfg_get_key(mo->odm->term->user->config, "Compositor", "BackColor"); + if (!opt) { + gf_cfg_set_key(mo->odm->term->user->config, "Compositor", "BackColor", "FF999999"); + opt = "FF999999"; + } + sscanf(opt, "%02X%02X%02X%02X", &a, &r, &g, &b); + back->backColor.red = INT2FIX(r)/255; + back->backColor.green = INT2FIX(g)/255; + back->backColor.blue = INT2FIX(b)/255; + break; + default: + back->backColor.red = back->backColor.green = back->backColor.blue = FIX_ONE; + break; + } + gf_node_dirty_set((GF_Node *)back, 0, 1); + } + } + } } /*get PAR settings*/ if (pixel_ar) { @@ -417,8 +447,12 @@ char *gf_mo_fetch_data(GF_MediaObject *mo, Bool resync, Bool *eos, u32 *timestam mo->frame = CU->data + CU->RenderedLength; if (mo->timestamp != CU->TS) { #ifndef GPAC_DISABLE_VRML - mediasensor_update_timing(mo->odm, *eos); + mediasensor_update_timing(mo->odm, mo->odm->codec->CB->HasSeenEOS); #endif + + if (mo->odm->parentscene->is_dynamic_scene) + mo->odm->parentscene->root_od->current_time = mo->odm->current_time; + mo->timestamp = CU->TS; GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("[ODM%d] At OTB %d fetch frame TS %d size %d - %d unit in CB\n", mo->odm->OD->objectDescriptorID, gf_clock_time(mo->odm->codec->ck), mo->timestamp, mo->framesize, mo->odm->codec->CB->UnitCount)); /*signal EOS after rendering last frame, not while rendering it*/ @@ -598,11 +632,14 @@ Bool gf_mo_stop(GF_MediaObject *mo) mo->num_open--; if (!mo->num_open && mo->odm) { + if (mo->odm->flags & GF_ODM_DESTROYED) return 1; + /*do not stop directly, this can delete channel data currently being decoded (BIFS anim & co)*/ gf_term_lock_media_queue(mo->odm->term, 1); /*if object not in media queue, add it*/ - if (gf_list_find(mo->odm->term->media_queue, mo->odm)<0) + if (gf_list_find(mo->odm->term->media_queue, mo->odm)<0) { gf_list_add(mo->odm->term->media_queue, mo->odm); + } /*signal STOP request*/ if ((mo->OD_ID==GF_MEDIA_EXTERNAL_ID) || (mo->odm && mo->odm->OD && (mo->odm->OD->objectDescriptorID==GF_MEDIA_EXTERNAL_ID))) { @@ -706,15 +743,10 @@ Bool gf_mo_is_same_url(GF_MediaObject *obj, MFURL *an_url, Bool *keep_fragment, u32 i; char szURL1[GF_MAX_PATH], szURL2[GF_MAX_PATH], *ext; - if (obj->OD_ID==GF_MEDIA_EXTERNAL_ID) { - if (!obj->URLs.count) { - if (!obj->odm) return 0; - strcpy(szURL1, obj->odm->net_service->url); - } else { - strcpy(szURL1, obj->URLs.vals[0].url); - } + if (!obj->URLs.count) { + if (!obj->odm) return 0; + strcpy(szURL1, obj->odm->net_service->url); } else { - if (!obj->URLs.count) return 0; strcpy(szURL1, obj->URLs.vals[0].url); } @@ -766,6 +798,8 @@ Bool gf_mo_is_same_url(GF_MediaObject *obj, MFURL *an_url, Bool *keep_fragment, for (i=0; icount; i++) { if (an_url->vals[i].url && !stricmp(szURL1, an_url->vals[i].url)) return 1; } + /*not same resource, we will have to check fragment as URL might point to a sub-service or single stream of a mux*/ + if (keep_fragment) *keep_fragment = 1; return 0; } ext = strrchr(szURL1, '#'); @@ -835,6 +869,8 @@ void gf_mo_set_speed(GF_MediaObject *mo, Fixed speed) ctrl = gf_odm_get_mediacontrol(mo->odm); if (ctrl) return; #endif + if (mo->odm->net_service && (mo->odm->net_service->owner->flags & GF_ODM_INHERIT_TIMELINE)) + return; gf_odm_set_speed(mo->odm, speed); } @@ -1034,7 +1070,7 @@ Bool gf_mo_set_position(GF_MediaObject *mo, GF_Window *src, GF_Window *dst) } GF_EXPORT -Bool gf_mo_has_audio(GF_MediaObject *mo) +u32 gf_mo_has_audio(GF_MediaObject *mo) { char *sub_url, *ext; u32 i; @@ -1043,6 +1079,7 @@ Bool gf_mo_has_audio(GF_MediaObject *mo) GF_Scene *scene; if (!mo || !mo->odm) return 0; if (mo->type != GF_MEDIA_OBJECT_VIDEO) return 0; + if (!mo->odm->net_service) return 2; ns = mo->odm->net_service; scene = mo->odm->parentscene; diff --git a/src/terminal/media_sensor.c b/src/terminal/media_sensor.c index 9c5afd0..4988b47 100644 --- a/src/terminal/media_sensor.c +++ b/src/terminal/media_sensor.c @@ -110,7 +110,7 @@ void MS_Modified(GF_Node *node) gf_node_event_out((GF_Node *) st->sensor, 4/*"isActive"*/); } } - st->stream = gf_mo_register(node, &st->sensor->url, 0, 0); + st->stream = NULL; st->is_init = 0; gf_term_invalidate_compositor(st->parent->root_od->term); } @@ -137,6 +137,10 @@ void mediasensor_update_timing(GF_ObjectManager *odm, Bool is_eos) GF_Clock *ck = gf_odm_get_media_clock(odm); if (ck->has_seen_eos && (1000*time>=(Double) (s64)odm->subscene->duration)) { if (media_sens->sensor->isActive) { + /*force notification of time (ntify the scene duration rather than the current clock*/ + media_sens->sensor->mediaCurrentTime = (Double) odm->subscene->duration; + media_sens->sensor->mediaCurrentTime /= 1000; + gf_node_event_out((GF_Node *) media_sens->sensor, 1/*"mediaCurrentTime"*/); media_sens->sensor->isActive = 0; gf_node_event_out((GF_Node *) media_sens->sensor, 4/*"isActive"*/); @@ -161,13 +165,21 @@ void mediasensor_update_timing(GF_ObjectManager *odm, Bool is_eos) gf_node_event_out((GF_Node *) media_sens->sensor, 3/*"mediaDuration"*/); } - if (media_sens->sensor->isActive && (media_sens->sensor->mediaCurrentTime != time)) { - media_sens->sensor->mediaCurrentTime = time; - gf_node_event_out((GF_Node *) media_sens->sensor, 1/*"mediaCurrentTime"*/); - } + if (is_eos && media_sens->sensor->isActive) { + if (media_sens->sensor->mediaDuration>=0) { + media_sens->sensor->mediaCurrentTime = media_sens->sensor->mediaDuration; + } else { + media_sens->sensor->mediaCurrentTime = time; + } + gf_node_event_out((GF_Node *) media_sens->sensor, 1/*"mediaCurrentTime"*/); media_sens->sensor->isActive = 0; gf_node_event_out((GF_Node *) media_sens->sensor, 4/*"isActive"*/); + } else { + if (media_sens->sensor->isActive && (media_sens->sensor->mediaCurrentTime != time)) { + media_sens->sensor->mediaCurrentTime = time; + gf_node_event_out((GF_Node *) media_sens->sensor, 1/*"mediaCurrentTime"*/); + } } continue; } diff --git a/src/terminal/mpeg4_inline.c b/src/terminal/mpeg4_inline.c index d2a5d0f..6a2b556 100644 --- a/src/terminal/mpeg4_inline.c +++ b/src/terminal/mpeg4_inline.c @@ -139,10 +139,24 @@ void gf_inline_on_modified(GF_Node *node) } } } - } else { - gf_node_dirty_parents(node); + } + /*force a redraw and load scene at next pass - we cannot load the scene now because + - we can be in a JS call (eg JS mutex blocked) + - locating scene objects matching the new url needs exclusive access to the MediaObject list, achieved with the term net mutex + - another service may already be setting up objects (eg exclusive access to the net mutex grabbed), which can trigger event forwarding + - some event forwarders may request JS context (eg access to JS mutex) + + In such a case we would end up in a deadlock - this needs urgent fixing ... + */ + + if (ODID) { + /*if no parent we must process the url change as we may not be traversed later on (not in the scene tree)*/ + if (gf_node_get_parent(node, 0)==NULL) { + gf_inline_set_scene(pInline); + } else { + gf_node_dirty_parents(node); + } } - if (ODID) gf_inline_set_scene(pInline); } static void gf_inline_check_restart(GF_Scene *scene) @@ -170,9 +184,12 @@ static void gf_inline_check_restart(GF_Scene *scene) e = -1; MC_GetRange(scene->root_od->media_ctrl, &s, &e); if ((e>=0) && (eneeds_restart = 1; scene->root_od->media_ctrl->current_seg = 0; + } else { + /*trigger render until to watch for restart...*/ + gf_term_invalidate_compositor(scene->root_od->term); } } } else { @@ -334,7 +351,9 @@ GF_SceneGraph *gf_inline_get_proto_lib(void *_is, MFURL *lib_url) i=0; while ((pl = (GF_ProtoLink*)gf_list_enum(scene->extern_protos, &i))) { - if (!pl->mo) continue; + /*not ready yet*/ + if (!pl->mo || !pl->mo->odm || ! pl->mo->odm->net_service) continue; + if (gf_mo_get_od_id(pl->url) != GF_MEDIA_EXTERNAL_ID) { if (gf_mo_get_od_id(pl->url) == gf_mo_get_od_id(lib_url)) { if (!pl->mo->odm || !pl->mo->odm->subscene) return NULL; @@ -352,6 +371,7 @@ GF_SceneGraph *gf_inline_get_proto_lib(void *_is, MFURL *lib_url) char *url1, *url2; Bool ok; if (!pl->mo) continue; + if (! pl->mo->odm->net_service) continue; if (gf_mo_get_od_id(pl->url) != GF_MEDIA_EXTERNAL_ID) continue; /*not the same url*/ if (!gf_mo_is_same_url(pl->mo, lib_url, NULL, 0)) continue; diff --git a/src/terminal/network_service.c b/src/terminal/network_service.c index 77fdce5..85015f1 100644 --- a/src/terminal/network_service.c +++ b/src/terminal/network_service.c @@ -26,6 +26,7 @@ #include #include #include "media_memory.h" +#include "media_control.h" @@ -77,17 +78,17 @@ static void term_on_connect(void *user_priv, GF_ClientService *service, LPNETCHA } /*this is service connection*/ if (!netch) { - gf_term_service_media_event(service->owner, GF_EVENT_MEDIA_END_SESSION_SETUP); + gf_term_service_media_event(service->owner, GF_EVENT_MEDIA_SETUP_DONE); if (err) { char msg[5000]; snprintf(msg, sizeof(msg), "Cannot open %s", service->url); gf_term_message(term, service->url, msg, err); - gf_term_service_media_event(service->owner, GF_EVENT_MEDIA_ERROR); + gf_term_service_media_event(service->owner, GF_EVENT_ERROR); /*destroy service only if attached*/ if (root) { - gf_mx_p(term->media_queue_mx); + gf_term_lock_media_queue(term, 1); service->ifce->CloseService(service->ifce); root->net_service = NULL; if (service->owner && service->nb_odm_users) service->nb_odm_users--; @@ -97,7 +98,7 @@ static void term_on_connect(void *user_priv, GF_ClientService *service, LPNETCHA /*and queue for destroy*/ gf_list_add(term->net_services_to_remove, service); } - gf_mx_v(term->media_queue_mx); + gf_term_lock_media_queue(term, 0); if (!root->parentscene) { GF_Event evt; @@ -214,13 +215,21 @@ static void term_on_disconnect(void *user_priv, GF_ClientService *service, LPNET } /*this is service disconnect*/ if (!netch) { - gf_mx_p(term->media_queue_mx); + if (service->subservice_disconnect) { + if (service->owner && service->subservice_disconnect==1) { + GF_Scene *scene = service->owner->subscene ? service->owner->subscene : service->owner->parentscene; + /*destrou all media*/ + gf_scene_disconnect(scene, 1); + } + return; + } + gf_term_lock_media_queue(term, 1); /*unregister from valid services*/ if (gf_list_del_item(term->net_services, service)>=0) { /*and queue for destroy*/ gf_list_add(term->net_services_to_remove, service); } - gf_mx_v(term->media_queue_mx); + gf_term_lock_media_queue(term, 0); return; } /*this is channel disconnect*/ @@ -273,10 +282,14 @@ static void term_on_media_add(void *user_priv, GF_ClientService *service, GF_Des GET_TERM(); root = service->owner; - if (!root){ + if (!root) { GF_LOG(GF_LOG_ERROR, GF_LOG_MEDIA, ("[Service %s] has not root, aborting !\n", service->url)); return; } + if (root->flags & GF_ODM_DESTROYED) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MEDIA, ("[Service %s] root has been scheduled for destruction - aborting !\n", service->url)); + return; + } scene = root->subscene ? root->subscene : root->parentscene; GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("[Service %s] %s\n", service->url, media_desc ? "Adding new media object" : "Regenerating scene graph")); @@ -309,17 +322,21 @@ static void term_on_media_add(void *user_priv, GF_ClientService *service, GF_Des char *frag, *ext; GF_ESD *esd; char *url; + u32 match_esid = 0; GF_MediaObject *mo = gf_list_get(scene->scene_objects, i); - if (!mo->odm) continue; if ((mo->OD_ID != GF_MEDIA_EXTERNAL_ID) && (min_od_idOD_ID)) min_od_id = mo->OD_ID; + if (!mo->odm) continue; + /*if object is attached to a service, don't bother looking in a different one*/ + if (mo->odm->net_service && (mo->odm->net_service != service)) continue; + /*already assigned object - this may happen since the compositor has no control on when objects are declared by the service, therefore opening file#video and file#audio may result in the objects being declared twice if the service doesn't keep track of declared objects*/ if (mo->odm->OD) { - if (is_same_od(mo->odm->OD, od)) { + if (od->objectDescriptorID && is_same_od(mo->odm->OD, od)) { /*reassign OD ID*/ mo->OD_ID = od->objectDescriptorID; gf_odf_desc_del(media_desc); @@ -348,14 +365,17 @@ static void term_on_media_add(void *user_priv, GF_ClientService *service, GF_Des if (!strnicmp(url, "file://localhost", 16)) url += 16; else if (!strnicmp(url, "file://", 7)) url += 7; else if (!strnicmp(url, "gpac://", 7)) url += 7; + else if (!strnicmp(url, "pid://", 6)) match_esid = atoi(url+6); - if (!strstr(service->url, url)) { + if (!match_esid && !strstr(service->url, url)) { if (ext) ext[0] = '#'; continue; } if (ext) ext[0] = '#'; esd = gf_list_get(od->ESDescriptors, 0); + if (match_esid && (esd->ESID != match_esid)) + continue; /*match type*/ switch (esd->decoderConfig->streamType) { case GF_STREAM_VISUAL: @@ -385,6 +405,17 @@ static void term_on_media_add(void *user_priv, GF_ClientService *service, GF_Des odm = mo->odm; break; } + + /*add a pass on scene->resource to check for min_od_id, + otherwise we may have another modules declaring an object with ID 0 from + another thread, which will assert (only one object with a givne OD ID)*/ + for (i=0; iresources); i++) { + GF_ObjectManager *an_odm = gf_list_get(scene->resources, i); + + if (an_odm->OD && (an_odm->OD->objectDescriptorID != GF_MEDIA_EXTERNAL_ID) && (min_od_id < an_odm->OD->objectDescriptorID)) + min_od_id = an_odm->OD->objectDescriptorID; + } + if (!odm) { odm = gf_odm_new(); odm->term = term; @@ -402,13 +433,14 @@ static void term_on_media_add(void *user_priv, GF_ClientService *service, GF_Des if (!scene->selected_service_id) scene->selected_service_id = od->ServiceID; - GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("[ODM%d] setup object - MO %08x\n", odm->OD->objectDescriptorID, odm->mo)); - gf_odm_setup_object(odm, service); - /*unlock net once the object has been added to the scene, otherwise we may have another modules declaring an object with ID 0 from - another thread, which will assert (only one object with a givne OD ID)*/ + /*net is unlocked before seting up the object as this might trigger events going into JS and deadlocks + with the compositor*/ gf_term_lock_net(term, 0); + GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("[ODM%d] setup object - MO %08x\n", odm->OD->objectDescriptorID, odm->mo)); + gf_odm_setup_object(odm, service); + /*OD inserted by service: resetup scene*/ if (!no_scene_check && scene->is_dynamic_scene) gf_scene_regenerate(scene); } @@ -443,6 +475,8 @@ static void term_on_command(void *user_priv, GF_ClientService *service, GF_Netwo /*get exclusive access to media scheduler, to make sure ODs are not being manipulated*/ gf_mx_p(term->mm_mx); + if (!gf_list_count(od_list)) + GF_LOG(GF_LOG_WARNING, GF_LOG_MEDIA, ("[ODM] No object manager found for the scene (URL: %s), buffer occupancy will remain unchanged\n", service->url)); i=0; while ((odm = (GF_ObjectManager*)gf_list_enum(od_list, &i))) { u32 j, count; @@ -496,7 +530,27 @@ static void term_on_command(void *user_priv, GF_ClientService *service, GF_Netwo case GF_NET_CHAN_MAP_TIME: ch->seed_ts = com->map_time.timestamp; ch->ts_offset = (u32) (com->map_time.media_time*1000); - if (com->map_time.reset_buffers) gf_es_reset_buffers(ch); + GF_LOG(GF_LOG_INFO, GF_LOG_SYNC, ("[SyncLayer] ES%d: mapping TS "LLD" to media time %f - current time %d\n", ch->esd->ESID, com->map_time.timestamp, com->map_time.media_time, gf_clock_time(ch->clock))); + + if (com->map_time.reset_buffers) { + gf_es_reset_buffers(ch); + } + /*if we were reassembling an AU, do not perform clock init check when dispatching it since we computed its timestamps + according to the previous clock origin*/ + else { + gf_mx_p(ch->mx); + ch->skip_time_check_for_pending = 1; + gf_mx_v(ch->mx); + } + /*if the channel is the clock, force a re-init*/ + if (gf_es_owns_clock(ch)) { + ch->IsClockInit = 0; + gf_clock_reset(ch->clock); + } + else if (ch->odm->flags & GF_ODM_INHERIT_TIMELINE) { + ch->IsClockInit = 0; +// ch->ts_offset -= ch->seed_ts*1000/ch->ts_res; + } break; /*duration changed*/ case GF_NET_CHAN_DURATION: @@ -560,7 +614,10 @@ static char *get_mime_type(GF_Terminal *term, const char *url, GF_Err *ret_code, if (*ret_code) break; if (gf_dm_sess_get_status(sess)>=GF_NETIO_DATA_EXCHANGE) { const char * mime = gf_dm_sess_mime_type(sess); - if (mime) ret = gf_strdup(mime); + /* The mime type is returned lower case */ + if (mime){ + ret = gf_strdup(mime); + } break; } } @@ -798,8 +855,9 @@ exit: GF_ClientService *gf_term_service_new(GF_Terminal *term, struct _od_manager *owner, const char *url, const char *parent_url, GF_Err *ret_code) { - GF_DownloadSession *download_session; + GF_DownloadSession *download_session = NULL; char *sURL; + const char *opt; GF_ClientService *serv; GF_InputService *ifce = gf_term_can_handle_service(term, url, parent_url, 0, &sURL, ret_code, &download_session); if (!ifce) return NULL; @@ -812,6 +870,20 @@ GF_ClientService *gf_term_service_new(GF_Terminal *term, struct _od_manager *own serv->Clocks = gf_list_new(); serv->dnloads = gf_list_new(); serv->pending_service_session = download_session; + + opt = gf_cfg_get_key(term->user->config, "Network", "HTTPRebuffer"); + if (!opt) { + opt = "5000"; + gf_cfg_set_key(term->user->config, "Network", "HTTPRebuffer", "5000"); + } + serv->download_rebuffer = atoi(opt); + opt = gf_cfg_get_key(term->user->config, "Network", "HTTPAutoRebuffer"); + if (!opt) { + opt = "no"; + gf_cfg_set_key(term->user->config, "Network", "HTTPAutoRebuffer", "no"); + } + serv->auto_rebuffer = !strcmp(opt, "yes") ? 1 : 0; + gf_list_add(term->net_services, serv); return serv; @@ -990,6 +1062,7 @@ GF_DownloadSession *gf_term_download_new(GF_ClientService *service, const char * GF_EXPORT void gf_term_download_del(GF_DownloadSession * sess) { + Bool locked; GF_ClientService *serv; if (!sess) return; serv = (GF_ClientService *)gf_dm_sess_get_private(sess); @@ -997,7 +1070,7 @@ void gf_term_download_del(GF_DownloadSession * sess) /*avoid sending data back to user*/ gf_dm_sess_abort(sess); - gf_mx_p(serv->term->media_queue_mx); + locked = gf_mx_try_lock(serv->term->media_queue_mx); /*unregister from service*/ gf_list_del_item(serv->dnloads, sess); @@ -1005,7 +1078,9 @@ void gf_term_download_del(GF_DownloadSession * sess) /*same as service: this may be called in the downloader thread (typically when download fails) so we must queue the downloader and let the term delete it later on*/ gf_list_add(serv->term->net_services_to_remove, sess); - gf_mx_v(serv->term->media_queue_mx); + + if (locked) + gf_term_lock_media_queue(serv->term, 0); } GF_EXPORT @@ -1029,6 +1104,9 @@ void gf_term_download_update_stats(GF_DownloadSession * sess) case GF_NETIO_WAIT_FOR_REPLY: gf_term_on_message(serv, GF_OK, "Waiting for reply..."); break; + case GF_NETIO_PARSE_REPLY: + gf_term_on_message(serv, GF_OK, "Starting download..."); + break; case GF_NETIO_DATA_EXCHANGE: /*notify some connection / ...*/ if (total_size) { @@ -1042,6 +1120,70 @@ void gf_term_download_update_stats(GF_DownloadSession * sess) gf_term_send_event(serv->term, &evt); } GF_LOG(GF_LOG_DEBUG, GF_LOG_NETWORK, ("[HTTP] %s received %d / %d\n", szURI, bytes_done, total_size)); + gf_term_service_media_event_with_download(serv->owner, GF_EVENT_MEDIA_PROGRESS, bytes_done, total_size, bytes_per_sec); + + if ( (serv->download_rebuffer || serv->auto_rebuffer) && serv->owner && !(serv->owner->flags & GF_ODM_DESTROYED) && serv->owner->duration) { + GF_Clock *ck = gf_odm_get_media_clock(serv->owner); + Double download_percent, playback_percent, adj_percent; + download_percent = 100 * bytes_done; + download_percent /= total_size; + + playback_percent = 100 * serv->owner->current_time; + playback_percent /= serv->owner->duration; + if (serv->auto_rebuffer) + adj_percent = 0.0; + else + adj_percent = 100.0 * serv->download_rebuffer / serv->owner->duration; + + if (playback_percent >= download_percent) { + if (gf_clock_is_started(ck)) { + GF_LOG(GF_LOG_INFO, GF_LOG_NETWORK, ("[HTTP Resource] Played %d %% but downloaded %d %% - Pausing\n", (u32) playback_percent, (u32) download_percent)); + if (!serv->is_paused) { + serv->is_paused = 1; + mediacontrol_pause(serv->owner); + } + gf_term_service_media_event(serv->owner, GF_EVENT_MEDIA_WAITING); + gf_term_on_message(serv, GF_OK, "HTTP Buffering ..."); + } + } else if (playback_percent + adj_percent <= download_percent) { + Double time_to_play = 0; + Double time_to_download = 0; + /*automatic rebuffer: make sure we can finish playback before resuming*/ + if (serv->auto_rebuffer) { + if (bytes_per_sec) { + time_to_download = 1000.0*(total_size - bytes_done); + time_to_download /= bytes_per_sec; + } + time_to_play = (Double) serv->owner->duration; + time_to_play -= serv->owner->current_time; + } + if ((time_to_download<=time_to_play) && !gf_clock_is_started(ck)) { + GF_LOG(GF_LOG_INFO, GF_LOG_NETWORK, ("[HTTP Resource] Played %d %% and downloaded %d %% - Resuming\n", (u32) playback_percent, (u32) download_percent)); + if (serv->auto_rebuffer) { + GF_LOG(GF_LOG_INFO, GF_LOG_NETWORK, ("[HTTP Resource] Auto-rebuffer done: should be done downloading in %d ms and remains %d ms to play\n", (u32) time_to_download, (u32) (serv->owner->duration - serv->owner->current_time) )); + } + gf_term_service_media_event(serv->owner, GF_EVENT_MEDIA_PLAYING); + if (serv->is_paused) { + serv->is_paused = 0; + mediacontrol_resume(serv->owner); + } + gf_term_on_message(serv, GF_OK, "HTTP Resuming playback"); + } + } + } + break; + case GF_NETIO_DATA_TRANSFERED: + gf_term_service_media_event(serv->owner, GF_EVENT_MEDIA_LOAD_DONE); + if (serv->owner && !(serv->owner->flags & GF_ODM_DESTROYED) && serv->owner->duration) { + GF_Clock *ck = gf_odm_get_media_clock(serv->owner); + if (!gf_clock_is_started(ck)) { + GF_LOG(GF_LOG_INFO, GF_LOG_NETWORK, ("[HTTP Resource] Done retrieving file - resuming playback\n")); + if (serv->is_paused) { + serv->is_paused = 0; + mediacontrol_resume(serv->owner); + } + } + } break; } } diff --git a/src/terminal/object_browser.c b/src/terminal/object_browser.c index 227a82b..ed6fed0 100644 --- a/src/terminal/object_browser.c +++ b/src/terminal/object_browser.c @@ -148,14 +148,19 @@ GF_Err gf_term_get_object_info(GF_Terminal *term, GF_ObjectManager *odm, GF_Medi if (odm->codec->ck) info->current_time = odm->codec->CB ? odm->current_time : gf_clock_time(odm->codec->ck); info->current_time /= 1000; info->nb_droped = odm->codec->nb_droped; - } else if (odm->subscene && odm->subscene->scene_codec) { - if (odm->subscene->scene_codec->ck) { - info->current_time = gf_clock_time(odm->subscene->scene_codec->ck); + } else if (odm->subscene) { + if (odm->subscene->scene_codec) { + if (odm->subscene->scene_codec->ck) { + info->current_time = gf_clock_time(odm->subscene->scene_codec->ck); + info->current_time /= 1000; + } + info->duration = (Double) (s64)odm->subscene->duration; + info->duration /= 1000; + info->nb_droped = odm->subscene->scene_codec->nb_droped; + } else if (odm->subscene->is_dynamic_scene && odm->subscene->dyn_ck) { + info->current_time = gf_clock_time(odm->subscene->dyn_ck); info->current_time /= 1000; } - info->duration = (Double) (s64)odm->subscene->duration; - info->duration /= 1000; - info->nb_droped = odm->subscene->scene_codec->nb_droped; } info->buffer = -2; diff --git a/src/terminal/object_manager.c b/src/terminal/object_manager.c index 7ff6164..d1621a8 100644 --- a/src/terminal/object_manager.c +++ b/src/terminal/object_manager.c @@ -56,7 +56,6 @@ GF_ObjectManager *gf_odm_new() void gf_odm_del(GF_ObjectManager *odm) { - Bool lock; #ifndef GPAC_DISABLE_VRML u32 i; MediaSensorStack *media_sens; @@ -66,9 +65,17 @@ void gf_odm_del(GF_ObjectManager *odm) /*make sure we are not in the media queue*/ gf_term_lock_media_queue(odm->term, 1); gf_list_del_item(odm->term->media_queue, odm); + gf_term_check_connections_for_delete(odm->term, odm); gf_term_lock_media_queue(odm->term, 0); - lock = gf_mx_try_lock(odm->mx); + /*detach media object as referenced by the scene - this should ensures that any attempt to lock the ODM from the + compositor will fail as the media object is no longer linked to object manager*/ + gf_mx_p(odm->mx); + if (odm->mo) odm->mo->odm = NULL; + gf_mx_v(odm->mx); + + /*relock the mutex for final object destruction*/ + gf_mx_p(odm->mx); #ifndef GPAC_DISABLE_VRML i=0; @@ -86,16 +93,15 @@ void gf_odm_del(GF_ObjectManager *odm) } gf_list_del(odm->mc_stack); #endif - if (odm->mo) odm->mo->odm = NULL; if (odm->raw_frame_sema) gf_sema_del(odm->raw_frame_sema); gf_list_del(odm->channels); odm->channels = NULL; + assert (!odm->net_service); gf_odf_desc_del((GF_Descriptor *)odm->OD); odm->OD = NULL; - assert (!odm->net_service); - if (lock) gf_mx_v(odm->mx); + gf_mx_v(odm->mx); gf_mx_del(odm->mx); gf_free(odm); } @@ -122,9 +128,14 @@ Bool gf_odm_lock_mo(GF_MediaObject *mo) GF_EXPORT void gf_odm_disconnect(GF_ObjectManager *odm, Bool do_remove) { + GF_Terminal *term; GF_Channel *ch; - if (do_remove) odm->flags |= GF_ODM_DESTROYED; + if (do_remove) { + gf_mx_p(odm->term->net_mx); + odm->flags |= GF_ODM_DESTROYED; + gf_mx_v(odm->term->net_mx); + } gf_odm_stop(odm, 1); /*disconnect sub-scene*/ @@ -195,23 +206,34 @@ void gf_odm_disconnect(GF_ObjectManager *odm, Bool do_remove) if (odm->net_service) { GF_ClientService *ns = odm->net_service; if (ns->nb_odm_users) ns->nb_odm_users--; - //if (odm->flags & GF_ODM_SERVICE_ENTRY) - { - if (ns->owner == odm) { - /*detach it!!*/ - ns->owner = NULL; - /*try to assign a new root in case this is not scene shutdown*/ - if (ns->nb_odm_users && odm->parentscene) { - GF_ObjectManager *new_root; - u32 i = 0; - while ((new_root = (GF_ObjectManager *)gf_list_enum(odm->parentscene->resources, &i)) ) { - if (new_root == odm) continue; - if (new_root->net_service != ns) continue; - ns->owner = new_root; - break; + if (ns->owner == odm) { + /*detach it!!*/ + ns->owner = NULL; + /*try to assign a new root in case this is not scene shutdown*/ + if (ns->nb_odm_users && odm->parentscene) { + GF_ObjectManager *new_root; + u32 i = 0; + while ((new_root = (GF_ObjectManager *)gf_list_enum(odm->parentscene->resources, &i)) ) { + if (new_root == odm) continue; + if (new_root->net_service != ns) continue; + + /*if the new root is not playing or assoicated with the scene, force a destroy on it - this + is needed for services declaring their objects dynamically*/ + if (!new_root->mo || (!new_root->mo->num_open)) { + gf_term_lock_media_queue(odm->term, 1); + new_root->action_type = GF_ODM_ACTION_DELETE; + if (gf_list_find(odm->term->media_queue, new_root)<0) { + assert(! (new_root->flags & GF_ODM_DESTROYED)); + gf_list_add(odm->term->media_queue, new_root); + } + gf_term_lock_media_queue(odm->term, 0); } + ns->owner = new_root; + break; } } + } else { + assert(ns->nb_odm_users); } odm->net_service = NULL; if (!ns->nb_odm_users) gf_term_close_service(odm->term, ns); @@ -219,6 +241,8 @@ void gf_odm_disconnect(GF_ObjectManager *odm, Bool do_remove) gf_odm_lock(odm, 0); + term = odm->term; + /*delete from the parent scene.*/ if (odm->parentscene) { GF_Event evt; @@ -226,9 +250,11 @@ void gf_odm_disconnect(GF_ObjectManager *odm, Bool do_remove) evt.connect.is_connected = 0; gf_term_forward_event(odm->term, &evt, 0, 1); + gf_term_lock_net(term, 1); gf_scene_remove_object(odm->parentscene, odm, do_remove); if (odm->subscene) gf_scene_del(odm->subscene); gf_odm_del(odm); + gf_term_lock_net(term, 0); return; } @@ -245,8 +271,11 @@ void gf_odm_disconnect(GF_ObjectManager *odm, Bool do_remove) gf_term_send_event(odm->term, &evt); } + gf_term_lock_net(term, 1); /*delete the ODMan*/ gf_odm_del(odm); + + gf_term_lock_net(term, 0); } /*setup service for OD (extract IOD and go)*/ @@ -258,6 +287,10 @@ void gf_odm_setup_entry_point(GF_ObjectManager *odm, const char *service_sub_url GF_Terminal *term; GF_Descriptor *desc; + if (odm->flags & GF_ODM_DESTROYED) { + GF_LOG(GF_LOG_WARNING, GF_LOG_MEDIA, ("[ODM] Root object of service %s has been scheduled for destruction - ignoring object setup\n", service_sub_url)); + return; + } // assert(odm->OD==NULL); term = odm->term; @@ -287,8 +320,9 @@ void gf_odm_setup_entry_point(GF_ObjectManager *odm, const char *service_sub_url if (!desc) { /*if desc is NULL for a media, the media will be declared later by the service (gf_term_media_add)*/ - if (od_type != GF_MEDIA_OBJECT_SCENE) + if (od_type != GF_MEDIA_OBJECT_SCENE) { return; + } /*create empty service descriptor, this will automatically create a dynamic scene*/ desc = gf_odf_desc_new(GF_ODF_OD_TAG); } @@ -331,9 +365,7 @@ void gf_odm_setup_entry_point(GF_ObjectManager *odm, const char *service_sub_url goto err_exit; } - gf_term_lock_net(term, 1); gf_odm_setup_object(odm, odm->net_service); - gf_term_lock_net(term, 0); /*it may happen that this object was inserted in a dynamic scene from a service through a URL redirect. In which case, the scene regeneration might not have been completed since the redirection was not done yet - force a scene regenerate*/ @@ -423,7 +455,7 @@ GF_Err ODM_ValidateOD(GF_ObjectManager *odm, Bool *hasInline) u16 es_id; GF_ESD *esd, *base_scene; const char *sOpt; - u32 lang, nb_od, nb_ocr, nb_scene, nb_mp7, nb_ipmp, nb_oci, nb_mpj, nb_other, prev_st; + u32 nb_od, nb_ocr, nb_scene, nb_mp7, nb_ipmp, nb_oci, nb_mpj, nb_other, prev_st; nb_od = nb_ocr = nb_scene = nb_mp7 = nb_ipmp = nb_oci = nb_mpj = nb_other = 0; prev_st = 0; @@ -478,8 +510,8 @@ GF_Err ODM_ValidateOD(GF_ObjectManager *odm, Bool *hasInline) gf_cfg_set_key(odm->term->user->config, "Systems", "Language2CC", "en"); gf_cfg_set_key(odm->term->user->config, "Systems", "LanguageName", "English"); } - lang = (sOpt[0]<<16) | (sOpt[1]<<8) | sOpt[2]; #if 0 + lang = (sOpt[0]<<16) | (sOpt[1]<<8) | sOpt[2]; if (gf_list_count(odm->OD->ESDescriptors)>1) { ODM_SelectAlternateStream(odm, lang, GF_STREAM_SCENE); ODM_SelectAlternateStream(odm, lang, GF_STREAM_OD); @@ -544,7 +576,14 @@ void gf_odm_setup_object(GF_ObjectManager *odm, GF_ClientService *serv) GF_ESD *esd; GF_MediaObject *syncRef; + gf_term_lock_net(odm->term, 1); + if (!odm->net_service) { + if (odm->flags & GF_ODM_DESTROYED) { + GF_LOG(GF_LOG_WARNING, GF_LOG_MEDIA, ("[ODM%d] Object has been scheduled for destruction - ignoring object setup\n", odm->OD->objectDescriptorID)); + gf_term_lock_net(odm->term, 0); + return; + } odm->net_service = serv; if (!odm->OD->URLString) odm->net_service->nb_odm_users++; @@ -570,8 +609,9 @@ void gf_odm_setup_object(GF_ObjectManager *odm, GF_ClientService *serv) odm->subscene = gf_scene_new(odm->parentscene); odm->subscene->root_od = odm; } - gf_term_connect_object(odm->term, odm, url, parent ? parent->url : NULL); + gf_term_post_connect_object(odm->term, odm, url, parent ? parent->url : NULL); gf_free(url); + gf_term_lock_net(odm->term, 0); return; } /*restore OD ID */ @@ -589,6 +629,7 @@ void gf_odm_setup_object(GF_ObjectManager *odm, GF_ClientService *serv) if (e) { gf_term_message(odm->term, odm->net_service->url, "MPEG-4 Service Error", e); gf_odm_disconnect(odm, 1); + gf_term_lock_net(odm->term, 0); return; } @@ -596,12 +637,20 @@ void gf_odm_setup_object(GF_ObjectManager *odm, GF_ClientService *serv) hasInline = 0; } + if (odm->net_service->owner && (odm->net_service->owner->flags & GF_ODM_INHERIT_TIMELINE)) { + odm->flags |= GF_ODM_INHERIT_TIMELINE; + } + /*if there is a BIFS stream in the OD, we need an GF_Scene (except if we already have one, which means this is the first IOD)*/ if (hasInline && !odm->subscene) { odm->subscene = gf_scene_new(odm->parentscene); odm->subscene->root_od = odm; } + /*patch for DASH and OD streams: we need to keep track of the original service demuxing the channel, which is not the DASH service*/ + if (!odm->OD->service_ifce && odm->parentscene) { + odm->OD->service_ifce = odm->parentscene->root_od->OD->service_ifce; + } numOK = odm->pending_channels = 0; /*empty IOD, use a dynamic scene*/ @@ -627,8 +676,7 @@ void gf_odm_setup_object(GF_ObjectManager *odm, GF_ClientService *serv) } odm->state = GF_ODM_STATE_STOP; gf_odm_lock(odm, 0); - } - + } /*setup mediaobject info except for top-level OD*/ if (odm->parentscene) { GF_Event evt; @@ -645,20 +693,29 @@ void gf_odm_setup_object(GF_ObjectManager *odm, GF_ClientService *serv) gf_odm_start(odm, 0); } + gf_term_lock_net(odm->term, 0); + evt.type = GF_EVENT_CONNECT; evt.connect.is_connected = 1; gf_term_forward_event(odm->term, &evt, 0, 1); + + gf_term_lock_net(odm->term, 1); } else { /*othewise send a connect ack for top level*/ GF_Event evt; GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("[ODM] Root object connected !\n", odm->net_service->url)); + gf_term_lock_net(odm->term, 0); + evt.type = GF_EVENT_CONNECT; evt.connect.is_connected = 1; gf_term_send_event(odm->term, &evt); + + gf_term_lock_net(odm->term, 1); } + /* start object*/ /*case 1: object is the root, always start*/ if (!odm->parentscene) { @@ -675,10 +732,21 @@ void gf_odm_setup_object(GF_ObjectManager *odm, GF_ClientService *serv) have to wait for an entire image carousel period to start filling the buffers, which is sub-optimal we also force a prefetch for object declared outside the OD stream to make sure we don't loose any data before object declaration and play as can be the case with MPEG2 TS (first video packet right after the PMT) - this should be refined*/ - else if (!odm->state && ((odm->flags & GF_ODM_NO_TIME_CTRL) || (odm->flags & GF_ODM_NOT_IN_OD_STREAM)) && (odm->parentscene->selected_service_id == odm->OD->ServiceID)) { - GF_LOG(GF_LOG_INFO, GF_LOG_MEDIA, ("[ODM%d] Inserted from broadcast - forcing play\n", odm->OD->objectDescriptorID)); - gf_odm_start(odm, 2); - odm->flags |= GF_ODM_PREFETCH; + else if ( ((odm->flags & GF_ODM_NO_TIME_CTRL) || (odm->flags & GF_ODM_NOT_IN_OD_STREAM)) && (odm->parentscene->selected_service_id == odm->OD->ServiceID)) { + Bool force_play = 0; + if (odm->state==GF_ODM_STATE_STOP) { + odm->flags |= GF_ODM_PREFETCH; + force_play = 1; + } + /*the object could have been queued for play when setting up the scene object. If so, remove from queue and start right away*/ + else if ((odm->state==GF_ODM_STATE_PLAY) && (gf_list_del_item(odm->term->media_queue, odm)>=0) ) { + force_play = 1; + } + if (force_play) { + odm->flags |= GF_ODM_INITIAL_BROADCAST_PLAY; + GF_LOG(GF_LOG_INFO, GF_LOG_MEDIA, ("[ODM%d] Inserted from broadcast - forcing play\n", odm->OD->objectDescriptorID)); + gf_odm_start(odm, 2); + } } /*for objects inserted by user (subs & co), auto select*/ @@ -692,10 +760,16 @@ void gf_odm_setup_object(GF_ObjectManager *odm, GF_ClientService *serv) odm->OD_PL = 0; } if (odm->parentscene==odm->term->root_scene) { + gf_term_lock_net(odm->term, 0); + gf_mx_v(odm->term->net_mx); evt.type = GF_EVENT_STREAMLIST; gf_term_send_event(odm->term,&evt); + + gf_term_lock_net(odm->term, 1); } } + + gf_term_lock_net(odm->term, 0); } @@ -734,11 +808,28 @@ GF_Err gf_odm_setup_es(GF_ObjectManager *odm, GF_ESD *esd, GF_ClientService *ser } /*timeline override*/ if (odm->flags & GF_ODM_INHERIT_TIMELINE) { - if (odm->parentscene->root_od->subscene->scene_codec) + if (odm->parentscene->root_od->subscene->scene_codec) { ck = odm->parentscene->root_od->subscene->scene_codec->ck; - else + } else { ck = odm->parentscene->root_od->subscene->dyn_ck; - goto clock_setup; + } + /**/ + if (!ck) { + GF_ObjectManager *odm_par = odm->parentscene->root_od->parentscene->root_od; + while (odm_par) { + if (odm_par->subscene->scene_codec) + ck = odm_par->subscene->scene_codec->ck; + else + ck = odm_par->subscene->dyn_ck; + + if (ck) break; + + odm_par = odm->parentscene->root_od->parentscene ? odm->parentscene->root_od->parentscene->root_od : NULL; + } + } + if (ck) + goto clock_setup; + GF_LOG(GF_LOG_ERROR, GF_LOG_MEDIA, ("[ODM] Cannot inherit timeline from parent scene for scene %s\n", odm->net_service->url)); } /*get clocks namespace (eg, parent scene)*/ @@ -816,22 +907,21 @@ clock_setup: /*OD codec acts as main scene codec when used to generate scene graph*/ if (! odm->subscene->od_codec) { odm->subscene->od_codec = gf_codec_new(odm, esd, odm->OD_PL, &e); - if (e) return e; - gf_term_add_codec(odm->term, odm->subscene->od_codec); + if (!e) gf_term_add_codec(odm->term, odm->subscene->od_codec); } dec = odm->subscene->od_codec; break; case GF_STREAM_OCR: /*OD codec acts as main scene codec when used to generate scene graph*/ dec = odm->ocr_codec = gf_codec_new(odm, esd, odm->OD_PL, &e); - gf_term_add_codec(odm->term, odm->ocr_codec); + if (!e) gf_term_add_codec(odm->term, odm->ocr_codec); break; case GF_STREAM_SCENE: /*animationStream */ if (!odm->subscene) { if (!odm->codec) { odm->codec = gf_codec_new(odm, esd, odm->Scene_PL, &e); - gf_term_add_codec(odm->term, odm->codec); + if (!e) gf_term_add_codec(odm->term, odm->codec); } dec = odm->codec; } @@ -852,7 +942,7 @@ clock_setup: } else { odm->oci_codec = gf_codec_new(odm, esd, odm->OD_PL, &e); odm->oci_codec->odm = odm; - gf_term_add_codec(odm->term, odm->oci_codec); + if (!e) gf_term_add_codec(odm->term, odm->oci_codec); } break; #endif @@ -1085,6 +1175,7 @@ GF_Err gf_odm_post_es_setup(GF_Channel *ch, GF_Codec *dec, GF_Err had_err) gf_term_lock_net(ch->odm->term, 1); gf_es_start(ch); + memset(&com, 0, sizeof(GF_NetworkCommand)); com.command_type = GF_NET_CHAN_PLAY; com.base.on_channel = ch; com.play.speed = FIX2FLT(ch->clock->speed); @@ -1236,6 +1327,7 @@ void gf_odm_start(GF_ObjectManager *odm, u32 media_queue_state) gf_odm_play(odm); } else if (!skip_register && (gf_list_find(odm->term->media_queue, odm)<0)) { odm->action_type = GF_ODM_ACTION_PLAY; + assert(! (odm->flags & GF_ODM_DESTROYED)); gf_list_add(odm->term->media_queue, odm); } } @@ -1284,7 +1376,15 @@ void gf_odm_play(GF_ObjectManager *odm) nb_failure = gf_list_count(odm->channels); /*send play command*/ + memset(&com, 0, sizeof(GF_NetworkCommand)); com.command_type = GF_NET_CHAN_PLAY; + + if (odm->flags & GF_ODM_INITIAL_BROADCAST_PLAY) { + odm->flags &= ~GF_ODM_INITIAL_BROADCAST_PLAY; + com.play.initial_broadcast_play = 1; + } + + i=0; while ( (ch = (GF_Channel*)gf_list_enum(odm->channels, &i)) ) { Double ck_time; @@ -1395,7 +1495,7 @@ void gf_odm_play(GF_ObjectManager *odm) return; } - gf_term_service_media_event(odm, GF_EVENT_MEDIA_DATA_REQUEST); + gf_term_service_media_event(odm, GF_EVENT_MEDIA_LOAD_START); /*start codecs last (otherwise we end up pulling data from channels not yet connected->pbs when seeking)*/ @@ -1490,6 +1590,7 @@ void gf_odm_stop(GF_ObjectManager *odm, Bool force_close) return; } + if (force_close && odm->mo) odm->mo->flags |= GF_MO_DISPLAY_REMOVE; /*stop codecs*/ if (odm->codec) { gf_term_stop_codec(odm->codec); @@ -1509,7 +1610,7 @@ void gf_odm_stop(GF_ObjectManager *odm, Bool force_close) if (odm->oci_codec) gf_term_stop_codec(odm->oci_codec); #endif - gf_term_lock_net(odm->term, 1); +// gf_term_lock_net(odm->term, 1); /*send stop command*/ com.command_type = GF_NET_CHAN_STOP; @@ -1530,7 +1631,7 @@ void gf_odm_stop(GF_ObjectManager *odm, Bool force_close) GF_LOG(GF_LOG_INFO, GF_LOG_MEDIA, ("[ODM%d] CH %d At OTB %d requesting STOP\n", odm->OD->objectDescriptorID, ch->esd->ESID, gf_clock_time(ch->clock))); } } - gf_term_service_media_event(odm, GF_EVENT_MEDIA_STOP); + gf_term_service_media_event(odm, GF_EVENT_ABORT); /*stop channels*/ i=0; @@ -1542,7 +1643,7 @@ void gf_odm_stop(GF_ObjectManager *odm, Bool force_close) gf_es_stop(ch); } - gf_term_lock_net(odm->term, 0); +// gf_term_lock_net(odm->term, 0); odm->state = GF_ODM_STATE_STOP; odm->current_time = 0; @@ -1588,7 +1689,7 @@ void gf_odm_on_eos(GF_ObjectManager *odm, GF_Channel *on_channel) } else { if (nb_eos != count) return; } - gf_term_service_media_event(odm, GF_EVENT_MEDIA_END_OF_DATA); + gf_term_service_media_event(odm, GF_EVENT_MEDIA_LOAD_DONE); if (odm->codec && (on_channel->esd->decoderConfig->streamType==odm->codec->type)) { gf_codec_set_status(odm->codec, GF_ESM_CODEC_EOS); diff --git a/src/terminal/scene.c b/src/terminal/scene.c index 44484ef..fb4fcfa 100644 --- a/src/terminal/scene.c +++ b/src/terminal/scene.c @@ -39,6 +39,8 @@ #include #endif +#include "input_sensor.h" + GF_EXPORT Double gf_scene_get_time(void *_is) { @@ -194,7 +196,6 @@ void gf_scene_disconnect(GF_Scene *scene, Bool for_shutdown) { u32 i; GF_MediaObject *obj; - GF_Node *root_node; GF_ObjectManager *odm; GF_SceneDecoder *dec = NULL; if (scene->scene_codec) dec = (GF_SceneDecoder *)scene->scene_codec->decio; @@ -202,36 +203,8 @@ void gf_scene_disconnect(GF_Scene *scene, Bool for_shutdown) GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("[Scene] disconnecting\n")); gf_term_lock_compositor(scene->root_od->term, 1); - - /*disconnect / kill all objects BEFORE reseting the scene graph since we have - potentially registered Inline nodes of the graph with the sub-scene*/ - if (!for_shutdown && scene->static_media_ressources) { - i=0; - /*stop all objects but DON'T DESTROY THEM*/ - while ((odm = (GF_ObjectManager *)gf_list_enum(scene->resources, &i))) { - if (odm->state) gf_odm_disconnect(odm, 0); - } - /*reset all stream associations*/ - i=0; - while ((obj = (GF_MediaObject*)gf_list_enum(scene->scene_objects, &i))) { - gf_sg_vrml_mf_reset(&obj->URLs, GF_SG_VRML_MFURL); - gf_list_reset(obj->nodes); - } - } else { - while (gf_list_count(scene->resources)) { - odm = (GF_ObjectManager *)gf_list_get(scene->resources, 0); - gf_odm_disconnect(odm, (for_shutdown || !scene->static_media_ressources) ? 1 : 0); - } -#ifndef GPAC_DISABLE_VRML - while (gf_list_count(scene->extern_protos)) { - GF_ProtoLink *pl = (GF_ProtoLink *)gf_list_get(scene->extern_protos, 0); - gf_list_rem(scene->extern_protos, 0); - gf_free(pl); - } -#endif - } - - root_node = gf_sg_get_root_node(scene->graph); + + /*force unregistering of inline nodes (for safety)*/ if (for_shutdown && scene->root_od->mo) { /*reset private stack of all inline nodes still registered*/ while (gf_list_count(scene->root_od->mo->nodes)) { @@ -250,9 +223,34 @@ void gf_scene_disconnect(GF_Scene *scene, Bool for_shutdown) } } - /*remove all associated eventTargets - THIS NEEDS CLEANUP*/ + //Ivica patch: Remove all Registered InputSensor nodes -> shut down the InputSensor threads -> prevent illegal access on deleted pointers +#ifndef GPAC_DISABLE_VRML + if (for_shutdown) { + i = 0; + while ((odm = (GF_ObjectManager *)gf_list_enum(scene->resources, &i))) { + if (for_shutdown && odm->mo) { + obj = odm->mo; + while (gf_list_count(obj->nodes)) { + GF_Node *n = (GF_Node *)gf_list_get(obj->nodes, 0); + switch (gf_node_get_tag(n)) { + case TAG_MPEG4_InputSensor: + { + M_InputSensor* is = (M_InputSensor*)n; + is->enabled = 0; + InputSensorModified(n); + break; + } + } + gf_list_rem(obj->nodes, 0); + } + } + } + } +#endif + + /*remove all associated eventTargets*/ i=0; - while ((obj = (GF_MediaObject *)gf_list_enum(scene->resources, &i))) { + while ((obj = (GF_MediaObject *)gf_list_enum(scene->scene_objects, &i))) { if (obj->nodes) gf_list_reset(obj->nodes); } @@ -267,15 +265,44 @@ void gf_scene_disconnect(GF_Scene *scene, Bool for_shutdown) if (scene->root_od->term->root_scene == scene) { gf_sc_set_scene(scene->root_od->term->compositor, NULL); } - /*release the scene*/ + + /*release the scene - at this stage, we no longer have any node stack refering to our media objects */ if (dec && dec->ReleaseScene) dec->ReleaseScene(dec); gf_sg_reset(scene->graph); scene->graph_attached = 0; - assert(!gf_list_count(scene->extra_scenes) ); + /*reset statc ressource flag since we destroyed scene objects*/ scene->static_media_ressources = 0; + + /*disconnect and kill all objects*/ + if (!for_shutdown && scene->static_media_ressources) { + i=0; + /*stop all objects but DON'T DESTROY THEM*/ + while ((odm = (GF_ObjectManager *)gf_list_enum(scene->resources, &i))) { + if (odm->state) gf_odm_disconnect(odm, 0); + } + /*reset all stream associations*/ + i=0; + while ((obj = (GF_MediaObject*)gf_list_enum(scene->scene_objects, &i))) { + gf_sg_vrml_mf_reset(&obj->URLs, GF_SG_VRML_MFURL); + gf_list_reset(obj->nodes); + } + } else { + while (gf_list_count(scene->resources)) { + odm = (GF_ObjectManager *)gf_list_get(scene->resources, 0); + gf_odm_disconnect(odm, (for_shutdown || !scene->static_media_ressources) ? 2 : 0); + } +#ifndef GPAC_DISABLE_VRML + while (gf_list_count(scene->extern_protos)) { + GF_ProtoLink *pl = (GF_ProtoLink *)gf_list_get(scene->extern_protos, 0); + gf_list_rem(scene->extern_protos, 0); + gf_free(pl); + } +#endif + } + /*remove stream associations*/ while (gf_list_count(scene->scene_objects)) { obj = (GF_MediaObject*)gf_list_get(scene->scene_objects, 0); @@ -285,9 +312,6 @@ void gf_scene_disconnect(GF_Scene *scene, Bool for_shutdown) gf_list_del(obj->nodes); gf_free(obj); } - - if (for_shutdown && scene->root_od && scene->root_od->mo) { - } gf_term_lock_compositor(scene->root_od->term, 0); } @@ -457,7 +481,7 @@ void gf_scene_remove_object(GF_Scene *scene, GF_ObjectManager *odm, Bool for_shu } } - if (discard_obj==1) { + if ((discard_obj==1) && !obj->num_open) { gf_list_rem(scene->scene_objects, i-1); gf_sg_vrml_mf_reset(&obj->URLs, GF_SG_VRML_MFURL); gf_list_del(obj->nodes); @@ -647,37 +671,68 @@ GF_MediaObject *gf_scene_get_media_object_ex(GF_Scene *scene, MFURL *url, u32 ob OD_ID = gf_mo_get_od_id(url); if (!OD_ID) return NULL; + gf_term_lock_net(scene->root_od->term, 1); + /*the first pass is needed to detect objects already inserted and registered with the given nodes, regardless of the force_new_if_not_attached flag. This ty^pically occurs when a resource is first created then linked to an animation/inline*/ restart: obj = NULL; i=0; while ((obj = (GF_MediaObject *)gf_list_enum(scene->scene_objects, &i))) { - if (obj->odm && ((obj->odm->flags & GF_ODM_DESTROYED) || (obj->odm->action_type == GF_ODM_ACTION_DELETE)) ) - continue; + Bool odm_matches = 0; if ( /*regular OD scheme*/ (OD_ID != GF_MEDIA_EXTERNAL_ID && (obj->OD_ID==OD_ID)) || - /*dynamic OD scheme*/ - ((OD_ID == GF_MEDIA_EXTERNAL_ID) && (obj->OD_ID==GF_MEDIA_EXTERNAL_ID) + /*dynamic OD scheme - !! obj->OD_ID may different from GF_MEDIA_EXTERNAL_ID when ODs are + directly added to the terminal by the service*/ + ((OD_ID == GF_MEDIA_EXTERNAL_ID) /*if object type unknown (media control, media sensor), return first obj matching URL otherwise check types*/ && is_match_obj_type(obj->type, obj_type_hint) /*locate sub-url in given one and handle fragments (viewpoint/segments/...)*/ && gf_mo_is_same_url(obj, url, &keep_fragment, obj_type_hint) ) - ) { - if (!first_pass && !force_new_if_not_attached) { - if (node && (gf_list_find(obj->nodes, node)<0)) - gf_list_add(obj->nodes, node); - return obj; - } - /*special case where the URL is requested twice for the same node: use the existing resource*/ - else if (node && (gf_list_find(obj->nodes, node)>=0)) { - return obj; + ) { + odm_matches = 1; + } + + if (!odm_matches) continue; + + if (obj->odm) { + Bool can_reuse = 1; + Bool timeline_locked = (obj->odm->flags & GF_ODM_INHERIT_TIMELINE) ? 1 : 0; + if (timeline_locked != lock_timelines) + continue; + + gf_term_lock_media_queue(scene->root_od->term, 1); + if (obj->odm->flags & GF_ODM_DESTROYED) can_reuse = 0; + else if (obj->odm->action_type == GF_ODM_ACTION_DELETE) { + /*check if object is being destroyed (no longer in the queue)*/ + if (gf_list_del_item(scene->root_od->term->media_queue, obj->odm)<0) { + can_reuse = 0; + } + /*otherwise reuse object, discard current destroy command*/ + else { + obj->odm->action_type = GF_ODM_ACTION_PLAY; + } } + gf_term_lock_media_queue(scene->root_od->term, 0); + if (!can_reuse) continue; + + } + + if (!first_pass && !force_new_if_not_attached) { + if (node && (gf_list_find(obj->nodes, node)<0)) + gf_list_add(obj->nodes, node); + gf_term_lock_net(scene->root_od->term, 0); + return obj; + } + /*special case where the URL is requested twice for the same node: use the existing resource*/ + else if (node && (gf_list_find(obj->nodes, node)>=0)) { + gf_term_lock_net(scene->root_od->term, 0); + return obj; } } if (first_pass) { @@ -686,7 +741,10 @@ restart: } /*we cannot create an OD manager at this point*/ - if (obj_type_hint==GF_MEDIA_OBJECT_UNDEF) return NULL; + if (obj_type_hint==GF_MEDIA_OBJECT_UNDEF) { + gf_term_lock_net(scene->root_od->term, 0); + return NULL; + } /*create a new object identification*/ obj = gf_mo_new(); @@ -711,16 +769,21 @@ restart: gf_sg_vrml_copy_mfurl(&obj->URLs, url); gf_scene_insert_object(scene, obj, lock_timelines, sync_ref, keep_fragment, original_parent_scene); /*safety check!!!*/ - if (gf_list_find(scene->scene_objects, obj)<0) + if (gf_list_find(scene->scene_objects, obj)<0) { + gf_term_lock_net(scene->root_od->term, 0); return NULL; + } if (obj->odm==NULL) { gf_list_del_item(scene->scene_objects, obj); if (obj->nodes) gf_list_del(obj->nodes); gf_free(obj); + gf_term_lock_net(scene->root_od->term, 0); return NULL; } } + + gf_term_lock_net(scene->root_od->term, 0); return obj; } @@ -742,6 +805,9 @@ void gf_scene_setup_object(GF_Scene *scene, GF_ObjectManager *odm) i=0; while ((obj = (GF_MediaObject*)gf_list_enum(scene->scene_objects, &i))) { + /*make sure services are different*/ + if (obj->odm && (odm->net_service != obj->odm->net_service)) continue; + if (obj->OD_ID==GF_MEDIA_EXTERNAL_ID) { //assert(obj->odm); if (obj->odm == odm) { @@ -812,6 +878,8 @@ void gf_scene_set_duration(GF_Scene *scene) if (scene->duration == max_dur) return; scene->duration = max_dur; + if (scene->is_dynamic_scene && !scene->root_od->duration) scene->root_od->duration = max_dur; + dur = (Double) (s64) scene->duration; dur /= 1000; @@ -955,7 +1023,7 @@ static void set_media_url(GF_Scene *scene, SFURL *media_url, GF_Node *node, MFU if (!odm->codec || ((odm->codec->type!=type) && (odm->codec->type!=GF_STREAM_ND_SUBPIC))) continue; } else if (type==GF_STREAM_SCENE) { - if (!odm->subscene || !odm->subscene->scene_codec) continue; + if (!odm->subscene || (!odm->subscene->scene_codec && !odm->subscene->is_dynamic_scene) ) continue; } else { if (!odm->codec || (odm->codec->type!=type)) continue; @@ -965,9 +1033,9 @@ static void set_media_url(GF_Scene *scene, SFURL *media_url, GF_Node *node, MFU if (media_url->OD_ID==GF_MEDIA_EXTERNAL_ID) media_url->url = gf_strdup(odm->net_service->url); if (!scene->dyn_ck) { - if (odm->subscene) { + if (odm->subscene && odm->subscene->scene_codec) { scene->dyn_ck = odm->subscene->scene_codec->ck; - } else { + } else if (odm->codec) { scene->dyn_ck = odm->codec->ck; } } @@ -1022,7 +1090,7 @@ void gf_scene_regenerate(GF_Scene *scene) M_AnimationStream *as; M_Inline *dims; - if (!scene->is_dynamic_scene) return; + if (scene->is_dynamic_scene != 1) return; GF_LOG(GF_LOG_DEBUG, GF_LOG_MEDIA, ("[Inline] Regenerating scene graph for service %s\n", scene->root_od->net_service->url)); @@ -1037,6 +1105,12 @@ void gf_scene_regenerate(GF_Scene *scene) gf_sg_set_root_node(scene->graph, n1); gf_node_register(n1, NULL); + if (! scene->root_od->parentscene) { + n2 = is_create_node(scene->graph, TAG_MPEG4_Background2D, "DYN_BACK"); + gf_node_list_add_child( &((GF_ParentNode *)n1)->children, n2); + gf_node_register(n2, n1); + } + /*create an sound2D and an audioClip node*/ n2 = is_create_node(scene->graph, TAG_MPEG4_Sound2D, NULL); gf_node_list_add_child( &((GF_ParentNode *)n1)->children, n2); @@ -1222,26 +1296,36 @@ void gf_scene_select_object(GF_Scene *scene, GF_ObjectManager *odm) void gf_scene_restart_dynamic(GF_Scene *scene, u64 from_time) { u32 i; + GF_Clock *ck; GF_List *to_restart; GF_ObjectManager *odm; - GF_LOG(GF_LOG_INFO, GF_LOG_MEDIA, ("[InlineScene] Restarting from "LLD"\n", LLD_CAST from_time)); + GF_LOG(GF_LOG_INFO, GF_LOG_MEDIA, ("[Scene] Restarting from "LLD"\n", LLD_CAST from_time)); + + ck = scene->dyn_ck; + if (scene->scene_codec) ck = scene->scene_codec->ck; + if (!ck) return; + to_restart = gf_list_new(); + + i=0; while ((odm = (GF_ObjectManager*)gf_list_enum(scene->resources, &i))) { - if (odm->state != GF_ODM_STATE_BLOCKED) { - gf_list_add(to_restart, odm); - if (odm->state == GF_ODM_STATE_PLAY) { - gf_odm_stop(odm, 1); + if (gf_odm_shares_clock(odm, ck)) { + if (odm->state != GF_ODM_STATE_BLOCKED) { + gf_list_add(to_restart, odm); + if (odm->state == GF_ODM_STATE_PLAY) { + gf_odm_stop(odm, 1); + } } } } /*reset clock*/ - if (scene->dyn_ck) { - gf_clock_reset(scene->dyn_ck); - scene->simulation_time = from_time/1000.0; - } + gf_clock_reset(ck); + scene->simulation_time = from_time/1000.0; + if (!scene->is_dynamic_scene) gf_clock_set_time(ck, 0); + /*restart objects*/ i=0; while ((odm = (GF_ObjectManager*)gf_list_enum(to_restart, &i))) { @@ -1251,7 +1335,7 @@ void gf_scene_restart_dynamic(GF_Scene *scene, u64 from_time) gf_list_del(to_restart); /*also check nodes since they may be deactivated (end of stream)*/ - { + if (scene->is_dynamic_scene) { M_AudioClip *ac = (M_AudioClip *) gf_sg_find_node_by_name(scene->graph, "DYN_AUDIO"); M_MovieTexture *mt = (M_MovieTexture *) gf_sg_find_node_by_name(scene->graph, "DYN_VIDEO"); M_AnimationStream *as = (M_AnimationStream *) gf_sg_find_node_by_name(scene->graph, "DYN_TEXT"); @@ -1447,6 +1531,13 @@ Bool gf_scene_is_over(GF_SceneGraph *sg) return 1; } +GF_SceneGraph *gf_scene_enum_extra_scene(GF_SceneGraph *sg, u32 *i) +{ + GF_Scene *scene = gf_sg_get_private(sg); + if (!scene) return NULL; + return gf_list_enum(scene->extra_scenes, i); +} + #define USE_TEXTURES 0 void gf_scene_generate_views(GF_Scene *scene, char *url) @@ -1516,7 +1607,7 @@ void gf_scene_generate_views(GF_Scene *scene, char *url) gf_sg_set_scene_size_info(scene->graph, 0, 0, 1); gf_sc_set_scene(scene->root_od->term->compositor, scene->graph); scene->graph_attached = 1; - scene->is_dynamic_scene = 1; + scene->is_dynamic_scene = 2; evt.type = GF_EVENT_CONNECT; evt.connect.is_connected = 1; diff --git a/src/terminal/terminal.c b/src/terminal/terminal.c index 3eb6d68..bd44c1d 100644 --- a/src/terminal/terminal.c +++ b/src/terminal/terminal.c @@ -32,11 +32,18 @@ #include #include "../utils/module_wrap.h" +#include "media_control.h" + /*textual command processing*/ #include #include "media_memory.h" + +/*creates service for given OD / URL +SHALL ONLY BE USED BY MAIN CLIENT*/ +static void gf_term_connect_object(GF_Terminal *app, GF_ObjectManager *odm, char *serviceURL, char *parent_url); + void gf_term_load_shortcuts(GF_Terminal *term); u32 gf_term_get_time(GF_Terminal *term) @@ -127,6 +134,54 @@ static Bool term_script_action(void *opaque, u32 type, GF_Node *n, GF_JSAPIParam return 1; } + /*special case for pause/stop/resume*/ + if (type==GF_JSAPI_OP_PAUSE_SVG) { + GF_SceneGraph *graph = gf_node_get_graph(n); + if (n == gf_sg_get_root_node(graph)) { + GF_Scene *scene = (GF_Scene *)gf_sg_get_private(graph); + GF_Clock *ck = scene->scene_codec ? scene->scene_codec->ck : scene->dyn_ck; + if (ck) gf_clock_pause(ck); + return 1; + } + } + if (type==GF_JSAPI_OP_RESUME_SVG) { + GF_SceneGraph *graph = gf_node_get_graph(n); + if (n == gf_sg_get_root_node(graph)) { + GF_Scene *scene = (GF_Scene *)gf_sg_get_private(graph); + GF_Clock *ck = scene->scene_codec ? scene->scene_codec->ck : scene->dyn_ck; + if (ck) gf_clock_resume(ck); + return 1; + } + } + if (type==GF_JSAPI_OP_RESTART_SVG) { + GF_SceneGraph *graph = gf_node_get_graph(n); + if (n == gf_sg_get_root_node(graph)) { + GF_Scene *scene = (GF_Scene *)gf_sg_get_private(graph); + GF_Clock *ck = scene->scene_codec ? scene->scene_codec->ck : scene->dyn_ck; + if (ck) { + Bool is_paused = ck->Paused; + if (is_paused) gf_clock_resume(ck); + gf_scene_restart_dynamic(scene, 0); + if (is_paused) gf_clock_pause(ck); + } + return 1; + } + return 0; + } + if (type==GF_JSAPI_OP_SET_SCENE_SPEED) { + GF_SceneGraph *graph = gf_node_get_graph(n); + if (n == gf_sg_get_root_node(graph)) { + GF_Scene *scene = (GF_Scene *)gf_sg_get_private(graph); + GF_Clock *ck = scene->scene_codec ? scene->scene_codec->ck : scene->dyn_ck; + if (ck) { + gf_clock_set_speed(ck, param->val); + } + return 1; + } + return 0; + } + + ret = gf_sc_script_action(term->compositor, type, n, param); if (ret) return ret; @@ -244,7 +299,6 @@ static Bool term_check_locales(void *__self, const char *locales_parent_path, co static void gf_term_reload_cfg(GF_Terminal *term) { const char *sOpt; - Double fps; u32 mode; s32 prio; @@ -264,14 +318,12 @@ static void gf_term_reload_cfg(GF_Terminal *term) else term->flags &= ~GF_TERM_SINGLE_CLOCK; - sOpt = gf_cfg_get_key(term->user->config, "Compositor", "FrameRate"); + sOpt = gf_cfg_get_key(term->user->config, "Systems", "TimeSlice"); if (!sOpt) { - sOpt = "30.0"; - gf_cfg_set_key(term->user->config, "Compositor", "FrameRate", "30.0"); + sOpt = "30"; + gf_cfg_set_key(term->user->config, "Systems", "TimeSlice", "30"); } - fps = atof(sOpt); - term->frame_duration = (u32) (1000/fps); - gf_sc_set_fps(term->compositor, fps); + term->frame_duration = atoi(sOpt); if (!(term->user->init_flags & GF_TERM_NO_DECODER_THREAD) ){ prio = GF_THREAD_PRIORITY_NORMAL; @@ -528,7 +580,7 @@ GF_Terminal *gf_term_new(GF_User *user) tmp->net_services = gf_list_new(); tmp->net_services_to_remove = gf_list_new(); - tmp->net_services_to_connect = gf_list_new(); + tmp->connection_tasks = gf_list_new(); tmp->channels_pending = gf_list_new(); tmp->media_queue = gf_list_new(); tmp->media_queue_mx = gf_mx_new("MediaQueue"); @@ -681,8 +733,8 @@ GF_Err gf_term_del(GF_Terminal * term) gf_sc_del(term->compositor); gf_list_del(term->net_services); - gf_list_del(term->net_services_to_connect); gf_list_del(term->net_services_to_remove); + gf_list_del(term->connection_tasks); gf_list_del(term->input_streams); gf_list_del(term->x3d_sensors); assert(!gf_list_count(term->channels_pending)); @@ -774,18 +826,36 @@ void gf_term_connect_with_path(GF_Terminal * term, const char *URL, const char * GF_EXPORT void gf_term_disconnect(GF_Terminal *term) { + Bool handle_services; if (!term->root_scene) return; /*resume*/ if (term->play_state != GF_STATE_PLAYING) gf_term_set_play_state(term, GF_STATE_PLAYING, 1, 1); if (term->root_scene->root_od) { - gf_odm_disconnect(term->root_scene->root_od, 1); + + gf_term_lock_media_queue(term, 1); + + term->root_scene->root_od->action_type = GF_ODM_ACTION_DELETE; + if (gf_list_find(term->media_queue, term->root_scene->root_od)<0) + gf_list_add(term->media_queue, term->root_scene->root_od); + + gf_term_lock_media_queue(term, 0); } else { gf_scene_del(term->root_scene); term->root_scene = NULL; } - while (term->root_scene || gf_list_count(term->net_services_to_remove)) { - gf_term_handle_services(term); + handle_services = 0; + if (term->flags & GF_TERM_NO_DECODER_THREAD) + handle_services = 1; + /*if an unthreaded term extension decides to disconnect the scene (validator does so), we must flush services now + because we are called from gf_term_handle_services*/ + if (term->thread_id_handling_services == gf_th_id()) + handle_services = 1; + + while (term->root_scene || gf_list_count(term->net_services_to_remove) || gf_list_count(term->connection_tasks) || gf_list_count(term->media_queue) ) { + if (handle_services) { + gf_term_handle_services(term); + } gf_sleep(10); } } @@ -922,6 +992,13 @@ GF_Err gf_term_set_size(GF_Terminal * term, u32 NewWidth, u32 NewHeight) return gf_sc_set_size(term->compositor, NewWidth, NewHeight); } +typedef struct +{ + GF_ObjectManager *odm; + char *service_url, *parent_url; +} GF_TermConnectObject; + + void gf_term_handle_services(GF_Terminal *term) { GF_ClientService *ns; @@ -929,31 +1006,9 @@ void gf_term_handle_services(GF_Terminal *term) /*we could run into a deadlock if some thread has requested opening of a URL. If we cannot grab the media queue now, we'll do our management at the next cycle*/ if (!gf_mx_try_lock(term->media_queue_mx)) - return; - - - while (gf_list_count(term->net_services_to_connect)) { - GF_ClientService *ns = (GF_ClientService*)gf_list_get(term->net_services_to_connect, 0); - GF_ObjectManager *odm = ns->owner; - assert(odm); - - gf_list_rem(term->net_services_to_connect, 0); - /*unlock media queue before connecting*/ - gf_mx_v(term->media_queue_mx); - - /*OK connect*/ - gf_term_service_media_event(odm, GF_EVENT_MEDIA_BEGIN_SESSION_SETUP); - odm->net_service->ifce->ConnectService(odm->net_service->ifce, odm->net_service, odm->net_service->url); - - /*remove pending download session if any*/ - gf_term_cleanup_pending_session(term, ns); - - /*lock media queue after connecting*/ - gf_mx_p(term->media_queue_mx); - } - - /*!! media queue is still locked here*/ + return; + term->thread_id_handling_services = gf_th_id(); /*play ODs that need it*/ while (gf_list_count(term->media_queue)) { @@ -962,7 +1017,7 @@ void gf_term_handle_services(GF_Terminal *term) GF_ObjectManager *odm = (GF_ObjectManager *)gf_list_get(term->media_queue, 0); gf_list_rem(term->media_queue, 0); /*unlock media queue before sending play/pause*/ - gf_mx_v(term->media_queue_mx); + gf_term_lock_media_queue(term, 0); act_type = odm->action_type; odm->action_type = GF_ODM_ACTION_PLAY; @@ -1000,9 +1055,36 @@ void gf_term_handle_services(GF_Terminal *term) } /*relock before sending play/pause*/ - gf_mx_p(term->media_queue_mx); + gf_term_lock_media_queue(term, 1); + } + + /*finally process all connection tasks - we MUST do that after processing ODM tasks, as an ODM might have just destroyed + a service we could query during the connection step*/ + while (gf_list_count(term->connection_tasks)) { + GF_TermConnectObject *connect = gf_list_get(term->connection_tasks, 0); + gf_list_rem(term->connection_tasks, 0); + + /*unlock media queue before sending connect*/ + gf_term_lock_media_queue(term, 0); + +// gf_mx_p(term->net_mx); + + /*if object has already been attached to its service (eg, task was posted but media_add occured inbetween), ignore*/ + if (!connect->odm->net_service && !(connect->odm->flags & GF_ODM_DESTROYED) ) { + gf_term_connect_object(term, connect->odm, connect->service_url, connect->parent_url); + } + +// gf_mx_v(term->net_mx); + + gf_free(connect->service_url); + if (connect->parent_url) gf_free(connect->parent_url); + gf_free(connect); + + /*relock media queue after sending connect*/ + gf_term_lock_media_queue(term, 1); } - gf_mx_v(term->media_queue_mx); + + gf_term_lock_media_queue(term, 0); /*lock to avoid any start attemps from compositor*/ if (gf_mx_try_lock(term->compositor->mx)) { @@ -1049,14 +1131,16 @@ void gf_term_handle_services(GF_Terminal *term) term->reload_state = 2; } if (term->reload_state == 2) { - if (gf_list_count(term->net_services)) return; - term->reload_state = 0; - if (term->reload_url) { - gf_term_connect(term, term->reload_url); - gf_free(term->reload_url); + if (! gf_list_count(term->net_services)) { + term->reload_state = 0; + if (term->reload_url) { + gf_term_connect(term, term->reload_url); + gf_free(term->reload_url); + } + term->reload_url = NULL; } - term->reload_url = NULL; } + term->thread_id_handling_services = 0; } void gf_term_queue_node_traverse(GF_Terminal *term, GF_Node *node) @@ -1079,30 +1163,58 @@ void gf_term_unqueue_node_traverse(GF_Terminal *term, GF_Node *node) gf_sc_lock(term->compositor, 0); } +void gf_term_check_connections_for_delete(GF_Terminal *term, GF_ObjectManager *odm) +{ + GF_TermConnectObject *ct; + u32 i = 0; + while (NULL != (ct = (gf_list_enum(term->connection_tasks, &i)))) { + if (ct->odm == odm) { + i--; + gf_list_rem(term->connection_tasks, i); + if (ct->parent_url) gf_free(ct->parent_url); + gf_free(ct->service_url); + gf_free(ct); + } + } +} + void gf_term_close_service(GF_Terminal *term, GF_ClientService *ns) { - s32 idx; GF_Err e; /*prevent the media manager / term to access the list of services to destroy, otherwise we could unload the module while poping its CloseService() call stack which can lead to random crashes (return adresses no longer valid) - cf any "stress mode" playback of a playlist*/ - gf_mx_p(term->media_queue_mx); - ns->owner = NULL; - idx = gf_list_find(term->net_services_to_connect, ns); - if (idx>=0) { - gf_list_rem(term->net_services_to_connect, idx); - e = GF_BAD_PARAM; - } else { - e = ns->ifce->CloseService(ns->ifce); + gf_term_lock_media_queue(term, 1); + +#if 0 + { + GF_ObjectManager *odm; + u32 i = 0; + while (odm = gf_list_enum(term->media_queue, &i)) { + assert(odm->net_service != ns); + } + } + + { + GF_TermConnectObject *ct; + u32 i = 0; + while (ct = gf_list_enum(term->connection_tasks, &i)) { + assert(ct->odm->net_service != ns); + } } +#endif + + ns->owner = NULL; + e = ns->ifce->CloseService(ns->ifce); + /*if error don't wait for ACK to remove from main list*/ if (e) { gf_list_del_item(term->net_services, ns); if (gf_list_find(term->net_services_to_remove, ns)<0) gf_list_add(term->net_services_to_remove, ns); } - gf_mx_v(term->media_queue_mx); + gf_term_lock_media_queue(term, 0); } void gf_term_lock_compositor(GF_Terminal *term, Bool LockIt) @@ -1129,7 +1241,7 @@ void gf_term_lock_net(GF_Terminal *term, Bool LockIt) } } -static void mae_collect_info(GF_ClientService *net, GF_ObjectManager *odm, GF_DOMMediaAccessEvent *mae, u32 transport, u32 *min_time, u32 *min_buffer) +static void media_event_collect_info(GF_ClientService *net, GF_ObjectManager *odm, GF_DOMMediaEvent *media_event, u32 *min_time, u32 *min_buffer) { u32 i=0; GF_Channel *ch; @@ -1138,7 +1250,7 @@ static void mae_collect_info(GF_ClientService *net, GF_ObjectManager *odm, GF_DO u32 val; if (ch->service != net) continue; - mae->bufferValid = 1; + media_event->bufferValid = 1; if (ch->BufferTime>0) { if (ch->MaxBuffer) { val = (ch->BufferTime * 100) / ch->MaxBuffer; @@ -1152,21 +1264,15 @@ static void mae_collect_info(GF_ClientService *net, GF_ObjectManager *odm, GF_DO *min_time = 0; *min_buffer = 0; } - if (mae->nb_streams<20) { - mae->streams[mae->nb_streams].streamType = ch->esd->decoderConfig->streamType; - mae->streams[mae->nb_streams].mediaType = ch->esd->decoderConfig->objectTypeIndication; - mae->streams[mae->nb_streams].transport = transport; - mae->nb_streams ++; - } } } -void gf_term_service_media_event(GF_ObjectManager *odm, u32 event_type) +void gf_term_service_media_event_with_download(GF_ObjectManager *odm, u32 event_type, u64 loaded_size, u64 total_size, u32 bytes_per_sec) { #ifndef GPAC_DISABLE_SVG u32 i, count, min_buffer, min_time, transport; Bool locked; - GF_DOMMediaAccessEvent mae; + GF_DOMMediaEvent media_event; GF_DOM_Event evt; GF_ObjectManager *an_od; GF_Scene *scene; @@ -1175,46 +1281,44 @@ void gf_term_service_media_event(GF_ObjectManager *odm, u32 event_type) if (odm->mo) { count = gf_list_count(odm->mo->nodes); if (!count) return; - if (!(gf_node_get_dom_event_filter(gf_list_get(odm->mo->nodes, 0)) & GF_DOM_EVENT_MEDIA_ACCESS)) + if (!(gf_node_get_dom_event_filter(gf_list_get(odm->mo->nodes, 0)) & GF_DOM_EVENT_MEDIA)) return; } else { count = 0; } - memset(&mae, 0, sizeof(GF_DOMMediaAccessEvent)); + memset(&media_event, 0, sizeof(GF_DOMMediaEvent)); transport = 0; - mae.bufferValid = 0; - mae.session_name = odm->net_service->url; - if (!strnicmp(mae.session_name, "rtsp:", 5) - || !strnicmp(mae.session_name, "sdp:", 4) - || !strnicmp(mae.session_name, "rtp:", 4) - ) - transport = 1; - else if (!strnicmp(mae.session_name, "dvb:", 4)) - transport = 2; + media_event.bufferValid = 0; + media_event.session_name = odm->net_service->url; min_time = min_buffer = (u32) -1; scene = odm->subscene ? odm->subscene : odm->parentscene; /*get buffering on root OD*/ - mae_collect_info(odm->net_service, scene->root_od, &mae, transport, &min_time, &min_buffer); + media_event_collect_info(odm->net_service, scene->root_od, &media_event, &min_time, &min_buffer); /*get buffering on all ODs*/ i=0; while ((an_od = (GF_ObjectManager*)gf_list_enum(scene->resources, &i))) { - mae_collect_info(odm->net_service, an_od, &mae, transport, &min_time, &min_buffer); + if (odm->net_service == an_od->net_service) + media_event_collect_info(odm->net_service, an_od, &media_event, &min_time, &min_buffer); } - mae.level = min_buffer; - mae.remaining_time = INT2FIX(min_time) / 60; - mae.status = 0; + media_event.level = min_buffer; + media_event.remaining_time = INT2FIX(min_time) / 60; + media_event.status = 0; + media_event.loaded_size = loaded_size; + media_event.total_size = total_size; memset(&evt, 0, sizeof(GF_DOM_Event)); - evt.mae = &mae; + evt.media_event = &media_event; evt.type = event_type; evt.bubbles = 0; /*the spec says yes but we force it to NO*/ /*lock scene to prevent concurrent access of scene data*/ locked = gf_mx_try_lock(odm->term->compositor->mx); + if (!locked) return; + for (i=0; imo->nodes, i); gf_dom_event_fire(node, &evt); @@ -1223,10 +1327,14 @@ void gf_term_service_media_event(GF_ObjectManager *odm, u32 event_type) GF_Node *root = gf_sg_get_root_node(scene->graph); if (root) gf_dom_event_fire(root, &evt); } - if (locked) gf_sc_lock(odm->term->compositor, 0); + gf_sc_lock(odm->term->compositor, 0); #endif } +void gf_term_service_media_event(GF_ObjectManager *odm, u32 event_type) +{ + gf_term_service_media_event_with_download(odm, event_type, 0, 0, 0); +} /* Browses all registered relocators (ZIP-based, ISOFF-based or file-system-based to relocate a URI based on the locale */ Bool gf_term_relocate_url(GF_Terminal *term, const char *service_url, const char *parent_url, char *out_relocated_url, char *out_localized_url) @@ -1244,15 +1352,30 @@ Bool gf_term_relocate_url(GF_Terminal *term, const char *service_url, const char return 0; } +/*in most cases we cannot directly connect an object, as the request may come from a different thread than the one handling +ODM disconnection. We could therefore end up attaching an object to a service currently being destroyed because of a concurrent +odm_disconnect*/ +void gf_term_post_connect_object(GF_Terminal *term, GF_ObjectManager *odm, char *serviceURL, char *parent_url) +{ + GF_TermConnectObject *connect; + GF_SAFEALLOC(connect, GF_TermConnectObject); + connect->odm = odm; + connect->service_url = gf_strdup(serviceURL); + connect->parent_url = parent_url ? gf_strdup(parent_url) : NULL; + + gf_term_lock_media_queue(term, 1); + gf_list_add(term->connection_tasks, connect); + gf_term_lock_media_queue(term, 0); +} + /*connects given OD manager to its URL*/ -void gf_term_connect_object(GF_Terminal *term, GF_ObjectManager *odm, char *serviceURL, char *parent_url) +static void gf_term_connect_object(GF_Terminal *term, GF_ObjectManager *odm, char *serviceURL, char *parent_url) { GF_ClientService *ns; u32 i, count; GF_Err e; - Bool reloc_result; + Bool reloc_result, net_locked; char relocated_url[GF_MAX_PATH], localized_url[GF_MAX_PATH]; - gf_term_lock_net(term, 1); /*try to relocate the url*/ reloc_result = gf_term_relocate_url(term, serviceURL, parent_url, relocated_url, localized_url); @@ -1290,46 +1413,81 @@ void gf_term_connect_object(GF_Terminal *term, GF_ObjectManager *odm, char *serv } } - /*for remoteODs/dynamic ODs, check if one of the running service cannot be used*/ + /*for remoteODs/dynamic ODs, check if one of the running service cannot be used + net mutex may be locked at this time (if another service sends a connect OK)*/ + net_locked = gf_mx_try_lock(term->net_mx); i=0; while ( (ns = (GF_ClientService*)gf_list_enum(term->net_services, &i)) ) { + /*we shall not have a service scheduled for destruction here*/ + if (ns->owner && ( (ns->owner->flags & GF_ODM_DESTROYED) || (ns->owner->action_type == GF_ODM_ACTION_DELETE)) ) + continue; + + /*if service has timeline locked to its parent scene, only reuse it if new object does as well*/ + if (ns->owner->flags & GF_ODM_INHERIT_TIMELINE) { + if (!(odm->flags & GF_ODM_INHERIT_TIMELINE)) continue; + } + if (gf_term_service_can_handle_url(ns, serviceURL)) { - gf_term_lock_net(term, 0); + if (net_locked) { + gf_term_lock_net(term, 0); + net_locked = 0; + } /*wait for service to setup - service may become destroyed if not available*/ while (1) { - gf_term_lock_net(term, 1); + net_locked = gf_mx_try_lock(term->net_mx); if (!ns->owner) { - gf_term_lock_net(term, 0); + if (net_locked) { + gf_term_lock_net(term, 0); + net_locked = 0; + } return; } - gf_term_lock_net(term, 0); + if (net_locked) { + gf_term_lock_net(term, 0); + net_locked = 0; + } + if (ns->owner->OD) break; gf_sleep(5); } + + gf_mx_p(term->net_mx); + if (odm->net_service) { + gf_mx_v(term->net_mx); + return; + } + if (odm->flags & GF_ODM_DESTROYED) { + gf_mx_v(term->net_mx); + GF_LOG(GF_LOG_WARNING, GF_LOG_MEDIA, ("[ODM%d] Object has been scheduled for destruction - ignoring object setup\n", odm->OD->objectDescriptorID)); + return; + } odm->net_service = ns; + odm->net_service->nb_odm_users ++; gf_odm_setup_entry_point(odm, serviceURL); + gf_mx_v(term->net_mx); return; } } + if (net_locked) + gf_term_lock_net(term, 0); odm->net_service = gf_term_service_new(term, odm, serviceURL, reloc_result ? NULL : parent_url, &e); if (!odm->net_service) { - gf_term_lock_net(term, 0); gf_term_message(term, serviceURL, "Cannot open service", e); gf_odm_disconnect(odm, 1); return; } odm->net_service->nb_odm_users++; - gf_term_lock_net(term, 0); - gf_mx_p(term->media_queue_mx); assert(odm->net_service->owner == odm); - /*we are all set but we cannot assume it is safe to call connect from this thread, as this could be a - script callback (JS locked) and connecting the media could trigger JS calls, which would deadlock*/ - gf_list_add(term->net_services_to_connect, odm->net_service); - - gf_mx_v(term->media_queue_mx); + + /*OK connect*/ + gf_term_service_media_event(odm, GF_EVENT_MEDIA_SETUP_BEGIN); + odm->net_service->ifce->ConnectService(odm->net_service->ifce, odm->net_service, odm->net_service->url); + + /*remove pending download session if any*/ + gf_term_cleanup_pending_session(term, ns); } /*connects given channel to its URL if needed*/ @@ -1835,13 +1993,16 @@ void gf_term_set_speed(GF_Terminal *term, Fixed speed) } } + opt = gf_cfg_get_key(term->user->config, "Systems", "TimeSlice"); + if (!opt) opt="30"; + i = (u32) ( atoi(opt) / FIX2FLT(speed) ); + if (!i) i = 1; + term->frame_duration = i; + opt = gf_cfg_get_key(term->user->config, "Compositor", "FrameRate"); - if (!opt) opt="30.0"; - - fps = atof(opt); + fps = atoi(opt); fps *= FIX2FLT(speed); if (fps>100) fps = 1000; - term->frame_duration = (u32) (1000/fps); gf_sc_set_fps(term->compositor, fps); } @@ -2049,27 +2210,33 @@ void gf_term_load_shortcuts(GF_Terminal *term) void gf_scene_switch_quality(GF_Scene *scene, Bool up) { u32 i; + GF_ClientService *root_service = NULL; GF_ObjectManager *odm; GF_CodecCapability caps; GF_NetworkCommand net_cmd; if (!scene) return; - caps.CapCode = GF_CODEC_MEDIA_SWITCH_QUALITY; - caps.cap.valueInt = up ? 1 : 0; + + /*send network command*/ net_cmd.command_type = GF_NET_SERVICE_QUALITY_SWITCH; net_cmd.switch_quality.on_channel = NULL; net_cmd.switch_quality.up = up; + if (scene->root_od->net_service) { + root_service = scene->root_od->net_service; + root_service->ifce->ServiceCommand(root_service->ifce, &net_cmd); + } + /*notify all codecs in the scene and subscenes*/ + caps.CapCode = GF_CODEC_MEDIA_SWITCH_QUALITY; + caps.cap.valueInt = up ? 1 : 0; if (scene->scene_codec) { scene->scene_codec->decio->SetCapabilities(scene->scene_codec->decio, caps); - if (scene->root_od->net_service) - scene->root_od->net_service->ifce->ServiceCommand(scene->root_od->net_service->ifce, &net_cmd); } i=0; while (NULL != (odm = gf_list_enum(scene->resources, &i))) { if (odm->codec) odm->codec->decio->SetCapabilities(odm->codec->decio, caps); - if (odm->net_service) + if (odm->net_service && (odm->net_service != root_service) ) odm->net_service->ifce->ServiceCommand(odm->net_service->ifce, &net_cmd); if (odm->subscene) gf_scene_switch_quality(odm->subscene, up); diff --git a/src/utils/alloc.c b/src/utils/alloc.c index 846fbab..7ef1876 100644 --- a/src/utils/alloc.c +++ b/src/utils/alloc.c @@ -183,6 +183,8 @@ static int unregister_address(void *ptr, char *filename, int line); static void gf_memory_log(unsigned int level, const char *fmt, ...); enum { + /*! Disable all Log message*/ + GF_MEMORY_QUIET = 0, /*! Log message describes an error*/ GF_MEMORY_ERROR = 1, /*! Log message describes a warning*/ @@ -538,6 +540,30 @@ static void register_address(void *ptr, size_t size, char *filename, int line) gf_mx_v(gpac_allocations_lock); } +#if 0 +void gf_check_address(void *ptr) +{ + int pos; + + /*lock*/ + gf_mx_p(gpac_allocations_lock); + + if ( (pos=gf_memory_find(memory_rem, ptr)) ) { + int i; + unsigned int hash = gf_memory_hash(ptr); + memory_element *element = memory_rem[hash]; + assert(element); + for (i=1; inext; + assert(element); + gf_memory_log(GF_MEMORY_ERROR, "[MemTracker] the block %p has already been freed\n in file %s at line %d\n", ptr, element->filename, element->line); + assert(0); + } + /*unlock*/ + gf_mx_v(gpac_allocations_lock); +} +#endif + /*returns the size of the unregistered block*/ static int unregister_address(void *ptr, char *filename, int line) { @@ -562,8 +588,12 @@ static int unregister_address(void *ptr, char *filename, int line) /* assert(0); */ /*don't assert since this is often due to allocations that occured out of gpac (fonts, etc.)*/ } else { int i; +#if GPAC_MEMORY_TRACKING_HASH_TABLE unsigned int hash = gf_memory_hash(ptr); memory_element *element = memory_rem[hash]; +#else + memory_element *element = memory_rem; +#endif assert(element); for (i=1; inext; @@ -621,7 +651,7 @@ static void gf_memory_log(unsigned int level, const char *fmt, ...) void gf_memory_size() { unsigned int level = gpac_nb_alloc_blocs ? GF_MEMORY_ERROR : GF_MEMORY_INFO; - gf_memory_log(level, "[MemTracker] Total: %d bytes allocated on %d blocks\n", gpac_allocated_memory, gpac_nb_alloc_blocs); + gf_memory_log(level, "[MemTracker] Total: %d bytes allocated in %d blocks\n", gpac_allocated_memory, gpac_nb_alloc_blocs); } /*prints the state of current allocations*/ @@ -663,7 +693,7 @@ void gf_memory_print() /*gf_asprintf(): as_printf portable implementation*/ -#if defined(WIN32) || defined(_WIN32_WCE) +#if defined(WIN32) || defined(_WIN32_WCE) || (defined (__SVR4) && defined (__sun)) static GFINLINE int gf_vasprintf (char **strp, const char *fmt, va_list ap) { int vsn_ret, size; @@ -675,6 +705,9 @@ static GFINLINE int gf_vasprintf (char **strp, const char *fmt, va_list ap) return -1; while (1) { +#if !defined(WIN32) && !defined(_WIN32_WCE) +#define _vsnprintf vsnprintf +#endif vsn_ret = _vsnprintf(buffer, size, fmt, ap); /* If that worked, return the string. */ @@ -702,7 +735,7 @@ int gf_asprintf(char **strp, const char *fmt, ...) s32 size; va_list args; va_start(args, fmt); -#if defined(WIN32) || defined(_WIN32_WCE) +#if defined(WIN32) || defined(_WIN32_WCE) || (defined (__SVR4) && defined (__sun)) size = gf_vasprintf(strp, fmt, args); #else size = asprintf(strp, fmt, args); diff --git a/src/utils/bitstream.c b/src/utils/bitstream.c index 5229775..5091850 100644 --- a/src/utils/bitstream.c +++ b/src/utils/bitstream.c @@ -493,7 +493,7 @@ u32 gf_bs_write_byte(GF_BitStream *bs, u8 byte, u32 repeat_count) return repeat_count; case GF_BITSTREAM_FILE_READ: case GF_BITSTREAM_FILE_WRITE: - if (fwrite(&byte, 1, repeat_count, bs->stream) != repeat_count) return 0; + if (gf_fwrite(&byte, 1, repeat_count, bs->stream) != repeat_count) return 0; if (bs->size == bs->position) bs->size += repeat_count; bs->position += repeat_count; return repeat_count; @@ -564,7 +564,7 @@ u32 gf_bs_write_data(GF_BitStream *bs, const char *data, u32 nbBytes) return nbBytes; case GF_BITSTREAM_FILE_READ: case GF_BITSTREAM_FILE_WRITE: - if (fwrite(data, nbBytes, 1, bs->stream) != 1) return 0; + if (gf_fwrite(data, nbBytes, 1, bs->stream) != 1) return 0; if (bs->size == bs->position) bs->size += nbBytes; bs->position += nbBytes; return nbBytes; diff --git a/src/utils/cache.c b/src/utils/cache.c index 318d45d..8745c8b 100644 --- a/src/utils/cache.c +++ b/src/utils/cache.c @@ -560,11 +560,11 @@ GF_Err gf_cache_open_write_cache( const DownloadedCacheEntry entry, const GF_Dow entry->write_session = sess; assert( ! entry->writeFilePtr); GF_LOG(GF_LOG_INFO, GF_LOG_NETWORK, - ("[CACHE] Opening cache file %s for write (%s)...", entry->cache_filename, entry->url)); + ("[CACHE] Opening cache file %s for write (%s)...\n", entry->cache_filename, entry->url)); entry->writeFilePtr = gf_f64_open(entry->cache_filename, "wb"); if (!entry->writeFilePtr) { GF_LOG(GF_LOG_ERROR, GF_LOG_NETWORK, - ("[CACHE] Error while opening cache file %s for writting.", entry->cache_filename)); + ("[CACHE] Error while opening cache file %s for writting.\n", entry->cache_filename)); entry->write_session = NULL; #ifdef ENABLE_WRITE_MX gf_mx_v(entry->write_mutex); @@ -583,7 +583,7 @@ GF_Err gf_cache_write_to_cache( const DownloadedCacheEntry entry, const GF_Downl GF_LOG(GF_LOG_WARNING, GF_LOG_NETWORK, ("Incorrect parameter : data=%p, entry->writeFilePtr=%p at "__FILE__, data, entry->writeFilePtr)); return GF_BAD_PARAM; } - readen = fwrite(data, sizeof(char), size, entry->writeFilePtr); + readen = gf_fwrite(data, sizeof(char), size, entry->writeFilePtr); if (readen > 0) entry->written_in_cache+= readen; if (readen != size) { diff --git a/src/utils/color.c b/src/utils/color.c index 9162063..3504752 100644 --- a/src/utils/color.c +++ b/src/utils/color.c @@ -223,13 +223,13 @@ static s32 mul255(s32 a, s32 b) } typedef void (*copy_row_proto)(u8 *src, u32 src_w, u8 *_dst, u32 dst_w, s32 h_inc, s32 x_pitch, u8 alpha); -typedef void (*load_line_proto)(u8 *src_bits, u32 x_offset, u32 y_offset, u32 y_pitch, u32 width, u8 *dst_bits); +typedef void (*load_line_proto)(u8 *src_bits, u32 x_offset, u32 y_offset, u32 y_pitch, u32 src_width, u32 src_height, u8 *dst_bits); static void copy_row_rgb_555(u8 *src, u32 src_w, u8 *_dst, u32 dst_w, s32 h_inc, s32 x_pitch, u8 alpha) { s32 pos; u16 *dst = (u16 *)_dst; - u8 a, r, g, b; + u8 a=0, r=0, g=0, b=0; x_pitch /= 2; pos = 0x10000; while (dst_w) { @@ -248,7 +248,7 @@ static void copy_row_rgb_565(u8 *src, u32 src_w, u8 *_dst, u32 dst_w, s32 h_inc, { s32 pos; u16 *dst = (u16 *)_dst; - u8 a, r, g, b; + u8 a=0, r=0, g=0, b=0; x_pitch /= 2; pos = 0x10000; while (dst_w) { @@ -267,7 +267,7 @@ static void copy_row_rgb_565(u8 *src, u32 src_w, u8 *_dst, u32 dst_w, s32 h_inc, static void copy_row_rgb_24(u8 *src, u32 src_w, u8 *dst, u32 dst_w, s32 h_inc, s32 x_pitch, u8 alpha) { s32 pos; - u8 a, r, g, b; + u8 a=0, r=0, g=0, b=0; pos = 0x10000; while (dst_w) { @@ -285,7 +285,7 @@ static void copy_row_rgb_24(u8 *src, u32 src_w, u8 *dst, u32 dst_w, s32 h_inc, s static void copy_row_bgr_24(u8 *src, u32 src_w, u8 *dst, u32 dst_w, s32 h_inc, s32 x_pitch, u8 alpha) { s32 pos; - u8 a, r, g, b; + u8 a=0, r=0, g=0, b=0; pos = 0x10000; while (dst_w) { @@ -302,7 +302,7 @@ static void copy_row_bgr_24(u8 *src, u32 src_w, u8 *dst, u32 dst_w, s32 h_inc, s static void copy_row_bgrx(u8 *src, u32 src_w, u8 *dst, u32 dst_w, s32 h_inc, s32 x_pitch, u8 alpha) { - u8 a, r, g, b; + u8 a=0, r=0, g=0, b=0; s32 pos = 0x10000L; while (dst_w) { @@ -324,7 +324,7 @@ static void copy_row_bgrx(u8 *src, u32 src_w, u8 *dst, u32 dst_w, s32 h_inc, s32 static void copy_row_rgbx(u8 *src, u32 src_w, u8 *dst, u32 dst_w, s32 h_inc, s32 x_pitch, u8 alpha) { - u8 a, r, g, b; + u8 a=0, r=0, g=0, b=0; s32 pos = 0x10000L; while ( dst_w) { @@ -346,7 +346,7 @@ static void copy_row_rgbx(u8 *src, u32 src_w, u8 *dst, u32 dst_w, s32 h_inc, s32 static void copy_row_rgbd(u8 *src, u32 src_w, u8 *dst, u32 dst_w, s32 h_inc, s32 x_pitch, u8 alpha) { - u8 a, r, g, b; + u8 a=0, r=0, g=0, b=0; s32 pos = 0x10000L; while ( dst_w) { @@ -368,7 +368,7 @@ static void copy_row_rgbd(u8 *src, u32 src_w, u8 *dst, u32 dst_w, s32 h_inc, s32 static void merge_row_rgb_555(u8 *src, u32 src_w, u8 *_dst, u32 dst_w, s32 h_inc, s32 x_pitch, u8 alpha) { - u32 _r, _g, _b, a, r, g, b; + u32 _r, _g, _b, a=0, r=0, g=0, b=0; s32 pos; u16 col, *dst = (u16 *)_dst; x_pitch /= 2; @@ -397,7 +397,7 @@ static void merge_row_rgb_555(u8 *src, u32 src_w, u8 *_dst, u32 dst_w, s32 h_inc static void merge_row_rgb_565(u8 *src, u32 src_w, u8 *_dst, u32 dst_w, s32 h_inc, s32 x_pitch, u8 alpha) { - u32 _r, _g, _b, a, r, g, b; + u32 _r, _g, _b, a=0, r=0, g=0, b=0; s32 pos; u16 col, *dst = (u16 *)_dst; x_pitch /= 2; @@ -427,7 +427,7 @@ static void merge_row_rgb_565(u8 *src, u32 src_w, u8 *_dst, u32 dst_w, s32 h_inc static void merge_row_rgb_24(u8 *src, u32 src_w, u8 *dst, u32 dst_w, s32 h_inc, s32 x_pitch, u8 alpha) { - u32 _r, _g, _b, a, r, g, b; + u32 _r, _g, _b, a=0, r=0, g=0, b=0; s32 pos; pos = 0x10000; @@ -451,7 +451,7 @@ static void merge_row_rgb_24(u8 *src, u32 src_w, u8 *dst, u32 dst_w, s32 h_inc, static void merge_row_bgr_24(u8 *src, u32 src_w, u8 *dst, u32 dst_w, s32 h_inc, s32 x_pitch, u8 alpha) { - u32 _r, _g, _b, a, r, g, b; + u32 _r, _g, _b, a=0, r=0, g=0, b=0; s32 pos; pos = 0x10000; @@ -479,7 +479,7 @@ static void merge_row_bgr_24(u8 *src, u32 src_w, u8 *dst, u32 dst_w, s32 h_inc, static void merge_row_bgrx(u8 *src, u32 src_w, u8 *dst, u32 dst_w, s32 h_inc, s32 x_pitch, u8 alpha) { - u32 _r, _g, _b, a, r, g, b; + u32 _r, _g, _b, a=0, r=0, g=0, b=0; s32 pos; pos = 0x10000; @@ -512,7 +512,7 @@ static void merge_row_bgrx(u8 *src, u32 src_w, u8 *dst, u32 dst_w, s32 h_inc, s3 static void merge_row_rgbx(u8 *src, u32 src_w, u8 *dst, u32 dst_w, s32 h_inc, s32 x_pitch, u8 alpha) { - u32 _r, _g, _b, a, r, g, b; + u32 _r, _g, _b, a=0, r=0, g=0, b=0; s32 pos; pos = 0x10000; @@ -544,7 +544,7 @@ static void merge_row_rgbx(u8 *src, u32 src_w, u8 *dst, u32 dst_w, s32 h_inc, s3 static void merge_row_bgra(u8 *src, u32 src_w, u8 *dst, u32 dst_w, s32 h_inc, s32 x_pitch, u8 alpha) { - u32 _a, _r, _g, _b, a, r, g, b; + u32 _a, _r, _g, _b, a=0, r=0, g=0, b=0; s32 pos; pos = 0x10000; @@ -583,7 +583,7 @@ static void merge_row_bgra(u8 *src, u32 src_w, u8 *dst, u32 dst_w, s32 h_inc, s3 static void merge_row_rgba(u8 *src, u32 src_w, u8 *dst, u32 dst_w, s32 h_inc, s32 x_pitch, u8 alpha) { - u32 _a, _r, _g, _b, a, r, g, b; + u32 _a, _r, _g, _b, a=0, r=0, g=0, b=0; s32 pos; pos = 0x10000; while (dst_w) { @@ -619,13 +619,36 @@ static void merge_row_rgba(u8 *src, u32 src_w, u8 *dst, u32 dst_w, s32 h_inc, s3 } } + +static void load_line_grey(u8 *src_bits, u32 x_offset, u32 y_offset, u32 y_pitch, u32 width, u32 height, u8 *dst_bits) +{ + u32 i; + src_bits += x_offset + y_offset*y_pitch; + for (i=0; i> n) & 1)) & mask); } -static void load_line_rgb_555(u8 *src_bits, u32 x_offset, u32 y_offset, u32 y_pitch, u32 width, u8 *dst_bits) +static void load_line_rgb_555(u8 *src_bits, u32 x_offset, u32 y_offset, u32 y_pitch, u32 width, u32 height, u8 *dst_bits) { u32 i; src_bits += x_offset*3 + y_offset*y_pitch; @@ -639,7 +662,7 @@ static void load_line_rgb_555(u8 *src_bits, u32 x_offset, u32 y_offset, u32 y_pi } } -static void load_line_rgb_565(u8 *src_bits, u32 x_offset, u32 y_offset, u32 y_pitch, u32 width, u8 *dst_bits) +static void load_line_rgb_565(u8 *src_bits, u32 x_offset, u32 y_offset, u32 y_pitch, u32 width, u32 height, u8 *dst_bits) { u32 i; src_bits += x_offset*3 + y_offset*y_pitch; @@ -653,7 +676,7 @@ static void load_line_rgb_565(u8 *src_bits, u32 x_offset, u32 y_offset, u32 y_pi } } -static void load_line_rgb_24(u8 *src_bits, u32 x_offset, u32 y_offset, u32 y_pitch, u32 width, u8 *dst_bits) +static void load_line_rgb_24(u8 *src_bits, u32 x_offset, u32 y_offset, u32 y_pitch, u32 width, u32 height, u8 *dst_bits) { u32 i; src_bits += x_offset*3 + y_offset*y_pitch; @@ -666,7 +689,7 @@ static void load_line_rgb_24(u8 *src_bits, u32 x_offset, u32 y_offset, u32 y_pit } } -static void load_line_bgr_24(u8 *src_bits, u32 x_offset, u32 y_offset, u32 y_pitch, u32 width, u8 *dst_bits) +static void load_line_bgr_24(u8 *src_bits, u32 x_offset, u32 y_offset, u32 y_pitch, u32 width, u32 height, u8 *dst_bits) { u32 i; src_bits += x_offset*3 + y_offset*y_pitch; @@ -679,7 +702,7 @@ static void load_line_bgr_24(u8 *src_bits, u32 x_offset, u32 y_offset, u32 y_pit } } -static void load_line_rgb_32(u8 *src_bits, u32 x_offset, u32 y_offset, u32 y_pitch, u32 width, u8 *dst_bits) +static void load_line_rgb_32(u8 *src_bits, u32 x_offset, u32 y_offset, u32 y_pitch, u32 width, u32 height, u8 *dst_bits) { u32 i; src_bits += x_offset*4 + y_offset*y_pitch; @@ -692,7 +715,7 @@ static void load_line_rgb_32(u8 *src_bits, u32 x_offset, u32 y_offset, u32 y_pit } } -static void load_line_rgbd(u8 *src_bits, u32 x_offset, u32 y_offset, u32 y_pitch, u32 width, u8 *dst_bits) +static void load_line_rgbd(u8 *src_bits, u32 x_offset, u32 y_offset, u32 y_pitch, u32 width, u32 height, u8 *dst_bits) { u32 i; src_bits += x_offset*4 + y_offset*y_pitch; @@ -706,7 +729,7 @@ static void load_line_rgbd(u8 *src_bits, u32 x_offset, u32 y_offset, u32 y_pitch } } -static void load_line_rgbds(u8 *src_bits, u32 x_offset, u32 y_offset, u32 y_pitch, u32 width, u8 *dst_bits) +static void load_line_rgbds(u8 *src_bits, u32 x_offset, u32 y_offset, u32 y_pitch, u32 width, u32 height, u8 *dst_bits) { u32 i; src_bits += x_offset*4 + y_offset*y_pitch; @@ -719,7 +742,7 @@ static void load_line_rgbds(u8 *src_bits, u32 x_offset, u32 y_offset, u32 y_pitc } } -static void load_line_argb(u8 *src_bits, u32 x_offset, u32 y_offset, u32 y_pitch, u32 width, u8 *dst_bits) +static void load_line_argb(u8 *src_bits, u32 x_offset, u32 y_offset, u32 y_pitch, u32 width, u32 height, u8 *dst_bits) { u32 i; src_bits += x_offset*4 + y_offset*y_pitch; @@ -732,7 +755,7 @@ static void load_line_argb(u8 *src_bits, u32 x_offset, u32 y_offset, u32 y_pitch } } -static void load_line_bgr_32(u8 *src_bits, u32 x_offset, u32 y_offset, u32 y_pitch, u32 width, u8 *dst_bits) +static void load_line_bgr_32(u8 *src_bits, u32 x_offset, u32 y_offset, u32 y_pitch, u32 width, u32 height, u8 *dst_bits) { src_bits += x_offset*4 + y_offset*y_pitch; memcpy(dst_bits, src_bits, sizeof(char)*4*width); @@ -765,7 +788,7 @@ static void load_line_yuva(char *src_bits, u32 x_offset, u32 y_offset, u32 y_pit gf_yuva_load_lines(dst_bits, 4*width, pY, pU, pV, pA, y_pitch, y_pitch/2, width); } -static void load_line_yuyv(u8 *src_bits, u32 x_offset, u32 y_offset, u32 y_pitch, u32 width, u8 *dst_bits) +static void load_line_yuyv(u8 *src_bits, u32 x_offset, u32 y_offset, u32 y_pitch, u32 width, u32 height, u8 *dst_bits) { u8 *pY, *pU, *pV; pY = (u8 *)src_bits + x_offset + y_offset*y_pitch; @@ -774,6 +797,52 @@ static void load_line_yuyv(u8 *src_bits, u32 x_offset, u32 y_offset, u32 y_pitch gf_yuv_load_lines_packed((unsigned char*)dst_bits, 4*width, pY, pU, pV, width); } +/*Ivica patch - todo, align it with other YUV loader (2 lines at a time) to avoid fetching twice U and V*/ +static void load_line_YUV420SP(u8 *src_bits, u32 x_offset, u32 y_offset, u32 y_pitch, u32 width, u32 height, u8 *dst_bits) +{ + s32 frameSize = width * height; + s32 j, yp, uvp, y, y1192, r, g, b, u, v; + u32 i; + + yp = (s32)(x_offset + y_offset * y_pitch / 1.5f); + j = y_offset; + + uvp = frameSize + (j >> 1) * width, u = 0, v = 0; + + for (i=0; i 262143) + r = 262143; + if (g < 0) + g = 0; + else if (g > 262143) + g = 262143; + if (b < 0) + b = 0; + else if (b > 262143) + b = 262143; + + *((u32*)dst_bits) = 0xff000000 | ((b << 6) & 0xff0000) + | ((g >> 2) & 0xff00) | ((r >> 10) & 0xff); + dst_bits+=4; + } +} + static void gf_cmx_apply_argb(GF_ColorMatrix *_this, u8 *a_, u8 *r_, u8 *g_, u8 *b_); @@ -790,7 +859,7 @@ GF_Err gf_stretch_bits(GF_VideoSurface *dst, GF_VideoSurface *src, GF_Window *ds u32 dst_bpp, dst_w_size; s32 pos_y, inc_y, inc_x, prev_row, x_off; u32 src_w, src_h, dst_w, dst_h; - u8 *src_bits = NULL, *dst_bits = NULL, *dst_bits_prev = NULL, *dst_temp_bits = NULL; + u8 *dst_bits = NULL, *dst_bits_prev = NULL, *dst_temp_bits = NULL; s32 dst_x_pitch = dst->pitch_x; copy_row_proto copy_row = NULL; @@ -800,6 +869,13 @@ GF_Err gf_stretch_bits(GF_VideoSurface *dst, GF_VideoSurface *src, GF_Window *ds else if (key && (key->alpha<0xFF)) has_alpha = 1; switch (src->pixel_format) { + case GF_PIXEL_GREYSCALE: + load_line = load_line_grey; + break; + case GF_PIXEL_ALPHAGREY: + load_line = load_line_alpha_grey; + has_alpha = 1; + break; case GF_PIXEL_RGB_555: load_line = load_line_rgb_555; break; @@ -839,6 +915,9 @@ GF_Err gf_stretch_bits(GF_VideoSurface *dst, GF_VideoSurface *src, GF_Window *ds yuv2rgb_init(); yuv_planar_type = 1; break; + case GF_PIXEL_NV21: + load_line = load_line_YUV420SP; + break; case GF_PIXEL_YUVA: has_alpha = 1; case GF_PIXEL_YUVD: @@ -909,7 +988,6 @@ GF_Err gf_stretch_bits(GF_VideoSurface *dst, GF_VideoSurface *src, GF_Window *ds tmp = (u8 *) gf_malloc(sizeof(u8) * src_w * (yuv_planar_type ? 8 : 4) ); rows = tmp; - src_bits = (u8 *) src->video_buffer; dst_bits = (u8 *) dst->video_buffer; pos_y = 0x10000; @@ -1002,7 +1080,7 @@ GF_Err gf_stretch_bits(GF_VideoSurface *dst, GF_VideoSurface *src, GF_Window *ds } } else { if (flip) the_row = src->height-1 - the_row; - load_line((u8*)src->video_buffer, x_off, the_row, src->pitch_y, src_w, tmp); + load_line((u8*)src->video_buffer, x_off, the_row, src->pitch_y, src_w, src->height, tmp); rows = tmp; if (cmat) { for (i=0; isections, &i)) ) { + if (!stricmp(secName, sec->section_name)) goto get_key; + } + return NULL; + +get_key: + i=0; + while ( (key = (IniKey *) gf_list_enum(sec->keys, &i)) ) { + if (!stricmp(key->name, keyName)) return key->value; + } + return NULL; +} GF_EXPORT diff --git a/src/utils/downloader.c b/src/utils/downloader.c index 3b4e063..8a7763e 100644 --- a/src/utils/downloader.c +++ b/src/utils/downloader.c @@ -60,20 +60,20 @@ static void gf_dm_connect(GF_DownloadSession *sess); /*internal flags*/ enum { - GF_DOWNLOAD_SESSION_USE_SSL = 1<<10, - GF_DOWNLOAD_SESSION_THREAD_DEAD = 1<<11 + GF_DOWNLOAD_SESSION_USE_SSL = 1<<10, + GF_DOWNLOAD_SESSION_THREAD_DEAD = 1<<11 }; typedef struct __gf_user_credentials { - char site[1024]; - char username[50]; - char digest[1024]; - Bool valid; + char site[1024]; + char username[50]; + char digest[1024]; + Bool valid; } gf_user_credentials_struct; enum REQUEST_TYPE { - GET = 0, + GET = 0, HEAD = 1, OTHER = 2 }; @@ -135,7 +135,8 @@ struct __gf_download_session /* True if cache file must be stored on disk */ Bool use_cache_file; Bool disable_cache; - + /*forces notification of data exchange to be sent regardless of threading mode*/ + Bool force_data_write_callback; #ifdef GPAC_HAS_SSL SSL *ssl; #endif @@ -595,6 +596,9 @@ void gf_dm_delete_cached_file_entry_session(const GF_DownloadSession * sess, co static void gf_dm_disconnect(GF_DownloadSession *sess, Bool force_close) { + assert( sess ); + if (sess->status >= GF_NETIO_DISCONNECTED) + return; GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[Downloader] gf_dm_disconnect(%p)\n", sess )); if (sess->mx) gf_mx_p(sess->mx); @@ -608,8 +612,10 @@ static void gf_dm_disconnect(GF_DownloadSession *sess, Bool force_close) } #endif if (sess->sock) { - gf_sk_del(sess->sock); + GF_Socket * sx = sess->sock; sess->sock = NULL; + gf_sk_del(sx); + //sess->sock = NULL; } } sess->status = GF_NETIO_DISCONNECTED; @@ -1007,12 +1013,8 @@ GF_DownloadSession *gf_dm_sess_new_simple(GF_DownloadManager * dm, const char *u return NULL; } assert( sess ); - if (!(sess->flags & GF_NETIO_SESSION_NOT_THREADED) ) { - sess->th = gf_th_new(url); - sess->mx = gf_mx_new(url); - gf_th_run(sess->th, gf_dm_session_thread, sess); - } sess->num_retry = SESSION_RETRY_COUNT; + /*threaded session must be started with gf_dm_sess_process*/ return sess; } @@ -1310,6 +1312,21 @@ GF_Err gf_dm_sess_set_range(GF_DownloadSession *sess, u64 start_range, u64 end_r GF_Err gf_dm_sess_process(GF_DownloadSession *sess) { Bool go; + + /*if session is threaded, start thread*/ + if (! (sess->flags & GF_NETIO_SESSION_NOT_THREADED)) { + if (sess->th) { + GF_LOG(GF_LOG_WARNING, GF_LOG_NETWORK, ("[HTTP] Session already started - ignoring start\n")); + return GF_OK; + } + sess->th = gf_th_new(sess->orig_url); + if (!sess->th) return GF_OUT_OF_MEM; + sess->mx = gf_mx_new(sess->orig_url); + if (!sess->mx) return GF_OUT_OF_MEM; + gf_th_run(sess->th, gf_dm_session_thread, sess); + return GF_OK; + } + /*otherwise do a synchronous download*/ go = 1; while (go) { switch (sess->status) { @@ -1706,7 +1723,7 @@ GF_EXPORT const char *gf_dm_sess_get_cache_name(GF_DownloadSession * sess) { if (!sess) return NULL; - assert( sess->cache_entry ); + if (! sess->cache_entry ) return NULL; return gf_cache_get_cache_filename(sess->cache_entry); } @@ -1719,6 +1736,7 @@ Bool gf_dm_sess_can_be_cached_on_disk(const GF_DownloadSession *sess) void gf_dm_sess_abort(GF_DownloadSession * sess) { + assert(sess); if (sess->mx) { gf_mx_p(sess->mx); gf_dm_disconnect(sess, 1); @@ -2030,28 +2048,32 @@ static GF_Err http_parse_remaining_body(GF_DownloadSession * sess, char * sHTTP) return GF_OK; } - if (e) { - if (e == GF_IP_CONNECTION_CLOSED){ - u32 len = gf_cache_get_content_length(sess->cache_entry); - if (size > 0) - gf_dm_data_received(sess, sHTTP, size); - if (len == 0){ - sess->total_size = sess->bytes_done; - // HTTP 1.1 without content length... - gf_dm_sess_notify_state(sess, GF_NETIO_DATA_TRANSFERED, GF_OK); - assert(sess->server_name); - GF_LOG(GF_LOG_ERROR, GF_LOG_NETWORK, ("[HTTP] Disconnected from %s: %s\n", sess->server_name, gf_error_to_string(e))); - sess->status = GF_NETIO_DISCONNECTED; - gf_cache_set_content_length(sess->cache_entry, sess->bytes_done); - e = GF_OK; + if (e) { + if (e == GF_IP_CONNECTION_CLOSED){ + u32 len = gf_cache_get_content_length(sess->cache_entry); + if (size > 0) + gf_dm_data_received(sess, sHTTP, size); + if ( ( (len == 0) && sess->use_cache_file) + /*ivica patch*/ + || (size==0) + ) { + sess->total_size = sess->bytes_done; + // HTTP 1.1 without content length... + gf_dm_sess_notify_state(sess, GF_NETIO_DATA_TRANSFERED, GF_OK); + assert(sess->server_name); + GF_LOG(GF_LOG_ERROR, GF_LOG_NETWORK, ("[HTTP] Disconnected from %s: %s\n", sess->server_name, gf_error_to_string(e))); + sess->status = GF_NETIO_DISCONNECTED; + if (sess->use_cache_file) + gf_cache_set_content_length(sess->cache_entry, sess->bytes_done); + e = GF_OK; + } + } + gf_dm_disconnect(sess, 1); + sess->last_error = e; + gf_dm_sess_notify_state(sess, sess->status, e); + return e; } - } - gf_dm_disconnect(sess, 1); - sess->last_error = e; - gf_dm_sess_notify_state(sess, sess->status, e); - return e; - } - gf_dm_data_received(sess, sHTTP, size); + gf_dm_data_received(sess, sHTTP, size); /*socket empty*/ if (size < GF_DOWNLOAD_BUFFER_SIZE) return GF_OK; @@ -2171,16 +2193,23 @@ static GF_Err wait_for_header_and_parse(GF_DownloadSession *sess, char * sHTTP) if (hdr_sep) hdr_sep[0] = 0; } - par.error = 0; + /*header signaling is moved after response processing*/ +#if 0 + par.error = 0; par.msg_type = GF_NETIO_PARSE_HEADER; par.name = hdr; par.value = hdr_val; - GF_LOG(GF_LOG_DEBUG, GF_LOG_NETWORK, ("[HTTP] Processing header %s: %s\n", hdr, hdr_val)); gf_dm_sess_user_io(sess, &par); +#endif + + GF_LOG(GF_LOG_DEBUG, GF_LOG_NETWORK, ("[HTTP] Processing header %s: %s\n", hdr, hdr_val)); if (!stricmp(hdr, "Content-Length") ) { ContentLength = (u32) atoi(hdr_val); gf_cache_set_content_length(sess->cache_entry, ContentLength); + /*Ivica patch*/ + if (ContentLength==0) + sess->use_cache_file = 0; } else if (!stricmp(hdr, "Content-Type")) { char * mime_type = gf_strdup(hdr_val); @@ -2195,6 +2224,7 @@ static GF_Err wait_for_header_and_parse(GF_DownloadSession *sess, char * sHTTP) } hdr = strchr(mime_type, ';'); if (hdr) hdr[0] = 0; + strlwr(mime_type); gf_cache_set_mime_type(sess->cache_entry, mime_type); gf_free(mime_type); } @@ -2240,12 +2270,11 @@ static GF_Err wait_for_header_and_parse(GF_DownloadSession *sess, char * sHTTP) /* else if (!stricmp(hdr, "Connection") ) if (strstr(hdr_val, "close")) sess->http_read_type = 1; */ - if (sep) sep[0]=':'; if (hdr_sep) hdr_sep[0] = '\r'; - if (sess->status==GF_NETIO_DISCONNECTED) return GF_OK; + if (sess->status==GF_NETIO_DISCONNECTED) return GF_OK; } if (no_range) first_byte = 0; @@ -2321,8 +2350,15 @@ static GF_Err wait_for_header_and_parse(GF_DownloadSession *sess, char * sHTTP) } return e; } + + par.error = 0; + par.msg_type = GF_NETIO_PARSE_HEADER; + par.name = "Content-Type"; + par.value = (char *) gf_cache_get_mime_type(sess->cache_entry); + gf_dm_sess_user_io(sess, &par); + sess->status = GF_NETIO_DATA_EXCHANGE; - if (! (sess->flags & GF_NETIO_SESSION_NOT_THREADED)) { + if (! (sess->flags & GF_NETIO_SESSION_NOT_THREADED) || sess->force_data_write_callback) { char file_cache_buff[16384]; int read = 0; u32 total_size = gf_cache_get_cache_filesize(sess->cache_entry); @@ -2409,7 +2445,7 @@ static GF_Err wait_for_header_and_parse(GF_DownloadSession *sess, char * sHTTP) return e; } - gf_dm_sess_user_io(sess, &par); + gf_dm_sess_user_io(sess, &par); e = GF_REMOTE_SERVICE_ERROR; goto exit; @@ -2427,6 +2463,36 @@ static GF_Err wait_for_header_and_parse(GF_DownloadSession *sess, char * sHTTP) goto exit; } + + /* FIXME UGLY CODE DUPLICATION to fix later on: we only send the headers to the user if no problem in response (eg no relocation, ...)*/ + while (1) { + char *sep, *hdr_sep, *hdr, *hdr_val; + if ( (s32) LinePos + 4 > BodyStart) break; + LinePos = gf_token_get_line(sHTTP, LinePos , bytesRead, buf, 1024); + if (LinePos < 0) break; + + hdr_sep = NULL; + hdr_val = NULL; + hdr = buf; + sep = strchr(buf, ':'); + if (sep) { + sep[0]=0; + hdr_val = sep+1; + while (hdr_val[0]==' ') hdr_val++; + hdr_sep = strrchr(hdr_val, '\r'); + if (hdr_sep) hdr_sep[0] = 0; + } + + par.error = 0; + par.msg_type = GF_NETIO_PARSE_HEADER; + par.name = hdr; + par.value = hdr_val; + gf_dm_sess_user_io(sess, &par); + if (sep) sep[0]=':'; + if (hdr_sep) hdr_sep[0] = '\r'; + } + + if (sess->http_read_type != GET) sess->use_cache_file = 0; @@ -2535,111 +2601,117 @@ void http_do_requests(GF_DownloadSession *sess) */ static void wget_NetIO(void *cbk, GF_NETIO_Parameter *param) { - FILE * f = (FILE*) cbk; + FILE * f = (FILE*) cbk; - /*handle service message*/ - if (param->msg_type == GF_NETIO_DATA_EXCHANGE) { - s32 written = fwrite( param->data, sizeof(char), param->size, f); + /*handle service message*/ + if (param->msg_type == GF_NETIO_DATA_EXCHANGE) { + s32 written = gf_fwrite( param->data, sizeof(char), param->size, f); if (written != param->size) { GF_LOG(GF_LOG_ERROR, GF_LOG_NETWORK, ("Failed to write data on disk\n")); } - } + } } GF_Err gf_dm_wget(const char *url, const char *filename){ - GF_Err e; - GF_DownloadManager * dm = NULL; - dm = gf_dm_new(NULL); - if (!dm) - return GF_OUT_OF_MEM; - e = gf_dm_wget_with_cache(dm, url, filename); - gf_dm_del(dm); - return e; + GF_Err e; + GF_DownloadManager * dm = NULL; + dm = gf_dm_new(NULL); + if (!dm) + return GF_OUT_OF_MEM; + e = gf_dm_wget_with_cache(dm, url, filename); + gf_dm_del(dm); + return e; } GF_Err gf_dm_wget_with_cache(GF_DownloadManager * dm, const char *url, const char *filename) { - GF_Err e; - FILE * f; - GF_DownloadSession *dnload; - if (!filename || !url || !dm) - return GF_BAD_PARAM; - f= fopen(filename, "w"); - if (!f){ + GF_Err e; + FILE * f; + GF_DownloadSession *dnload; + if (!filename || !url || !dm) + return GF_BAD_PARAM; + f= fopen(filename, "w"); + if (!f){ GF_LOG(GF_LOG_ERROR, GF_LOG_NETWORK, ("[WGET] Failed to open file %s for write.\n", filename)); return GF_IO_ERR; - } - dnload = gf_dm_sess_new_simple(dm, (char *)url, GF_NETIO_SESSION_NOT_THREADED, &wget_NetIO, f, &e); - if (!dnload) { - return GF_BAD_PARAM; - } - dnload->use_cache_file = 1; - if (e == GF_OK) { - e = gf_dm_sess_process(dnload); - } - e|= gf_cache_close_write_cache(dnload->cache_entry, dnload, e == GF_OK); - fclose(f); - gf_dm_sess_del(dnload); - return e; + } + dnload = gf_dm_sess_new_simple(dm, (char *)url, GF_NETIO_SESSION_NOT_THREADED, &wget_NetIO, f, &e); + if (!dnload) { + return GF_BAD_PARAM; + } + dnload->use_cache_file = 1; + dnload->force_data_write_callback = 1; + if (e == GF_OK) { + e = gf_dm_sess_process(dnload); + } + e |= gf_cache_close_write_cache(dnload->cache_entry, dnload, e == GF_OK); + fclose(f); + gf_dm_sess_del(dnload); + return e; } GF_Err gf_dm_get_file_memory(const char *url, char **out_data, u32 *out_size, char **out_mime) { - GF_Err e; - FILE * f; - GF_DownloadSession *dnload; - GF_DownloadManager *dm; + GF_Err e; + FILE * f; + GF_DownloadSession *dnload; + GF_DownloadManager *dm; if (!url || !out_data || !out_size) - return GF_BAD_PARAM; - f = gf_temp_file_new(); - if (!f) { + return GF_BAD_PARAM; + f = gf_temp_file_new(); + if (!f) { GF_LOG(GF_LOG_ERROR, GF_LOG_NETWORK, ("[WGET] Failed to create temp file for write.\n")); return GF_IO_ERR; - } + } dm = gf_dm_new(NULL); if (!dm) { fclose(f); - return GF_OUT_OF_MEM; + return GF_OUT_OF_MEM; } - dnload = gf_dm_sess_new_simple(dm, (char *)url, GF_NETIO_SESSION_NOT_THREADED, &wget_NetIO, f, &e); - if (!dnload) { + dnload = gf_dm_sess_new_simple(dm, (char *)url, GF_NETIO_SESSION_NOT_THREADED, &wget_NetIO, f, &e); + if (!dnload) { gf_dm_del(dm); - return GF_BAD_PARAM; - } - dnload->use_cache_file = 0; + return GF_BAD_PARAM; + } + dnload->use_cache_file = 0; dnload->disable_cache = 1; - if (!e) + if (!e) e = gf_dm_sess_process(dnload); - + if (!e) - e = gf_cache_close_write_cache(dnload->cache_entry, dnload, e == GF_OK); + e = gf_cache_close_write_cache(dnload->cache_entry, dnload, e == GF_OK); if (!e) { - u32 size = ftell(f); + u32 size = ftell(f), read; *out_size = size; *out_data = gf_malloc(sizeof(char)* ( 1 + size)); fseek(f, 0, SEEK_SET); - fread(*out_data, 1, size, f); - (*out_data)[size] = 0; - if (out_mime) { - const char *mime = gf_dm_sess_mime_type(dnload); - if (mime) *out_mime = gf_strdup(mime); + read = fread(*out_data, 1, size, f); + if (read != size) { + gf_free(*out_data); + e = GF_IO_ERR; + } else { + (*out_data)[size] = 0; + if (out_mime) { + const char *mime = gf_dm_sess_mime_type(dnload); + if (mime) *out_mime = gf_strdup(mime); + } } } fclose(f); - gf_dm_sess_del(dnload); + gf_dm_sess_del(dnload); gf_dm_del(dm); - return e; + return e; } const char *gf_dm_sess_get_resource_name(GF_DownloadSession *dnload) { - return dnload ? dnload->orig_url : NULL; + return dnload ? dnload->orig_url : NULL; } const char *gf_dm_sess_get_original_resource_name(GF_DownloadSession *dnload) @@ -2726,7 +2798,7 @@ const char * gf_cache_get_cache_filename_range( const GF_DownloadSession * sess, read = fread(copyBuff, sizeof(char), MIN(sizeof(copyBuff), (size_t) total), fr); if (read > 0) { total-= read; - write = fwrite(copyBuff, sizeof(char), (size_t) read, fw); + write = gf_fwrite(copyBuff, sizeof(char), (size_t) read, fw); if (write != read) { /* Something bad happened */ fclose( fw ); @@ -2819,12 +2891,7 @@ GF_Err gf_dm_sess_reassign(GF_DownloadSession *sess, u32 flags, gf_dm_user_io us if (sess->status==GF_NETIO_DISCONNECTED) sess->status = GF_NETIO_SETUP; - if ( ! (flags & GF_NETIO_SESSION_NOT_THREADED) ) { - sess->th = gf_th_new(sess->orig_url); - sess->mx = gf_mx_new(sess->orig_url); - gf_th_run(sess->th, gf_dm_session_thread, sess); - } - + /*threaded session shall be started with gf_dm_sess_process*/ return GF_OK; } @@ -2843,4 +2910,3 @@ u32 gf_dm_get_data_rate(GF_DownloadManager *dm) { return dm->limit_data_rate; } - diff --git a/src/utils/error.c b/src/utils/error.c index ea44a57..d1059f6 100644 --- a/src/utils/error.c +++ b/src/utils/error.c @@ -126,65 +126,187 @@ void gf_set_progress_callback(void *_user_cbk, gf_on_progress_cbk _prog_cbk) user_cbk = _user_cbk; } +/*ENTRIES MUST BE IN THE SAME ORDER AS LOG_TOOL DECLARATION IN */ +static struct log_tool_info {u32 type; const char *name; u32 level; } global_log_tools [] = +{ + { GF_LOG_CORE, "core", GF_LOG_WARNING }, + { GF_LOG_CODING, "coding", GF_LOG_WARNING }, + { GF_LOG_CONTAINER, "container", GF_LOG_WARNING }, + { GF_LOG_NETWORK, "network", GF_LOG_WARNING }, + { GF_LOG_RTP, "rtp", GF_LOG_WARNING }, + { GF_LOG_AUTHOR, "author", GF_LOG_WARNING }, + { GF_LOG_SYNC, "sync", GF_LOG_WARNING }, + { GF_LOG_CODEC, "codec", GF_LOG_WARNING }, + { GF_LOG_PARSER, "parser", GF_LOG_WARNING }, + { GF_LOG_MEDIA, "media", GF_LOG_WARNING }, + { GF_LOG_SCENE, "scene", GF_LOG_WARNING }, + { GF_LOG_SCRIPT, "script", GF_LOG_WARNING }, + { GF_LOG_INTERACT, "interact", GF_LOG_WARNING }, + { GF_LOG_COMPOSE, "compose", GF_LOG_WARNING }, + { GF_LOG_CACHE, "cache", GF_LOG_WARNING }, + { GF_LOG_MMIO, "mmio", GF_LOG_WARNING }, + { GF_LOG_RTI, "rti", GF_LOG_WARNING }, + { GF_LOG_SMIL, "smil", GF_LOG_WARNING }, + { GF_LOG_MEMORY, "mem", GF_LOG_WARNING }, + { GF_LOG_AUDIO, "audio", GF_LOG_WARNING }, + { GF_LOG_MODULE, "module", GF_LOG_WARNING }, + { GF_LOG_MUTEX, "mutex", GF_LOG_WARNING }, + { GF_LOG_CONSOLE, "console", GF_LOG_INFO } +}; -u32 global_log_level = 0; -u32 global_log_tools = 0; +GF_EXPORT +GF_Err gf_log_modify_tools_levels(const char *val) +{ +#ifndef GPAC_DISABLE_LOG + u32 level; + char *sep, *sep_level; + while (val && strlen(val)) { + const char *next_val = NULL; + const char *tools = NULL; + /*look for log level*/ + sep_level = strchr(val, '@'); + if (!sep_level) { + fprintf(stderr, "Unrecognized log format %s - expecting logTool@logLevel\n", val); + return GF_BAD_PARAM; + } -u32 gf_log_parse_level(const char *val) + level = 0; + if (!strnicmp(sep_level+1, "error", 5)) { + level = GF_LOG_ERROR; + next_val = sep_level+1 + 5; + } + else if (!strnicmp(sep_level+1, "warning", 7)) { + level = GF_LOG_WARNING; + next_val = sep_level+1 + 7; + } + else if (!strnicmp(sep_level+1, "info", 4)) { + level = GF_LOG_INFO; + next_val = sep_level+1 + 4; + } + else if (!strnicmp(sep_level+1, "debug", 5)) { + level = GF_LOG_DEBUG; + next_val = sep_level+1 + 5; + } + else if (!strnicmp(sep_level+1, "quiet", 5)) { + level = GF_LOG_QUIET; + next_val = sep_level+1 + 5; + } + else { + fprintf(stderr, "Unknown log level specified: %s\n", sep_level+1); + return GF_BAD_PARAM; + } + + sep_level[0] = 0; + tools = val; + while (tools) { + u32 i; + + sep = strchr(tools, ':'); + if (sep) sep[0] = 0; + + if (!stricmp(tools, "all")) { + for (i=0; iGF_LOG_TOOL_MAX/2) { + strcpy(szLogs, szLogTools); + strcpy(szLogTools, "all"); + strcat(szLogTools, levelstr); + if (strlen(szLogs)) { + strcat(szLogTools, ":"); + strcat(szLogTools, szLogs); + } + } else { + if (strlen(szLogTools)) { + strcat(szLogTools, ":"); + } + /*remove last ':' from tool*/ + szLogs[ strlen(szLogs) - 1 ] = 0; + strcat(szLogTools, szLogs); + strcat(szLogTools, levelstr); + } + } + level++; + } + len = strlen(szLogTools); + if (len) { + /*remove last ':' from level*/ + if (szLogTools[ len-1 ] == ':') szLogTools[ len-1 ] = 0; + return gf_strdup(szLogTools); } #endif - return flags; + return gf_strdup("all@quiet"); } #ifndef GPAC_DISABLE_LOG @@ -192,10 +314,12 @@ u32 call_lev = 0; u32 call_tool = 0; GF_EXPORT -u32 gf_log_get_level() { return global_log_level; } - -GF_EXPORT -u32 gf_log_get_tools() { return global_log_tools; } +Bool gf_log_tool_level_on(u32 log_tool, u32 log_level) +{ + assert(log_tool= log_level) return 1; + return 0; +} void default_log_callback(void *cbck, u32 level, u32 tool, const char* fmt, va_list vlist) { @@ -219,12 +343,6 @@ void gf_log(const char *fmt, ...) exit(1); } -GF_EXPORT -void gf_log_set_level(u32 level) -{ - global_log_level = level; -} - GF_EXPORT void gf_log_set_strict_error(Bool strict) { @@ -232,9 +350,16 @@ void gf_log_set_strict_error(Bool strict) } GF_EXPORT -void gf_log_set_tools(u32 modules) +void gf_log_set_tool_level(u32 tool, u32 level) { - global_log_tools = modules; + assert(tool<=GF_LOG_TOOL_MAX); + if (tool==GF_LOG_ALL) { + u32 i; + for (i=0; ix - b->x; + d.y = a->y - b->y; + return gf_v2d_len(&d); +} GF_EXPORT Fixed gf_angle_diff(Fixed angle1, Fixed angle2) @@ -1897,7 +1905,7 @@ Bool gf_ray_hit_box(GF_Ray *ray, GF_Vec box_min, GF_Vec box_max, GF_Vec *outPoin { Fixed t1, t2, tNEAR=FIX_MIN, tFAR=FIX_MAX; Fixed temp; - s8 xyorz, sign; + //s8 xyorz, sign; if (ray->dir.x == 0) { if ((ray->orig.x < box_min.x) || (ray->orig.x > box_max.x)) @@ -1912,8 +1920,8 @@ Bool gf_ray_hit_box(GF_Ray *ray, GF_Vec box_min, GF_Vec box_max, GF_Vec *outPoin } if (t1 > tNEAR) { tNEAR = t1; - xyorz = XPLANE; - sign = (ray->dir.x < 0) ? 1 : -1; + //xyorz = XPLANE; + //sign = (ray->dir.x < 0) ? 1 : -1; } if (t2 < tFAR) tFAR = t2; if (tNEAR > tFAR) return 0; // box missed @@ -1935,8 +1943,8 @@ Bool gf_ray_hit_box(GF_Ray *ray, GF_Vec box_min, GF_Vec box_max, GF_Vec *outPoin } if (t1 > tNEAR) { tNEAR = t1; - xyorz = YPLANE; - sign = (ray->dir.y < 0) ? 1 : -1; + //xyorz = YPLANE; + //sign = (ray->dir.y < 0) ? 1 : -1; } if (t2 < tFAR) tFAR = t2; if (tNEAR > tFAR) return 0; // box missed @@ -1959,8 +1967,8 @@ Bool gf_ray_hit_box(GF_Ray *ray, GF_Vec box_min, GF_Vec box_max, GF_Vec *outPoin } if (t1 > tNEAR) { tNEAR = t1; - xyorz = ZPLANE; - sign = (ray->dir.z < 0) ? 1 : -1; + //xyorz = ZPLANE; + //sign = (ray->dir.z < 0) ? 1 : -1; } if (t2 < tFAR) tFAR = t2; if (tNEAR>tFAR) return 0; // box missed diff --git a/src/utils/module.c b/src/utils/module.c index 802cdc7..38539dc 100644 --- a/src/utils/module.c +++ b/src/utils/module.c @@ -219,10 +219,10 @@ GF_BaseInterface *gf_modules_load_interface_by_name(GF_ModuleManager *pm, const const char *file_name; u32 i, count; GF_BaseInterface *ifce; - if (!pm || !plug_name || !pm->plug_list || !pm->cfg){ - GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[Core] gf_modules_load_interface_by_name has bad parameters pm=%p, plug_name=%s.\n", pm, plug_name)); - return NULL; - } + if (!pm || !plug_name || !pm->plug_list || !pm->cfg){ + GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[Core] gf_modules_load_interface_by_name has bad parameters pm=%p, plug_name=%s.\n", pm, plug_name)); + return NULL; + } count = gf_list_count(pm->plug_list); /*look for cache entry*/ file_name = gf_cfg_get_key(pm->cfg, "PluginsCache", plug_name); @@ -237,14 +237,14 @@ GF_BaseInterface *gf_modules_load_interface_by_name(GF_ModuleManager *pm, const } } } - GF_LOG(GF_LOG_INFO, GF_LOG_CORE, ("[Core] Plugin %s of type %d not found in cache, searching for it...\n", plug_name, InterfaceFamily)); + GF_LOG(GF_LOG_INFO, GF_LOG_CORE, ("[Core] Plugin %s of type %d not found in cache, searching for it...\n", plug_name, InterfaceFamily)); for (i=0; imodule_name && !strnicmp(ifce->module_name, plug_name, MIN(strlen(ifce->module_name), strlen(plug_name)) )) { /*update cache entry*/ gf_cfg_set_key(pm->cfg, "PluginsCache", plug_name, ((ModuleInstance*)ifce->HPLUG)->name); - GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[Core] Added plugin cache %s for %s\n", plug_name, ((ModuleInstance*)ifce->HPLUG)->name)); + GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[Core] Added plugin cache %s for %s\n", plug_name, ((ModuleInstance*)ifce->HPLUG)->name)); return ifce; } gf_modules_close_interface(ifce); @@ -282,7 +282,7 @@ const char *gf_modules_get_option(GF_BaseInterface *ifce, const char *secName, c if (!ifce || !ifce->HPLUG) return NULL; cfg = ((ModuleInstance *)ifce->HPLUG)->plugman->cfg; if (!cfg) return NULL; - return gf_cfg_get_key(cfg, secName, keyName); + return gf_cfg_get_ikey(cfg, secName, keyName); } GF_EXPORT diff --git a/src/utils/os_config_init.c b/src/utils/os_config_init.c index c25aa41..6a0a23d 100644 --- a/src/utils/os_config_init.c +++ b/src/utils/os_config_init.c @@ -107,10 +107,10 @@ static Bool get_default_install_path(char *file_path, u32 path_type) if (!strstr(file_path, "gpac")) { HKEY hKey = NULL; DWORD dwSize = GF_MAX_PATH; - DWORD dwType = REG_SZ; /*locate the key in current user, then in local machine*/ #ifdef _WIN32_WCE + DWORD dwType = REG_SZ; u16 w_path[1024]; RegOpenKeyEx(HKEY_CURRENT_USER, TEXT("Software\\GPAC"), 0, KEY_READ, &hKey); #ifdef _DEBUG @@ -153,6 +153,7 @@ static Bool get_default_install_path(char *file_path, u32 path_type) if (path_type==GF_PATH_MODULES) return 1; /*we are looking for the config file path - make sure it is writable*/ + assert(path_type == GF_PATH_CFG); strcpy(szPath, file_path); strcat(szPath, "\\gpaccfgtest.txt"); @@ -375,6 +376,7 @@ static GF_Config *create_default_config(char *file_path) gf_cfg_set_key(cfg, "General", "CacheDirectory", cache_dir); gf_free(cache_dir); } + gf_cfg_set_key(cfg, "DSMCC", "Activated", "false"); gf_cfg_set_key(cfg, "Compositor", "Raster2D", "GPAC 2D Raster"); gf_cfg_set_key(cfg, "Audio", "ForceConfig", "yes"); @@ -383,7 +385,7 @@ static GF_Config *create_default_config(char *file_path) gf_cfg_set_key(cfg, "Audio", "DisableNotification", "no"); /*Setup font engine to FreeType by default, and locate TrueType font directory on the system*/ - gf_cfg_set_key(cfg, "FontEngine", "FontReader", "ft_font"); + gf_cfg_set_key(cfg, "FontEngine", "FontReader", "FreeType Font Reader"); gf_cfg_set_key(cfg, "FontEngine", "RescanFonts", "yes"); @@ -409,7 +411,7 @@ static GF_Config *create_default_config(char *file_path) gf_cfg_set_key(cfg, "Downloader", "CleanCache", "yes"); gf_cfg_set_key(cfg, "Compositor", "AntiAlias", "All"); - gf_cfg_set_key(cfg, "Compositor", "FrameRate", "30"); + gf_cfg_set_key(cfg, "Compositor", "FrameRate", "30.0"); /*use power-of-2 emulation in OpenGL if no rectangular texture extension*/ gf_cfg_set_key(cfg, "Compositor", "EmulatePOW2", "yes"); gf_cfg_set_key(cfg, "Compositor", "ScalableZoom", "yes"); @@ -504,7 +506,7 @@ GF_Config *gf_cfg_init(const char *file, Bool *new_cfg) } if (!cfg) { - fprintf(stdout, "cannot create config file %s in %s directory\n", CFG_FILE_NAME, szPath); + fprintf(stdout, "Cannot create config file %s in %s directory\n", CFG_FILE_NAME, szPath); return NULL; } diff --git a/src/utils/os_divers.c b/src/utils/os_divers.c index 87ce36c..f69a7a8 100644 --- a/src/utils/os_divers.c +++ b/src/utils/os_divers.c @@ -29,6 +29,7 @@ #include #include #include +//#include #if !defined(__GNUC__) #pragma comment(lib, "toolhelp") @@ -41,6 +42,7 @@ #include #include #include +#include #if !defined(__GNUC__) #pragma comment(lib, "winmm") @@ -121,6 +123,92 @@ void gf_sleep(u32 ms) #endif } + +GF_Err gf_rmdir(char *DirPathName) +{ +#if defined (_WIN32_WCE) + TCHAR swzName[MAX_PATH]; + BOOL res; + CE_CharToWide(DirPathName, swzName); + res = RemoveDirectory(swzName); + if (! res) { + int err = GetLastError(); + GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("Cannot delete director %s: last error %d\n", DirPathName, err )); + } +#elif defined (WIN32) + int res = rmdir(DirPathName); + if (res==-1) { + int err = GetLastError(); + GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("Cannot delete director %s: last error %d\n", DirPathName, err )); + return GF_IO_ERR; + } +#else + int res = rmdir(DirPathName); + if (res==-1) { + GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("Cannot delete director %s: last error %d\n", DirPathName, errno )); + return GF_IO_ERR; + } +#endif + return GF_OK; +} + +GF_Err gf_mkdir(char* DirPathName) +{ +#if defined (_WIN32_WCE) + TCHAR swzName[MAX_PATH]; + BOOL res; + CE_CharToWide(DirPathName, swzName); + res = CreateDirectory(swzName, NULL); + if (! res) { + int err = GetLastError(); + GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("Cannot create director %s: last error %d\n", DirPathName, err )); + } +#elif defined (WIN32) + int res = mkdir(DirPathName); + if (res==-1) { + int err = GetLastError(); + GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("Cannot create director %s: last error %d\n", DirPathName, err )); + } +#else + int res = mkdir(DirPathName, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); + if (res==-1) { + if(errno == 17){ + GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("Cannot create director %s, it already exists: last error %d \n", DirPathName, errno )); + return GF_BAD_PARAM; + }else{ + GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("Cannot create director %s: last error %d\n", DirPathName, errno )); + return GF_IO_ERR; + } + } +#endif + return GF_OK; +} + +static Bool delete_carousel_data(void *cbck, char *item_name, char *item_path) +{ + Bool directory_clean_mode = *(Bool*)cbck; + + if(directory_clean_mode){ + gf_cleanup_dir(item_path); + gf_rmdir(item_path); + }else{ + gf_delete_file(item_path); + } + return GF_OK; +} + +GF_Err gf_cleanup_dir(char* DirPathName) +{ + Bool directory_clean_mode; + + directory_clean_mode = 1; + gf_enum_directory(DirPathName, 1, delete_carousel_data, &directory_clean_mode, NULL); + directory_clean_mode = 0; + gf_enum_directory(DirPathName, 0, delete_carousel_data, &directory_clean_mode, NULL); + + return GF_OK; +} + #ifndef gettimeofday #ifdef _WIN32_WCE @@ -138,7 +226,7 @@ s32 gettimeofday(struct timeval *tp, void *tz) s32 val; GetSystemTime(&st); - SystemTimeToFileTime(&st, &ft); + SystemTimeToFileTime(&st, &ft); val = (s32) ((*(LONGLONG *) &ft - TIMESPEC_TO_FILETIME_OFFSET) / 10000000); tp->tv_sec = (u32) val; @@ -249,12 +337,17 @@ u64 gf_file_modification_time(const char *filename) HANDLE fh; ULARGE_INTEGER uli; ULONGLONG time_ms; + BOOL ret; CE_CharToWide((char *) filename, _file); fh = FindFirstFile(_file, &FindData); if (fh == INVALID_HANDLE_VALUE) return 0; uli.LowPart = FindData.ftLastWriteTime.dwLowDateTime; uli.HighPart = FindData.ftLastWriteTime.dwHighDateTime; - FindClose(fh); + ret = FindClose(fh); + if (!ret) { + DWORD err = GetLastError(); + GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[core] FindClose() in gf_file_modification_time() returned the following error code: %d\n", err)); + } time_ms = uli.QuadPart/10000; return time_ms; #elif defined(WIN32) && !defined(__GNUC__) @@ -513,7 +606,11 @@ GF_Err gf_enum_directory(const char *dir, Bool enum_directory, gf_enum_dir_item #endif if (enum_dir_fct(cbck, file, item_path)) { #ifdef WIN32 - FindClose(SearchH); + BOOL ret = FindClose(SearchH); + if (!ret) { + DWORD err = GetLastError(); + GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[core] FindClose() in gf_enum_directory() returned(1) the following error code: %d\n", err)); + } #endif break; } @@ -521,7 +618,11 @@ GF_Err gf_enum_directory(const char *dir, Bool enum_directory, gf_enum_dir_item next: #ifdef WIN32 if (!FindNextFile(SearchH, &FindData)) { - FindClose(SearchH); + BOOL ret = FindClose(SearchH); + if (!ret) { + DWORD err = GetLastError(); + GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[core] FindClose() in gf_enum_directory() returned(2) the following error code: %d\n", err)); + } break; } #else @@ -601,6 +702,41 @@ FILE *gf_f64_open(const char *file_name, const char *mode) #endif } +#if (_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600) && ! defined(_GNU_SOURCE) +#define HAVE_STRERROR_R 1 +#endif +size_t gf_fwrite(const void *ptr, size_t size, size_t nmemb, + FILE *stream) +{ + size_t result = fwrite(ptr, size, nmemb, stream); + if (result != nmemb) { +#ifdef _WIN32_WCE + GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("Error writing data: %d blocks to write but %d blocks written\n", nmemb, result)); +#else +#if defined WIN32 && !defined(GPAC_CONFIG_WIN32) + errno_t errno_save; + _get_errno(&errno_save); +#else + int errno_save = errno; +#endif + //if (errno_save!=0) + { +#ifdef HAVE_STRERROR_R +#define ERRSTR_BUF_SIZE 256 + char errstr[ERRSTR_BUF_SIZE]; + if(strerror_r(errno_save, errstr, ERRSTR_BUF_SIZE) != 0) + { + strerror_r(0, errstr, ERRSTR_BUF_SIZE); + } +#else + char *errstr = (char*)strerror(errno_save); +#endif + GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("Error writing data (%s): %d blocks to write but %d blocks written\n", errstr, nmemb, result)); + } +#endif + } + return result; +} /*seems OK under mingw also*/ @@ -631,7 +767,11 @@ void gf_prompt_set_echo_off(Bool echo_off) { DWORD flags; HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE); - GetConsoleMode(hStdin, &flags); + BOOL ret = GetConsoleMode(hStdin, &flags); + if (!ret) { + DWORD err = GetLastError(); + GF_LOG(GF_LOG_ERROR, GF_LOG_CONSOLE, ("[Console] GetConsoleMode() return with the following error code: %d\n", err)); + } if (echo_off) flags &= ~ENABLE_ECHO_INPUT; else flags |= ENABLE_ECHO_INPUT; SetConsoleMode(hStdin, flags); @@ -821,7 +961,7 @@ sh4_get_fpscr() static void sh4_put_fpscr(int nv) { - asm volatile ("lds %0,fpscr" : : "r" (nv)); + asm volatile ("lds %0,fpscr" : : "r" (nv)); } #define SH4_FPSCR_FR 0x00200000 @@ -870,6 +1010,10 @@ void gf_sys_init(Bool enable_memory_tracker) gf_mem_enable_tracker(); #endif } + /*by default log subsystem is initialized to error on all tools, and info on console to debug scripts*/ + gf_log_set_tool_level(GF_LOG_ALL, GF_LOG_ERROR); + gf_log_set_tool_level(GF_LOG_CONSOLE, GF_LOG_INFO); + #if defined(__sh__) /* Round all denormalized floatting point number to 0.0 */ @@ -1090,7 +1234,7 @@ Bool gf_sys_get_rti_os(u32 refresh_time_ms, GF_SystemRTInfo *rti, u32 flags) } /*no special API available, ONLY FETCH TIMES if requested (may eat up some time)*/ else if (flags & GF_RTI_ALL_PROCESSES_TIMES) { - PROCESSENTRY32 pentry; + PROCESSENTRY32 pentry; /*get a snapshot of all running threads*/ hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (!hSnapShot) return 0; @@ -1247,6 +1391,7 @@ Bool gf_sys_get_rti_os(u32 refresh_time_ms, GF_SystemRTInfo *rti, u32 flags) #else #include #endif +#include static u64 total_physical_memory = 0; @@ -1254,37 +1399,37 @@ Bool gf_sys_get_rti_os(u32 refresh_time_ms, GF_SystemRTInfo *rti, u32 flags) { size_t length; u32 entry_time, i, percent; - int mib[6]; - int result; - int pagesize; + int mib[6]; + int result; + int pagesize; u64 process_u_k_time; - mach_msg_type_number_t count = HOST_VM_INFO_COUNT; - vm_statistics_data_t vmstat; - task_t task; - kern_return_t error; - thread_array_t thread_table; - thread_basic_info_t thi; - thread_basic_info_data_t thi_data; - unsigned table_size; - struct task_basic_info ti; double utime, stime; - + vm_statistics_data_t vmstat; + task_t task; + kern_return_t error; + thread_array_t thread_table; + unsigned table_size; + thread_basic_info_t thi; + thread_basic_info_data_t thi_data; + struct task_basic_info ti; + mach_msg_type_number_t count = HOST_VM_INFO_COUNT, size = sizeof(ti); + entry_time = gf_sys_clock(); if (last_update_time && (entry_time - last_update_time < refresh_time_ms)) { memcpy(rti, &the_rti, sizeof(GF_SystemRTInfo)); return 0; } - + mib[0] = CTL_HW; - mib[1] = HW_PAGESIZE; - length = sizeof(pagesize); - if (sysctl(mib, 2, &pagesize, &length, NULL, 0) < 0) { + mib[1] = HW_PAGESIZE; + length = sizeof(pagesize); + if (sysctl(mib, 2, &pagesize, &length, NULL, 0) < 0) { return 0; - } + } if (host_statistics(mach_host_self(), HOST_VM_INFO, (host_info_t)&vmstat, &count) != KERN_SUCCESS) { return 0; - } + } the_rti.physical_memory = (vmstat.wire_count + vmstat.active_count + vmstat.inactive_count + vmstat.free_count)* pagesize; the_rti.physical_memory_avail = vmstat.free_count * pagesize; @@ -1298,9 +1443,15 @@ Bool gf_sys_get_rti_os(u32 refresh_time_ms, GF_SystemRTInfo *rti, u32 flags) } } - - error = task_for_pid(mach_task_self(), the_rti.pid, &task); + error = task_for_pid(mach_task_self(), the_rti.pid, &task); if (error) { + GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[RTI] Cannot get process task for PID %d: error %d\n", the_rti.pid, error)); + return 0; + } + + error = task_info(mach_task_self(), TASK_BASIC_INFO, (task_info_t)&ti, &size); + if (error) { + GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[RTI] Cannot get process task info (PID %d): error %d\n", the_rti.pid, error)); return 0; } @@ -1308,12 +1459,19 @@ Bool gf_sys_get_rti_os(u32 refresh_time_ms, GF_SystemRTInfo *rti, u32 flags) utime = ti.user_time.seconds + ti.user_time.microseconds * 1e-6; stime = ti.system_time.seconds + ti.system_time.microseconds * 1e-6; error = task_threads(task, &thread_table, &table_size); - assert(error == KERN_SUCCESS); + if (error != KERN_SUCCESS) { + GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[RTI] Cannot get threads task for PID %d: error %d\n", the_rti.pid, error)); + return 0; + } thi = &thi_data; for (i = 0; i != table_size; ++i) { count = THREAD_BASIC_INFO_COUNT; error = thread_info(thread_table[i], THREAD_BASIC_INFO, (thread_info_t)thi, &count); - assert(error == KERN_SUCCESS); + if (error != KERN_SUCCESS) { + mach_error("[RTI] Unexpected thread_info() call return", error); + GF_LOG(GF_LOG_WARNING, GF_LOG_CORE, ("[RTI] Unexpected thread info for PID %d\n", the_rti.pid)); + break; + } if ((thi->flags & TH_FLAGS_IDLE) == 0) { utime += thi->user_time.seconds + thi->user_time.microseconds * 1e-6; stime += thi->system_time.seconds + thi->system_time.microseconds * 1e-6; @@ -1321,7 +1479,7 @@ Bool gf_sys_get_rti_os(u32 refresh_time_ms, GF_SystemRTInfo *rti, u32 flags) } } error = vm_deallocate(mach_task_self(), (vm_offset_t)thread_table, table_size * sizeof(thread_array_t)); - mach_port_deallocate(mach_task_self(), task); + mach_port_deallocate(mach_task_self(), task); process_u_k_time = utime + stime; @@ -1372,77 +1530,77 @@ Bool gf_sys_get_rti_os(u32 refresh_time_ms, GF_SystemRTInfo *rti, u32 flags) entry_time = gf_sys_clock(); if (last_update_time && (entry_time - last_update_time < refresh_time_ms)) { - memcpy(rti, &the_rti, sizeof(GF_SystemRTInfo)); - return 0; + memcpy(rti, &the_rti, sizeof(GF_SystemRTInfo)); + return 0; } u_k_time = idle_time = 0; f = gf_f64_open("/proc/stat", "r"); if (f) { - u32 k_time, nice_time, u_time; - if (fgets(line, 128, f) != NULL) { - if (sscanf(line, "cpu %u %u %u %u\n", &u_time, &k_time, &nice_time, &idle_time) == 4) { - u_k_time = u_time + k_time + nice_time; - } - } - fclose(f); + u32 k_time, nice_time, u_time; + if (fgets(line, 128, f) != NULL) { + if (sscanf(line, "cpu %u %u %u %u\n", &u_time, &k_time, &nice_time, &idle_time) == 4) { + u_k_time = u_time + k_time + nice_time; + } + } + fclose(f); } process_u_k_time = 0; the_rti.process_memory = 0; /*FIXME? under LinuxThreads this will only fetch stats for the calling thread, we would have to enumerate /proc to get - the complete CPU usage of all therads of the process...*/ + the complete CPU usage of all therads of the process...*/ #if 0 sprintf(szProc, "/proc/%d/stat", the_rti.pid); f = gf_f64_open(szProc, "r"); if (f) { - fflush(f); - if (fgets(line, 2048, f) != NULL) { - char state; - char *start; - long cutime, cstime, priority, nice, itrealvalue, rss; - int exit_signal, processor; - unsigned long flags, minflt, cminflt, majflt, cmajflt, utime, stime,starttime, vsize, rlim, startcode, endcode, startstack, kstkesp, kstkeip, signal, blocked, sigignore, sigcatch, wchan, nswap, cnswap, rem; - int ppid, pgrp ,session, tty_nr, tty_pgrp, res; - start = strchr(line, ')'); - if (start) start += 2; - else { - start = strchr(line, ' '); - start++; - } - res = sscanf(start,"%c %d %d %d %d %d %lu %lu %lu %lu \ + fflush(f); + if (fgets(line, 2048, f) != NULL) { + char state; + char *start; + long cutime, cstime, priority, nice, itrealvalue, rss; + int exit_signal, processor; + unsigned long flags, minflt, cminflt, majflt, cmajflt, utime, stime,starttime, vsize, rlim, startcode, endcode, startstack, kstkesp, kstkeip, signal, blocked, sigignore, sigcatch, wchan, nswap, cnswap, rem; + int ppid, pgrp ,session, tty_nr, tty_pgrp, res; + start = strchr(line, ')'); + if (start) start += 2; + else { + start = strchr(line, ' '); + start++; + } + res = sscanf(start,"%c %d %d %d %d %d %lu %lu %lu %lu \ %lu %lu %lu %ld %ld %ld %ld %ld %ld %lu \ %lu %ld %lu %lu %lu %lu %lu %lu %lu %lu \ %lu %lu %lu %lu %lu %d %d", - &state, &ppid, &pgrp, &session, &tty_nr, &tty_pgrp, &flags, &minflt, &cminflt, &majflt, - &cmajflt, &utime, &stime, &cutime, &cstime, &priority, &nice, &itrealvalue, &rem, &starttime, - &vsize, &rss, &rlim, &startcode, &endcode, &startstack, &kstkesp, &kstkeip, &signal, &blocked, - &sigignore, &sigcatch, &wchan, &nswap, &cnswap, &exit_signal, &processor); - - if (res) process_u_k_time = (u64) (cutime + cstime); - else { - GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[RTI] PROC %s parse error\n", szProc)); + &state, &ppid, &pgrp, &session, &tty_nr, &tty_pgrp, &flags, &minflt, &cminflt, &majflt, + &cmajflt, &utime, &stime, &cutime, &cstime, &priority, &nice, &itrealvalue, &rem, &starttime, + &vsize, &rss, &rlim, &startcode, &endcode, &startstack, &kstkesp, &kstkeip, &signal, &blocked, + &sigignore, &sigcatch, &wchan, &nswap, &cnswap, &exit_signal, &processor); + + if (res) process_u_k_time = (u64) (cutime + cstime); + else { + GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[RTI] PROC %s parse error\n", szProc)); + } + } else { + GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[RTI] error reading pid/stat\n\n", szProc)); } - } else { - GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[RTI] error reading pid/stat\n\n", szProc)); - } - fclose(f); + fclose(f); } else { - GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[RTI] cannot open %s\n", szProc)); + GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[RTI] cannot open %s\n", szProc)); } sprintf(szProc, "/proc/%d/status", the_rti.pid); f = gf_f64_open(szProc, "r"); if (f) { - while (fgets(line, 1024, f) != NULL) { - if (!strnicmp(line, "VmSize:", 7)) { - sscanf(line, "VmSize: %"LLD" kB", &the_rti.process_memory); - the_rti.process_memory *= 1024; - } - } - fclose(f); + while (fgets(line, 1024, f) != NULL) { + if (!strnicmp(line, "VmSize:", 7)) { + sscanf(line, "VmSize: %"LLD" kB", &the_rti.process_memory); + the_rti.process_memory *= 1024; + } + } + fclose(f); } else { - GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[RTI] cannot open %s\n", szProc)); + GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[RTI] cannot open %s\n", szProc)); } #endif @@ -1450,57 +1608,57 @@ Bool gf_sys_get_rti_os(u32 refresh_time_ms, GF_SystemRTInfo *rti, u32 flags) the_rti.physical_memory = the_rti.physical_memory_avail = 0; f = gf_f64_open("/proc/meminfo", "r"); if (f) { - while (fgets(line, 1024, f) != NULL) { - if (!strnicmp(line, "MemTotal:", 9)) { - sscanf(line, "MemTotal: "LLU" kB", &the_rti.physical_memory); - the_rti.physical_memory *= 1024; - }else if (!strnicmp(line, "MemFree:", 8)) { - sscanf(line, "MemFree: "LLU" kB", &the_rti.physical_memory_avail); - the_rti.physical_memory_avail *= 1024; - break; - } - } - fclose(f); + while (fgets(line, 1024, f) != NULL) { + if (!strnicmp(line, "MemTotal:", 9)) { + sscanf(line, "MemTotal: "LLU" kB", &the_rti.physical_memory); + the_rti.physical_memory *= 1024; + }else if (!strnicmp(line, "MemFree:", 8)) { + sscanf(line, "MemFree: "LLU" kB", &the_rti.physical_memory_avail); + the_rti.physical_memory_avail *= 1024; + break; + } + } + fclose(f); } else { - GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[RTI] cannot open /proc/meminfo\n")); + GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[RTI] cannot open /proc/meminfo\n")); } the_rti.sampling_instant = last_update_time; - + if (last_update_time) { - the_rti.sampling_period_duration = (entry_time - last_update_time); - the_rti.process_cpu_time_diff = (process_u_k_time - last_process_k_u_time) * 10; - - /*oops, we have no choice but to assume 100% cpu usage during this period*/ - if (!u_k_time) { - the_rti.total_cpu_time_diff = the_rti.sampling_period_duration; - u_k_time = last_cpu_u_k_time + the_rti.sampling_period_duration; - the_rti.cpu_idle_time = 0; - the_rti.total_cpu_usage = 100; - if (!the_rti.process_cpu_time_diff) the_rti.process_cpu_time_diff = the_rti.total_cpu_time_diff; - the_rti.process_cpu_usage = (u32) ( 100 * the_rti.process_cpu_time_diff / the_rti.sampling_period_duration); - } else { - u64 samp_sys_time; - /*move to ms (/proc/stat gives times in 100 ms unit*/ - the_rti.total_cpu_time_diff = (u_k_time - last_cpu_u_k_time)*10; - - /*we're not that accurate....*/ - if (the_rti.total_cpu_time_diff > the_rti.sampling_period_duration) - the_rti.sampling_period_duration = the_rti.total_cpu_time_diff; - - - if (!idle_time) idle_time = (the_rti.sampling_period_duration - the_rti.total_cpu_time_diff)/10; - samp_sys_time = u_k_time - last_cpu_u_k_time; - the_rti.cpu_idle_time = idle_time - last_cpu_idle_time; - the_rti.total_cpu_usage = (u32) ( 100 * samp_sys_time / (the_rti.cpu_idle_time + samp_sys_time ) ); - /*move to ms (/proc/stat gives times in 100 ms unit*/ - the_rti.cpu_idle_time *= 10; - if (!the_rti.process_cpu_time_diff) the_rti.process_cpu_time_diff = the_rti.total_cpu_time_diff; - the_rti.process_cpu_usage = (u32) ( 100 * the_rti.process_cpu_time_diff / (the_rti.cpu_idle_time + 10*samp_sys_time ) ); - } + the_rti.sampling_period_duration = (entry_time - last_update_time); + the_rti.process_cpu_time_diff = (process_u_k_time - last_process_k_u_time) * 10; + + /*oops, we have no choice but to assume 100% cpu usage during this period*/ + if (!u_k_time) { + the_rti.total_cpu_time_diff = the_rti.sampling_period_duration; + u_k_time = last_cpu_u_k_time + the_rti.sampling_period_duration; + the_rti.cpu_idle_time = 0; + the_rti.total_cpu_usage = 100; + if (!the_rti.process_cpu_time_diff) the_rti.process_cpu_time_diff = the_rti.total_cpu_time_diff; + the_rti.process_cpu_usage = (u32) ( 100 * the_rti.process_cpu_time_diff / the_rti.sampling_period_duration); + } else { + u64 samp_sys_time; + /*move to ms (/proc/stat gives times in 100 ms unit*/ + the_rti.total_cpu_time_diff = (u_k_time - last_cpu_u_k_time)*10; + + /*we're not that accurate....*/ + if (the_rti.total_cpu_time_diff > the_rti.sampling_period_duration) + the_rti.sampling_period_duration = the_rti.total_cpu_time_diff; + + + if (!idle_time) idle_time = (the_rti.sampling_period_duration - the_rti.total_cpu_time_diff)/10; + samp_sys_time = u_k_time - last_cpu_u_k_time; + the_rti.cpu_idle_time = idle_time - last_cpu_idle_time; + the_rti.total_cpu_usage = (u32) ( 100 * samp_sys_time / (the_rti.cpu_idle_time + samp_sys_time ) ); + /*move to ms (/proc/stat gives times in 100 ms unit*/ + the_rti.cpu_idle_time *= 10; + if (!the_rti.process_cpu_time_diff) the_rti.process_cpu_time_diff = the_rti.total_cpu_time_diff; + the_rti.process_cpu_usage = (u32) ( 100 * the_rti.process_cpu_time_diff / (the_rti.cpu_idle_time + 10*samp_sys_time ) ); + } } else { - mem_at_startup = the_rti.physical_memory_avail; + mem_at_startup = the_rti.physical_memory_avail; } the_rti.process_memory = mem_at_startup - the_rti.physical_memory_avail; #ifdef GPAC_MEMORY_TRACKING @@ -1531,16 +1689,14 @@ Bool gf_sys_get_rti(u32 refresh_time_ms, GF_SystemRTInfo *rti, u32 flags) char * gf_get_default_cache_directory(){ #ifdef _WIN32_WCE - return gf_strdup( "\\windows\\temp" ); + return gf_strdup( "\\windows\\temp" ); #elif defined(WIN32) - char szPath[512]; - GetWindowsDirectory(szPath, 507); - if (szPath[strlen(szPath)-1] != '\\') - strcat((char*)szPath, "\\"); - strcat((char *)szPath, "Temp"); - return gf_strdup( szPath ); + char szPath[MAX_PATH]; + /*ivica patch*/ + GetTempPath(MAX_PATH, szPath); + return gf_strdup( szPath ); #else - return gf_strdup("/tmp"); + return gf_strdup("/tmp"); #endif } @@ -1571,7 +1727,7 @@ Bool gf_sys_get_battery_state(Bool *onBattery, u32 *onCharge, u32*level, u32 *ba struct GF_GlobalLock { - const char * resourceName; + const char * resourceName; }; @@ -1583,92 +1739,92 @@ struct GF_GlobalLock { #include struct _GF_GlobalLock_opaque { - char * resourceName; - char * pidFile; - int fd; + char * resourceName; + char * pidFile; + int fd; }; GF_GlobalLock * gf_create_PID_file( const char * resourceName ) { - const char * prefix = "/gpac_lock_"; - const char * dir = gf_get_default_cache_directory(); - char * pidfile; - int flags; - int status; - pidfile = gf_malloc(strlen(dir)+strlen(prefix)+strlen(resourceName)+1); - strcpy(pidfile, dir); - strcat(pidfile, prefix); - /* Use only valid names for file */ - { - const char *res; - char * pid = &(pidfile[strlen(pidfile)]); - for (res = resourceName; *res ; res++){ - if (*res >= 'A' && *res <= 'z') - *pid = * res; - else - *pid = '_'; - pid++; - } - *pid = '\0'; - } - int fd = open(pidfile, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); - if (fd == -1) - goto exit; - /* Get the flags */ - flags = fcntl(fd, F_GETFD); - if (flags == -1){ - goto exit; - } - /* Set FD_CLOEXEC, so exclusive lock will be removed on exit, so even if GPAC crashes, - * lock will be allowed for next instance */ - flags |= FD_CLOEXEC; - /* Now, update the flags */ - if (fcntl(fd, F_SETFD, flags) == -1){ - goto exit; - } - - /* Now, we try to lock the file */ - { - struct flock fl; - fl.l_type = F_WRLCK; - fl.l_whence = SEEK_SET; - fl.l_start = fl.l_len = 0; - status = fcntl(fd, F_SETLK, &fl); - } + const char * prefix = "/gpac_lock_"; + const char * dir = gf_get_default_cache_directory(); + char * pidfile; + int flags; + int status; + pidfile = gf_malloc(strlen(dir)+strlen(prefix)+strlen(resourceName)+1); + strcpy(pidfile, dir); + strcat(pidfile, prefix); + /* Use only valid names for file */ + { + const char *res; + char * pid = &(pidfile[strlen(pidfile)]); + for (res = resourceName; *res ; res++){ + if (*res >= 'A' && *res <= 'z') + *pid = * res; + else + *pid = '_'; + pid++; + } + *pid = '\0'; + } + int fd = open(pidfile, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); + if (fd == -1) + goto exit; + /* Get the flags */ + flags = fcntl(fd, F_GETFD); + if (flags == -1){ + goto exit; + } + /* Set FD_CLOEXEC, so exclusive lock will be removed on exit, so even if GPAC crashes, + * lock will be allowed for next instance */ + flags |= FD_CLOEXEC; + /* Now, update the flags */ + if (fcntl(fd, F_SETFD, flags) == -1){ + goto exit; + } - if (status == -1) { - goto exit; - } + /* Now, we try to lock the file */ + { + struct flock fl; + fl.l_type = F_WRLCK; + fl.l_whence = SEEK_SET; + fl.l_start = fl.l_len = 0; + status = fcntl(fd, F_SETLK, &fl); + } - if (ftruncate(fd, 0) == -1){ - goto exit; - } - /* Write the PID */ - { - int sz = 100; - char * buf = gf_malloc( sz ); - sz = snprintf(buf, sz, "%ld\n", (long) getpid()); - if (write(fd, buf, sz) != sz){ - gf_free(buf); - goto exit; - } - } - sync(); - { - GF_GlobalLock * lock = gf_malloc( sizeof(GF_GlobalLock)); - lock->resourceName = gf_strdup(resourceName); - lock->pidFile = pidfile; - lock->fd = fd; - return lock; - } + if (status == -1) { + goto exit; + } + + if (ftruncate(fd, 0) == -1){ + goto exit; + } + /* Write the PID */ + { + int sz = 100; + char * buf = gf_malloc( sz ); + sz = snprintf(buf, sz, "%ld\n", (long) getpid()); + if (write(fd, buf, sz) != sz){ + gf_free(buf); + goto exit; + } + } + sync(); + { + GF_GlobalLock * lock = gf_malloc( sizeof(GF_GlobalLock)); + lock->resourceName = gf_strdup(resourceName); + lock->pidFile = pidfile; + lock->fd = fd; + return lock; + } exit: - if (fd >= 0) - close(fd); - return NULL; + if (fd >= 0) + close(fd); + return NULL; } #else /* WIN32 */ struct _GF_GlobalLock_opaque { - char * resourceName; + char * resourceName; HANDLE hMutex; /*a named mutex is a system-mode object on windows*/ }; #endif @@ -1679,6 +1835,7 @@ GF_GlobalLock * gf_global_resource_lock(const char * resourceName){ #ifdef _WIN32_WCE unsigned short sWResourceName[MAX_PATH]; #endif + DWORD lastErr; GF_GlobalLock *lock = gf_malloc(sizeof(GF_GlobalLock)); lock->resourceName = gf_strdup(resourceName); @@ -1689,10 +1846,13 @@ GF_GlobalLock * gf_global_resource_lock(const char * resourceName){ #else lock->hMutex = CreateMutex(NULL, TRUE, resourceName); #endif - if (!lock->hMutex) { - DWORD lastErr = GetLastError(); - if (lastErr != ERROR_ALREADY_EXISTS) - return NULL; + lastErr = GetLastError(); + if (lastErr && lastErr == ERROR_ALREADY_EXISTS) + return NULL; + if (!lock->hMutex) + { + GF_LOG(GF_LOG_ERROR, GF_LOG_MUTEX, ("[Mutex] Couldn't create mutex for global lock: %d\n", lastErr)); + return NULL; } /*then lock it*/ @@ -1700,6 +1860,7 @@ GF_GlobalLock * gf_global_resource_lock(const char * resourceName){ case WAIT_ABANDONED: case WAIT_TIMEOUT: assert(0); /*serious error: someone has modified the object elsewhere*/ + GF_LOG(GF_LOG_ERROR, GF_LOG_MUTEX, ("[Mutex] Couldn't get the global lock\n")); gf_global_resource_unlock(lock); return NULL; } @@ -1716,28 +1877,52 @@ GF_GlobalLock * gf_global_resource_lock(const char * resourceName){ * \return GF_OK if evertything went fine */ GF_Err gf_global_resource_unlock(GF_GlobalLock * lock){ - if (!lock) - return GF_BAD_PARAM; + if (!lock) + return GF_BAD_PARAM; #ifndef WIN32 - assert( lock->pidFile); - close(lock->fd); - if (unlink(lock->pidFile)) - perror("Failed to unlink lock file"); - gf_free(lock->pidFile); - lock->pidFile = NULL; - lock->fd = -1; + assert( lock->pidFile); + close(lock->fd); + if (unlink(lock->pidFile)) + perror("Failed to unlink lock file"); + gf_free(lock->pidFile); + lock->pidFile = NULL; + lock->fd = -1; #else /* WIN32 */ { /*MSDN: "The mutex object is destroyed when its last handle has been closed."*/ BOOL ret = ReleaseMutex(lock->hMutex); - assert(ret); + if (!ret) { + DWORD err = GetLastError(); + GF_LOG(GF_LOG_ERROR, GF_LOG_MUTEX, ("[Mutex] Couldn't release mutex for global lock: %d\n", err)); + } ret = CloseHandle(lock->hMutex); - assert(ret); + if (!ret) { + DWORD err = GetLastError(); + GF_LOG(GF_LOG_ERROR, GF_LOG_MUTEX, ("[Mutex] Couldn't destroy mutex for global lock: %d\n", err)); + } } #endif if (lock->resourceName) gf_free(lock->resourceName); - lock->resourceName = NULL; - gf_free(lock); - return GF_OK; + lock->resourceName = NULL; + gf_free(lock); + return GF_OK; } + +#ifdef GPAC_ANDROID + +fm_callback_func fm_cbk = NULL; +static void *fm_cbk_obj = NULL; + +void gf_fm_request_set_callback(void *cbk_obj, fm_callback_func cbk_func) { + fm_cbk = cbk_func; + fm_cbk_obj = cbk_obj; +} + +void gf_fm_request_call(u32 type, u32 param, int *value) { + if (fm_cbk) + fm_cbk(fm_cbk_obj, type, param, value); +} + +#endif //GPAC_ANDROID + diff --git a/src/utils/os_module.c b/src/utils/os_module.c index bd2595b..36bd874 100644 --- a/src/utils/os_module.c +++ b/src/utils/os_module.c @@ -79,6 +79,10 @@ Bool gf_modules_load_library(ModuleInstance *inst) if (inst->lib_handle) return 1; GF_LOG(GF_LOG_INFO, GF_LOG_CORE, ("[Core] Load module file %s\n", inst->name)); +#if _WIN32_WINNT >= 0x0502 + SetDllDirectory(inst->plugman->dir); +#endif + #ifdef _WIN32_WCE sprintf(s_path, "%s%c%s", inst->plugman->dir, GF_PATH_SEPARATOR, inst->name); CE_CharToWide(s_path, path); @@ -321,9 +325,16 @@ u32 gf_modules_refresh(GF_ModuleManager *pm) GF_SAFEALLOC(inst, ModuleInstance); inst->interfaces = gf_list_new(); inst->plugman = pm; - inst->name = gf_strdup("gm_gpac_js.dylib"); + inst->name = gf_strdup("gm_hyb_in.dylib"); gf_list_add(pm->plug_list, inst); } + { + ModuleInstance *inst; + GF_SAFEALLOC(inst, ModuleInstance); + inst->interfaces = gf_list_new(); + inst->plugman = pm; + inst->name = gf_strdup("gm_gpac_js.dylib"); + } { ModuleInstance *inst; GF_SAFEALLOC(inst, ModuleInstance); diff --git a/src/utils/os_net.c b/src/utils/os_net.c index 71f2546..569916e 100644 --- a/src/utils/os_net.c +++ b/src/utils/os_net.c @@ -145,7 +145,7 @@ typedef s32 SOCKET; static u32 ipv6_check_state = 0; #endif -#ifdef __x86_64__ +#ifdef _LP64 #define NULL_SOCKET 0 #else #define NULL_SOCKET (SOCKET)NULL @@ -407,9 +407,11 @@ GF_Err gf_sk_set_block_mode(GF_Socket *sock, u32 NonBlockingOn) return GF_OK; } +#include static void gf_sk_free(GF_Socket *sock) { + assert( sock ); /*leave multicast*/ if (sock->socket && (sock->flags & GF_SOCK_IS_MULTICAST) ) { struct ip_mreq mreq; @@ -441,8 +443,10 @@ static void gf_sk_free(GF_Socket *sock) } } + void gf_sk_del(GF_Socket *sock) { + assert( sock ); gf_sk_free(sock); #ifdef WIN32 wsa_init --; @@ -799,7 +803,6 @@ GF_Err gf_sk_bind(GF_Socket *sock, const char *local_ip, u16 port, const char *p //send length bytes of a buffer GF_Err gf_sk_send(GF_Socket *sock, const char *buffer, u32 length) { - GF_Err e; u32 count; s32 res; #ifndef __SYMBIAN32__ @@ -808,8 +811,6 @@ GF_Err gf_sk_send(GF_Socket *sock, const char *buffer, u32 length) fd_set Group; #endif - e = GF_OK; - //the socket must be bound or connected if (!sock || !sock->socket) return GF_BAD_PARAM; @@ -1077,7 +1078,6 @@ GF_Err gf_sk_setup_multicast(GF_Socket *sock, const char *multi_IPAdd, u16 Multi //BytesRead is the number of bytes read from the network GF_Err gf_sk_receive(GF_Socket *sock, char *buffer, u32 length, u32 startFrom, u32 *BytesRead) { - GF_Err e; s32 res; #ifndef __SYMBIAN32__ s32 ready; @@ -1085,8 +1085,6 @@ GF_Err gf_sk_receive(GF_Socket *sock, char *buffer, u32 length, u32 startFrom, u fd_set Group; #endif - e = GF_OK; - *BytesRead = 0; if (!sock->socket) return GF_BAD_PARAM; if (startFrom >= length) return GF_IO_ERR; @@ -1169,7 +1167,6 @@ GF_Err gf_sk_listen(GF_Socket *sock, u32 MaxConnection) GF_Err gf_sk_accept(GF_Socket *sock, GF_Socket **newConnection) { u32 client_address_size; - s32 res; SOCKET sk; #ifndef __SYMBIAN32__ s32 ready; @@ -1186,7 +1183,6 @@ GF_Err gf_sk_accept(GF_Socket *sock, GF_Socket **newConnection) timeout.tv_sec = 0; timeout.tv_usec = SOCK_MICROSEC_WAIT; - res = 0; ready = select(sock->socket+1, &Group, NULL, NULL, &timeout); if (ready == SOCKET_ERROR) { switch (LASTSOCKERROR) { @@ -1401,19 +1397,16 @@ GF_Err gf_sk_send_to(GF_Socket *sock, const char *buffer, u32 length, char *remo GF_Err gf_sk_receive_wait(GF_Socket *sock, char *buffer, u32 length, u32 startFrom, u32 *BytesRead, u32 Second ) { - GF_Err e; s32 res; #ifndef __SYMBIAN32__ s32 ready; struct timeval timeout; fd_set Group; #endif - e = GF_OK; *BytesRead = 0; if (startFrom >= length) return GF_OK; - #ifndef __SYMBIAN32__ //can we read? FD_ZERO(&Group); @@ -1454,8 +1447,6 @@ GF_Err gf_sk_receive_wait(GF_Socket *sock, char *buffer, u32 length, u32 startFr //send length bytes of a buffer GF_Err gf_sk_send_wait(GF_Socket *sock, const char *buffer, u32 length, u32 Second ) { - - GF_Err e; u32 count; s32 res; #ifndef __SYMBIAN32__ @@ -1463,7 +1454,6 @@ GF_Err gf_sk_send_wait(GF_Socket *sock, const char *buffer, u32 length, u32 Seco struct timeval timeout; fd_set Group; #endif - e = GF_OK; //the socket must be bound or connected if (!sock || !sock->socket) return GF_BAD_PARAM; diff --git a/src/utils/os_thread.c b/src/utils/os_thread.c index a01a114..f74cc08 100644 --- a/src/utils/os_thread.c +++ b/src/utils/os_thread.c @@ -65,7 +65,7 @@ struct __tag_thread char *log_name; #endif #ifdef GPAC_ANDROID - u32 (*RunBeforeExit)(void *param); + u32 (*RunBeforeExit)(void *param); #endif }; @@ -133,17 +133,19 @@ static pthread_once_t currentThreadInfoKey_once = PTHREAD_ONCE_INIT; GF_Err gf_register_before_exit_function(GF_Thread *t, u32 (*toRunBeforePthreadExit)(void *param) ) { - if (!t) - return GF_BAD_PARAM; - t->RunBeforeExit = toRunBeforePthreadExit; - return GF_OK; + if (!t) + return GF_BAD_PARAM; + t->RunBeforeExit = toRunBeforePthreadExit; + return GF_OK; } /* Unique key allocation */ static void currentThreadInfoKey_alloc() { + int err; /* We do not use any destructor */ - pthread_key_create(¤tThreadInfoKey, NULL); + if (err = pthread_key_create(¤tThreadInfoKey, NULL)) + GF_LOG(GF_LOG_ERROR, GF_LOG_MUTEX, ("[Mutex] pthread_key_create() failed with error %d\n", err)); } GF_Thread * gf_th_current(){ @@ -167,8 +169,8 @@ void * RunThread(void *ptr) /* Signal the caller */ if (! t->_signal) goto exit; #ifdef GPAC_ANDROID - pthread_once(¤tThreadInfoKey_once, ¤tThreadInfoKey_alloc); - pthread_setspecific(currentThreadInfoKey, t); + if (pthread_once(¤tThreadInfoKey_once, ¤tThreadInfoKey_alloc) || pthread_setspecific(currentThreadInfoKey, t)) + GF_LOG(GF_LOG_ERROR, GF_LOG_MUTEX, ("[Mutex] Couldn't run thread %s, ID 0x%08x\n", t->log_name, t->id)); #endif /* GPAC_ANDROID */ t->status = GF_THREAD_STATUS_RUN; gf_sema_notify(t->_signal, 1); @@ -191,17 +193,20 @@ exit: t->status = GF_THREAD_STATUS_DEAD; t->Run = NULL; #ifdef WIN32 - CloseHandle(t->threadH); + if (!CloseHandle(t->threadH)) { + DWORD err = GetLastError(); + GF_LOG(GF_LOG_ERROR, GF_LOG_MUTEX, ("[Thread %s] Couldn't close handle when exiting thread proc, error code: %d\n", t->log_name, err)); + } t->threadH = NULL; return ret; #else #ifdef GPAC_ANDROID - #ifndef GPAC_DISABLE_LOG - GF_LOG(GF_LOG_INFO, GF_LOG_MUTEX, ("[Thread %s] RunBeforeExit=%p\n", t->log_name, t->RunBeforeExit)); - #endif - if (t->RunBeforeExit) - t->RunBeforeExit(t->args); + #ifndef GPAC_DISABLE_LOG + GF_LOG(GF_LOG_INFO, GF_LOG_MUTEX, ("[Thread %s] RunBeforeExit=%p\n", t->log_name, t->RunBeforeExit)); + #endif + if (t->RunBeforeExit) + t->RunBeforeExit(t->args); #endif /* GPAC_ANDROID */ pthread_exit((void *)0); return (void *)ret; @@ -248,7 +253,11 @@ void Thread_Stop(GF_Thread *t, Bool Destroy) #ifdef WIN32 if (Destroy) { DWORD dw = 1; - TerminateThread(t->threadH, dw); + BOOL ret = TerminateThread(t->threadH, dw); + if (!ret) { + DWORD err = GetLastError(); + GF_LOG(GF_LOG_ERROR, GF_LOG_MUTEX, ("[Thread %s] Couldn't stop thread ID 0x%08x, error code %d\n", t->log_name, t->id, err)); + } t->threadH = NULL; } else { WaitForSingleObject(t->threadH, INFINITE); @@ -256,14 +265,16 @@ void Thread_Stop(GF_Thread *t, Bool Destroy) #else if (Destroy) { #ifdef GPAC_ANDROID - pthread_kill(t->threadH, SIGQUIT); + if (pthread_kill(t->threadH, SIGQUIT)) #else - pthread_cancel(t->threadH); + if (pthread_cancel(t->threadH)) #endif + GF_LOG(GF_LOG_ERROR, GF_LOG_MUTEX, ("[Thread %s] Couldn't kill thread ID 0x%08x\n", t->log_name, t->id)); t->threadH = 0; } else { /*gracefully wait for Run to finish*/ - pthread_join(t->threadH, NULL); + if (pthread_join(t->threadH, NULL)) + GF_LOG(GF_LOG_ERROR, GF_LOG_MUTEX, ("[Thread %s] pthread_join() returned an error with thread ID 0x%08x\n", t->log_name, t->id)); } #endif } @@ -300,7 +311,39 @@ void gf_th_set_priority(GF_Thread *t, s32 priority) #ifdef WIN32 /*!! in WinCE, changin thread priority is extremely dangerous, it may freeze threads randomly !!*/ #ifndef _WIN32_WCE - SetThreadPriority(t ? t->threadH : GetCurrentThread(), priority); + int _prio; + BOOL ret; + switch (priority) { + case GF_THREAD_PRIORITY_IDLE: + _prio = THREAD_PRIORITY_IDLE; + break; + case GF_THREAD_PRIORITY_LESS_IDLE: + _prio = THREAD_PRIORITY_IDLE; + break; + case GF_THREAD_PRIORITY_LOWEST: + _prio = THREAD_PRIORITY_LOWEST; + break; + case GF_THREAD_PRIORITY_LOW: + _prio = THREAD_PRIORITY_BELOW_NORMAL; + break; + case GF_THREAD_PRIORITY_NORMAL: + _prio = THREAD_PRIORITY_NORMAL; + break; + case GF_THREAD_PRIORITY_HIGH: + _prio = THREAD_PRIORITY_ABOVE_NORMAL; + break; + case GF_THREAD_PRIORITY_HIGHEST: + _prio = THREAD_PRIORITY_HIGHEST; + break; + default: /*GF_THREAD_PRIORITY_REALTIME -> GF_THREAD_PRIORITY_REALTIME_END*/ + _prio = THREAD_PRIORITY_TIME_CRITICAL; + break; + } + ret = SetThreadPriority(t ? t->threadH : GetCurrentThread(), _prio); + if (!ret) { + DWORD err = GetLastError(); + GF_LOG(GF_LOG_WARNING, GF_LOG_MUTEX, ("[Thread %s] Couldn't set priority for thread ID 0x%08x, error %d\n", t->log_name, t->id, err)); + } #endif #else @@ -311,10 +354,12 @@ void gf_th_set_priority(GF_Thread *t, s32 priority) /* consider this as real-time priority */ if (priority > 200) { s_par.sched_priority = priority - 200; - pthread_setschedparam(t->threadH, SCHED_RR, &s_par); + if (pthread_setschedparam(t->threadH, SCHED_RR, &s_par)) + GF_LOG(GF_LOG_WARNING, GF_LOG_MUTEX, ("[Thread %s] Couldn't set priority(1) for thread ID 0x%08x\n", t->log_name, t->id)); } else { s_par.sched_priority = priority; - pthread_setschedparam(t->threadH, SCHED_OTHER, &s_par); + if (pthread_setschedparam(t->threadH, SCHED_OTHER, &s_par)) + GF_LOG(GF_LOG_WARNING, GF_LOG_MUTEX, ("[Thread %s] Couldn't set priority(2) for thread ID 0x%08x\n", t->log_name, t->id)); } #endif @@ -372,6 +417,7 @@ GF_Mutex *gf_mx_new(const char *name) pthread_mutexattr_init(&attr); if ( pthread_mutex_init(&tmp->hMutex, &attr) != 0 ) { #endif + GF_LOG(GF_LOG_ERROR, GF_LOG_MUTEX, ("[Mutex] Couldn't create mutex %s\n", strlen(name) ? name : "")); gf_free(tmp); return NULL; } @@ -393,9 +439,15 @@ GF_Mutex *gf_mx_new(const char *name) void gf_mx_del(GF_Mutex *mx) { #ifdef WIN32 - CloseHandle(mx->hMutex); + if (!CloseHandle(mx->hMutex)) { + DWORD err = GetLastError(); + GF_LOG(GF_LOG_ERROR, GF_LOG_MUTEX, ("[Mutex %s] CloseHandle when deleting mutex failed with error code %d\n", mx->log_name, err)); + } #else - pthread_mutex_destroy(&mx->hMutex); + int err = pthread_mutex_destroy(&mx->hMutex); + if (err) + GF_LOG(GF_LOG_ERROR, GF_LOG_MUTEX, ("[Mutex %s] pthread_mutex_destroy failed with error code %d\n", mx->log_name, err)); + #endif #ifndef GPAC_DISABLE_LOG gf_free(mx->log_name); @@ -422,9 +474,16 @@ void gf_mx_v(GF_Mutex *mx) #endif mx->Holder = 0; #ifdef WIN32 - ReleaseMutex(mx->hMutex); + { + BOOL ret = ReleaseMutex(mx->hMutex); + if (!ret) { + DWORD err = GetLastError(); + GF_LOG(GF_LOG_ERROR, GF_LOG_MUTEX, ("[Mutex] Couldn't release mutex (thread %s, error %d)\n", log_th_name(mx->Holder), err)); + } + } #else - pthread_mutex_unlock(&mx->hMutex); + if (pthread_mutex_unlock(&mx->hMutex)) + GF_LOG(GF_LOG_ERROR, GF_LOG_MUTEX, ("[Mutex] Couldn't release mutex (thread %s)\n", log_th_name(mx->Holder))); #endif } } @@ -475,6 +534,18 @@ u32 gf_mx_p(GF_Mutex *mx) return 1; } + +s32 gf_mx_get_num_locks(GF_Mutex *mx) +{ + u32 caller; + if (!mx) return 0; + caller = gf_th_id(); + if (caller == mx->Holder) { + return mx->HolderCount; + } + return -1; +} + Bool gf_mx_try_lock(GF_Mutex *mx) { u32 caller; @@ -538,6 +609,13 @@ GF_Semaphore *gf_sema_new(u32 MaxCount, u32 InitCount) if (!tmp) return NULL; #if defined(WIN32) tmp->hSemaphore = CreateSemaphore(NULL, InitCount, MaxCount, NULL); + + if (!tmp->hSemaphore) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MUTEX, ("Couldn't create semaphore\n")); + gf_free(tmp); + return NULL; + } + #elif defined(__DARWIN__) || defined(__APPLE__) /* sem_init isn't supported on Mac OS X 10.3 & 10.4; it returns ENOSYS To get around this, a NAMED semaphore needs to be used @@ -550,28 +628,31 @@ GF_Semaphore *gf_sema_new(u32 MaxCount, u32 InitCount) tmp->SemName = gf_strdup(semaName); } tmp->hSemaphore = sem_open(tmp->SemName, O_CREAT, S_IRUSR|S_IWUSR, InitCount); + if (tmp->hSemaphore==SEM_FAILED) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MUTEX, ("Couldn't init semaphore: error %d\n", errno)); + gf_free(tmp); + return NULL; + } #else if (sem_init(&tmp->SemaData, 0, InitCount) < 0 ) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MUTEX, ("Couldn't init semaphore: error %d\n", errno)); gf_free(tmp); return NULL; } tmp->hSemaphore = &tmp->SemaData; #endif - - if (!tmp->hSemaphore) { - gf_free(tmp); - return NULL; - } return tmp; } void gf_sema_del(GF_Semaphore *sm) { #if defined(WIN32) - CloseHandle(sm->hSemaphore); + if (!CloseHandle(sm->hSemaphore)) { + DWORD err = GetLastError(); + GF_LOG(GF_LOG_ERROR, GF_LOG_MUTEX, ("[Mutex] CloseHandle when deleting semaphore failed with error code %d\n", err)); + } #elif defined(__DARWIN__) || defined(__APPLE__) - sem_t *sema = sem_open(sm->SemName, 0); - sem_destroy(sema); + sem_destroy(sm->hSemaphore); gf_free(sm->SemName); #else sem_destroy(sm->hSemaphore); @@ -593,7 +674,7 @@ u32 gf_sema_notify(GF_Semaphore *sm, u32 NbRelease) #else #if defined(__DARWIN__) || defined(__APPLE__) - hSem = sem_open(sm->SemName, 0); + hSem = sm->hSemaphore; #else hSem = sm->hSemaphore; #endif @@ -610,14 +691,14 @@ void gf_sema_wait(GF_Semaphore *sm) { #ifdef WIN32 WaitForSingleObject(sm->hSemaphore, INFINITE); +#elif defined (__DARWIN__) || defined(__APPLE__) + if (sem_wait(sm->hSemaphore)) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MUTEX, ("[Semaphore] failed to wait for semaphore %s: %d\n", sm->SemName, errno)); + } #else - sem_t *hSem; -#if defined(__DARWIN__) || defined(__APPLE__) - hSem = sem_open(sm->SemName, 0); -#else - hSem = sm->hSemaphore; -#endif - sem_wait(hSem); + if (sem_wait(sm->hSemaphore)) { + GF_LOG(GF_LOG_ERROR, GF_LOG_MUTEX, ("[Semaphore] failed to wait for semaphore: %d\n", errno)); + } #endif } @@ -629,7 +710,7 @@ Bool gf_sema_wait_for(GF_Semaphore *sm, u32 TimeOut) #else sem_t *hSem; #if defined(__DARWIN__) || defined(__APPLE__) - hSem = sem_open(sm->SemName, 0); + hSem = sm->hSemaphore; #else hSem = sm->hSemaphore; #endif diff --git a/src/utils/path2d.c b/src/utils/path2d.c index 0e0033f..8a618da 100644 --- a/src/utils/path2d.c +++ b/src/utils/path2d.c @@ -337,15 +337,13 @@ static void gf_add_n_bezier(GF_Path *gp, GF_Point2D *newpts, u32 nbPoints) { Double mu; u32 numPoints, i; - GF_Point2D start, end; + GF_Point2D end; numPoints = (u32) FIX2INT(GF_2D_DEFAULT_RES * gp->fineness); mu = 0.0; if (numPoints) mu = 1/(Double)numPoints; - start = newpts[0]; for (i=1; iflags &= ~GF_PATH_BBOX_DIRTY; e = gf_path_get_control_bounds(gp, &gp->bbox); *rc = gp->bbox; - return GF_OK; + return e; } gp->flags &= ~GF_PATH_BBOX_DIRTY; diff --git a/src/utils/xml_parser.c b/src/utils/xml_parser.c index 75c06c8..dafa872 100644 --- a/src/utils/xml_parser.c +++ b/src/utils/xml_parser.c @@ -485,6 +485,11 @@ att_retry: sep = strchr(parser->buffer + parser->current_pos, parser->att_sep); if (!sep || !sep[1]) return 1; + if (sep[1]==parser->att_sep) { + format_sax_error(parser, sep - parser->buffer, "Invalid character %c after attribute value separator %c ", sep[1], parser->att_sep); + return 1; + } + if (!parser->init_state && (strchr(" />\n\t\r", sep[1])==NULL)) { parser->current_pos = sep - parser->buffer + 1; goto att_retry; @@ -1577,6 +1582,7 @@ static void on_dom_node_end(void *cbk, const char *name, const char *ns) gf_list_rem_last(par->stack); if (!last || strcmp(last->name, name) || (!ns && last->ns) || (ns && !last->ns) || (ns && strcmp(last->ns, ns) ) ) { + format_sax_error(par->parser, 0, "Invalid node stack: closing node is %s but %s was expected", name, last->name); par->parser->suspended = 1; gf_xml_dom_node_del(last); if (last==par->root) par->root=NULL; @@ -1671,6 +1677,18 @@ GF_Err gf_xml_dom_parse(GF_DOMParser *dom, const char *file, gf_xml_sax_progress return e<0 ? e : GF_OK; } +GF_EXPORT +GF_Err gf_xml_dom_parse_string(GF_DOMParser *dom, char *string) +{ + GF_Err e; + gf_xml_dom_reset(dom, 1); + dom->stack = gf_list_new(); + dom->parser = gf_xml_sax_new(on_dom_node_start, on_dom_node_end, on_dom_text_content, dom); + e = gf_xml_sax_init(dom->parser, string); + gf_xml_dom_reset(dom, 0); + return e<0 ? e : GF_OK; +} + GF_EXPORT GF_XMLNode *gf_xml_dom_get_root(GF_DOMParser *parser) { -- 2.30.2

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 R5(@r%2m-%o4M^xA{||#T6E6S& literal 0 HcmV?d00001 diff --git a/applications/hbbtvplayer/hbbtvbrowserplugin/doc/listeOIPFProfile.xlsx b/applications/hbbtvplayer/hbbtvbrowserplugin/doc/listeOIPFProfile.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..b5302b4623e8f316ba467c83298135c1613038a7 GIT binary patch literal 18967 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 QAb7w}J;0>o#eSase literal 0 HcmV?d00001 diff --git a/applications/hbbtvplayer/hbbtvbrowserplugin/hbbtvbrowserplugin.pc.in b/applications/hbbtvplayer/hbbtvbrowserplugin/hbbtvbrowserplugin.pc.in new file mode 100644 index 0000000..d67b557 --- /dev/null +++ b/applications/hbbtvplayer/hbbtvbrowserplugin/hbbtvbrowserplugin.pc.in @@ -0,0 +1,12 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ +plugindir=/usr/lib/mozilla/plugins/ + +Name: hbbtvbrowserplugin +Description: The Hbbtv BrowserPlugin (NPAPI) +Version:@PACKAGE_VERSION@ +Cflags: -I${includedir}/hbbtvbrowserplugin +Libs: -L${libdir} -L${plugindir} -lhbbtvbrowserplugin + diff --git a/applications/hbbtvplayer/hbbtvbrowserplugin/hbbtvbrowserplugin.vcproj b/applications/hbbtvplayer/hbbtvbrowserplugin/hbbtvbrowserplugin.vcproj new file mode 100644 index 0000000..c54579b --- /dev/null +++ b/applications/hbbtvplayer/hbbtvbrowserplugin/hbbtvbrowserplugin.vcproj @@ -0,0 +1,311 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/applications/hbbtvplayer/hbbtvbrowserplugin/makefile.am b/applications/hbbtvplayer/hbbtvbrowserplugin/makefile.am new file mode 100644 index 0000000..585a151 --- /dev/null +++ b/applications/hbbtvplayer/hbbtvbrowserplugin/makefile.am @@ -0,0 +1,7 @@ +SUBDIRS = src + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = hbbtvbrowserplugin.pc + + + diff --git a/applications/hbbtvplayer/hbbtvbrowserplugin/makefile.old b/applications/hbbtvplayer/hbbtvbrowserplugin/makefile.old new file mode 100644 index 0000000..cea09b8 --- /dev/null +++ b/applications/hbbtvplayer/hbbtvbrowserplugin/makefile.old @@ -0,0 +1,110 @@ +############################################################################### +#### +#### MAKEFILE +#### +#### Copyright Telecom Paristech 2011 +#### +#### Author : Stanislas Selle +#### +############################################################################### + +############################################################################### +## Files and Path + +PLUGINDIR = /usr/lib/mozilla/plugins +INSTALL_INCLUDEDIR = /usr/local/include/hbbtvbrowserplugin +INSTALL_LIBDIR = /usr/local/lib +INSTALL_PKGDIR = /usr/lib/pkgconfig + +PKGFILE = hbbtvbrowserplugin.pc + +MAINTARGET = bin/libhbbtvbrowserplugin.so + +SRCDIR = src +HEADERSDIR = src + +OBJ = obj/hbbtvbrowserplugin.o\ + obj/oipfapplicationmanager.o\ + obj/applicationclass.o\ + obj/applicationprivatedataclass.o\ + obj/oipfconfiguration.o\ + obj/configurationclass.o\ + obj/oipfdownloadmanager.o\ + obj/oipfdownloadtrigger.o\ + obj/downloadclass.o\ + obj/downloadcollectionclass.o\ + obj/drmcontrolinfocollectionclass.o\ + obj/drmcontrolinformationclass.o\ + obj/videobroadcast.o\ + obj/keysetclass.o + +EXPORTHEADERS = hbbtvbrowserpluginapi.h + +#TESTURL = file:///home/selle/ressources/HbbTVapps/TPT/app1/index.html +TESTURL = http://aquila.enst.fr:8080/subwebsite/hbbtvtest/test0003/index.php +TESTLOG = /tmp/test-err.txt +############################################################################### +## Programs + +COMPILER = cc +DELETER = rm -f +BROWSERTEST = firefox + + +############################################################################### +## Options, Flags and LinkS + +CFLAGS = -Wall -DXP_UNIX=1 -DMOZ_X11=1 -fPIC -g + +INCLUDEFLAGS = -Isrc +LIBRARYFLAGS = + +FROMPKG = libxul + +PKGFLAGS = `pkg-config --cflags $(FROMPKG) ` +PKGLIBS = `pkg-config --libs $(FROMPKG) ` + +INCLUDEFLAGS += $(PKGFLAGS) +LIBRARYFLAGS += $(PKGLIBS) + +############################################################################### +## Rules + +all : $(MAINTARGET) + +$(MAINTARGET) : $(OBJ) + @echo "====> Compiling $(MAINTARGET)" + $(COMPILER) $(CFLAGS) $(LIBRARYFLAGS) -shared $(OBJ) -o $(MAINTARGET) + +obj/%.o: $(SRCDIR)/%.c $(HEADERSDIR)/%.h + @echo "====> Compiling $< " + $(COMPILER) $(CFLAGS) -c $< $(INCLUDEFLAGS) -o $@ + +test : all + $(BROWSERTEST) $(TESTURL) 2> $(TESTLOG); cat $(TESTLOG) + +install : all + @echo "====> Installing" + cp $(MAINTARGET) $(PLUGINDIR) + @if test -d $(INSTALL_INCLUDEDIR);\ + then echo " ===> $(INSTALL_INCLUDEDIR) existing already" ; \ + else mkdir $(INSTALL_INCLUDEDIR); fi; + cp $(HEADERSDIR)/$(EXPORTHEADERS) $(INSTALL_INCLUDEDIR) + cp $(PKGFILE) $(INSTALL_PKGDIR) + @echo "Done." + +uninstall : + @echo "====> Uninstalling" + $(DELETER) $(INSTALL_PKGDIR)/$(PKGFILE) $(INSTALL_INCLUDEDIR)/$(EXPORTHEADERS) $(INSTALL_INCLUDEDIR)/$(MAINTARGET) + if test -d $(INSTALL_INCLUDEDIR);\ + then rmdir $(INSTALL_INCLUDEDIR); fi; + +clean : + @echo "====> Cleaning" + @$(DELETER) $(OBJ) + @echo "Deleting objects" + @$(DELETER) $(MAINTARGET) + @echo "Deleting $(MAINTARGET)" + +############################################################################### +############################################################################### diff --git a/applications/hbbtvplayer/hbbtvbrowserplugin/projectmanager/codeblocks/hbbtvbrowserplugin/hbbtvbrowserplugin.cbp b/applications/hbbtvplayer/hbbtvbrowserplugin/projectmanager/codeblocks/hbbtvbrowserplugin/hbbtvbrowserplugin.cbp new file mode 100644 index 0000000..54b6c97 --- /dev/null +++ b/applications/hbbtvplayer/hbbtvbrowserplugin/projectmanager/codeblocks/hbbtvbrowserplugin/hbbtvbrowserplugin.cbp @@ -0,0 +1,92 @@ + + + + + + diff --git a/applications/hbbtvplayer/hbbtvbrowserplugin/projectmanager/codeblocks/hbbtvbrowserplugin/hbbtvbrowserplugin.layout b/applications/hbbtvplayer/hbbtvbrowserplugin/projectmanager/codeblocks/hbbtvbrowserplugin/hbbtvbrowserplugin.layout new file mode 100644 index 0000000..5c17549 --- /dev/null +++ b/applications/hbbtvplayer/hbbtvbrowserplugin/projectmanager/codeblocks/hbbtvbrowserplugin/hbbtvbrowserplugin.layout @@ -0,0 +1,106 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/applications/hbbtvplayer/hbbtvbrowserplugin/releases/hbbtvbrowserplugin-0.01.tar.gz b/applications/hbbtvplayer/hbbtvbrowserplugin/releases/hbbtvbrowserplugin-0.01.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..c8595c8f094456f4202363a37b9a5e3c4c0bfafb GIT binary patch literal 11234 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 v_APPLICATIONPropertyNames, kAPPLICATION_NUM_PROPERTY_IDENTIFIERS, v_APPLICATIONPropertyIdentifiers ); + sBrowserFuncs->getstringidentifiers( v_APPLICATIONMethodNames, kAPPLICATION_NUM_METHOD_IDENTIFIERS, v_APPLICATIONMethodIdentifiers ); +} + +NPClass stAPPLICATIONclass; +NPClass* pAPPLICATIONclass = NULL; + +NPClass* fillAPPLICATIONpclass(void) +{ + TRACEINFO; + if (pAPPLICATIONclass == NULL) + { + stAPPLICATIONclass.allocate = APPLICATION_Allocate; + stAPPLICATIONclass.deallocate = APPLICATION_Deallocate; + stAPPLICATIONclass.invalidate = APPLICATION_Invalidate; + stAPPLICATIONclass.hasMethod = APPLICATION_HasMethod; + stAPPLICATIONclass.invoke = APPLICATION_Invoke; + stAPPLICATIONclass.invokeDefault = APPLICATION_InvokeDefault; + stAPPLICATIONclass.hasProperty = APPLICATION_HasProperty; + stAPPLICATIONclass.getProperty = APPLICATION_GetProperty; + stAPPLICATIONclass.setProperty = APPLICATION_SetProperty; + stAPPLICATIONclass.removeProperty = APPLICATION_RemoveProperty; + stAPPLICATIONclass.enumerate = APPLICATION_Enumerate; + pAPPLICATIONclass = &stAPPLICATIONclass; + } + + return pAPPLICATIONclass; +} + + +NPObject * APPLICATION_Allocate(NPP npp, NPClass *theClass) +{ + TRACEINFO; + if (!v_bAPPLICATIONIdentifiersInitialized) + { + v_bAPPLICATIONIdentifiersInitialized = true; + APPLICATIONinitializeIdentifiers(); + } + + NPObj_Application* newapplication = (NPObj_Application*)MEMALLOC(sizeof(NPObj_Application)); + fprintf(stderr, "\t%s : Allocation at \x1b[%i;3%im%p\n\x1b[0m",__FUNCTION__, 1, 1, newapplication ); + newapplication->npp = npp; + newapplication->visible = true; + newapplication->privateData = sBrowserFuncs->createobject(npp, fillAPPPRIVDATApclass()); + fprintf(stderr, "\t%s : Create privateData property at \x1b[%i;3%im%p\n\x1b[0m ",__FUNCTION__, 1, 1, newapplication->privateData ); + newapplication->visible = true; + return (NPObject*)newapplication; +} + + void APPLICATION_Deallocate(NPObject* obj) +{ + TRACEINFO; + NPObj_Application* applicationobj = (NPObj_Application*)obj; + sBrowserFuncs->releaseobject(applicationobj->privateData); + MEMFREE(applicationobj); + return; +} + + void APPLICATION_Invalidate(NPObject* obj) +{ + TRACEINFO; + return; +} + + bool APPLICATION_HasMethod(NPObject* obj, NPIdentifier name) +{ + TRACEINFO; + + bool result = false; + int i = 0; + NPUTF8* utf8methodname = (char*)sBrowserFuncs->utf8fromidentifier(name); + while ((i < kAPPLICATION_NUM_METHOD_IDENTIFIERS) && (result == false)) + { + if ( name == v_APPLICATIONMethodIdentifiers[i] ) + { + result= true; + } + i++; + } + + return result; +} + +bool APPLICATION_Invoke(NPObject* obj, NPIdentifier name, const NPVariant* args, uint32_t argCount, NPVariant* result) +{ + TRACEINFO; + bool fctresult = false; + if (name == v_APPLICATIONMethodIdentifiers[kAPPLICATION_ID_METHOD_CREATEAPPLICATION]) + { + APPLICATION_Invoke_CreateApplication(obj, args, argCount); + fctresult = true; + } + else if (name == v_APPLICATIONMethodIdentifiers[kAPPLICATION_ID_METHOD_DESTROYAPPLICATION]) + { + APPLICATION_Invoke_DestroyApplication(obj, args, argCount); + fctresult = true; + } + else if (name == v_APPLICATIONMethodIdentifiers[kAPPLICATION_ID_METHOD_HIDE]) + { + APPLICATION_Invoke_Hide(obj, args, argCount); + fctresult = true; + } + else if (name == v_APPLICATIONMethodIdentifiers[kAPPLICATION_ID_METHOD_SHOW]) + { + APPLICATION_Invoke_Show(obj, args, argCount); + fctresult = true; + } + else + { + fctresult = false; + } + return fctresult; +} + + bool APPLICATION_InvokeDefault(NPObject *npobj, const NPVariant *args, uint32_t argCount, NPVariant *result) +{ + TRACEINFO; + return true; +} + + bool APPLICATION_HasProperty(NPObject* obj, NPIdentifier name) +{ + TRACEINFO; + bool result = false; + NPUTF8* utf8propertyname = (char*)sBrowserFuncs->utf8fromidentifier(name); + + int i = 0; + while ((i < kAPPLICATION_NUM_PROPERTY_IDENTIFIERS) && (result == false)) + { + if ( name == v_APPLICATIONPropertyIdentifiers[i] ) + { + result= true; + } + i++; + } + + return result; +} + +bool APPLICATION_GetProperty(NPObject* obj, NPIdentifier name, NPVariant* result) +{ + TRACEINFO; + + bool fctresult = false; + NPObj_Application* AppliObj = (NPObj_Application*)obj; + + if (name == v_APPLICATIONPropertyIdentifiers[kAPPLICATION_ID_PROPERTY_PRIVATEDATA]) + { + sBrowserFuncs->retainobject(AppliObj->privateData); + OBJECT_TO_NPVARIANT(AppliObj->privateData, *result); + fctresult = true; + } + else if (name == v_APPLICATIONPropertyIdentifiers[kAPPLICATION_ID_PROPERTY_VISIBLE]) + { + BOOLEAN_TO_NPVARIANT(AppliObj->visible, *result); + fctresult = true; + } + else + + return fctresult; +} + + bool APPLICATION_SetProperty(NPObject *obj, NPIdentifier name, const NPVariant *value) +{ + TRACEINFO; + return true; +} + + bool APPLICATION_RemoveProperty(NPObject *npobj, NPIdentifier name) +{ + TRACEINFO; + return true; +} + + + bool APPLICATION_Enumerate(NPObject *npobj, NPIdentifier **value, uint32_t *count) +{ + TRACEINFO; + return true; +} + +/** implementation **/ + +void APPLICATION_Invoke_CreateApplication(NPObject* obj,const NPVariant* args, uint32_t argCount) +{ + TRACEINFO; + NOTIMPLEMENTED; +} + +void APPLICATION_Invoke_DestroyApplication(NPObject* obj,const NPVariant* args, uint32_t argCount) +{ + TRACEINFO; + NOTIMPLEMENTED; +} + +void APPLICATION_Invoke_Show(NPObject* obj,const NPVariant* args, uint32_t argCount) +{ + TRACEINFO; + NPObj_Application* AppliObj = (NPObj_Application*)obj; + OnAPPLICATION_Show(); + AppliObj->visible = true; + NOTIMPLEMENTED; +} + +void APPLICATION_Invoke_Hide(NPObject* obj,const NPVariant* args, uint32_t argCount) +{ + TRACEINFO; + + NPObj_Application* AppliObj = (NPObj_Application*)obj; + OnAPPLICATION_Hide(); + AppliObj->visible = false; + NOTIMPLEMENTED; +} diff --git a/applications/hbbtvplayer/hbbtvbrowserplugin/src/applicationclass.h b/applications/hbbtvplayer/hbbtvbrowserplugin/src/applicationclass.h new file mode 100644 index 0000000..967c1f5 --- /dev/null +++ b/applications/hbbtvplayer/hbbtvbrowserplugin/src/applicationclass.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2010-2011 Telecom-Paristech + * All Rights Reserved + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * Authors: Stanislas Selle + * + */ +#ifndef __APPLICATIONCLASS_H__ +#define __APPLICATIONCLASS_H__ + +#include "hbbtvbrowserplugin.h" + +typedef struct +{ + NPObject header; + NPP npp; + + NPBool visible; + NPObject* privateData; + +} NPObj_Application; + + +NPClass* fillAPPLICATIONpclass(void); + +NPObject * APPLICATION_Allocate(NPP npp, NPClass *aClass); +void APPLICATION_Deallocate(NPObject *obj); +void APPLICATION_Invalidate(NPObject *obj); +bool APPLICATION_HasMethod(NPObject *obj, NPIdentifier name); +bool APPLICATION_Invoke(NPObject *obj, NPIdentifier name, const NPVariant *args, uint32_t argCount, NPVariant *result); +bool APPLICATION_InvokeDefault(NPObject *npobj, const NPVariant *args, uint32_t argCount, NPVariant *result); +bool APPLICATION_HasProperty(NPObject *obj, NPIdentifier name); +bool APPLICATION_GetProperty(NPObject *obj, NPIdentifier name, NPVariant *result); +bool APPLICATION_SetProperty(NPObject *obj, NPIdentifier name, const NPVariant *value); + +bool APPLICATION_RemoveProperty(NPObject *npobj, NPIdentifier name); + +bool APPLICATION_Enumerate(NPObject *npobj, NPIdentifier **value, uint32_t *count); + + + +void APPLICATION_Invoke_CreateApplication(NPObject* obj,const NPVariant* args, uint32_t argCount); +void APPLICATION_Invoke_DestroyApplication(NPObject* obj,const NPVariant* args, uint32_t argCount); +void APPLICATION_Invoke_Show(NPObject* obj,const NPVariant* args, uint32_t argCount); +void APPLICATION_Invoke_Hide(NPObject* obj,const NPVariant* args, uint32_t argCount); + +#endif diff --git a/applications/hbbtvplayer/hbbtvbrowserplugin/src/applicationprivatedataclass.c b/applications/hbbtvplayer/hbbtvbrowserplugin/src/applicationprivatedataclass.c new file mode 100644 index 0000000..fdb8443 --- /dev/null +++ b/applications/hbbtvplayer/hbbtvbrowserplugin/src/applicationprivatedataclass.c @@ -0,0 +1,219 @@ +/* + * Copyright (c) 2010-2011 Telecom-Paristech + * All Rights Reserved + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * Authors: Stanislas Selle + * + */ +#include "applicationprivatedataclass.h" + +#define kAPPPRIVDATA_ID_PROPERTY_KEYSET 0 +#define kAPPPRIVDATA_ID_PROPERTY_CURRENTCHANNEL 1 +#define kAPPPRIVDATA_NUM_PROPERTY_IDENTIFIERS 2 + +#define kAPPPRIVDATA_ID_METHOD_GETMEMFREE 0 +#define kAPPPRIVDATA_NUM_METHOD_IDENTIFIERS 1 + + + +bool v_bAPPPRIVDATAIdentifiersInitialized = false; + +NPIdentifier v_APPPRIVDATAPropertyIdentifiers[kAPPPRIVDATA_NUM_PROPERTY_IDENTIFIERS]; +const NPUTF8 * v_APPPRIVDATAPropertyNames[kAPPPRIVDATA_NUM_PROPERTY_IDENTIFIERS] = { + "keyset", + "currentChannel" + }; + +NPIdentifier v_APPPRIVDATAMethodIdentifiers[kAPPPRIVDATA_NUM_METHOD_IDENTIFIERS]; +const NPUTF8 * v_APPPRIVDATAMethodNames[kAPPPRIVDATA_NUM_METHOD_IDENTIFIERS] = { + "getMEMFREE" + }; + +static void APPPRIVDATAinitializeIdentifiers(void) +{ + sBrowserFuncs->getstringidentifiers( v_APPPRIVDATAPropertyNames, kAPPPRIVDATA_NUM_PROPERTY_IDENTIFIERS, v_APPPRIVDATAPropertyIdentifiers ); + sBrowserFuncs->getstringidentifiers( v_APPPRIVDATAMethodNames, kAPPPRIVDATA_NUM_METHOD_IDENTIFIERS, v_APPPRIVDATAMethodIdentifiers ); +} + + +NPClass stAPPPRIVDATAclass; +NPClass* pAPPPRIVDATAclass = NULL; + +NPClass* fillAPPPRIVDATApclass(void) +{ + TRACEINFO; + if (pAPPPRIVDATAclass == NULL) + { + stAPPPRIVDATAclass.allocate = APPPRIVDATA_Allocate; + stAPPPRIVDATAclass.deallocate = APPPRIVDATA_Deallocate; + stAPPPRIVDATAclass.invalidate = APPPRIVDATA_Invalidate; + stAPPPRIVDATAclass.hasMethod = APPPRIVDATA_HasMethod; + stAPPPRIVDATAclass.invoke = APPPRIVDATA_Invoke; + stAPPPRIVDATAclass.invokeDefault = APPPRIVDATA_InvokeDefault; + stAPPPRIVDATAclass.hasProperty = APPPRIVDATA_HasProperty; + stAPPPRIVDATAclass.getProperty = APPPRIVDATA_GetProperty; + stAPPPRIVDATAclass.setProperty = APPPRIVDATA_SetProperty; + stAPPPRIVDATAclass.removeProperty = APPPRIVDATA_RemoveProperty; + stAPPPRIVDATAclass.enumerate = APPPRIVDATA_Enumerate; + pAPPPRIVDATAclass = &stAPPPRIVDATAclass; + } + + return pAPPPRIVDATAclass; +} + + +NPObject * APPPRIVDATA_Allocate(NPP npp, NPClass *theClass) +{ + TRACEINFO; + + NPObject* result; + + + if (!v_bAPPPRIVDATAIdentifiersInitialized) + { + v_bAPPPRIVDATAIdentifiersInitialized = true; + APPPRIVDATAinitializeIdentifiers(); + } + + NPObj_AppPrivData* newappprivdata = (NPObj_AppPrivData*)MEMALLOC(sizeof(NPObj_AppPrivData)); + fprintf(stderr, "\t%s : Allocation at \x1b[%i;3%im%p\n\x1b[0m",__FUNCTION__, 1, 1, newappprivdata); + newappprivdata->npp = npp; + newappprivdata->keyset = sBrowserFuncs->createobject(npp, fillKEYSETpclass()); + fprintf(stderr, "\t%s : Create keyset property at \x1b[%i;3%im%p\n\x1b[0m",__FUNCTION__, 1, 1, newappprivdata->keyset ); + result = (NPObject*)newappprivdata; + return result; +} + + +void APPPRIVDATA_Deallocate(NPObject* obj) +{ + TRACEINFO; + NPObj_AppPrivData* appprivdataobj = (NPObj_AppPrivData*)obj; + sBrowserFuncs->releaseobject(appprivdataobj->keyset); + MEMFREE(appprivdataobj); + return; +} + +void APPPRIVDATA_Invalidate(NPObject* obj) +{ + TRACEINFO; + return; +} + +bool APPPRIVDATA_HasMethod(NPObject* obj, NPIdentifier name) +{ + TRACEINFO; + bool result = false; + int i = 0; + NPUTF8* utf8methodname = (char*)sBrowserFuncs->utf8fromidentifier(name); + while ((i < kAPPPRIVDATA_NUM_METHOD_IDENTIFIERS) && (result == false)) + { + if ( name == v_APPPRIVDATAMethodIdentifiers[i] ) + { + result= true; + } + i++; + } + + return result; +} + +bool APPPRIVDATA_Invoke(NPObject* obj, NPIdentifier name, const NPVariant* args, uint32_t argCount, NPVariant* result) +{ + TRACEINFO; + bool fctresult = false; + if (name == v_APPPRIVDATAMethodIdentifiers[kAPPPRIVDATA_ID_METHOD_GETMEMFREE]) + { + APPPRIVDATA_Invoke_GetFreeMen(obj, args, argCount); + fctresult = true; + } + else + { + fctresult = false; + } + return fctresult; +} + +bool APPPRIVDATA_InvokeDefault(NPObject *npobj, const NPVariant *args, uint32_t argCount, NPVariant *result) +{ + TRACEINFO; + return true; +} + +bool APPPRIVDATA_HasProperty(NPObject* obj, NPIdentifier name) +{ + TRACEINFO; + bool result = false; + NPUTF8* utf8propertyname = (char*)sBrowserFuncs->utf8fromidentifier(name); + + int i = 0; + while ((i < kAPPPRIVDATA_NUM_PROPERTY_IDENTIFIERS) && (result == false)) + { + if ( name == v_APPPRIVDATAPropertyIdentifiers[i] ) + { + result= true; + } + i++; + } + + + return result; +} + +bool APPPRIVDATA_GetProperty(NPObject* obj, NPIdentifier name, NPVariant* result) +{ + TRACEINFO; + + bool fctresult = false; + NPObj_AppPrivData* AppPrivDataObj = (NPObj_AppPrivData*)obj; + if (!strcmp(sBrowserFuncs->utf8fromidentifier(name), v_APPPRIVDATAPropertyNames[kAPPPRIVDATA_ID_PROPERTY_KEYSET])) + { + sBrowserFuncs->retainobject(AppPrivDataObj->keyset); + OBJECT_TO_NPVARIANT(AppPrivDataObj->keyset, *result); + + fctresult = true; + } + else + + return fctresult; +} + +bool APPPRIVDATA_SetProperty(NPObject *obj, NPIdentifier name, const NPVariant *value) +{ + TRACEINFO; + bool fctresult = false; + return fctresult; +} + +bool APPPRIVDATA_RemoveProperty(NPObject *npobj, NPIdentifier name) +{ + TRACEINFO; + return true; +} + + +bool APPPRIVDATA_Enumerate(NPObject *npobj, NPIdentifier **value, uint32_t *count) +{ + TRACEINFO; + return true; +} + + +/** implementation **/ +void APPPRIVDATA_Invoke_GetFreeMen(NPObject* obj,const NPVariant* args, uint32_t argCount) +{ + TRACEINFO; + NOTIMPLEMENTED; +} diff --git a/applications/hbbtvplayer/hbbtvbrowserplugin/src/applicationprivatedataclass.h b/applications/hbbtvplayer/hbbtvbrowserplugin/src/applicationprivatedataclass.h new file mode 100644 index 0000000..fc8a867 --- /dev/null +++ b/applications/hbbtvplayer/hbbtvbrowserplugin/src/applicationprivatedataclass.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2010-2011 Telecom-Paristech + * All Rights Reserved + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * Authors: Stanislas Selle + * + */ +#ifndef __APPLICATIONPRIVATEDATACLASS_H__ +#define __APPLICATIONPRIVATEDATACLASS_H__ + +#include "keysetclass.h" +#include "hbbtvbrowserplugin.h" + +typedef struct +{ + NPObject header; + NPP npp; + + NPObject* keyset; + NPObject* currentChannel; +} NPObj_AppPrivData; + +NPClass* fillAPPPRIVDATApclass(void); + +NPObject * APPPRIVDATA_Allocate(NPP npp, NPClass *aClass); +void APPPRIVDATA_Deallocate(NPObject *obj); +void APPPRIVDATA_Invalidate(NPObject *obj); +bool APPPRIVDATA_HasMethod(NPObject *obj, NPIdentifier name); +bool APPPRIVDATA_Invoke(NPObject *obj, NPIdentifier name, const NPVariant *args, uint32_t argCount, NPVariant *result); +bool APPPRIVDATA_InvokeDefault(NPObject *npobj, const NPVariant *args, uint32_t argCount, NPVariant *result); +bool APPPRIVDATA_HasProperty(NPObject *obj, NPIdentifier name); +bool APPPRIVDATA_GetProperty(NPObject *obj, NPIdentifier name, NPVariant *result); +bool APPPRIVDATA_SetProperty(NPObject *obj, NPIdentifier name, const NPVariant *value); + +bool APPPRIVDATA_RemoveProperty(NPObject *npobj, NPIdentifier name); + +bool APPPRIVDATA_Enumerate(NPObject *npobj, NPIdentifier **value, uint32_t *count); + + +void APPPRIVDATA_Invoke_GetFreeMen(NPObject* obj,const NPVariant* args, uint32_t argCount); + +#endif diff --git a/applications/hbbtvplayer/hbbtvbrowserplugin/src/configurationclass.c b/applications/hbbtvplayer/hbbtvbrowserplugin/src/configurationclass.c new file mode 100644 index 0000000..b4f14ff --- /dev/null +++ b/applications/hbbtvplayer/hbbtvbrowserplugin/src/configurationclass.c @@ -0,0 +1,182 @@ +/* + * Copyright (c) 2010-2011 Telecom-Paristech + * All Rights Reserved + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Authors: Stanislas Selle + * + */ +#include "configurationclass.h" + + +#define kCONFIGURATION_ID_PROPERTY_PREFERREDAUDIOLANGUAGE 0 +#define kCONFIGURATION_ID_PROPERTY_PREFERREDSUBTITLELANGUAGE 1 +#define kCONFIGURATION_ID_PROPERTY_COUNTRYID 2 +#define kCONFIGURATION_NUM_PROPERTY_IDENTIFIERS 3 + +#define kCONFIGURATION_NUM_METHOD_IDENTIFIERS 0 + + + +bool v_bCONFIGURATIONIdentifiersInitialized = false; + +NPIdentifier v_CONFIGURATIONPropertyIdentifiers[kCONFIGURATION_NUM_PROPERTY_IDENTIFIERS]; +const NPUTF8 * v_CONFIGURATIONPropertyNames[kCONFIGURATION_NUM_PROPERTY_IDENTIFIERS] = { + "preferredAudioLanguage", + "preferredSubtitleLanguage", + "countryId" + }; + +NPIdentifier v_CONFIGURATIONMethodIdentifiers[kCONFIGURATION_NUM_METHOD_IDENTIFIERS]; +const NPUTF8 * v_CONFIGURATIONMethodNames[kCONFIGURATION_NUM_METHOD_IDENTIFIERS] = {}; + +static void CONFIGURATIONinitializeIdentifiers(void) +{ + sBrowserFuncs->getstringidentifiers( v_CONFIGURATIONPropertyNames, kCONFIGURATION_NUM_PROPERTY_IDENTIFIERS, v_CONFIGURATIONPropertyIdentifiers ); + sBrowserFuncs->getstringidentifiers( v_CONFIGURATIONMethodNames, kCONFIGURATION_NUM_METHOD_IDENTIFIERS, v_CONFIGURATIONMethodIdentifiers ); +} + + +NPClass stCONFIGURATIONclass; +NPClass* pCONFIGURATIONclass = NULL; + +NPClass* fillCONFIGURATIONpclass(void) +{ + TRACEINFO; + if (pCONFIGURATIONclass == NULL) + { + stCONFIGURATIONclass.allocate = CONFIGURATION_Allocate; + stCONFIGURATIONclass.deallocate = CONFIGURATION_Deallocate; + stCONFIGURATIONclass.invalidate = CONFIGURATION_Invalidate; + stCONFIGURATIONclass.hasMethod = CONFIGURATION_HasMethod; + stCONFIGURATIONclass.invoke = CONFIGURATION_Invoke; + stCONFIGURATIONclass.invokeDefault = CONFIGURATION_InvokeDefault; + stCONFIGURATIONclass.hasProperty = CONFIGURATION_HasProperty; + stCONFIGURATIONclass.getProperty = CONFIGURATION_GetProperty; + stCONFIGURATIONclass.setProperty = CONFIGURATION_SetProperty; + stCONFIGURATIONclass.removeProperty = CONFIGURATION_RemoveProperty; + stCONFIGURATIONclass.enumerate = CONFIGURATION_Enumerate; + pCONFIGURATIONclass = &stCONFIGURATIONclass; + } + + return pCONFIGURATIONclass; +} + + +NPObject * CONFIGURATION_Allocate(NPP npp, NPClass *theClass) +{ + TRACEINFO; + + if (!v_bCONFIGURATIONIdentifiersInitialized) + { + v_bCONFIGURATIONIdentifiersInitialized = true; + CONFIGURATIONinitializeIdentifiers(); + } + NPObject* newconfiguration = NULL; + + newconfiguration = (NPObject *)MEMALLOC(sizeof(NPObject)); + + return newconfiguration; +} + + +void CONFIGURATION_Deallocate(NPObject* obj) +{ + TRACEINFO; + MEMFREE(obj); + return; +} + +void CONFIGURATION_Invalidate(NPObject* obj) +{ + TRACEINFO; + return; +} + +bool CONFIGURATION_HasMethod(NPObject* obj, NPIdentifier name) +{ + TRACEINFO; + bool result = false; + int i = 0; + NPUTF8* utf8methodname = (char*)sBrowserFuncs->utf8fromidentifier(name); + while ((i < kCONFIGURATION_NUM_METHOD_IDENTIFIERS) && (result == false)) + { + if ( name == v_CONFIGURATIONMethodIdentifiers[i] ) + { + result= true; + } + i++; + } + + return result; +} + +bool CONFIGURATION_Invoke(NPObject* obj, NPIdentifier name, const NPVariant* args, uint32_t argCount, NPVariant* result) +{ + TRACEINFO; + return true; +} + +bool CONFIGURATION_InvokeDefault(NPObject *npobj, const NPVariant *args, uint32_t argCount, NPVariant *result) +{ + TRACEINFO; + return true; +} + +bool CONFIGURATION_HasProperty(NPObject* obj, NPIdentifier name) +{ + TRACEINFO; + bool result = false; + NPUTF8* utf8propertyname = (char*)sBrowserFuncs->utf8fromidentifier(name); + + int i = 0; + while ((i < kCONFIGURATION_NUM_PROPERTY_IDENTIFIERS) && (result == false)) + { + if ( name == v_CONFIGURATIONPropertyIdentifiers[i] ) + { + result= true; + } + i++; + } + + + return result; +} + +bool CONFIGURATION_GetProperty(NPObject* obj, NPIdentifier name, NPVariant* result) +{ + TRACEINFO; + return true; +} + +bool CONFIGURATION_SetProperty(NPObject *obj, NPIdentifier name, const NPVariant *value) +{ + TRACEINFO; + return true; +} + +bool CONFIGURATION_RemoveProperty(NPObject *npobj, NPIdentifier name) +{ + TRACEINFO; + return true; +} + + +bool CONFIGURATION_Enumerate(NPObject *npobj, NPIdentifier **value, uint32_t *count) +{ + TRACEINFO; + return true; +} + diff --git a/applications/hbbtvplayer/hbbtvbrowserplugin/src/configurationclass.h b/applications/hbbtvplayer/hbbtvbrowserplugin/src/configurationclass.h new file mode 100644 index 0000000..ddba67b --- /dev/null +++ b/applications/hbbtvplayer/hbbtvbrowserplugin/src/configurationclass.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2010-2011 Telecom-Paristech + * All Rights Reserved + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * Authors: Stanislas Selle + * + */ +#ifndef __CONFIGURATIONCLASS_H__ +#define __CONFIGURATIONCLASS_H__ + +#include "hbbtvbrowserplugin.h" + + +NPClass* fillCONFIGURATIONpclass(void); + +NPObject * CONFIGURATION_Allocate(NPP npp, NPClass *aClass); +void CONFIGURATION_Deallocate(NPObject *obj); +void CONFIGURATION_Invalidate(NPObject *obj); +bool CONFIGURATION_HasMethod(NPObject *obj, NPIdentifier name); +bool CONFIGURATION_Invoke(NPObject *obj, NPIdentifier name, const NPVariant *args, uint32_t argCount, NPVariant *result); +bool CONFIGURATION_InvokeDefault(NPObject *npobj, const NPVariant *args, uint32_t argCount, NPVariant *result); +bool CONFIGURATION_HasProperty(NPObject *obj, NPIdentifier name); +bool CONFIGURATION_GetProperty(NPObject *obj, NPIdentifier name, NPVariant *result); +bool CONFIGURATION_SetProperty(NPObject *obj, NPIdentifier name, const NPVariant *value); + +bool CONFIGURATION_RemoveProperty(NPObject *npobj, NPIdentifier name); + +bool CONFIGURATION_Enumerate(NPObject *npobj, NPIdentifier **value, uint32_t *count); + + +#endif diff --git a/applications/hbbtvplayer/hbbtvbrowserplugin/src/downloadclass.c b/applications/hbbtvplayer/hbbtvbrowserplugin/src/downloadclass.c new file mode 100644 index 0000000..6cfb967 --- /dev/null +++ b/applications/hbbtvplayer/hbbtvbrowserplugin/src/downloadclass.c @@ -0,0 +1,209 @@ +/* + * Copyright (c) 2010-2011 Telecom-Paristech + * All Rights Reserved + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Authors: Stanislas Selle + * + */ +#include "downloadclass.h" + +#define kDOWNLOAD_ID_PROPERTY_TOTALIZE 0 +#define kDOWNLOAD_ID_PROPERTY_STATE 1 +#define kDOWNLOAD_ID_PROPERTY_AMOUNTDOWNLOADED 2 +#define kDOWNLOAD_ID_PROPERTY_NAME 3 +#define kDOWNLOAD_ID_PROPERTY_ID 4 +#define kDOWNLOAD_ID_PROPERTY_CONTENTURL 5 +#define kDOWNLOAD_ID_PROPERTY_DESCRIPTION 6 +#define kDOWNLOAD_ID_PROPERTY_PARENTALRATINGS 7 +#define kDOWNLOAD_ID_PROPERTY_DRMCONTROL 8 +#define kDOWNLOAD_ID_PROPERTY_STARTTIME 9 +#define kDOWNLOAD_ID_PROPERTY_TIMEELAPSED 10 +#define kDOWNLOAD_ID_PROPERTY_TIMEREMAINING 11 +#define kDOWNLOAD_ID_PROPERTY_TRANSFERTYPE 12 +#define kDOWNLOAD_ID_PROPERTY_ORIGINSITE 13 +#define kDOWNLOAD_ID_PROPERTY_ORIGINSITENAME 14 +#define kDOWNLOAD_ID_PROPERTY_CONTENTID 15 +#define kDOWNLOAD_ID_PROPERTY_ICONURL 16 +#define kDOWNLOAD_NUM_PROPERTY_IDENTIFIERS 17 + + +#define kDOWNLOAD_NUM_METHOD_IDENTIFIERS 0 + + +bool v_bDOWNLOADIdentifiersInitialized = false; + +NPIdentifier v_DOWNLOADPropertyIdentifiers[kDOWNLOAD_NUM_PROPERTY_IDENTIFIERS]; +const NPUTF8 * v_DOWNLOADPropertyNames[kDOWNLOAD_NUM_PROPERTY_IDENTIFIERS] = { + "totalize", + "state", + "amountDownloaded", + "name", + "id", + "contentURL", + "description", + "parentalRatings", + "drmControl", + "startTime", + "timeElapsed", + "timeRemaining", + "transferType", + "originSite", + "originSiteName", + "contentID", + "iconURL" + }; + +NPIdentifier v_DOWNLOADMethodIdentifiers[kDOWNLOAD_NUM_METHOD_IDENTIFIERS]; +const NPUTF8 * v_DOWNLOADMethodNames[kDOWNLOAD_NUM_METHOD_IDENTIFIERS] = { +}; + + +static void DOWNLOADinitializeIdentifiers(void) +{ + sBrowserFuncs->getstringidentifiers( v_DOWNLOADPropertyNames, kDOWNLOAD_NUM_PROPERTY_IDENTIFIERS, v_DOWNLOADPropertyIdentifiers ); + sBrowserFuncs->getstringidentifiers( v_DOWNLOADMethodNames, kDOWNLOAD_NUM_METHOD_IDENTIFIERS, v_DOWNLOADMethodIdentifiers ); +} + +NPClass stDOWNLOADclass; +NPClass* pDOWNLOADclass = NULL; + +NPClass* fillDOWNLOADpclass(void) +{ + TRACEINFO; + if (pDOWNLOADclass == NULL) + { + stDOWNLOADclass.allocate = DOWNLOAD_Allocate; + stDOWNLOADclass.deallocate = DOWNLOAD_Deallocate; + stDOWNLOADclass.invalidate = DOWNLOAD_Invalidate; + stDOWNLOADclass.hasMethod = DOWNLOAD_HasMethod; + stDOWNLOADclass.invoke = DOWNLOAD_Invoke; + stDOWNLOADclass.invokeDefault = DOWNLOAD_InvokeDefault; + stDOWNLOADclass.hasProperty = DOWNLOAD_HasProperty; + stDOWNLOADclass.getProperty = DOWNLOAD_GetProperty; + stDOWNLOADclass.setProperty = DOWNLOAD_SetProperty; + stDOWNLOADclass.removeProperty = DOWNLOAD_RemoveProperty; + stDOWNLOADclass.enumerate = DOWNLOAD_Enumerate; + pDOWNLOADclass = &stDOWNLOADclass; + } + + return pDOWNLOADclass; +} + + +NPObject * DOWNLOAD_Allocate(NPP npp, NPClass *theClass) +{ + TRACEINFO; + + + if (!v_bDOWNLOADIdentifiersInitialized) + { + v_bDOWNLOADIdentifiersInitialized = true; + DOWNLOADinitializeIdentifiers(); + } + + NPObject* newdownload = (NPObject *)MEMALLOC(sizeof(NPObject)); + + return newdownload; +} + + void DOWNLOAD_Deallocate(NPObject* obj) +{ + TRACEINFO; + MEMFREE(obj); + return; +} + + void DOWNLOAD_Invalidate(NPObject* obj) +{ + TRACEINFO; + return; +} + + bool DOWNLOAD_HasMethod(NPObject* obj, NPIdentifier name) +{ + TRACEINFO; + + bool result = false; + int i = 0; + NPUTF8* utf8methodname = (char*)sBrowserFuncs->utf8fromidentifier(name); + while ((i < kDOWNLOAD_NUM_METHOD_IDENTIFIERS) && (result == false)) + { + if ( name == v_DOWNLOADMethodIdentifiers[i] ) + { + result= true; + } + i++; + } + + return result; +} + +bool DOWNLOAD_Invoke(NPObject* obj, NPIdentifier name, const NPVariant* args, uint32_t argCount, NPVariant* result) +{ + TRACEINFO; + return true; +} + + bool DOWNLOAD_InvokeDefault(NPObject *npobj, const NPVariant *args, uint32_t argCount, NPVariant *result) +{ + TRACEINFO; + return true; +} + + bool DOWNLOAD_HasProperty(NPObject* obj, NPIdentifier name) +{ + TRACEINFO; + bool result = false; + NPUTF8* utf8propertyname = (char*)sBrowserFuncs->utf8fromidentifier(name); + + int i = 0; + while ((i < kDOWNLOAD_NUM_PROPERTY_IDENTIFIERS) && (result == false)) + { + if ( name == v_DOWNLOADPropertyIdentifiers[i] ) + { + result= true; + } + i++; + } + + return result; +} + + bool DOWNLOAD_GetProperty(NPObject* obj, NPIdentifier name, NPVariant* result) +{ + TRACEINFO; + return true; +} + + bool DOWNLOAD_SetProperty(NPObject *obj, NPIdentifier name, const NPVariant *value) +{ + TRACEINFO; + return true; +} + + bool DOWNLOAD_RemoveProperty(NPObject *npobj, NPIdentifier name) +{ + TRACEINFO; + return true; +} + + + bool DOWNLOAD_Enumerate(NPObject *npobj, NPIdentifier **value, uint32_t *count) +{ + TRACEINFO; + return true; +} + diff --git a/applications/hbbtvplayer/hbbtvbrowserplugin/src/downloadclass.h b/applications/hbbtvplayer/hbbtvbrowserplugin/src/downloadclass.h new file mode 100644 index 0000000..a7e5673 --- /dev/null +++ b/applications/hbbtvplayer/hbbtvbrowserplugin/src/downloadclass.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2010-2011 Telecom-Paristech + * All Rights Reserved + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * Authors: Stanislas Selle + * + */ +#ifndef __DOWNLOADCLASS_H__ +#define __DOWNLOADCLASS_H__ + +#include "hbbtvbrowserplugin.h" + +NPClass* fillDOWNLOADpclass(void); + +NPObject * DOWNLOAD_Allocate(NPP npp, NPClass *aClass); +void DOWNLOAD_Deallocate(NPObject *obj); +void DOWNLOAD_Invalidate(NPObject *obj); +bool DOWNLOAD_HasMethod(NPObject *obj, NPIdentifier name); +bool DOWNLOAD_Invoke(NPObject *obj, NPIdentifier name, const NPVariant *args, uint32_t argCount, NPVariant *result); +bool DOWNLOAD_InvokeDefault(NPObject *npobj, const NPVariant *args, uint32_t argCount, NPVariant *result); +bool DOWNLOAD_HasProperty(NPObject *obj, NPIdentifier name); +bool DOWNLOAD_GetProperty(NPObject *obj, NPIdentifier name, NPVariant *result); +bool DOWNLOAD_SetProperty(NPObject *obj, NPIdentifier name, const NPVariant *value); + +bool DOWNLOAD_RemoveProperty(NPObject *npobj, NPIdentifier name); + +bool DOWNLOAD_Enumerate(NPObject *npobj, NPIdentifier **value, uint32_t *count); + + +#endif diff --git a/applications/hbbtvplayer/hbbtvbrowserplugin/src/downloadcollectionclass.c b/applications/hbbtvplayer/hbbtvbrowserplugin/src/downloadcollectionclass.c new file mode 100644 index 0000000..40524f6 --- /dev/null +++ b/applications/hbbtvplayer/hbbtvbrowserplugin/src/downloadcollectionclass.c @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2010-2011 Telecom-Paristech + * All Rights Reserved + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * Authors: Stanislas Selle + * + */ +#include "downloadcollectionclass.h" + + +#define kDWLDCOLLECTION_ID_PROPERTY_LENGH 0 +#define kDWLDCOLLECTION_NUM_PROPERTY_IDENTIFIERS 1 + +#define kDWLDCOLLECTION_ID_METHOD_ITEM 0 +#define kDWLDCOLLECTION_NUM_METHOD_IDENTIFIERS 1 + +bool v_bDWLDCOLLECTIONIdentifiersInitialized = false; + +NPIdentifier v_DWLDCOLLECTIONPropertyIdentifiers[kDWLDCOLLECTION_NUM_PROPERTY_IDENTIFIERS]; +const NPUTF8 * v_DWLDCOLLECTIONPropertyNames[kDWLDCOLLECTION_NUM_PROPERTY_IDENTIFIERS] = { + "lengh" + }; + +NPIdentifier v_DWLDCOLLECTIONMethodIdentifiers[kDWLDCOLLECTION_NUM_METHOD_IDENTIFIERS]; +const NPUTF8 * v_DWLDCOLLECTIONMethodNames[kDWLDCOLLECTION_NUM_METHOD_IDENTIFIERS] = { + "item" + }; + +static void DWLDCOLLECTIONinitializeIdentifiers(void) +{ + sBrowserFuncs->getstringidentifiers( v_DWLDCOLLECTIONPropertyNames, kDWLDCOLLECTION_NUM_PROPERTY_IDENTIFIERS, v_DWLDCOLLECTIONPropertyIdentifiers ); + sBrowserFuncs->getstringidentifiers( v_DWLDCOLLECTIONMethodNames, kDWLDCOLLECTION_NUM_METHOD_IDENTIFIERS, v_DWLDCOLLECTIONMethodIdentifiers ); +} + + +NPClass stDWLDCOLLECTIONclass; +NPClass* pDWLDCOLLECTIONclass = NULL; + +NPClass* fillDWLDCOLLECTIONpclass(void) +{ + TRACEINFO; + if (pDWLDCOLLECTIONclass == NULL) + { + stDWLDCOLLECTIONclass.allocate = DWLDCOLLECTION_Allocate; + stDWLDCOLLECTIONclass.deallocate = DWLDCOLLECTION_Deallocate; + stDWLDCOLLECTIONclass.invalidate = DWLDCOLLECTION_Invalidate; + stDWLDCOLLECTIONclass.hasMethod = DWLDCOLLECTION_HasMethod; + stDWLDCOLLECTIONclass.invoke = DWLDCOLLECTION_Invoke; + stDWLDCOLLECTIONclass.invokeDefault = DWLDCOLLECTION_InvokeDefault; + stDWLDCOLLECTIONclass.hasProperty = DWLDCOLLECTION_HasProperty; + stDWLDCOLLECTIONclass.getProperty = DWLDCOLLECTION_GetProperty; + stDWLDCOLLECTIONclass.setProperty = DWLDCOLLECTION_SetProperty; + stDWLDCOLLECTIONclass.removeProperty = DWLDCOLLECTION_RemoveProperty; + stDWLDCOLLECTIONclass.enumerate = DWLDCOLLECTION_Enumerate; + pDWLDCOLLECTIONclass = &stDWLDCOLLECTIONclass; + } + + return pDWLDCOLLECTIONclass; +} + + +NPObject * DWLDCOLLECTION_Allocate(NPP npp, NPClass *theClass) +{ + TRACEINFO; + + if (!v_bDWLDCOLLECTIONIdentifiersInitialized) + { + v_bDWLDCOLLECTIONIdentifiersInitialized = true; + DWLDCOLLECTIONinitializeIdentifiers(); + } + + NPObject* newdwldcollection = (NPObject *)MEMALLOC(sizeof(NPObject)); + + return newdwldcollection; +} + + +void DWLDCOLLECTION_Deallocate(NPObject* obj) +{ + TRACEINFO; + MEMFREE(obj); + return; +} + +void DWLDCOLLECTION_Invalidate(NPObject* obj) +{ + TRACEINFO; + return; +} + +bool DWLDCOLLECTION_HasMethod(NPObject* obj, NPIdentifier name) +{ + TRACEINFO; + bool result = false; + int i = 0; + NPUTF8* utf8methodname = (char*)sBrowserFuncs->utf8fromidentifier(name); + while ((i < kDWLDCOLLECTION_NUM_METHOD_IDENTIFIERS) && (result == false)) + { + if ( name == v_DWLDCOLLECTIONMethodIdentifiers[i] ) + { + result= true; + } + i++; + } + + return result; +} + +bool DWLDCOLLECTION_Invoke(NPObject* obj, NPIdentifier name, const NPVariant* args, uint32_t argCount, NPVariant* result) +{ + TRACEINFO; + bool fctresult = false; + if (name == v_DWLDCOLLECTIONMethodIdentifiers[kDWLDCOLLECTION_ID_METHOD_ITEM ]) + { + DWLDCOLLECTION_Invoke_Item(obj, args, argCount); + fctresult = true; + } + else + { + fctresult = false; + } + return fctresult; + +} + +bool DWLDCOLLECTION_InvokeDefault(NPObject *npobj, const NPVariant *args, uint32_t argCount, NPVariant *result) +{ + TRACEINFO; + return true; +} + +bool DWLDCOLLECTION_HasProperty(NPObject* obj, NPIdentifier name) +{ + TRACEINFO; + bool result = false; + NPUTF8* utf8propertyname = (char*)sBrowserFuncs->utf8fromidentifier(name); + + int i = 0; + while ((i < kDWLDCOLLECTION_NUM_PROPERTY_IDENTIFIERS) && (result == false)) + { + if ( name == v_DWLDCOLLECTIONPropertyIdentifiers[i] ) + { + result= true; + } + i++; + } + + + return result; +} + +bool DWLDCOLLECTION_GetProperty(NPObject* obj, NPIdentifier name, NPVariant* result) +{ + TRACEINFO; + return true; +} + +bool DWLDCOLLECTION_SetProperty(NPObject *obj, NPIdentifier name, const NPVariant *value) +{ + TRACEINFO; + return true; +} + +bool DWLDCOLLECTION_RemoveProperty(NPObject *npobj, NPIdentifier name) +{ + TRACEINFO; + return true; +} + + +bool DWLDCOLLECTION_Enumerate(NPObject *npobj, NPIdentifier **value, uint32_t *count) +{ + TRACEINFO; + return true; +} + +/** implementation **/ +void DWLDCOLLECTION_Invoke_Item(NPObject* obj,const NPVariant* args, uint32_t argCount) +{ + TRACEINFO; + NOTIMPLEMENTED; +} diff --git a/applications/hbbtvplayer/hbbtvbrowserplugin/src/downloadcollectionclass.h b/applications/hbbtvplayer/hbbtvbrowserplugin/src/downloadcollectionclass.h new file mode 100644 index 0000000..c178d1b --- /dev/null +++ b/applications/hbbtvplayer/hbbtvbrowserplugin/src/downloadcollectionclass.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2010-2011 Telecom-Paristech + * All Rights Reserved + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * Authors: Stanislas Selle + * + */ +#ifndef __DWLDCOLLECTIONCOLLECTIONCLASS_H__ +#define __DWLDCOLLECTIONCOLLECTIONCLASS_H__ + +#include "hbbtvbrowserplugin.h" + + +NPClass* fillDWLDCOLLECTIONpclass(void); + +NPObject * DWLDCOLLECTION_Allocate(NPP npp, NPClass *aClass); +void DWLDCOLLECTION_Deallocate(NPObject *obj); +void DWLDCOLLECTION_Invalidate(NPObject *obj); +bool DWLDCOLLECTION_HasMethod(NPObject *obj, NPIdentifier name); +bool DWLDCOLLECTION_Invoke(NPObject *obj, NPIdentifier name, const NPVariant *args, uint32_t argCount, NPVariant *result); +bool DWLDCOLLECTION_InvokeDefault(NPObject *npobj, const NPVariant *args, uint32_t argCount, NPVariant *result); +bool DWLDCOLLECTION_HasProperty(NPObject *obj, NPIdentifier name); +bool DWLDCOLLECTION_GetProperty(NPObject *obj, NPIdentifier name, NPVariant *result); +bool DWLDCOLLECTION_SetProperty(NPObject *obj, NPIdentifier name, const NPVariant *value); + +bool DWLDCOLLECTION_RemoveProperty(NPObject *npobj, NPIdentifier name); + +bool DWLDCOLLECTION_Enumerate(NPObject *npobj, NPIdentifier **value, uint32_t *count); + +void DWLDCOLLECTION_Invoke_Item(NPObject* obj,const NPVariant* args, uint32_t argCount); +#endif diff --git a/applications/hbbtvplayer/hbbtvbrowserplugin/src/drmcontrolinfocollectionclass.c b/applications/hbbtvplayer/hbbtvbrowserplugin/src/drmcontrolinfocollectionclass.c new file mode 100644 index 0000000..0dc0bd0 --- /dev/null +++ b/applications/hbbtvplayer/hbbtvbrowserplugin/src/drmcontrolinfocollectionclass.c @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2010-2011 Telecom-Paristech + * All Rights Reserved + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * Authors: Stanislas Selle + * + */ +#include "drmcontrolinfocollectionclass.h" diff --git a/applications/hbbtvplayer/hbbtvbrowserplugin/src/drmcontrolinfocollectionclass.h b/applications/hbbtvplayer/hbbtvbrowserplugin/src/drmcontrolinfocollectionclass.h new file mode 100644 index 0000000..cb20d97 --- /dev/null +++ b/applications/hbbtvplayer/hbbtvbrowserplugin/src/drmcontrolinfocollectionclass.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2010-2011 Telecom-Paristech + * All Rights Reserved + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * Authors: Stanislas Selle + * + */ +#ifndef __DRMCONTROLINFOCOLLECTIONCLASS_H__ +#define __DRMCONTROLINFOCOLLECTIONCLASS_H__ + +#include "hbbtvbrowserplugin.h" + + +#endif diff --git a/applications/hbbtvplayer/hbbtvbrowserplugin/src/drmcontrolinformationclass.c b/applications/hbbtvplayer/hbbtvbrowserplugin/src/drmcontrolinformationclass.c new file mode 100644 index 0000000..0dc0bd0 --- /dev/null +++ b/applications/hbbtvplayer/hbbtvbrowserplugin/src/drmcontrolinformationclass.c @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2010-2011 Telecom-Paristech + * All Rights Reserved + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * Authors: Stanislas Selle + * + */ +#include "drmcontrolinfocollectionclass.h" diff --git a/applications/hbbtvplayer/hbbtvbrowserplugin/src/drmcontrolinformationclass.h b/applications/hbbtvplayer/hbbtvbrowserplugin/src/drmcontrolinformationclass.h new file mode 100644 index 0000000..b9a2121 --- /dev/null +++ b/applications/hbbtvplayer/hbbtvbrowserplugin/src/drmcontrolinformationclass.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2010-2011 Telecom-Paristech + * All Rights Reserved + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * Authors: Stanislas Selle + * + */ +#ifndef __DRMCONTROLINFORMATIONCLASS_H__ +#define __DRMCONTROLINFORMATIONCLASS_H__ + +#include "hbbtvbrowserplugin.h" + + +#endif diff --git a/applications/hbbtvplayer/hbbtvbrowserplugin/src/hbbtvbrowserplugin.c b/applications/hbbtvplayer/hbbtvbrowserplugin/src/hbbtvbrowserplugin.c new file mode 100644 index 0000000..82c6425 --- /dev/null +++ b/applications/hbbtvplayer/hbbtvbrowserplugin/src/hbbtvbrowserplugin.c @@ -0,0 +1,335 @@ +/* + * Copyright (c) 2010-2011 Telecom-Paristech + * All Rights Reserved + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * Authors: Stanislas Selle + * + */ +/* +** HbbTV Plugin PC +*/ + + +#include +#ifdef XP_UNIX +#include +#include +#endif +#include + +#include "hbbtvbrowserplugin.h" +#include "oipfapplicationmanager.h" +#include "oipfconfiguration.h" +#include "videobroadcast.h" + +#define MIMETYPEDESCRIPTION "application/hbbtvbrowserplugin::Hbbtv Browser Plugin;" \ + "video/broadcast::Video Broadcast;"\ + "application/oipfapplicationmanager::Oipf Application Manager;"\ + "application/oipfconfiguration::Oipf Configuration;" + + +#define PLUGIN_NAME "hbbtvbrowserplugin" +#define PLUGIN_DESCRIPTION "HbbTV browser plugin " +#define PLUGIN_VERSION "0.20110704" + + + +static void +fillPluginFunctionTable(NPPluginFuncs* pFuncs) +{ + TRACEINFO; + pFuncs->version = 11; + pFuncs->size = sizeof(*pFuncs); + pFuncs->newp = OIPF_NPP_New; + pFuncs->destroy = OIPF_NPP_Destroy; + pFuncs->setwindow = OIPF_NPP_SetWindow; + pFuncs->newstream = OIPF_NPP_NewStream; +// pFuncs->destroystream = NPP_DestroyStream; +// pFuncs->asfile = NPP_StreamAsFile; +// pFuncs->writeready = NPP_WriteReady; +// pFuncs->write = NPP_Write; +// pFuncs->print = NPP_Print; + pFuncs->event = OIPF_NPP_HandleEvent; +// pFuncs->urlnotify = NPP_URLNotify; + pFuncs->getvalue = OIPF_NPP_GetValue; + pFuncs->setvalue = OIPF_NPP_SetValue; +} + +char* booltostr(bool data) +{ + char *result; + if (data) + result = "true"; + else + result = "false"; + return result; +} + +/* + void drawWindow (HBBTVPluginData* instanceData, GdkDrawable* gdkWindow) +{ + TRACEINFO; + NPWindow window = *(instanceData->window); + int x = window.x; + int y = window.y; + int width = window.width; + int height = window.height; + fprintf(stderr,"%p, x = %i y=%i, width = %i height = %i\n",gdkWindow, x ,y,width,height); + + NPP npp = instanceData->npp; + if (!npp) + return; + + const char* uaString = "DemoPlug"; // sBrowserFuncs->uagent(npp); + if (!uaString) + return; + + GdkGC* gdkContext = gdk_gc_new(gdkWindow); + + // draw a grey background for the plugin frame + GdkColor grey; + grey.red = 24000; grey.blue = 24000; grey.green = 24000; + gdk_gc_set_rgb_fg_color(gdkContext, &grey); + gdk_draw_rectangle(gdkWindow, gdkContext, TRUE, x, y, width, height); + + // draw a 3-pixel-thick black frame around the plugin + GdkColor black; + black.red = black.green = black.blue = 0; + gdk_gc_set_rgb_fg_color(gdkContext, &black); + gdk_gc_set_line_attributes(gdkContext, 3, GDK_LINE_SOLID, GDK_CAP_NOT_LAST, GDK_JOIN_MITER); + gdk_draw_rectangle(gdkWindow, gdkContext, FALSE, x + 1, y + 1, + width - 3, height - 3); + + // paint the UA string + PangoContext* pangoContext = gdk_pango_context_get(); + PangoLayout* pangoTextLayout = pango_layout_new(pangoContext); + pango_layout_set_width(pangoTextLayout, (width - 10) * PANGO_SCALE); + pango_layout_set_text(pangoTextLayout, uaString, -1); + gdk_draw_layout(gdkWindow, gdkContext, x + 5, y + 5, pangoTextLayout); + g_object_unref(pangoTextLayout); + + g_object_unref(gdkContext); +} +*/ + + +NPError NP_Initialize(NPNetscapeFuncs* bFuncs, NPPluginFuncs* pFuncs) +{ + TRACEINFO; + + sBrowserFuncs = bFuncs; + + fillPluginFunctionTable(pFuncs); + + return NPERR_NO_ERROR; +} + +char* NP_GetPluginVersion() +{ + TRACEINFO; + return PLUGIN_VERSION; +} + +NPError NP_Shutdown() +{ + TRACEINFO; + return NPERR_NO_ERROR; +} + +char* NP_GetMIMEDescription() +{ + TRACEINFO; + return MIMETYPEDESCRIPTION; +} + + +NPError NP_GetValue(void *instance, NPPVariable variable, void *value) +{ + TRACEINFO; + switch (variable) + { + case NPPVpluginNameString : + *(char**)value = PLUGIN_NAME; + break; + case NPPVpluginDescriptionString : + *(char**)value = PLUGIN_DESCRIPTION; + break; + default : + break; + } + return NPERR_NO_ERROR; +} + +NPError OIPF_NPP_New(NPMIMEType pluginType, NPP instance, uint16_t mode, int16_t argc, char *argn[], char *argv[], NPSavedData *saved) +{ + TRACEINFO; + int i = 0; + + NPBool browserSupportsWindowless = false; + sBrowserFuncs->getvalue(instance, NPNVSupportsWindowless, &browserSupportsWindowless); + if (!browserSupportsWindowless) + { + return NPERR_GENERIC_ERROR; + } + sBrowserFuncs->setvalue(instance, NPPVpluginWindowBool, (void*)false); + sBrowserFuncs->setvalue(instance, NPPVpluginTransparentBool, (void*)false); + + + NPObject* obj; + for(i = 0 ; ipdata = (HBBTVPluginData*)MEMALLOC(sizeof(HBBTVPluginData)); + HBBTVPluginData* pdata = ((HBBTVPluginData*)instance->pdata); + + if (strcmp(argv[i], "application/oipfApplicationManager") == 0) + { + obj = sBrowserFuncs->createobject(instance, fillOAMpclass()); + } + else if (strcmp(argv[i], "application/oipfConfiguration") == 0) + { + obj = sBrowserFuncs->createobject(instance, fillOCFGpclass()); + } + else if (strcmp(argv[i], "video/broadcast") == 0) + { + obj = sBrowserFuncs->createobject(instance, fillVIDBRCpclass()); + }else{ + + obj = NULL; + } + pdata->type = (char*)MEMALLOC(strlen(argv[i])); + strcpy(pdata->type, argv[i] ); + pdata->obj = obj; + pdata->npp = instance; + + }else{ + + obj = NULL; + } + } + + return NPERR_NO_ERROR; +} + +NPError OIPF_NPP_Destroy(NPP instance, NPSavedData **save) +{ + TRACEINFO; + HBBTVPluginData* pdata = (HBBTVPluginData*)instance->pdata; + if (pdata) + { + sBrowserFuncs->releaseobject(pdata->obj); + MEMFREE(pdata->type); + MEMFREE(pdata); + } + return NPERR_NO_ERROR; +} + + +NPError OIPF_NPP_SetWindow(NPP instance, NPWindow *window) +{ + TRACEINFO; + + + + HBBTVPluginData* pdata = (HBBTVPluginData*)instance->pdata; + if(pdata) + { + pdata->window = window; + if (pdata->type) + { + if (strcmp(pdata->type, "video/broadcast") == 0) + { + fprintf(stderr, "%s found \n", pdata->type); + if (pdata->obj) + { + NPObj_VidBrc* vidbrcobj = (NPObj_VidBrc*)pdata->obj; + if (!(vidbrcobj->fullscreen)) + { + OnNoFullscreenSetWindow(pdata->window->x, + pdata->window->y, + pdata->window->width, + pdata->window->height); + } + } + } + } + /*fprintf(stderr, "\tWindow found : %p\n", window); + fprintf(stderr, "\twindow = %p ws_info %p \n\tx = %d, y= %d\n\tclipRect.left = %d clipRect.top= %d\n\twidth=%d, height= %d\n", + pdata->window->window, + pdata->window->ws_info, + pdata->window->x, + pdata->window->y, + pdata->window->clipRect.left, + pdata->window->clipRect.top, + pdata->window->width, + pdata->window->height);*/ + } + + return NPERR_NO_ERROR; +} + +NPError OIPF_NPP_HandleEvent(NPP instance, void* event) +{ + //TRACEINFO; // frequent event, avoid over log... + + XEvent *nativeEvent = (XEvent*)event; + if (nativeEvent->type == GraphicsExpose) + { + XGraphicsExposeEvent *expose = &nativeEvent->xgraphicsexpose; + GC gc = XCreateGC(expose->display, expose->drawable, 0, 0); + XFillRectangle(expose->display, expose->drawable, gc, expose->x, expose->y,expose->width, expose->height); + XFreeGC(expose->display, gc); + return true; + } + + + return false; +} + + + +NPError OIPF_NPP_GetValue(NPP instance, NPPVariable variable, void *value) +{ + TRACEINFO; + + if ( variable == NPPVpluginScriptableNPObject ) + { + void ** v = (void **)value; + if(instance->pdata) + { + NPObject* obj = ((HBBTVPluginData*)instance->pdata)->obj; + if(obj == NULL){ + }else{ + sBrowserFuncs->retainobject(obj); + } + *v = obj; + } + } + return NPERR_NO_ERROR; +} + + +NPError OIPF_NPP_SetValue(NPP instance, NPNVariable variable, void *value) +{ + TRACEINFO; + return NPERR_GENERIC_ERROR; +} + + +NPError OIPF_NPP_NewStream(NPP instance, NPMIMEType type, NPStream* stream, NPBool seekable, uint16_t* stype) +{ + TRACEINFO; + return NPERR_GENERIC_ERROR; +} + diff --git a/applications/hbbtvplayer/hbbtvbrowserplugin/src/hbbtvbrowserplugin.h b/applications/hbbtvplayer/hbbtvbrowserplugin/src/hbbtvbrowserplugin.h new file mode 100644 index 0000000..87e61c0 --- /dev/null +++ b/applications/hbbtvplayer/hbbtvbrowserplugin/src/hbbtvbrowserplugin.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2010-2011 Telecom-Paristech + * All Rights Reserved + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * Authors: Stanislas Selle + * + */ +#ifndef hbbtvbrowserplugin_h +#define hbbtvbrowserplugin_h + +#ifdef __cplusplus +extern "C" { +#endif +#include +#include +#include +#include +#include +#include + +#include "hbbtvbrowserpluginapi.h" + +#ifndef DEBUG + #define DEBUG true + #define PROJECTNAME "HbbTVBrowserPlugin" + #define TRACEINFO if (DEBUG) { fprintf(stderr,"\x1b[%i;3%im%s\x1b[0m : call %s\n", 1, 3, PROJECTNAME, __FUNCTION__); } + #define NOTIMPLEMENTED if (DEBUG) { printf("%s : %s is \x1b[1;31mNOT IMPLEMENTED\x1b[0m : TODO \n", PROJECTNAME, __FUNCTION__); } +#endif + +typedef struct +{ + NPObject* obj; + char* type; + NPWindow* window; + NPP npp; +}HBBTVPluginData; + +NPNetscapeFuncs* sBrowserFuncs; + +#define ALLOCBROWSERMEMORY true + +#ifdef ALLOCBROWSERMEMORY + #define MEMALLOC sBrowserFuncs->memalloc + #define MEMFREE sBrowserFuncs->memfree +#else + #define MEMALLOC malloc + #define MEMFREE free +#endif + +char* booltostr(bool data); + +NPError NP_Initialize(NPNetscapeFuncs* bFuncs, NPPluginFuncs* pFuncs); +NPError NP_Shutdown(); +NPError NP_GetValue(void *instance, NPPVariable variable, void *value); +char* NP_GetPluginVersion(); +char* NP_GetMIMEDescription(); + +NPError OIPF_NPP_New(NPMIMEType pluginType, NPP instance, uint16_t mode, int16_t argc, char *argn[], char *argv[], NPSavedData *saved); +NPError OIPF_NPP_Destroy(NPP instance, NPSavedData **save); +NPError OIPF_NPP_SetWindow(NPP instance, NPWindow *window); +NPError OIPF_NPP_HandleEvent(NPP instance, void* Event); +NPError OIPF_NPP_GetValue(NPP instance, NPPVariable variable, void *value); +NPError OIPF_NPP_SetValue(NPP instance, NPNVariable variable, void *value); +NPError OIPF_NPP_NewStream(NPP instance, NPMIMEType type, NPStream* stream, NPBool seekable, uint16_t* stype); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/applications/hbbtvplayer/hbbtvbrowserplugin/src/hbbtvbrowserpluginapi.c b/applications/hbbtvplayer/hbbtvbrowserplugin/src/hbbtvbrowserpluginapi.c new file mode 100644 index 0000000..aa7ff81 --- /dev/null +++ b/applications/hbbtvplayer/hbbtvbrowserplugin/src/hbbtvbrowserpluginapi.c @@ -0,0 +1,45 @@ + +/* + * Copyright (c) 2010-2011 Telecom-Paristech + * All Rights Reserved + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * Authors: Stanislas Selle + * + */#include "hbbtvbrowserpluginapi.h" + + +void OnNoFullscreenSetWindow(int x, int y, int width, int height) +{ +} + +void OnAPPLICATION_Show() +{ +} + +void OnAPPLICATION_Hide() +{ +} + +void OnVIDBRC_SetFullScreen(int fullscreenparam) +{ +} + +void OnKEYSET_SetValue(double param) +{ +} + +void OnKEYSET_Allocate() +{ +} diff --git a/applications/hbbtvplayer/hbbtvbrowserplugin/src/hbbtvbrowserpluginapi.h b/applications/hbbtvplayer/hbbtvbrowserplugin/src/hbbtvbrowserpluginapi.h new file mode 100644 index 0000000..70408a1 --- /dev/null +++ b/applications/hbbtvplayer/hbbtvbrowserplugin/src/hbbtvbrowserpluginapi.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2010-2011 Telecom-Paristech + * All Rights Reserved + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * Authors: Stanislas Selle + * + */ +#ifndef hbbtvbrowserpluginapi_h +#define hbbtvbrowserpluginapi_h + +#include + +///Callbacks +void OnNoFullscreenSetWindow(int x, int y, int width, int height); +void OnAPPLICATION_Show(); +void OnAPPLICATION_Hide(); +void OnVIDBRC_SetFullScreen(int fullscreenparam); +void OnKEYSET_SetValue(double param); +void OnKEYSET_Allocate(); +#endif diff --git a/applications/hbbtvplayer/hbbtvbrowserplugin/src/keysetclass.c b/applications/hbbtvplayer/hbbtvbrowserplugin/src/keysetclass.c new file mode 100644 index 0000000..6e9a9d1 --- /dev/null +++ b/applications/hbbtvplayer/hbbtvbrowserplugin/src/keysetclass.c @@ -0,0 +1,330 @@ + +/* + * Copyright (c) 2010-2011 Telecom-Paristech + * All Rights Reserved + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * Authors: Stanislas Selle + * + */#include "keysetclass.h" + +#define kKEYSET_ID_PROPERTY_VALUE 0 +#define kKEYSET_ID_PROPERTY_MAXIMUMVALUE 1 +#define kKEYSET_ID_PROPERTY_RED 2 +#define kKEYSET_ID_PROPERTY_GREEN 3 +#define kKEYSET_ID_PROPERTY_YELLOW 4 +#define kKEYSET_ID_PROPERTY_BLUE 5 +#define kKEYSET_ID_PROPERTY_NAVIGATION 6 +#define kKEYSET_ID_PROPERTY_VCR 7 +#define kKEYSET_ID_PROPERTY_SCROLL 8 +#define kKEYSET_ID_PROPERTY_INFO 9 +#define kKEYSET_ID_PROPERTY_NUMERIC 10 +#define kKEYSET_ID_PROPERTY_ALPHA 11 +#define kKEYSET_ID_PROPERTY_OTHER 12 +#define kKEYSET_NUM_PROPERTY_IDENTIFIERS 13 + +#define kKEYSET_ID_METHOD_SETVALUE 0 +#define kKEYSET_NUM_METHOD_IDENTIFIERS 1 + + + +bool v_bKEYSETIdentifiersInitialized = false; + +NPIdentifier v_KEYSETPropertyIdentifiers[kKEYSET_NUM_PROPERTY_IDENTIFIERS]; +const NPUTF8 * v_KEYSETPropertyNames[kKEYSET_NUM_PROPERTY_IDENTIFIERS] = { + "value", + "maximumValue", + "RED", + "GREEN", + "YELLOW", + "BLUE", + "NAVIGATION", + "VCR", + "SCROLL", + "INFO", + "NUMERIC", + "ALPHA", + "OTHER" + }; + +NPIdentifier v_KEYSETMethodIdentifiers[kKEYSET_NUM_METHOD_IDENTIFIERS]; +const NPUTF8 * v_KEYSETMethodNames[kKEYSET_NUM_METHOD_IDENTIFIERS] = { + "setValue" + }; + +static void KEYSETinitializeIdentifiers(void) +{ + sBrowserFuncs->getstringidentifiers( v_KEYSETPropertyNames, kKEYSET_NUM_PROPERTY_IDENTIFIERS, v_KEYSETPropertyIdentifiers ); + sBrowserFuncs->getstringidentifiers( v_KEYSETMethodNames, kKEYSET_NUM_METHOD_IDENTIFIERS, v_KEYSETMethodIdentifiers ); +} + + +NPClass stKEYSETclass; +NPClass* pKEYSETclass = NULL; + +NPClass* fillKEYSETpclass(void) +{ + TRACEINFO; + if (pKEYSETclass == NULL) + { + stKEYSETclass.allocate = KEYSET_Allocate; + stKEYSETclass.deallocate = KEYSET_Deallocate; + stKEYSETclass.invalidate = KEYSET_Invalidate; + stKEYSETclass.hasMethod = KEYSET_HasMethod; + stKEYSETclass.invoke = KEYSET_Invoke; + stKEYSETclass.invokeDefault = KEYSET_InvokeDefault; + stKEYSETclass.hasProperty = KEYSET_HasProperty; + stKEYSETclass.getProperty = KEYSET_GetProperty; + stKEYSETclass.setProperty = KEYSET_SetProperty; + stKEYSETclass.removeProperty = KEYSET_RemoveProperty; + stKEYSETclass.enumerate = KEYSET_Enumerate; + pKEYSETclass = &stKEYSETclass; + } + + return pKEYSETclass; +} + + +NPObject * KEYSET_Allocate(NPP npp, NPClass *theClass) +{ + TRACEINFO; + + NPObject* result; + + + if (!v_bKEYSETIdentifiersInitialized) + { + v_bKEYSETIdentifiersInitialized = true; + KEYSETinitializeIdentifiers(); + } + + NPObj_KeySet* newkeyset = (NPObj_KeySet *)MEMALLOC(sizeof(NPObj_KeySet)); + newkeyset->npp = npp; + newkeyset->value = (double*)MEMALLOC(sizeof(double)); + newkeyset->maximumValue = (double*)MEMALLOC(sizeof(double)); + result = (NPObject*)newkeyset; + OnKEYSET_Allocate(); + return result; +} + + +void KEYSET_Deallocate(NPObject* obj) +{ + TRACEINFO; + NPObj_KeySet* keysetobj = (NPObj_KeySet*)obj; + MEMFREE(keysetobj->value); + MEMFREE(keysetobj->maximumValue); + MEMFREE(keysetobj); + return; +} + +void KEYSET_Invalidate(NPObject* obj) +{ + TRACEINFO; + return; +} + +bool KEYSET_HasMethod(NPObject* obj, NPIdentifier name) +{ + TRACEINFO; + bool result = false; + int i = 0; + NPUTF8* utf8methodname = (char*)sBrowserFuncs->utf8fromidentifier(name); + while ((i < kKEYSET_NUM_METHOD_IDENTIFIERS) && (result == false)) + { + if ( name == v_KEYSETMethodIdentifiers[i] ) + { + result= true; + } + i++; + } + + return result; +} + +bool KEYSET_Invoke(NPObject* obj, NPIdentifier name, const NPVariant* args, uint32_t argCount, NPVariant* result) +{ + TRACEINFO; + bool fctresult = false; + if (name == v_KEYSETMethodIdentifiers[kKEYSET_ID_METHOD_SETVALUE]) + { + KEYSET_Invoke_SetValue(obj, args, argCount, result); + fctresult = true; + } + else + { + fctresult = false; + } + return fctresult; +} + +bool KEYSET_InvokeDefault(NPObject *npobj, const NPVariant *args, uint32_t argCount, NPVariant *result) +{ + TRACEINFO; + return true; +} + +bool KEYSET_HasProperty(NPObject* obj, NPIdentifier name) +{ + TRACEINFO; + bool result = false; + NPUTF8* utf8propertyname = (char*)sBrowserFuncs->utf8fromidentifier(name); + + int i = 0; + while ((i < kKEYSET_NUM_PROPERTY_IDENTIFIERS) && (result == false)) + { + if ( name == v_KEYSETPropertyIdentifiers[i] ) + { + result= true; + } + i++; + } + + + return result; +} + +bool KEYSET_GetProperty(NPObject* obj, NPIdentifier name, NPVariant* result) +{ + TRACEINFO; + bool fctresult = false; + NPObj_KeySet* keysetobj = (NPObj_KeySet*)obj; + + double KEYMASK_RED = 0x1; + double KEYMASK_GREEN = 0x2; + double KEYMASK_YELLOW = 0x4; + double KEYMASK_BLUE = 0x8; + double KEYMASK_NAVIGATION = 0x10; + double KEYMASK_VCR = 0x20; + double KEYMASK_SCROLL = 0x40; + double KEYMASK_INFO = 0x80; + double KEYMASK_NUMERIC = 0x100; + double KEYMASK_ALPHA = 0x200; + double KEYMASK_OTHER = 0x400; + + if (name == v_KEYSETPropertyIdentifiers[kKEYSET_ID_PROPERTY_RED]) + { + DOUBLE_TO_NPVARIANT(KEYMASK_RED, *result); + fctresult = true; + } + else + if (name == v_KEYSETPropertyIdentifiers[kKEYSET_ID_PROPERTY_GREEN]) + { + DOUBLE_TO_NPVARIANT(KEYMASK_GREEN, *result); + fctresult = true; + } + if (name == v_KEYSETPropertyIdentifiers[kKEYSET_ID_PROPERTY_YELLOW]) + { + DOUBLE_TO_NPVARIANT(KEYMASK_YELLOW, *result); + fctresult = true; + } + if (name == v_KEYSETPropertyIdentifiers[kKEYSET_ID_PROPERTY_BLUE]) + { + DOUBLE_TO_NPVARIANT(KEYMASK_BLUE, *result); + fctresult = true; + } + if (name == v_KEYSETPropertyIdentifiers[kKEYSET_ID_PROPERTY_NAVIGATION]) + { + DOUBLE_TO_NPVARIANT(KEYMASK_NAVIGATION, *result); + fctresult = true; + } + if (name == v_KEYSETPropertyIdentifiers[kKEYSET_ID_PROPERTY_VCR]) + { + DOUBLE_TO_NPVARIANT(KEYMASK_VCR, *result); + fctresult = true; + } + if (name == v_KEYSETPropertyIdentifiers[kKEYSET_ID_PROPERTY_SCROLL]) + { + DOUBLE_TO_NPVARIANT(KEYMASK_SCROLL, *result); + fctresult = true; + } + if (name == v_KEYSETPropertyIdentifiers[kKEYSET_ID_PROPERTY_INFO]) + { + DOUBLE_TO_NPVARIANT(KEYMASK_INFO, *result); + fctresult = true; + } + if (name == v_KEYSETPropertyIdentifiers[kKEYSET_ID_PROPERTY_NUMERIC]) + { + DOUBLE_TO_NPVARIANT(KEYMASK_NUMERIC, *result); + fctresult = true; + } + if (name == v_KEYSETPropertyIdentifiers[kKEYSET_ID_PROPERTY_ALPHA]) + { + DOUBLE_TO_NPVARIANT(KEYMASK_ALPHA, *result); + fctresult = true; + } + if (name == v_KEYSETPropertyIdentifiers[kKEYSET_ID_PROPERTY_OTHER]) + { + DOUBLE_TO_NPVARIANT(KEYMASK_OTHER, *result); + fctresult = true; + } + if (name == v_KEYSETPropertyIdentifiers[kKEYSET_ID_PROPERTY_VALUE]) + { + double* value = keysetobj->value; + DOUBLE_TO_NPVARIANT(*value, *result); + fctresult = true; + } + + if (fctresult) + { + + } + + + + return fctresult; +} + +bool KEYSET_SetProperty(NPObject *obj, NPIdentifier name, const NPVariant *value) +{ + TRACEINFO; + bool fctresult = false; + + return fctresult; +} + +bool KEYSET_RemoveProperty(NPObject *npobj, NPIdentifier name) +{ + TRACEINFO; + return true; +} + + +bool KEYSET_Enumerate(NPObject *npobj, NPIdentifier **value, uint32_t *count) +{ + TRACEINFO; + return true; +} + + +/** implementation **/ +void KEYSET_Invoke_SetValue(NPObject* obj,const NPVariant* args, uint32_t argCount, NPVariant *result) +{ + TRACEINFO; + //~ NOTIMPLEMENTED; // in progress + double param; + NPObj_KeySet* keysetobj = (NPObj_KeySet*)obj; + double* value = keysetobj->value; + fprintf(stderr,"nb args : %i, %f", argCount,NPVARIANT_TO_DOUBLE(args[0]) ); + if (!NPVARIANT_IS_DOUBLE(args[0])) { + fprintf(stderr,"\t%s : error\n",__FUNCTION__); + return; + } + else { + param = NPVARIANT_TO_DOUBLE( args[0] ); + fprintf(stderr, "\t%s : param to transmit %f \n", __FUNCTION__, param); + *value = param; + OnKEYSET_SetValue(param); + } +} diff --git a/applications/hbbtvplayer/hbbtvbrowserplugin/src/keysetclass.h b/applications/hbbtvplayer/hbbtvbrowserplugin/src/keysetclass.h new file mode 100644 index 0000000..5448d64 --- /dev/null +++ b/applications/hbbtvplayer/hbbtvbrowserplugin/src/keysetclass.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2010-2011 Telecom-Paristech + * All Rights Reserved + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * Authors: Stanislas Selle + * + */ +#ifndef __HBBTVKEYSET_H__ +#define __HBBTVKEYSET_H__ + +#include "hbbtvbrowserplugin.h" + +typedef struct +{ + NPObject header; + NPP npp; + + double* value; + double* maximumValue; +} NPObj_KeySet; + +NPClass* fillKEYSETpclass(void); + +NPObject * KEYSET_Allocate(NPP npp, NPClass *aClass); +void KEYSET_Deallocate(NPObject *obj); +void KEYSET_Invalidate(NPObject *obj); +bool KEYSET_HasMethod(NPObject *obj, NPIdentifier name); +bool KEYSET_Invoke(NPObject *obj, NPIdentifier name, const NPVariant *args, uint32_t argCount, NPVariant *result); +bool KEYSET_InvokeDefault(NPObject *npobj, const NPVariant *args, uint32_t argCount, NPVariant *result); +bool KEYSET_HasProperty(NPObject *obj, NPIdentifier name); +bool KEYSET_GetProperty(NPObject *obj, NPIdentifier name, NPVariant *result); +bool KEYSET_SetProperty(NPObject *obj, NPIdentifier name, const NPVariant *value); +bool KEYSET_RemoveProperty(NPObject *npobj, NPIdentifier name); +bool KEYSET_Enumerate(NPObject *npobj, NPIdentifier **value, uint32_t *count); + + +void KEYSET_Invoke_SetValue(NPObject* obj,const NPVariant* args, uint32_t argCount, NPVariant *result); + +#endif diff --git a/applications/hbbtvplayer/hbbtvbrowserplugin/src/makefile.am b/applications/hbbtvplayer/hbbtvbrowserplugin/src/makefile.am new file mode 100644 index 0000000..59c0d4c --- /dev/null +++ b/applications/hbbtvplayer/hbbtvbrowserplugin/src/makefile.am @@ -0,0 +1,47 @@ + +lib_LTLIBRARIES = libhbbtvbrowserplugin.la + +libhbbtvbrowserplugin_la_SOURCES = hbbtvbrowserplugin.c\ + hbbtvbrowserpluginapi.c\ + oipfapplicationmanager.c\ + applicationclass.c\ + applicationprivatedataclass.c\ + oipfconfiguration.c\ + configurationclass.c\ + oipfdownloadmanager.c\ + oipfdownloadtrigger.c\ + downloadclass.c\ + downloadcollectionclass.c\ + drmcontrolinfocollectionclass.c\ + drmcontrolinformationclass.c\ + videobroadcast.c\ + keysetclass.c\ + hbbtvbrowserplugin.h\ + hbbtvbrowserpluginapi.h\ + oipfapplicationmanager.h\ + applicationclass.h\ + applicationprivatedataclass.h\ + oipfconfiguration.h\ + configurationclass.h\ + oipfdownloadmanager.h\ + oipfdownloadtrigger.h\ + downloadclass.h\ + downloadcollectionclass.h\ + drmcontrolinfocollectionclass.h\ + drmcontrolinformationclass.h\ + videobroadcast.h\ + keysetclass.h + +library_includedir=$(includedir)/@PACKAGE_NAME@ +library_include_HEADERS = hbbtvbrowserpluginapi.h + +SPECIALCFLAGS = -g -Wall -DXP_UNIX=1 -DMOZ_X11=1 -fPIC +libhbbtvbrowserplugin_la_CFLAGS = $(GLIB_CFLAGS) $(GTK_CFLAGS) $(NPAPI_CFLAGS) $(SPECIALCFLAGS) -I./webkit-plugin-header/ -I./src/webkit-plugin-header/ +libhbbtvbrowserplugin_la_LDFLAGS = $(GLIB_LIBS) $(GTK_LIBS) $(NPAPI_LIBS) $(SPECIALCFLAGS) +libhbbtvbrowserplugin_la_LIBADD = $(GLIB_LIBS) $(GTK_LIBS) $(NPAPI_LIBS) + +install-exec-local : + ln -fs $(libdir)/lib@PACKAGE_NAME@.so.0.0.0 $(PLUGINDIR)/lib@PACKAGE_NAME@.so + +uninstall-local : + rm -f $(PLUGINDIR)/lib@PACKAGE_NAME@.so diff --git a/applications/hbbtvplayer/hbbtvbrowserplugin/src/oipfapplicationmanager.c b/applications/hbbtvplayer/hbbtvbrowserplugin/src/oipfapplicationmanager.c new file mode 100644 index 0000000..5adebaf --- /dev/null +++ b/applications/hbbtvplayer/hbbtvbrowserplugin/src/oipfapplicationmanager.c @@ -0,0 +1,203 @@ +/* + * Copyright (c) 2010-2011 Telecom-Paristech + * All Rights Reserved + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * Authors: Stanislas Selle + * + */ +#include "oipfapplicationmanager.h" +#include "applicationclass.h" + +#define kOAM_ID_PROPERTY_ON_LOW_MEMORY 0 +#define kOAM_NUM_PROPERTY_IDENTIFIERS 1 + +#define kOAM_ID_METHOD_GET_OWNER_APPLICATION 0 +#define kOAM_NUM_METHOD_IDENTIFIERS 1 + +bool v_bOAMIdentifiersInitialized = false; + +NPIdentifier v_OAMPropertyIdentifiers[kOAM_NUM_PROPERTY_IDENTIFIERS]; +const NPUTF8 * v_OAMPropertyNames[kOAM_NUM_PROPERTY_IDENTIFIERS] = { + "onLowMemory" + }; + +NPIdentifier v_OAMMethodIdentifiers[kOAM_NUM_METHOD_IDENTIFIERS]; +const NPUTF8 * v_OAMMethodNames[kOAM_NUM_METHOD_IDENTIFIERS] = { + "getOwnerApplication" +}; + + + + +static void OAMinitializeIdentifiers(void) +{ + sBrowserFuncs->getstringidentifiers( v_OAMPropertyNames, kOAM_NUM_PROPERTY_IDENTIFIERS, v_OAMPropertyIdentifiers ); + sBrowserFuncs->getstringidentifiers( v_OAMMethodNames, kOAM_NUM_METHOD_IDENTIFIERS, v_OAMMethodIdentifiers ); +} + +NPClass stOAMclass; +NPClass* pOAMclass = NULL; + +NPClass* fillOAMpclass(void) +{ + TRACEINFO; + if (pOAMclass == NULL) + { + stOAMclass.allocate = OAM_Allocate; + stOAMclass.deallocate = OAM_Deallocate; + stOAMclass.invalidate = OAM_Invalidate; + stOAMclass.hasMethod = OAM_HasMethod; + stOAMclass.invoke = OAM_Invoke; + stOAMclass.invokeDefault = OAM_InvokeDefault; + stOAMclass.hasProperty = OAM_HasProperty; + stOAMclass.getProperty = OAM_GetProperty; + stOAMclass.setProperty = OAM_SetProperty; + stOAMclass.removeProperty = OAM_RemoveProperty; + stOAMclass.enumerate = OAM_Enumerate; + pOAMclass = &stOAMclass; + } + + return pOAMclass; +} + + +NPObject * OAM_Allocate(NPP npp, NPClass *theClass) +{ + TRACEINFO; + + + if (!v_bOAMIdentifiersInitialized) + { + v_bOAMIdentifiersInitialized = true; + OAMinitializeIdentifiers(); + } + + NPObj_OAM* newapplicationmanager = (NPObj_OAM*)MEMALLOC(sizeof(NPObj_OAM)); + fprintf(stderr, "\t%s : Allocation at \x1b[%i;3%im%p\n\x1b[0m ",__FUNCTION__, 1, 1, newapplicationmanager ); + newapplicationmanager->npp = npp; + newapplicationmanager->ownerApplication = sBrowserFuncs->createobject(npp, fillAPPLICATIONpclass()); + fprintf(stderr, "\t%s : Create ownerApplication member at \x1b[%i;3%im%p\n\x1b[0m ",__FUNCTION__, 1, 1, newapplicationmanager->ownerApplication ); + return (NPObject *)newapplicationmanager; +} + + void OAM_Deallocate(NPObject* obj) +{ + TRACEINFO; + NPObj_OAM* oamobj = (NPObj_OAM*)obj; + sBrowserFuncs->releaseobject(oamobj->ownerApplication); + MEMFREE(oamobj); + return; +} + + void OAM_Invalidate(NPObject* obj) +{ + TRACEINFO; + return; +} + + bool OAM_HasMethod(NPObject* obj, NPIdentifier name) +{ + TRACEINFO; + + bool result = false; + int i = 0; + NPUTF8* utf8methodname = (char*)sBrowserFuncs->utf8fromidentifier(name); + while ((i < kOAM_NUM_METHOD_IDENTIFIERS) && (result == false)) + { + if ( name == v_OAMMethodIdentifiers[i] ) + { + result= true; + } + i++; + } + + return result; +} + +bool OAM_Invoke(NPObject* obj, NPIdentifier name, const NPVariant* args, uint32_t argCount, NPVariant* result) +{ + TRACEINFO; + //OAM_ObjectMain * pMainObj = reinterpret_cast(obj); + bool fctresult = false; + if (name == v_OAMMethodIdentifiers[kOAM_ID_METHOD_GET_OWNER_APPLICATION]) + { + OAM_ObjectMain_Invoke_GetOwnerApplication((NPObj_OAM*)obj, args, argCount, result); + fctresult = true; + } + else + { + + fctresult = false; + } + return fctresult; +} + + bool OAM_InvokeDefault(NPObject *npobj, const NPVariant *args, uint32_t argCount, NPVariant *result) +{ + TRACEINFO; + return true; +} + + bool OAM_HasProperty(NPObject* obj, NPIdentifier name) +{ + TRACEINFO; + bool result = false; + NPUTF8* utf8propertyname = (char*)sBrowserFuncs->utf8fromidentifier(name); + + int i = 0; + while ((i < kOAM_NUM_PROPERTY_IDENTIFIERS) && (result == false)) + { + if ( name == v_OAMPropertyIdentifiers[i] ) + { + result= true; + } + i++; + } + + return result; +} + +bool OAM_GetProperty(NPObject* obj, NPIdentifier name, NPVariant* result) +{ + TRACEINFO; + return true; +} + + bool OAM_SetProperty(NPObject *obj, NPIdentifier name, const NPVariant *value) +{ + TRACEINFO; + return true; +} + + bool OAM_RemoveProperty(NPObject *npobj, NPIdentifier name) +{ + TRACEINFO; + return true; +} + + + bool OAM_Enumerate(NPObject *npobj, NPIdentifier **value, uint32_t *count) +{ + TRACEINFO; + return true; +} + +/** implementation **/ +void OAM_ObjectMain_Invoke_GetOwnerApplication(NPObj_OAM* obj,const NPVariant* args, uint32_t argCount, NPVariant* result) +{ + TRACEINFO; + sBrowserFuncs->retainobject(obj->ownerApplication); + OBJECT_TO_NPVARIANT((NPObject*)(obj->ownerApplication), *result); +} diff --git a/applications/hbbtvplayer/hbbtvbrowserplugin/src/oipfapplicationmanager.h b/applications/hbbtvplayer/hbbtvbrowserplugin/src/oipfapplicationmanager.h new file mode 100644 index 0000000..4334fe4 --- /dev/null +++ b/applications/hbbtvplayer/hbbtvbrowserplugin/src/oipfapplicationmanager.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2010-2011 Telecom-Paristech + * All Rights Reserved + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * Authors: Stanislas Selle + * + */ +#ifndef __OIPFAPPLICATIONMANAGER_H__ +#define __OIPFAPPLICATIONMANAGER_H__ + +#include "hbbtvbrowserplugin.h" + +typedef struct { + NPObject header; + NPP npp; + + NPObject* ownerApplication; +} NPObj_OAM; + +NPClass* fillOAMpclass(void); + +NPObject * OAM_Allocate(NPP npp, NPClass *aClass); +void OAM_Deallocate(NPObject *obj); +void OAM_Invalidate(NPObject *obj); +bool OAM_HasMethod(NPObject *obj, NPIdentifier name); +bool OAM_Invoke(NPObject *obj, NPIdentifier name, const NPVariant *args, uint32_t argCount, NPVariant *result); +bool OAM_InvokeDefault(NPObject *npobj, const NPVariant *args, uint32_t argCount, NPVariant *result); +bool OAM_HasProperty(NPObject *obj, NPIdentifier name); +bool OAM_GetProperty(NPObject *obj, NPIdentifier name, NPVariant *result); +bool OAM_SetProperty(NPObject *obj, NPIdentifier name, const NPVariant *value); +bool OAM_RemoveProperty(NPObject *npobj, NPIdentifier name); +bool OAM_Enumerate(NPObject *npobj, NPIdentifier **value, uint32_t *count); + +void OAM_ObjectMain_Invoke_GetOwnerApplication(NPObj_OAM* obj,const NPVariant* args, uint32_t argCount, NPVariant* result); + +/* +typedef struct{ + NPObject headear; + NPP npp; + NPObject* onLowMemory; + NPObject* ownerApplication; + NPObject* newApplication; +} OAM_ObjectMain; +*/ + + +#endif diff --git a/applications/hbbtvplayer/hbbtvbrowserplugin/src/oipfconfiguration.c b/applications/hbbtvplayer/hbbtvbrowserplugin/src/oipfconfiguration.c new file mode 100644 index 0000000..cb20c44 --- /dev/null +++ b/applications/hbbtvplayer/hbbtvbrowserplugin/src/oipfconfiguration.c @@ -0,0 +1,182 @@ +/* + * Copyright (c) 2010-2011 Telecom-Paristech + * All Rights Reserved + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * Authors: Stanislas Selle + * + */ +#include "oipfconfiguration.h" + +#define kOCFG_ID_PROPERTY_CONFIGURATION 0 +#define kOCFG_NUM_PROPERTY_IDENTIFIERS 1 + +#define kOCFG_NUM_METHOD_IDENTIFIERS 0 + +bool v_bOCFGIdentifiersInitialized = false; + +NPIdentifier v_OCFGPropertyIdentifiers[kOCFG_NUM_PROPERTY_IDENTIFIERS]; +const NPUTF8 * v_OCFGPropertyNames[kOCFG_NUM_PROPERTY_IDENTIFIERS] = { + "configuration" + }; + +NPIdentifier v_OCFGMethodIdentifiers[kOCFG_NUM_METHOD_IDENTIFIERS]; +const NPUTF8 * v_OCFGMethodNames[kOCFG_NUM_METHOD_IDENTIFIERS] = {}; + +static void OCFGinitializeIdentifiers(void) +{ + sBrowserFuncs->getstringidentifiers( v_OCFGPropertyNames, kOCFG_NUM_PROPERTY_IDENTIFIERS, v_OCFGPropertyIdentifiers ); + sBrowserFuncs->getstringidentifiers( v_OCFGMethodNames, kOCFG_NUM_METHOD_IDENTIFIERS, v_OCFGMethodIdentifiers ); +} + + +NPClass stOCFGclass; +NPClass* pOCFGclass = NULL; + +NPClass* fillOCFGpclass(void) +{ + TRACEINFO; + if (pOCFGclass == NULL) + { + stOCFGclass.allocate = OCFG_Allocate; + stOCFGclass.deallocate = OCFG_Deallocate; + stOCFGclass.invalidate = OCFG_Invalidate; + stOCFGclass.hasMethod = OCFG_HasMethod; + stOCFGclass.invoke = OCFG_Invoke; + stOCFGclass.invokeDefault = OCFG_InvokeDefault; + stOCFGclass.hasProperty = OCFG_HasProperty; + stOCFGclass.getProperty = OCFG_GetProperty; + stOCFGclass.setProperty = OCFG_SetProperty; + stOCFGclass.removeProperty = OCFG_RemoveProperty; + stOCFGclass.enumerate = OCFG_Enumerate; + pOCFGclass = &stOCFGclass; + } + + return pOCFGclass; +} + + +NPObject * OCFG_Allocate(NPP npp, NPClass *theClass) +{ + TRACEINFO; + + NPObject* result; + + + if (!v_bOCFGIdentifiersInitialized) + { + v_bOCFGIdentifiersInitialized = true; + OCFGinitializeIdentifiers(); + } + + NPObject* newocfg = (NPObject*)MEMALLOC(sizeof(NPObject)); + result = newocfg; + + return result; +} + + +void OCFG_Deallocate(NPObject* obj) +{ + TRACEINFO; + MEMFREE(obj); + return; +} + +void OCFG_Invalidate(NPObject* obj) +{ + TRACEINFO; + return; +} + +bool OCFG_HasMethod(NPObject* obj, NPIdentifier name) +{ + TRACEINFO; + bool result = false; + int i = 0; + NPUTF8* utf8methodname = (char*)sBrowserFuncs->utf8fromidentifier(name); + while ((i < kOCFG_NUM_METHOD_IDENTIFIERS) && (result == false)) + { + if ( name == v_OCFGMethodIdentifiers[i] ) + { + result= true; + } + i++; + } + + return result; +} + +bool OCFG_Invoke(NPObject* obj, NPIdentifier name, const NPVariant* args, uint32_t argCount, NPVariant* result) +{ + TRACEINFO; + + bool fctresult; + + fctresult = false; + + return fctresult; + +} + +bool OCFG_InvokeDefault(NPObject *npobj, const NPVariant *args, uint32_t argCount, NPVariant *result) +{ + TRACEINFO; + return true; +} + +bool OCFG_HasProperty(NPObject* obj, NPIdentifier name) +{ + TRACEINFO; + bool result = false; + NPUTF8* utf8propertyname = (char*)sBrowserFuncs->utf8fromidentifier(name); + + int i = 0; + while ((i < kOCFG_NUM_PROPERTY_IDENTIFIERS) && (result == false)) + { + if ( name == v_OCFGPropertyIdentifiers[i] ) + { + result= true; + } + i++; + } + + return result; +} + +bool OCFG_GetProperty(NPObject* obj, NPIdentifier name, NPVariant* result) +{ + TRACEINFO; + return true; +} + +bool OCFG_SetProperty(NPObject *obj, NPIdentifier name, const NPVariant *value) +{ + TRACEINFO; + return true; +} + +bool OCFG_RemoveProperty(NPObject *npobj, NPIdentifier name) +{ + TRACEINFO; + return true; +} + + +bool OCFG_Enumerate(NPObject *npobj, NPIdentifier **value, uint32_t *count) +{ + TRACEINFO; + return true; +} + diff --git a/applications/hbbtvplayer/hbbtvbrowserplugin/src/oipfconfiguration.h b/applications/hbbtvplayer/hbbtvbrowserplugin/src/oipfconfiguration.h new file mode 100644 index 0000000..25f8cf3 --- /dev/null +++ b/applications/hbbtvplayer/hbbtvbrowserplugin/src/oipfconfiguration.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2010-2011 Telecom-Paristech + * All Rights Reserved + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * Authors: Stanislas Selle + * + */ +#ifndef __OIPFCONFIGURATION_H__ +#define __OIPFCONFIGURATION_H__ + +#include "hbbtvbrowserplugin.h" + +NPClass* fillOCFGpclass(void); + +NPObject * OCFG_Allocate(NPP npp, NPClass *aClass); +void OCFG_Deallocate(NPObject *obj); +void OCFG_Invalidate(NPObject *obj); +bool OCFG_HasMethod(NPObject *obj, NPIdentifier name); +bool OCFG_Invoke(NPObject *obj, NPIdentifier name, const NPVariant *args, uint32_t argCount, NPVariant *result); +bool OCFG_InvokeDefault(NPObject *npobj, const NPVariant *args, uint32_t argCount, NPVariant *result); +bool OCFG_HasProperty(NPObject *obj, NPIdentifier name); +bool OCFG_GetProperty(NPObject *obj, NPIdentifier name, NPVariant *result); +bool OCFG_SetProperty(NPObject *obj, NPIdentifier name, const NPVariant *value); + +bool OCFG_RemoveProperty(NPObject *npobj, NPIdentifier name); + +bool OCFG_Enumerate(NPObject *npobj, NPIdentifier **value, uint32_t *count); + + +#endif diff --git a/applications/hbbtvplayer/hbbtvbrowserplugin/src/oipfdownloadmanager.c b/applications/hbbtvplayer/hbbtvbrowserplugin/src/oipfdownloadmanager.c new file mode 100644 index 0000000..cfe9e0f --- /dev/null +++ b/applications/hbbtvplayer/hbbtvbrowserplugin/src/oipfdownloadmanager.c @@ -0,0 +1,233 @@ +/* + * Copyright (c) 2010-2011 Telecom-Paristech + * All Rights Reserved + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * Authors: Stanislas Selle + * + */ +#include "oipfdownloadmanager.h" + +#define kODWLDMAN_ID_PROPERTY_ONDOWNLOADSTATECHANGE 0 +#define kODWLDMAN_NUM_PROPERTY_IDENTIFIERS 1 + +#define kODWLDMAN_ID_METHOD_PAUSE 0 +#define kODWLDMAN_ID_METHOD_RESUME 1 +#define kODWLDMAN_ID_METHOD_REMOVE 2 +#define kODWLDMAN_ID_METHOD_GETDOWNLOADS 3 +#define kODWLDMAN_NUM_METHOD_IDENTIFIERS 4 + +bool v_bODWLDMANIdentifiersInitialized = false; + +NPIdentifier v_ODWLDMANPropertyIdentifiers[kODWLDMAN_NUM_PROPERTY_IDENTIFIERS]; +const NPUTF8 * v_ODWLDMANPropertyNames[kODWLDMAN_NUM_PROPERTY_IDENTIFIERS] = { + "onDownloadStateChange" + }; + +NPIdentifier v_ODWLDMANMethodIdentifiers[kODWLDMAN_NUM_METHOD_IDENTIFIERS]; +const NPUTF8 * v_ODWLDMANMethodNames[kODWLDMAN_NUM_METHOD_IDENTIFIERS] = { + "pause", + "resume", + "remove", + "getDownloads" +}; + + +static void ODWLDMANinitializeIdentifiers(void) +{ + sBrowserFuncs->getstringidentifiers( v_ODWLDMANPropertyNames, kODWLDMAN_NUM_PROPERTY_IDENTIFIERS, v_ODWLDMANPropertyIdentifiers ); + sBrowserFuncs->getstringidentifiers( v_ODWLDMANMethodNames, kODWLDMAN_NUM_METHOD_IDENTIFIERS, v_ODWLDMANMethodIdentifiers ); +} + +NPClass stODWLDMANclass; +NPClass* pODWLDMANclass = NULL; + +NPClass* fillODWLDMANpclass(void) +{ + TRACEINFO; + if (pODWLDMANclass == NULL) + { + stODWLDMANclass.allocate = ODWLDMAN_Allocate; + stODWLDMANclass.deallocate = ODWLDMAN_Deallocate; + stODWLDMANclass.invalidate = ODWLDMAN_Invalidate; + stODWLDMANclass.hasMethod = ODWLDMAN_HasMethod; + stODWLDMANclass.invoke = ODWLDMAN_Invoke; + stODWLDMANclass.invokeDefault = ODWLDMAN_InvokeDefault; + stODWLDMANclass.hasProperty = ODWLDMAN_HasProperty; + stODWLDMANclass.getProperty = ODWLDMAN_GetProperty; + stODWLDMANclass.setProperty = ODWLDMAN_SetProperty; + stODWLDMANclass.removeProperty = ODWLDMAN_RemoveProperty; + stODWLDMANclass.enumerate = ODWLDMAN_Enumerate; + pODWLDMANclass = &stODWLDMANclass; + } + + return pODWLDMANclass; +} + + +NPObject * ODWLDMAN_Allocate(NPP npp, NPClass *theClass) +{ + TRACEINFO; + + NPObject* newdwldman = NULL; + if (!v_bODWLDMANIdentifiersInitialized) + { + v_bODWLDMANIdentifiersInitialized = true; + ODWLDMANinitializeIdentifiers(); + } + + newdwldman = (NPObject*)MEMALLOC(sizeof(NPObject)); + + return newdwldman; +} + + void ODWLDMAN_Deallocate(NPObject* obj) +{ + TRACEINFO; + MEMFREE(obj); + return; +} + + void ODWLDMAN_Invalidate(NPObject* obj) +{ + TRACEINFO; + return; +} + + bool ODWLDMAN_HasMethod(NPObject* obj, NPIdentifier name) +{ + TRACEINFO; + + bool result = false; + int i = 0; + NPUTF8* utf8methodname = (char*)sBrowserFuncs->utf8fromidentifier(name); + while ((i < kODWLDMAN_NUM_METHOD_IDENTIFIERS) && (result == false)) + { + if ( name == v_ODWLDMANMethodIdentifiers[i] ) + { + result= true; + } + i++; + } + + return result; +} + +bool ODWLDMAN_Invoke(NPObject* obj, NPIdentifier name, const NPVariant* args, uint32_t argCount, NPVariant* result) +{ + TRACEINFO; + bool fctresult = false; + if (name == v_ODWLDMANMethodIdentifiers[kODWLDMAN_ID_METHOD_GETDOWNLOADS]) + { + ODWLDMAN_Invoke_GetDownloads(obj, args, argCount); + fctresult = true; + } + else if (name == v_ODWLDMANMethodIdentifiers[kODWLDMAN_ID_METHOD_PAUSE]) + { + ODWLDMAN_Invoke_Pause(obj, args, argCount); + fctresult = true; + } + else if (name == v_ODWLDMANMethodIdentifiers[kODWLDMAN_ID_METHOD_REMOVE]) + { + ODWLDMAN_Invoke_Remove(obj, args, argCount); + fctresult = true; + } + else if (name == v_ODWLDMANMethodIdentifiers[kODWLDMAN_ID_METHOD_RESUME]) + { + ODWLDMAN_Invoke_Resume(obj, args, argCount); + fctresult = true; + } + else + { + fctresult = false; + } + return fctresult; +} + + bool ODWLDMAN_InvokeDefault(NPObject *npobj, const NPVariant *args, uint32_t argCount, NPVariant *result) +{ + TRACEINFO; + return true; +} + + bool ODWLDMAN_HasProperty(NPObject* obj, NPIdentifier name) +{ + TRACEINFO; + bool result = false; + NPUTF8* utf8propertyname = (char*)sBrowserFuncs->utf8fromidentifier(name); + + int i = 0; + while ((i < kODWLDMAN_NUM_PROPERTY_IDENTIFIERS) && (result == false)) + { + if ( name == v_ODWLDMANPropertyIdentifiers[i] ) + { + result= true; + } + i++; + } + + return result; +} + + bool ODWLDMAN_GetProperty(NPObject* obj, NPIdentifier name, NPVariant* result) +{ + TRACEINFO; + return true; +} + + bool ODWLDMAN_SetProperty(NPObject *obj, NPIdentifier name, const NPVariant *value) +{ + TRACEINFO; + return true; +} + + bool ODWLDMAN_RemoveProperty(NPObject *npobj, NPIdentifier name) +{ + TRACEINFO; + return true; +} + + + bool ODWLDMAN_Enumerate(NPObject *npobj, NPIdentifier **value, uint32_t *count) +{ + TRACEINFO; + return true; +} + +/** implementation */ + + +void ODWLDMAN_Invoke_GetDownloads(NPObject* obj,const NPVariant* args, uint32_t argCount) +{ + TRACEINFO; + NOTIMPLEMENTED; +} + +void ODWLDMAN_Invoke_Pause(NPObject* obj,const NPVariant* args, uint32_t argCount) +{ + TRACEINFO; + NOTIMPLEMENTED; +} + +void ODWLDMAN_Invoke_Remove(NPObject* obj,const NPVariant* args, uint32_t argCount) +{ + TRACEINFO; + NOTIMPLEMENTED; +} + +void ODWLDMAN_Invoke_Resume(NPObject* obj,const NPVariant* args, uint32_t argCount) +{ + TRACEINFO; + NOTIMPLEMENTED; +} diff --git a/applications/hbbtvplayer/hbbtvbrowserplugin/src/oipfdownloadmanager.h b/applications/hbbtvplayer/hbbtvbrowserplugin/src/oipfdownloadmanager.h new file mode 100644 index 0000000..e9c3226 --- /dev/null +++ b/applications/hbbtvplayer/hbbtvbrowserplugin/src/oipfdownloadmanager.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2010-2011 Telecom-Paristech + * All Rights Reserved + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * Authors: Stanislas Selle + * + */ +#ifndef __OIPFDOWNLOADMANAGER_H__ +#define __OIPFDOWNLOADMANAGER_H__ + +#include "hbbtvbrowserplugin.h" + + +NPClass* fillODWLDMANpclass(void); + +NPObject * ODWLDMAN_Allocate(NPP npp, NPClass *aClass); +void ODWLDMAN_Deallocate(NPObject *obj); +void ODWLDMAN_Invalidate(NPObject *obj); +bool ODWLDMAN_HasMethod(NPObject *obj, NPIdentifier name); +bool ODWLDMAN_Invoke(NPObject *obj, NPIdentifier name, const NPVariant *args, uint32_t argCount, NPVariant *result); +bool ODWLDMAN_InvokeDefault(NPObject *npobj, const NPVariant *args, uint32_t argCount, NPVariant *result); +bool ODWLDMAN_HasProperty(NPObject *obj, NPIdentifier name); +bool ODWLDMAN_GetProperty(NPObject *obj, NPIdentifier name, NPVariant *result); +bool ODWLDMAN_SetProperty(NPObject *obj, NPIdentifier name, const NPVariant *value); + +bool ODWLDMAN_RemoveProperty(NPObject *npobj, NPIdentifier name); + +bool ODWLDMAN_Enumerate(NPObject *npobj, NPIdentifier **value, uint32_t *count); + + +void ODWLDMAN_Invoke_GetDownloads(NPObject* obj,const NPVariant* args, uint32_t argCount); +void ODWLDMAN_Invoke_Pause(NPObject* obj,const NPVariant* args, uint32_t argCount); +void ODWLDMAN_Invoke_Remove(NPObject* obj,const NPVariant* args, uint32_t argCount); +void ODWLDMAN_Invoke_Resume(NPObject* obj,const NPVariant* args, uint32_t argCount); + +#endif diff --git a/applications/hbbtvplayer/hbbtvbrowserplugin/src/oipfdownloadtrigger.c b/applications/hbbtvplayer/hbbtvbrowserplugin/src/oipfdownloadtrigger.c new file mode 100644 index 0000000..ae17f4b --- /dev/null +++ b/applications/hbbtvplayer/hbbtvbrowserplugin/src/oipfdownloadtrigger.c @@ -0,0 +1,205 @@ +/* + * Copyright (c) 2010-2011 Telecom-Paristech + * All Rights Reserved + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Authors: Stanislas Selle + * + */ +#include "oipfdownloadtrigger.h" + + +#define kODWLDTRG_NUM_PROPERTY_IDENTIFIERS 0 + +#define kODWLDTRG_ID_METHOD_REGISTER_DOWNLOAD 0 +#define kODWLDTRG_ID_METHOD_REGISTER_DOWNLOADURL 1 +#define kODWLDTRG_NUM_METHOD_IDENTIFIERS 2 + +bool v_bODWLDTRGIdentifiersInitialized = false; + +NPIdentifier v_ODWLDTRGPropertyIdentifiers[kODWLDTRG_NUM_PROPERTY_IDENTIFIERS]; +const NPUTF8 * v_ODWLDTRGPropertyNames[kODWLDTRG_NUM_PROPERTY_IDENTIFIERS] = { +}; + +NPIdentifier v_ODWLDTRGMethodIdentifiers[kODWLDTRG_NUM_METHOD_IDENTIFIERS]; +const NPUTF8 * v_ODWLDTRGMethodNames[kODWLDTRG_NUM_METHOD_IDENTIFIERS] = { + "registerDownload", + "registerDownloadURL" +}; + + + +static void ODWLDTRGinitializeIdentifiers(void) +{ + sBrowserFuncs->getstringidentifiers( v_ODWLDTRGPropertyNames, kODWLDTRG_NUM_PROPERTY_IDENTIFIERS, v_ODWLDTRGPropertyIdentifiers ); + sBrowserFuncs->getstringidentifiers( v_ODWLDTRGMethodNames, kODWLDTRG_NUM_METHOD_IDENTIFIERS, v_ODWLDTRGMethodIdentifiers ); +} + +NPClass stODWLDTRGclass; +NPClass* pODWLDTRGclass = NULL; + +NPClass* fillODWLDTRGpclass(void) +{ + TRACEINFO; + if (pODWLDTRGclass == NULL) + { + stODWLDTRGclass.allocate = ODWLDTRG_Allocate; + stODWLDTRGclass.deallocate = ODWLDTRG_Deallocate; + stODWLDTRGclass.invalidate = ODWLDTRG_Invalidate; + stODWLDTRGclass.hasMethod = ODWLDTRG_HasMethod; + stODWLDTRGclass.invoke = ODWLDTRG_Invoke; + stODWLDTRGclass.invokeDefault = ODWLDTRG_InvokeDefault; + stODWLDTRGclass.hasProperty = ODWLDTRG_HasProperty; + stODWLDTRGclass.getProperty = ODWLDTRG_GetProperty; + stODWLDTRGclass.setProperty = ODWLDTRG_SetProperty; + stODWLDTRGclass.removeProperty = ODWLDTRG_RemoveProperty; + stODWLDTRGclass.enumerate = ODWLDTRG_Enumerate; + pODWLDTRGclass = &stODWLDTRGclass; + } + + return pODWLDTRGclass; +} + + +NPObject * ODWLDTRG_Allocate(NPP npp, NPClass *theClass) +{ + TRACEINFO; + + NPObject* newdwldtrg = NULL; + if (!v_bODWLDTRGIdentifiersInitialized) + { + v_bODWLDTRGIdentifiersInitialized = true; + ODWLDTRGinitializeIdentifiers(); + } + + newdwldtrg = (NPObject*)MEMALLOC(sizeof(NPObject)); + + return newdwldtrg; +} + + void ODWLDTRG_Deallocate(NPObject* obj) +{ + TRACEINFO; + MEMFREE(obj); + return; +} + + void ODWLDTRG_Invalidate(NPObject* obj) +{ + TRACEINFO; + return; +} + + bool ODWLDTRG_HasMethod(NPObject* obj, NPIdentifier name) +{ + TRACEINFO; + + bool result = false; + int i = 0; + NPUTF8* utf8methodname = (char*)sBrowserFuncs->utf8fromidentifier(name); + while ((i < kODWLDTRG_NUM_METHOD_IDENTIFIERS) && (result == false)) + { + if ( name == v_ODWLDTRGMethodIdentifiers[i] ) + { + result= true; + } + i++; + } + + return result; +} + +bool ODWLDTRG_Invoke(NPObject* obj, NPIdentifier name, const NPVariant* args, uint32_t argCount, NPVariant* result) +{ + TRACEINFO; + bool fctresult = false; + if (name == v_ODWLDTRGMethodIdentifiers[kODWLDTRG_ID_METHOD_REGISTER_DOWNLOAD]) + { + ODWLDTRG_Invoke_registerDownload(obj, args, argCount); + fctresult = true; + } + else if (name == v_ODWLDTRGMethodIdentifiers[kODWLDTRG_ID_METHOD_REGISTER_DOWNLOADURL]) + { + ODWLDTRG_Invoke_registerDownloadURL(obj, args, argCount); + fctresult = true; + } + else + { + fctresult = false; + } + return fctresult; +} + + bool ODWLDTRG_InvokeDefault(NPObject *npobj, const NPVariant *args, uint32_t argCount, NPVariant *result) +{ + TRACEINFO; + return true; +} + + bool ODWLDTRG_HasProperty(NPObject* obj, NPIdentifier name) +{ + TRACEINFO; + bool result = false; + NPUTF8* utf8propertyname = (char*)sBrowserFuncs->utf8fromidentifier(name); + + int i = 0; + while ((i < kODWLDTRG_NUM_PROPERTY_IDENTIFIERS) && (result == false)) + { + if ( name == v_ODWLDTRGPropertyIdentifiers[i] ) + { + result= true; + } + i++; + } + + return result; +} + + bool ODWLDTRG_GetProperty(NPObject* obj, NPIdentifier name, NPVariant* result) +{ + TRACEINFO; + return true; +} + + bool ODWLDTRG_SetProperty(NPObject *obj, NPIdentifier name, const NPVariant *value) +{ + TRACEINFO; + return true; +} + + bool ODWLDTRG_RemoveProperty(NPObject *npobj, NPIdentifier name) +{ + TRACEINFO; + return true; +} + + + bool ODWLDTRG_Enumerate(NPObject *npobj, NPIdentifier **value, uint32_t *count) +{ + TRACEINFO; + return true; +} + +void ODWLDTRG_Invoke_registerDownload(NPObject* obj,const NPVariant* args, uint32_t argCount) +{ + TRACEINFO; + NOTIMPLEMENTED; +} + +void ODWLDTRG_Invoke_registerDownloadURL(NPObject* obj,const NPVariant* args, uint32_t argCount) +{ + TRACEINFO; + NOTIMPLEMENTED; +} diff --git a/applications/hbbtvplayer/hbbtvbrowserplugin/src/oipfdownloadtrigger.h b/applications/hbbtvplayer/hbbtvbrowserplugin/src/oipfdownloadtrigger.h new file mode 100644 index 0000000..cd7c337 --- /dev/null +++ b/applications/hbbtvplayer/hbbtvbrowserplugin/src/oipfdownloadtrigger.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2010-2011 Telecom-Paristech + * All Rights Reserved + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Authors: Stanislas Selle + * + */ +#ifndef __OIPFDOWNLOADTRIGGER_H__ +#define __OIPFDOWNLOADTRIGGER_H__ + +#include "hbbtvbrowserplugin.h" + + + +NPClass* fillODWLDTRGpclass(void); + +NPObject * ODWLDTRG_Allocate(NPP npp, NPClass *aClass); +void ODWLDTRG_Deallocate(NPObject *obj); +void ODWLDTRG_Invalidate(NPObject *obj); +bool ODWLDTRG_HasMethod(NPObject *obj, NPIdentifier name); +bool ODWLDTRG_Invoke(NPObject *obj, NPIdentifier name, const NPVariant *args, uint32_t argCount, NPVariant *result); +bool ODWLDTRG_InvokeDefault(NPObject *npobj, const NPVariant *args, uint32_t argCount, NPVariant *result); +bool ODWLDTRG_HasProperty(NPObject *obj, NPIdentifier name); +bool ODWLDTRG_GetProperty(NPObject *obj, NPIdentifier name, NPVariant *result); +bool ODWLDTRG_SetProperty(NPObject *obj, NPIdentifier name, const NPVariant *value); + +bool ODWLDTRG_RemoveProperty(NPObject *npobj, NPIdentifier name); + +bool ODWLDTRG_Enumerate(NPObject *npobj, NPIdentifier **value, uint32_t *count); + +void ODWLDTRG_Invoke_registerDownload(NPObject* obj,const NPVariant* args, uint32_t argCount); +void ODWLDTRG_Invoke_registerDownloadURL(NPObject* obj,const NPVariant* args, uint32_t argCount); +#endif diff --git a/applications/hbbtvplayer/hbbtvbrowserplugin/src/videobroadcast.c b/applications/hbbtvplayer/hbbtvbrowserplugin/src/videobroadcast.c new file mode 100644 index 0000000..bfcfed7 --- /dev/null +++ b/applications/hbbtvplayer/hbbtvbrowserplugin/src/videobroadcast.c @@ -0,0 +1,431 @@ +/* + * Copyright (c) 2010-2011 Telecom-Paristech + * All Rights Reserved + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * Authors: Stanislas Selle + * + */ +#include "videobroadcast.h" + +#define kVIDBRC_ID_PROPERTY_WIDTH 0 +#define kVIDBRC_ID_PROPERTY_HEIGHT 1 +#define kVIDBRC_ID_PROPERTY_FULLSCREEN 2 +#define kVIDBRC_ID_PROPERTY_ONCHANNELCHANGEERROR 3 +#define kVIDBRC_ID_PROPERTY_PLAYSTATE 4 +#define kVIDBRC_ID_PROPERTY_ONPLAYSTATECHANGE 5 +#define kVIDBRC_ID_PROPERTY_ONCHANNELCHANGESUCCEEDED 6 +#define kVIDBRC_ID_PROPERTY_ONFULLSCREENCHANGE 7 +#define kVIDBRC_ID_PROPERTY_ONFOCUS 8 +#define kVIDBRC_ID_PROPERTY_ONBLUR 9 +#define kVIDBRC_NUM_PROPERTY_IDENTIFIERS 10 + +#define kVIDBRC_ID_METHOD_GETCHANNELCONFIG 0 +#define kVIDBRC_ID_METHOD_BINDTOCURRENTCHANNEL 1 +#define kVIDBRC_ID_METHOD_CREATECHANNELOBJECT 2 +#define kVIDBRC_ID_METHOD_CREATECHANNELOBJECT2 3 +#define kVIDBRC_ID_METHOD_SETCHANNEL 4 +#define kVIDBRC_ID_METHOD_PREVCHANNEL 5 +#define kVIDBRC_ID_METHOD_NEXTCHANNEL 6 +#define kVIDBRC_ID_METHOD_SETFULLSCREEN 7 +#define kVIDBRC_ID_METHOD_GETVOLUME 8 +#define kVIDBRC_ID_METHOD_RELEASE 9 +#define kVIDBRC_NUM_METHOD_IDENTIFIERS 10 + + +bool v_bVIDBRCIdentifiersInitialized = false; + +NPIdentifier v_VIDBRCPropertyIdentifiers[kVIDBRC_NUM_PROPERTY_IDENTIFIERS]; +const NPUTF8 * v_VIDBRCPropertyNames[kVIDBRC_NUM_PROPERTY_IDENTIFIERS] = { + "width", + "height", + "fullScreen", + "onChannelChangeError", + "playState", + "onPlayStateChange", + "onChannelChangeSucceeded", + "onFullScreenChange", + "onFocus", + "onBlur" + }; + +NPIdentifier v_VIDBRCMethodIdentifiers[kVIDBRC_NUM_METHOD_IDENTIFIERS]; +const NPUTF8 * v_VIDBRCMethodNames[kVIDBRC_NUM_METHOD_IDENTIFIERS] = { + "getChannelConfig", + "bindToCurrentChannel", + "createChannelObject", + "createChannelObject2", + "setChannel", + "prevChannel", + "nextChannel", + "setFullScreen", + "getVolume", + "release" +}; + +static void VIDBRCinitializeIdentifiers(void) +{ + sBrowserFuncs->getstringidentifiers( v_VIDBRCPropertyNames, kVIDBRC_NUM_PROPERTY_IDENTIFIERS, v_VIDBRCPropertyIdentifiers ); + sBrowserFuncs->getstringidentifiers( v_VIDBRCMethodNames, kVIDBRC_NUM_METHOD_IDENTIFIERS, v_VIDBRCMethodIdentifiers ); +} + +NPClass stVIDBRCclass; +NPClass* pVIDBRCclass = NULL; + +NPClass* fillVIDBRCpclass(void) +{ + TRACEINFO; + if (pVIDBRCclass == NULL) + { + stVIDBRCclass.allocate = VIDBRC_Allocate; + stVIDBRCclass.deallocate = VIDBRC_Deallocate; + stVIDBRCclass.invalidate = VIDBRC_Invalidate; + stVIDBRCclass.hasMethod = VIDBRC_HasMethod; + stVIDBRCclass.invoke = VIDBRC_Invoke; + stVIDBRCclass.invokeDefault = VIDBRC_InvokeDefault; + stVIDBRCclass.hasProperty = VIDBRC_HasProperty; + stVIDBRCclass.getProperty = VIDBRC_GetProperty; + stVIDBRCclass.setProperty = VIDBRC_SetProperty; + stVIDBRCclass.removeProperty = VIDBRC_RemoveProperty; + stVIDBRCclass.enumerate = VIDBRC_Enumerate; + pVIDBRCclass = &stVIDBRCclass; + } + + return pVIDBRCclass; +} + + +NPObject * VIDBRC_Allocate(NPP npp, NPClass *theClass) +{ + TRACEINFO; + + + if (!v_bVIDBRCIdentifiersInitialized) + { + v_bVIDBRCIdentifiersInitialized = true; + VIDBRCinitializeIdentifiers(); + } + + NPObj_VidBrc* newvidbrc = (NPObj_VidBrc*)MEMALLOC(sizeof(NPObj_VidBrc)); + newvidbrc->npp = npp; + + return (NPObject*) newvidbrc; +} + +void VIDBRC_Deallocate(NPObject* obj) +{ + TRACEINFO; + OnVIDBRC_SetFullScreen(true); + NPObj_VidBrc* vidbrc = (NPObj_VidBrc*)obj; + /* + if (vidbrc->pcArg_onChannelChangeSucceeded) + { + MEMFREE(vidbrc->pcArg_onChannelChangeSucceeded); + // vidbrc->pcArg_onChannelChangeSucceeded = NULL; + } + if ( vidbrc->onChannelChangeSucceeded ) + { + // sBrowserFuncs->releaseobject(vidbrc->onChannelChangeSucceeded); + // vidbrc->onChannelChangeSucceeded = NULL; + } + + */ + MEMFREE(obj); + return; +} + + void VIDBRC_Invalidate(NPObject* obj) +{ + TRACEINFO; + return; +} + + bool VIDBRC_HasMethod(NPObject* obj, NPIdentifier name) +{ + TRACEINFO; + + bool result = false; + int i = 0; + NPUTF8* utf8methodname = (char*)sBrowserFuncs->utf8fromidentifier(name); + while ((i < kVIDBRC_NUM_METHOD_IDENTIFIERS) && (result == false)) + { + if ( name == v_VIDBRCMethodIdentifiers[i] ) + { + result= true; + } + i++; + } + + return result; +} + +bool VIDBRC_Invoke(NPObject* obj, NPIdentifier name, const NPVariant* args, uint32_t argCount, NPVariant* result) +{ + TRACEINFO; + bool fctresult = false; + + if (name == v_VIDBRCMethodIdentifiers[kVIDBRC_ID_METHOD_GETCHANNELCONFIG]) + { + VIDBRC_Invoke_getChannelConfig((NPObj_VidBrc*)obj, args, argCount, result); + fctresult = true; + } + + if (name == v_VIDBRCMethodIdentifiers[kVIDBRC_ID_METHOD_SETFULLSCREEN]) + { + VIDBRC_Invoke_setFullScreen((NPObj_VidBrc*)obj, args, argCount); + fctresult = true; + } + + if (name == v_VIDBRCMethodIdentifiers[kVIDBRC_ID_METHOD_BINDTOCURRENTCHANNEL]) + { + VIDBRC_Invoke_bindToCurrentChannel(obj, args, argCount); + fctresult = true; + } + + + return fctresult; +} + + bool VIDBRC_InvokeDefault(NPObject *npobj, const NPVariant *args, uint32_t argCount, NPVariant *result) +{ + TRACEINFO; + return true; +} + + bool VIDBRC_HasProperty(NPObject* obj, NPIdentifier name) +{ + TRACEINFO; bool result = false; + NPUTF8* utf8propertyname = (char*)sBrowserFuncs->utf8fromidentifier(name); + int i = 0; + while ((i < kVIDBRC_NUM_PROPERTY_IDENTIFIERS) && (result == false)) + { + if ( name == v_VIDBRCPropertyIdentifiers[i] ) + { + result= true; + } + i++; + } + + return result; +} + + bool VIDBRC_GetProperty(NPObject* obj, NPIdentifier name, NPVariant* result) +{ + TRACEINFO; + bool fctresult = false; + NPObj_VidBrc* vidbrc = (NPObj_VidBrc*)obj; + + if (name == v_VIDBRCPropertyIdentifiers[kVIDBRC_ID_PROPERTY_WIDTH]) { + INT32_TO_NPVARIANT(vidbrc->width,*result); + fctresult = true; + } else + if (name == v_VIDBRCPropertyIdentifiers[kVIDBRC_ID_PROPERTY_HEIGHT]) { + INT32_TO_NPVARIANT(vidbrc->height,*result); + fctresult = true; + } else + if (name == v_VIDBRCPropertyIdentifiers[kVIDBRC_ID_PROPERTY_FULLSCREEN]) { + BOOLEAN_TO_NPVARIANT(vidbrc->fullscreen, *result); + fctresult = true; + } else + if (name == v_VIDBRCPropertyIdentifiers[kVIDBRC_ID_PROPERTY_PLAYSTATE]) { + fctresult = true; + } else + if (name == v_VIDBRCPropertyIdentifiers[kVIDBRC_ID_PROPERTY_ONPLAYSTATECHANGE]) { + fctresult = true; + } else + if (name == v_VIDBRCPropertyIdentifiers[kVIDBRC_ID_PROPERTY_ONCHANNELCHANGEERROR]) { + fctresult = true; + } else + if (name == v_VIDBRCPropertyIdentifiers[kVIDBRC_ID_PROPERTY_ONCHANNELCHANGESUCCEEDED]) { + /* if ( vidbrc->pcArg_onChannelChangeSucceeded != NULL ) + { + char* dup_str = strdup(vidbrc->pcArg_onChannelChangeSucceeded); + NPString npstr = { dup_str, (uint32_t)(strlen(dup_str)) }; + result->type = NPVariantType_String; + result->value.stringValue = npstr; + fctresult = true; + } + else + { + OBJECT_TO_NPVARIANT(vidbrc->onPlayStateChange,*result); + }*/ + } else + if (name == v_VIDBRCPropertyIdentifiers[kVIDBRC_ID_PROPERTY_ONFULLSCREENCHANGE]) { + fctresult = true; + } else + if (name == v_VIDBRCPropertyIdentifiers[kVIDBRC_ID_PROPERTY_ONFOCUS]) { + fctresult = true; + } else + if (name == v_VIDBRCPropertyIdentifiers[kVIDBRC_ID_PROPERTY_ONBLUR]) { + fctresult = true; + } + + return fctresult; +} + + bool VIDBRC_SetProperty(NPObject *obj, NPIdentifier name, const NPVariant *value) +{ + TRACEINFO; + bool fctresult = false; + NPObj_VidBrc* vidbrc = (NPObj_VidBrc*)obj; + if (name == v_VIDBRCPropertyIdentifiers[kVIDBRC_ID_PROPERTY_WIDTH]) { + vidbrc->width = NPVARIANT_TO_INT32(*value); + VIDBRC_setsize(vidbrc, vidbrc->width, vidbrc->height); + fctresult = true; + } else + if (name == v_VIDBRCPropertyIdentifiers[kVIDBRC_ID_PROPERTY_HEIGHT]) { + vidbrc->height = NPVARIANT_TO_INT32(*value); + VIDBRC_setsize(vidbrc, vidbrc->width, vidbrc->height); + fctresult = true; + } else + if (name == v_VIDBRCPropertyIdentifiers[kVIDBRC_ID_PROPERTY_FULLSCREEN]) { + fctresult = true; + } else + if (name == v_VIDBRCPropertyIdentifiers[kVIDBRC_ID_PROPERTY_PLAYSTATE]) { + fctresult = true; + } else + if (name == v_VIDBRCPropertyIdentifiers[kVIDBRC_ID_PROPERTY_ONPLAYSTATECHANGE]) { + fctresult = true; + } else + if (name == v_VIDBRCPropertyIdentifiers[kVIDBRC_ID_PROPERTY_ONCHANNELCHANGEERROR]) { + fctresult = true; + } else + if (name == v_VIDBRCPropertyIdentifiers[kVIDBRC_ID_PROPERTY_ONCHANNELCHANGESUCCEEDED]) { + /* if ( vidbrc->onChannelChangeSucceeded != NULL ) + { + sBrowserFuncs->releaseobject(vidbrc->onChannelChangeSucceeded); + vidbrc->onChannelChangeSucceeded = NULL; + } + if ( vidbrc->pcArg_onChannelChangeSucceeded ) + { + MEMFREE( vidbrc->pcArg_onChannelChangeSucceeded ); + vidbrc->pcArg_onChannelChangeSucceeded = NULL; + } + vidbrc->onChannelChangeSucceeded = NPVARIANT_TO_OBJECT(*value); + sBrowserFuncs->retainobject( vidbrc->onChannelChangeSucceeded);*/ + fctresult = true; + } else + if (name == v_VIDBRCPropertyIdentifiers[kVIDBRC_ID_PROPERTY_ONFULLSCREENCHANGE]) { + fctresult = true; + } else + if (name == v_VIDBRCPropertyIdentifiers[kVIDBRC_ID_PROPERTY_ONFOCUS]) { + fctresult = true; + } else + if (name == v_VIDBRCPropertyIdentifiers[kVIDBRC_ID_PROPERTY_ONBLUR]) { + fctresult = true; + } + return fctresult; +} + + bool VIDBRC_RemoveProperty(NPObject *npobj, NPIdentifier name) +{ + TRACEINFO; + return true; +} + + + bool VIDBRC_Enumerate(NPObject *npobj, NPIdentifier **value, uint32_t *count) +{ + TRACEINFO; + return true; +} + +/** implementation of methods **/ +void VIDBRC_Invoke_getChannelConfig(NPObj_VidBrc* obj,const NPVariant* args, uint32_t argCount, NPVariant* result) +{ + NOTIMPLEMENTED; + NULL_TO_NPVARIANT(*result); + return; +} + +void VIDBRC_Invoke_setFullScreen(NPObj_VidBrc* obj,const NPVariant* args, uint32_t argCount) +{ + TRACEINFO; + //NOTIMPLEMENTED; ///inprogress + + NPP npp = obj->npp; + int param; + if (argCount != 1 || !NPVARIANT_IS_BOOLEAN(args[0])) { + return; + } + else { + param = NPVARIANT_TO_BOOLEAN( args[0] ); + if (param != obj->fullscreen) + { + if (param){ + obj->fullscreen = true; + VIDBRC_setsize(obj, FULLSIZE_WIDTH, FULLSIZE_HEIGHT); + } + else{ + obj->fullscreen = false; + } + + HBBTVPluginData* pdata = (HBBTVPluginData*)npp->pdata; + if(pdata){ + if (pdata->window){ + OnVIDBRC_SetFullScreen(param); + } + } + } +} + +} + +void VIDBRC_Invoke_bindToCurrentChannel(NPObject* obj,const NPVariant* args, uint32_t argCount) +{ + TRACEINFO; + NOTIMPLEMENTED; + //OnBindToCurrentChannel(); +} + +/** implementation of intermediary function **/ + +void VIDBRC_setsize(NPObj_VidBrc* obj,int32_t width, int32_t height) +{ + ///set the size + obj->width = width; + obj->height = height; + + /// get the plugin object and his style object + NPIdentifier npIdent; + NPVariant npVar; + NPObject * npObjPlugin = NULL; + NPObject * npObjStyle = NULL; + + if (obj->npp) + { + sBrowserFuncs->getvalue( obj->npp , + NPNVPluginElementNPObject , + &npObjPlugin ); + } + if (npObjPlugin) + { + npIdent = sBrowserFuncs->getstringidentifier( (const NPUTF8 *)"style" ); + sBrowserFuncs->getproperty( obj->npp , + npObjPlugin, + npIdent , + &npVar ); + npObjStyle = NPVARIANT_TO_OBJECT(npVar); + } + if (npObjStyle) + { + INT32_TO_NPVARIANT(width,npVar); + npIdent = v_VIDBRCPropertyIdentifiers[kVIDBRC_ID_PROPERTY_WIDTH]; + sBrowserFuncs->setproperty( obj->npp,npObjStyle, npIdent, &npVar); + INT32_TO_NPVARIANT(height,npVar); + npIdent = v_VIDBRCPropertyIdentifiers[kVIDBRC_ID_PROPERTY_HEIGHT]; + sBrowserFuncs->setproperty( obj->npp,npObjStyle, npIdent, &npVar); + } +} + diff --git a/applications/hbbtvplayer/hbbtvbrowserplugin/src/videobroadcast.h b/applications/hbbtvplayer/hbbtvbrowserplugin/src/videobroadcast.h new file mode 100644 index 0000000..0027b76 --- /dev/null +++ b/applications/hbbtvplayer/hbbtvbrowserplugin/src/videobroadcast.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2010-2011 Telecom-Paristech + * All Rights Reserved + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * Authors: Stanislas Selle + * + */ +#ifndef __VIDEOBROADCAST_H__ +#define __VIDEOBROADCAST_H__ + +#include +#include +#include +#include +#include +#include +#include + +#include "hbbtvbrowserplugin.h" + +#define FULLSIZE_WIDTH 1280 +#define FULLSIZE_HEIGHT 720 + +typedef struct +{ + NPObject header; + NPP npp; + NPBool fullscreen; + int32_t width; + int32_t height; + + + //~ char* pcArg_onChannelChangeSucceeded; + //~ NPObject* onChannelChangeSucceeded; + //NPObject* channelConfig; + +} NPObj_VidBrc; + +NPClass* fillVIDBRCpclass(void); + +NPObject * VIDBRC_Allocate(NPP npp, NPClass *aClass); +void VIDBRC_Deallocate(NPObject *obj); +void VIDBRC_Invalidate(NPObject *obj); +bool VIDBRC_HasMethod(NPObject *obj, NPIdentifier name); +bool VIDBRC_Invoke(NPObject *obj, NPIdentifier name, const NPVariant *args, uint32_t argCount, NPVariant *result); +bool VIDBRC_InvokeDefault(NPObject *npobj, const NPVariant *args, uint32_t argCount, NPVariant *result); +bool VIDBRC_HasProperty(NPObject *obj, NPIdentifier name); +bool VIDBRC_GetProperty(NPObject *obj, NPIdentifier name, NPVariant *result); +bool VIDBRC_SetProperty(NPObject *obj, NPIdentifier name, const NPVariant *value); +bool VIDBRC_RemoveProperty(NPObject *npobj, NPIdentifier name); +bool VIDBRC_Enumerate(NPObject *npobj, NPIdentifier **value, uint32_t *count); + +void VIDBRC_Invoke_setFullScreen(NPObj_VidBrc* obj,const NPVariant* args, uint32_t argCount); +void VIDBRC_Invoke_bindToCurrentChannel(NPObject* obj,const NPVariant* args, uint32_t argCount); +void VIDBRC_Invoke_getChannelConfig(NPObj_VidBrc* obj,const NPVariant* args, uint32_t argCount, NPVariant* result); + +void VIDBRC_setsize(NPObj_VidBrc* obj,int32_t witdh, int32_t height); +#endif diff --git a/applications/hbbtvplayer/hbbtvbrowserplugin/src/webkit-plugin-header/npapi.h b/applications/hbbtvplayer/hbbtvbrowserplugin/src/webkit-plugin-header/npapi.h new file mode 100644 index 0000000..c217718 --- /dev/null +++ b/applications/hbbtvplayer/hbbtvbrowserplugin/src/webkit-plugin-header/npapi.h @@ -0,0 +1,901 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef npapi_h_ +#define npapi_h_ + +#if defined(__OS2__) +#pragma pack(1) +#endif + +#include "nptypes.h" + +#if defined(__OS2__) || defined(OS2) +#ifndef XP_OS2 +#define XP_OS2 1 +#endif +#endif + +#ifdef INCLUDE_JAVA +#include "jri.h" /* Java Runtime Interface */ +#else +#define jref void * +#define JRIEnv void +#endif + +#if defined(_WIN32) && !defined(__SYMBIAN32__) +#include +#ifndef XP_WIN +#define XP_WIN 1 +#endif +#endif + +#if defined(__SYMBIAN32__) +#ifndef XP_SYMBIAN +#define XP_SYMBIAN 1 +#endif +#endif + +#if defined(__APPLE_CC__) && !defined(XP_UNIX) +#ifndef XP_MACOSX +#define XP_MACOSX 1 +#endif +#endif + +#if defined(XP_MACOSX) && defined(__LP64__) +#define NP_NO_QUICKDRAW +#define NP_NO_CARBON +#endif + +#if defined(XP_MACOSX) +#include +#include +#ifndef NP_NO_CARBON +#include +#endif +#endif + +#if defined(XP_UNIX) +#include +#include +#include +#endif + +#if defined(XP_SYMBIAN) +#include +#include +#endif + +/*----------------------------------------------------------------------*/ +/* Plugin Version Constants */ +/*----------------------------------------------------------------------*/ + +#define NP_VERSION_MAJOR 0 +#define NP_VERSION_MINOR 24 + + +/* The OS/2 version of Netscape uses RC_DATA to define the + mime types, file extensions, etc that are required. + Use a vertical bar to separate types, end types with \0. + FileVersion and ProductVersion are 32bit ints, all other + entries are strings that MUST be terminated with a \0. + +AN EXAMPLE: + +RCDATA NP_INFO_ProductVersion { 1,0,0,1,} + +RCDATA NP_INFO_MIMEType { "video/x-video|", + "video/x-flick\0" } +RCDATA NP_INFO_FileExtents { "avi|", + "flc\0" } +RCDATA NP_INFO_FileOpenName{ "MMOS2 video player(*.avi)|", + "MMOS2 Flc/Fli player(*.flc)\0" } + +RCDATA NP_INFO_FileVersion { 1,0,0,1 } +RCDATA NP_INFO_CompanyName { "Netscape Communications\0" } +RCDATA NP_INFO_FileDescription { "NPAVI32 Extension DLL\0" +RCDATA NP_INFO_InternalName { "NPAVI32\0" ) +RCDATA NP_INFO_LegalCopyright { "Copyright Netscape Communications \251 1996\0" +RCDATA NP_INFO_OriginalFilename { "NVAPI32.DLL" } +RCDATA NP_INFO_ProductName { "NPAVI32 Dynamic Link Library\0" } +*/ +/* RC_DATA types for version info - required */ +#define NP_INFO_ProductVersion 1 +#define NP_INFO_MIMEType 2 +#define NP_INFO_FileOpenName 3 +#define NP_INFO_FileExtents 4 +/* RC_DATA types for version info - used if found */ +#define NP_INFO_FileDescription 5 +#define NP_INFO_ProductName 6 +/* RC_DATA types for version info - optional */ +#define NP_INFO_CompanyName 7 +#define NP_INFO_FileVersion 8 +#define NP_INFO_InternalName 9 +#define NP_INFO_LegalCopyright 10 +#define NP_INFO_OriginalFilename 11 + +#ifndef RC_INVOKED + +/*----------------------------------------------------------------------*/ +/* Definition of Basic Types */ +/*----------------------------------------------------------------------*/ + +#ifndef FALSE +#define FALSE (0) +#endif +#ifndef TRUE +#define TRUE (1) +#endif +#ifndef NULL +#define NULL (0L) +#endif + +typedef unsigned char NPBool; +typedef int16_t NPError; +typedef int16_t NPReason; +typedef char* NPMIMEType; + +/*----------------------------------------------------------------------*/ +/* Structures and definitions */ +/*----------------------------------------------------------------------*/ + +#if !defined(__LP64__) +#if defined(XP_MACOSX) +#pragma options align=mac68k +#endif +#endif /* __LP64__ */ + +/* + * NPP is a plug-in's opaque instance handle + */ +typedef struct _NPP +{ + void* pdata; /* plug-in private data */ + void* ndata; /* netscape private data */ +} NPP_t; + +typedef NPP_t* NPP; + +typedef struct _NPStream +{ + void* pdata; /* plug-in private data */ + void* ndata; /* netscape private data */ + const char* url; + uint32_t end; + uint32_t lastmodified; + void* notifyData; + const char* headers; /* Response headers from host. + * Exists only for >= NPVERS_HAS_RESPONSE_HEADERS. + * Used for HTTP only; NULL for non-HTTP. + * Available from NPP_NewStream onwards. + * Plugin should copy this data before storing it. + * Includes HTTP status line and all headers, + * preferably verbatim as received from server, + * headers formatted as in HTTP ("Header: Value"), + * and newlines (\n, NOT \r\n) separating lines. + * Terminated by \n\0 (NOT \n\n\0). */ +} NPStream; + +typedef struct _NPByteRange +{ + int32_t offset; /* negative offset means from the end */ + uint32_t length; + struct _NPByteRange* next; +} NPByteRange; + +typedef struct _NPSavedData +{ + int32_t len; + void* buf; +} NPSavedData; + +typedef struct _NPRect +{ + uint16_t top; + uint16_t left; + uint16_t bottom; + uint16_t right; +} NPRect; + +typedef struct _NPSize +{ + int32_t width; + int32_t height; +} NPSize; + +typedef enum { + NPFocusNext = 0, + NPFocusPrevious = 1 +} NPFocusDirection; + +/* Return values for NPP_HandleEvent */ +#define kNPEventNotHandled 0 +#define kNPEventHandled 1 +/* Exact meaning must be spec'd in event model. */ +#define kNPEventStartIME 2 + +#if defined(XP_UNIX) +/* + * Unix specific structures and definitions + */ + +/* + * Callback Structures. + * + * These are used to pass additional platform specific information. + */ +enum { + NP_SETWINDOW = 1, + NP_PRINT +}; + +typedef struct +{ + int32_t type; +} NPAnyCallbackStruct; + +typedef struct +{ + int32_t type; + Display* display; + Visual* visual; + Colormap colormap; + unsigned int depth; +} NPSetWindowCallbackStruct; + +typedef struct +{ + int32_t type; + FILE* fp; +} NPPrintCallbackStruct; + +#endif /* XP_UNIX */ + +#if defined(XP_MACOSX) +typedef enum { +#ifndef NP_NO_QUICKDRAW + NPDrawingModelQuickDraw = 0, +#endif + NPDrawingModelCoreGraphics = 1, + NPDrawingModelOpenGL = 2, + NPDrawingModelCoreAnimation = 3 +} NPDrawingModel; + +typedef enum { +#ifndef NP_NO_CARBON + NPEventModelCarbon = 0, +#endif + NPEventModelCocoa = 1 +} NPEventModel; +#endif + +/* + * The following masks are applied on certain platforms to NPNV and + * NPPV selectors that pass around pointers to COM interfaces. Newer + * compilers on some platforms may generate vtables that are not + * compatible with older compilers. To prevent older plugins from + * not understanding a new browser's ABI, these masks change the + * values of those selectors on those platforms. To remain backwards + * compatible with different versions of the browser, plugins can + * use these masks to dynamically determine and use the correct C++ + * ABI that the browser is expecting. This does not apply to Windows + * as Microsoft's COM ABI will likely not change. + */ + +#define NP_ABI_GCC3_MASK 0x10000000 +/* + * gcc 3.x generated vtables on UNIX and OSX are incompatible with + * previous compilers. + */ +#if (defined(XP_UNIX) && defined(__GNUC__) && (__GNUC__ >= 3)) +#define _NP_ABI_MIXIN_FOR_GCC3 NP_ABI_GCC3_MASK +#else +#define _NP_ABI_MIXIN_FOR_GCC3 0 +#endif + +#if defined(XP_MACOSX) +#define NP_ABI_MACHO_MASK 0x01000000 +#define _NP_ABI_MIXIN_FOR_MACHO NP_ABI_MACHO_MASK +#else +#define _NP_ABI_MIXIN_FOR_MACHO 0 +#endif + +#define NP_ABI_MASK (_NP_ABI_MIXIN_FOR_GCC3 | _NP_ABI_MIXIN_FOR_MACHO) + +/* + * List of variable names for which NPP_GetValue shall be implemented + */ +typedef enum { + NPPVpluginNameString = 1, + NPPVpluginDescriptionString, + NPPVpluginWindowBool, + NPPVpluginTransparentBool, + NPPVjavaClass, /* Not implemented in WebKit */ + NPPVpluginWindowSize, /* Not implemented in WebKit */ + NPPVpluginTimerInterval, /* Not implemented in WebKit */ + NPPVpluginScriptableInstance = (10 | NP_ABI_MASK), /* Not implemented in WebKit */ + NPPVpluginScriptableIID = 11, /* Not implemented in WebKit */ + NPPVjavascriptPushCallerBool = 12, /* Not implemented in WebKit */ + NPPVpluginKeepLibraryInMemory = 13, /* Not implemented in WebKit */ + NPPVpluginNeedsXEmbed = 14, /* Not implemented in WebKit */ + + /* Get the NPObject for scripting the plugin. Introduced in NPAPI minor version 14. + */ + NPPVpluginScriptableNPObject = 15, + + /* Get the plugin value (as \0-terminated UTF-8 string data) for + * form submission if the plugin is part of a form. Use + * NPN_MemAlloc() to allocate memory for the string data. Introduced + * in NPAPI minor version 15. + */ + NPPVformValue = 16, /* Not implemented in WebKit */ + + NPPVpluginUrlRequestsDisplayedBool = 17, /* Not implemented in WebKit */ + + /* Checks if the plugin is interested in receiving the http body of + * all http requests (including failed ones, http status != 200). + */ + NPPVpluginWantsAllNetworkStreams = 18, + + /* Browsers can retrieve a native ATK accessibility plug ID via this variable. */ + NPPVpluginNativeAccessibleAtkPlugId = 19, + + /* Checks to see if the plug-in would like the browser to load the "src" attribute. */ + NPPVpluginCancelSrcStream = 20 + +#if defined(XP_MACOSX) + /* Used for negotiating drawing models */ + , NPPVpluginDrawingModel = 1000 + /* Used for negotiating event models */ + , NPPVpluginEventModel = 1001 + /* In the NPDrawingModelCoreAnimation drawing model, the browser asks the plug-in for a Core Animation layer. */ + , NPPVpluginCoreAnimationLayer = 1003 +#endif + +#if defined(MOZ_PLATFORM_MAEMO) && (MOZ_PLATFORM_MAEMO >= 5) + , NPPVpluginWindowlessLocalBool = 2002 +#endif +} NPPVariable; + +/* + * List of variable names for which NPN_GetValue should be implemented. + */ +typedef enum { + NPNVxDisplay = 1, + NPNVxtAppContext, + NPNVnetscapeWindow, + NPNVjavascriptEnabledBool, + NPNVasdEnabledBool, + NPNVisOfflineBool, + + NPNVserviceManager = (10 | NP_ABI_MASK), /* Not implemented in WebKit */ + NPNVDOMElement = (11 | NP_ABI_MASK), /* Not implemented in WebKit */ + NPNVDOMWindow = (12 | NP_ABI_MASK), /* Not implemented in WebKit */ + NPNVToolkit = (13 | NP_ABI_MASK), /* Not implemented in WebKit */ + NPNVSupportsXEmbedBool = 14, /* Not implemented in WebKit */ + + /* Get the NPObject wrapper for the browser window. */ + NPNVWindowNPObject = 15, + + /* Get the NPObject wrapper for the plugins DOM element. */ + NPNVPluginElementNPObject = 16, + + NPNVSupportsWindowless = 17, + + NPNVprivateModeBool = 18 + +#if defined(XP_MACOSX) + /* Used for negotiating drawing models */ + , NPNVpluginDrawingModel = 1000 +#ifndef NP_NO_QUICKDRAW + , NPNVsupportsQuickDrawBool = 2000 +#endif + , NPNVsupportsCoreGraphicsBool = 2001 + , NPNVsupportsOpenGLBool = 2002 + , NPNVsupportsCoreAnimationBool = 2003 +#ifndef NP_NO_CARBON + , NPNVsupportsCarbonBool = 3000 /* TRUE if the browser supports the Carbon event model */ +#endif + , NPNVsupportsCocoaBool = 3001 /* TRUE if the browser supports the Cocoa event model */ +#endif /* XP_MACOSX */ +#if defined(MOZ_PLATFORM_MAEMO) && (MOZ_PLATFORM_MAEMO >= 5) + , NPNVSupportsWindowlessLocal = 2002 +#endif +} NPNVariable; + +typedef enum { + NPNURLVCookie = 501, + NPNURLVProxy +} NPNURLVariable; + +/* + * The type of Toolkit the widgets use + */ +typedef enum { + NPNVGtk12 = 1, + NPNVGtk2 +} NPNToolkitType; + +/* + * The type of a NPWindow - it specifies the type of the data structure + * returned in the window field. + */ +typedef enum { + NPWindowTypeWindow = 1, + NPWindowTypeDrawable +} NPWindowType; + +typedef struct _NPWindow +{ + void* window; /* Platform specific window handle */ + /* OS/2: x - Position of bottom left corner */ + /* OS/2: y - relative to visible netscape window */ + int32_t x; /* Position of top left corner relative */ + int32_t y; /* to a netscape page. */ + uint32_t width; /* Maximum window size */ + uint32_t height; + NPRect clipRect; /* Clipping rectangle in port coordinates */ +#if defined(XP_UNIX) || defined(XP_SYMBIAN) + void * ws_info; /* Platform-dependent additonal data */ +#endif /* XP_UNIX || XP_SYMBIAN */ + NPWindowType type; /* Is this a window or a drawable? */ +} NPWindow; + +typedef struct _NPImageExpose +{ + char* data; /* image pointer */ + int32_t stride; /* Stride of data image pointer */ + int32_t depth; /* Depth of image pointer */ + int32_t x; /* Expose x */ + int32_t y; /* Expose y */ + uint32_t width; /* Expose width */ + uint32_t height; /* Expose height */ + NPSize dataSize; /* Data buffer size */ + float translateX; /* translate X matrix value */ + float translateY; /* translate Y matrix value */ + float scaleX; /* scale X matrix value */ + float scaleY; /* scale Y matrix value */ +} NPImageExpose; + +typedef struct _NPFullPrint +{ + NPBool pluginPrinted;/* Set TRUE if plugin handled fullscreen printing */ + NPBool printOne; /* TRUE if plugin should print one copy to default + printer */ + void* platformPrint; /* Platform-specific printing info */ +} NPFullPrint; + +typedef struct _NPEmbedPrint +{ + NPWindow window; + void* platformPrint; /* Platform-specific printing info */ +} NPEmbedPrint; + +typedef struct _NPPrint +{ + uint16_t mode; /* NP_FULL or NP_EMBED */ + union + { + NPFullPrint fullPrint; /* if mode is NP_FULL */ + NPEmbedPrint embedPrint; /* if mode is NP_EMBED */ + } print; +} NPPrint; + +#if defined(XP_MACOSX) +#ifndef NP_NO_CARBON +typedef EventRecord NPEvent; +#else +typedef void* NPEvent; +#endif +#elif defined(XP_SYMBIAN) +typedef QEvent NPEvent; +#elif defined(XP_WIN) +typedef struct _NPEvent +{ + uint16_t event; + uintptr_t wParam; + uintptr_t lParam; +} NPEvent; +#elif defined(XP_OS2) +typedef struct _NPEvent +{ + uint32_t event; + uint32_t wParam; + uint32_t lParam; +} NPEvent; +#elif defined(XP_UNIX) +typedef XEvent NPEvent; +#else +typedef void* NPEvent; +#endif + +#if defined(XP_MACOSX) +typedef void* NPRegion; +#ifndef NP_NO_QUICKDRAW +typedef RgnHandle NPQDRegion; +#endif +typedef CGPathRef NPCGRegion; +#elif defined(XP_WIN) +typedef HRGN NPRegion; +#elif defined(XP_UNIX) +typedef Region NPRegion; +#elif defined(XP_SYMBIAN) +typedef QRegion* NPRegion; +#else +typedef void *NPRegion; +#endif + +typedef struct _NPNSString NPNSString; +typedef struct _NPNSWindow NPNSWindow; +typedef struct _NPNSMenu NPNSMenu; + +#if defined(XP_MACOSX) +typedef NPNSMenu NPMenu; +#else +typedef void *NPMenu; +#endif + +typedef enum { + NPCoordinateSpacePlugin = 1, + NPCoordinateSpaceWindow, + NPCoordinateSpaceFlippedWindow, + NPCoordinateSpaceScreen, + NPCoordinateSpaceFlippedScreen +} NPCoordinateSpace; + +#if defined(XP_MACOSX) + +#ifndef NP_NO_QUICKDRAW +typedef struct NP_Port +{ + CGrafPtr port; + int32_t portx; /* position inside the topmost window */ + int32_t porty; +} NP_Port; +#endif /* NP_NO_QUICKDRAW */ + +/* + * NP_CGContext is the type of the NPWindow's 'window' when the plugin specifies NPDrawingModelCoreGraphics + * as its drawing model. + */ + +typedef struct NP_CGContext +{ + CGContextRef context; +#ifdef NP_NO_CARBON + NPNSWindow *window; +#else + void *window; /* A WindowRef or NULL for the Cocoa event model. */ +#endif +} NP_CGContext; + +/* + * NP_GLContext is the type of the NPWindow's 'window' when the plugin specifies NPDrawingModelOpenGL as its + * drawing model. + */ + +typedef struct NP_GLContext +{ + CGLContextObj context; +#ifdef NP_NO_CARBON + NPNSWindow *window; +#else + void *window; /* Can be either an NSWindow or a WindowRef depending on the event model */ +#endif +} NP_GLContext; + +typedef enum { + NPCocoaEventDrawRect = 1, + NPCocoaEventMouseDown, + NPCocoaEventMouseUp, + NPCocoaEventMouseMoved, + NPCocoaEventMouseEntered, + NPCocoaEventMouseExited, + NPCocoaEventMouseDragged, + NPCocoaEventKeyDown, + NPCocoaEventKeyUp, + NPCocoaEventFlagsChanged, + NPCocoaEventFocusChanged, + NPCocoaEventWindowFocusChanged, + NPCocoaEventScrollWheel, + NPCocoaEventTextInput +} NPCocoaEventType; + +typedef struct _NPCocoaEvent { + NPCocoaEventType type; + uint32_t version; + union { + struct { + uint32_t modifierFlags; + double pluginX; + double pluginY; + int32_t buttonNumber; + int32_t clickCount; + double deltaX; + double deltaY; + double deltaZ; + } mouse; + struct { + uint32_t modifierFlags; + NPNSString *characters; + NPNSString *charactersIgnoringModifiers; + NPBool isARepeat; + uint16_t keyCode; + } key; + struct { + CGContextRef context; + double x; + double y; + double width; + double height; + } draw; + struct { + NPBool hasFocus; + } focus; + struct { + NPNSString *text; + } text; + } data; +} NPCocoaEvent; + +#ifndef NP_NO_CARBON +/* Non-standard event types that can be passed to HandleEvent */ +enum NPEventType { + NPEventType_GetFocusEvent = (osEvt + 16), + NPEventType_LoseFocusEvent, + NPEventType_AdjustCursorEvent, + NPEventType_MenuCommandEvent, + NPEventType_ClippingChangedEvent, + NPEventType_ScrollingBeginsEvent = 1000, + NPEventType_ScrollingEndsEvent +}; +#endif /* NP_NO_CARBON */ + +#endif /* XP_MACOSX */ + +/* + * Values for mode passed to NPP_New: + */ +#define NP_EMBED 1 +#define NP_FULL 2 + +/* + * Values for stream type passed to NPP_NewStream: + */ +#define NP_NORMAL 1 +#define NP_SEEK 2 +#define NP_ASFILE 3 +#define NP_ASFILEONLY 4 + +#define NP_MAXREADY (((unsigned)(~0)<<1)>>1) + +/* + * Flags for NPP_ClearSiteData. + */ +#define NP_CLEAR_ALL 0 +#define NP_CLEAR_CACHE (1 << 0) + +#if !defined(__LP64__) +#if defined(XP_MACOSX) +#pragma options align=reset +#endif +#endif /* __LP64__ */ + +/*----------------------------------------------------------------------*/ +/* Error and Reason Code definitions */ +/*----------------------------------------------------------------------*/ + +/* + * Values of type NPError: + */ +#define NPERR_BASE 0 +#define NPERR_NO_ERROR (NPERR_BASE + 0) +#define NPERR_GENERIC_ERROR (NPERR_BASE + 1) +#define NPERR_INVALID_INSTANCE_ERROR (NPERR_BASE + 2) +#define NPERR_INVALID_FUNCTABLE_ERROR (NPERR_BASE + 3) +#define NPERR_MODULE_LOAD_FAILED_ERROR (NPERR_BASE + 4) +#define NPERR_OUT_OF_MEMORY_ERROR (NPERR_BASE + 5) +#define NPERR_INVALID_PLUGIN_ERROR (NPERR_BASE + 6) +#define NPERR_INVALID_PLUGIN_DIR_ERROR (NPERR_BASE + 7) +#define NPERR_INCOMPATIBLE_VERSION_ERROR (NPERR_BASE + 8) +#define NPERR_INVALID_PARAM (NPERR_BASE + 9) +#define NPERR_INVALID_URL (NPERR_BASE + 10) +#define NPERR_FILE_NOT_FOUND (NPERR_BASE + 11) +#define NPERR_NO_DATA (NPERR_BASE + 12) +#define NPERR_STREAM_NOT_SEEKABLE (NPERR_BASE + 13) + +/* + * Values of type NPReason: + */ +#define NPRES_BASE 0 +#define NPRES_DONE (NPRES_BASE + 0) +#define NPRES_NETWORK_ERR (NPRES_BASE + 1) +#define NPRES_USER_BREAK (NPRES_BASE + 2) + +/* + * Don't use these obsolete error codes any more. + */ +#define NP_NOERR NP_NOERR_is_obsolete_use_NPERR_NO_ERROR +#define NP_EINVAL NP_EINVAL_is_obsolete_use_NPERR_GENERIC_ERROR +#define NP_EABORT NP_EABORT_is_obsolete_use_NPRES_USER_BREAK + +/* + * Version feature information + */ +#define NPVERS_HAS_STREAMOUTPUT 8 +#define NPVERS_HAS_NOTIFICATION 9 +#define NPVERS_HAS_LIVECONNECT 9 +#define NPVERS_WIN16_HAS_LIVECONNECT 9 +#define NPVERS_68K_HAS_LIVECONNECT 11 +#define NPVERS_HAS_WINDOWLESS 11 +#define NPVERS_HAS_XPCONNECT_SCRIPTING 13 /* Not implemented in WebKit */ +#define NPVERS_HAS_NPRUNTIME_SCRIPTING 14 +#define NPVERS_HAS_FORM_VALUES 15 /* Not implemented in WebKit; see bug 13061 */ +#define NPVERS_HAS_POPUPS_ENABLED_STATE 16 /* Not implemented in WebKit */ +#define NPVERS_HAS_RESPONSE_HEADERS 17 +#define NPVERS_HAS_NPOBJECT_ENUM 18 +#define NPVERS_HAS_PLUGIN_THREAD_ASYNC_CALL 19 +#define NPVERS_HAS_ALL_NETWORK_STREAMS 20 +#define NPVERS_HAS_URL_AND_AUTH_INFO 21 +#define NPVERS_HAS_PRIVATE_MODE 22 +#define NPVERS_MACOSX_HAS_EVENT_MODELS 23 +#define NPVERS_HAS_CANCEL_SRC_STREAM 24 +#define NPVERS_HAS_ADVANCED_KEY_HANDLING 25 +#define NPVERS_HAS_URL_REDIRECT_HANDLING 26 +#define NPVERS_HAS_CLEAR_SITE_DATA 27 + +/*----------------------------------------------------------------------*/ +/* Function Prototypes */ +/*----------------------------------------------------------------------*/ + +#if defined(__OS2__) +#define NP_LOADDS _System +#else +#define NP_LOADDS +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* NPP_* functions are provided by the plugin and called by the navigator. */ + +#if defined(XP_UNIX) +char* NPP_GetMIMEDescription(void); +#endif + +NPError NP_LOADDS NPP_Initialize(void); +void NP_LOADDS NPP_Shutdown(void); +NPError NP_LOADDS NPP_New(NPMIMEType pluginType, NPP instance, + uint16_t mode, int16_t argc, char* argn[], + char* argv[], NPSavedData* saved); +NPError NP_LOADDS NPP_Destroy(NPP instance, NPSavedData** save); +NPError NP_LOADDS NPP_SetWindow(NPP instance, NPWindow* window); +NPError NP_LOADDS NPP_NewStream(NPP instance, NPMIMEType type, + NPStream* stream, NPBool seekable, + uint16_t* stype); +NPError NP_LOADDS NPP_DestroyStream(NPP instance, NPStream* stream, + NPReason reason); +int32_t NP_LOADDS NPP_WriteReady(NPP instance, NPStream* stream); +int32_t NP_LOADDS NPP_Write(NPP instance, NPStream* stream, int32_t offset, + int32_t len, void* buffer); +void NP_LOADDS NPP_StreamAsFile(NPP instance, NPStream* stream, + const char* fname); +void NP_LOADDS NPP_Print(NPP instance, NPPrint* platformPrint); +int16_t NP_LOADDS NPP_HandleEvent(NPP instance, void* event); +void NP_LOADDS NPP_URLNotify(NPP instance, const char* url, + NPReason reason, void* notifyData); +jref NP_LOADDS NPP_GetJavaClass(void); +NPError NP_LOADDS NPP_GetValue(NPP instance, NPPVariable variable, void *value); +NPError NP_LOADDS NPP_SetValue(NPP instance, NPNVariable variable, void *value); +NPBool NP_LOADDS NPP_GotFocus(NPP instance, NPFocusDirection direction); +void NP_LOADDS NPP_LostFocus(NPP instance); +void NP_LOADDS NPP_URLRedirectNotify(NPP instance, const char* url, int32_t status, void* notifyData); +NPError NP_LOADDS NPP_ClearSiteData(const char* site, uint64_t flags, uint64_t maxAge); +char** NP_LOADDS NPP_GetSitesWithData(void); + +/* NPN_* functions are provided by the navigator and called by the plugin. */ +void NP_LOADDS NPN_Version(int* plugin_major, int* plugin_minor, + int* netscape_major, int* netscape_minor); +NPError NP_LOADDS NPN_GetURLNotify(NPP instance, const char* url, + const char* target, void* notifyData); +NPError NP_LOADDS NPN_GetURL(NPP instance, const char* url, + const char* target); +NPError NP_LOADDS NPN_PostURLNotify(NPP instance, const char* url, + const char* target, uint32_t len, + const char* buf, NPBool file, + void* notifyData); +NPError NP_LOADDS NPN_PostURL(NPP instance, const char* url, + const char* target, uint32_t len, + const char* buf, NPBool file); +NPError NP_LOADDS NPN_RequestRead(NPStream* stream, NPByteRange* rangeList); +NPError NP_LOADDS NPN_NewStream(NPP instance, NPMIMEType type, + const char* target, NPStream** stream); +int32_t NP_LOADDS NPN_Write(NPP instance, NPStream* stream, int32_t len, + void* buffer); +NPError NP_LOADDS NPN_DestroyStream(NPP instance, NPStream* stream, + NPReason reason); +void NP_LOADDS NPN_Status(NPP instance, const char* message); +const char* NP_LOADDS NPN_UserAgent(NPP instance); +void* NP_LOADDS NPN_MemAlloc(uint32_t size); +void NP_LOADDS NPN_MemFree(void* ptr); +uint32_t NP_LOADDS NPN_MemFlush(uint32_t size); +void NP_LOADDS NPN_ReloadPlugins(NPBool reloadPages); +JRIEnv* NP_LOADDS NPN_GetJavaEnv(void); +jref NP_LOADDS NPN_GetJavaPeer(NPP instance); +NPError NP_LOADDS NPN_GetValue(NPP instance, NPNVariable variable, + void *value); +NPError NP_LOADDS NPN_SetValue(NPP instance, NPPVariable variable, + void *value); +void NP_LOADDS NPN_InvalidateRect(NPP instance, NPRect *invalidRect); +void NP_LOADDS NPN_InvalidateRegion(NPP instance, + NPRegion invalidRegion); +void NP_LOADDS NPN_ForceRedraw(NPP instance); +void NP_LOADDS NPN_PushPopupsEnabledState(NPP instance, NPBool enabled); +void NP_LOADDS NPN_PopPopupsEnabledState(NPP instance); +void NP_LOADDS NPN_PluginThreadAsyncCall(NPP instance, + void (*func) (void *), + void *userData); +NPError NP_LOADDS NPN_GetValueForURL(NPP instance, NPNURLVariable variable, + const char *url, char **value, + uint32_t *len); +NPError NP_LOADDS NPN_SetValueForURL(NPP instance, NPNURLVariable variable, + const char *url, const char *value, + uint32_t len); +NPError NP_LOADDS NPN_GetAuthenticationInfo(NPP instance, + const char *protocol, + const char *host, int32_t port, + const char *scheme, + const char *realm, + char **username, uint32_t *ulen, + char **password, + uint32_t *plen); +uint32_t NP_LOADDS NPN_ScheduleTimer(NPP instance, uint32_t interval, NPBool repeat, void (*timerFunc)(NPP npp, uint32_t timerID)); +void NP_LOADDS NPN_UnscheduleTimer(NPP instance, uint32_t timerID); +NPError NP_LOADDS NPN_PopUpContextMenu(NPP instance, NPMenu* menu); +NPBool NP_LOADDS NPN_ConvertPoint(NPP instance, double sourceX, double sourceY, NPCoordinateSpace sourceSpace, double *destX, double *destY, NPCoordinateSpace destSpace); + +#ifdef __cplusplus +} /* end extern "C" */ +#endif + +#endif /* RC_INVOKED */ +#if defined(__OS2__) +#pragma pack() +#endif + +#endif /* npapi_h_ */ diff --git a/applications/hbbtvplayer/hbbtvbrowserplugin/src/webkit-plugin-header/npfunctions.h b/applications/hbbtvplayer/hbbtvbrowserplugin/src/webkit-plugin-header/npfunctions.h new file mode 100644 index 0000000..c54e473 --- /dev/null +++ b/applications/hbbtvplayer/hbbtvbrowserplugin/src/webkit-plugin-header/npfunctions.h @@ -0,0 +1,223 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef NPFUNCTIONS_H +#define NPFUNCTIONS_H + + +#include "npruntime.h" +#include "npapi.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(XP_WIN) +#define EXPORTED_CALLBACK(_type, _name) _type (__stdcall * _name) +#else +#define EXPORTED_CALLBACK(_type, _name) _type (* _name) +#endif + +typedef NPError (*NPN_GetURLNotifyProcPtr)(NPP instance, const char* URL, const char* window, void* notifyData); +typedef NPError (*NPN_PostURLNotifyProcPtr)(NPP instance, const char* URL, const char* window, uint32_t len, const char* buf, NPBool file, void* notifyData); +typedef NPError (*NPN_RequestReadProcPtr)(NPStream* stream, NPByteRange* rangeList); +typedef NPError (*NPN_NewStreamProcPtr)(NPP instance, NPMIMEType type, const char* window, NPStream** stream); +typedef int32_t (*NPN_WriteProcPtr)(NPP instance, NPStream* stream, int32_t len, void* buffer); +typedef NPError (*NPN_DestroyStreamProcPtr)(NPP instance, NPStream* stream, NPReason reason); +typedef void (*NPN_StatusProcPtr)(NPP instance, const char* message); +typedef const char*(*NPN_UserAgentProcPtr)(NPP instance); +typedef void* (*NPN_MemAllocProcPtr)(uint32_t size); +typedef void (*NPN_MemFreeProcPtr)(void* ptr); +typedef uint32_t (*NPN_MemFlushProcPtr)(uint32_t size); +typedef void (*NPN_ReloadPluginsProcPtr)(NPBool reloadPages); +typedef NPError (*NPN_GetValueProcPtr)(NPP instance, NPNVariable variable, void *ret_value); +typedef NPError (*NPN_SetValueProcPtr)(NPP instance, NPPVariable variable, void *value); +typedef void (*NPN_InvalidateRectProcPtr)(NPP instance, NPRect *rect); +typedef void (*NPN_InvalidateRegionProcPtr)(NPP instance, NPRegion region); +typedef void (*NPN_ForceRedrawProcPtr)(NPP instance); +typedef NPError (*NPN_GetURLProcPtr)(NPP instance, const char* URL, const char* window); +typedef NPError (*NPN_PostURLProcPtr)(NPP instance, const char* URL, const char* window, uint32_t len, const char* buf, NPBool file); +typedef void* (*NPN_GetJavaEnvProcPtr)(void); +typedef void* (*NPN_GetJavaPeerProcPtr)(NPP instance); +typedef void (*NPN_PushPopupsEnabledStateProcPtr)(NPP instance, NPBool enabled); +typedef void (*NPN_PopPopupsEnabledStateProcPtr)(NPP instance); +typedef void (*NPN_PluginThreadAsyncCallProcPtr)(NPP npp, void (*func)(void *), void *userData); +typedef NPError (*NPN_GetValueForURLProcPtr)(NPP npp, NPNURLVariable variable, const char* url, char** value, uint32_t* len); +typedef NPError (*NPN_SetValueForURLProcPtr)(NPP npp, NPNURLVariable variable, const char* url, const char* value, uint32_t len); +typedef NPError (*NPN_GetAuthenticationInfoProcPtr)(NPP npp, const char* protocol, const char* host, int32_t port, const char* scheme, const char *realm, char** username, uint32_t* ulen, char** password, uint32_t* plen); + +typedef uint32_t (*NPN_ScheduleTimerProcPtr)(NPP npp, uint32_t interval, NPBool repeat, void (*timerFunc)(NPP npp, uint32_t timerID)); +typedef void (*NPN_UnscheduleTimerProcPtr)(NPP npp, uint32_t timerID); +typedef NPError (*NPN_PopUpContextMenuProcPtr)(NPP instance, NPMenu* menu); +typedef NPBool (*NPN_ConvertPointProcPtr)(NPP npp, double sourceX, double sourceY, NPCoordinateSpace sourceSpace, double *destX, double *destY, NPCoordinateSpace destSpace); + +typedef void (*NPN_ReleaseVariantValueProcPtr) (NPVariant *variant); + +typedef NPIdentifier (*NPN_GetStringIdentifierProcPtr) (const NPUTF8 *name); +typedef void (*NPN_GetStringIdentifiersProcPtr) (const NPUTF8 **names, int32_t nameCount, NPIdentifier *identifiers); +typedef NPIdentifier (*NPN_GetIntIdentifierProcPtr) (int32_t intid); +typedef int32_t (*NPN_IntFromIdentifierProcPtr) (NPIdentifier identifier); +typedef bool (*NPN_IdentifierIsStringProcPtr) (NPIdentifier identifier); +typedef NPUTF8 *(*NPN_UTF8FromIdentifierProcPtr) (NPIdentifier identifier); + +typedef NPObject* (*NPN_CreateObjectProcPtr) (NPP, NPClass *aClass); +typedef NPObject* (*NPN_RetainObjectProcPtr) (NPObject *obj); +typedef void (*NPN_ReleaseObjectProcPtr) (NPObject *obj); +typedef bool (*NPN_InvokeProcPtr) (NPP npp, NPObject *obj, NPIdentifier methodName, const NPVariant *args, unsigned argCount, NPVariant *result); +typedef bool (*NPN_InvokeDefaultProcPtr) (NPP npp, NPObject *obj, const NPVariant *args, unsigned argCount, NPVariant *result); +typedef bool (*NPN_EvaluateProcPtr) (NPP npp, NPObject *obj, NPString *script, NPVariant *result); +typedef bool (*NPN_GetPropertyProcPtr) (NPP npp, NPObject *obj, NPIdentifier propertyName, NPVariant *result); +typedef bool (*NPN_SetPropertyProcPtr) (NPP npp, NPObject *obj, NPIdentifier propertyName, const NPVariant *value); +typedef bool (*NPN_HasPropertyProcPtr) (NPP, NPObject *npobj, NPIdentifier propertyName); +typedef bool (*NPN_HasMethodProcPtr) (NPP npp, NPObject *npobj, NPIdentifier methodName); +typedef bool (*NPN_RemovePropertyProcPtr) (NPP npp, NPObject *obj, NPIdentifier propertyName); +typedef void (*NPN_SetExceptionProcPtr) (NPObject *obj, const NPUTF8 *message); +typedef bool (*NPN_EnumerateProcPtr) (NPP npp, NPObject *npobj, NPIdentifier **identifier, uint32_t *count); +typedef bool (*NPN_ConstructProcPtr)(NPP npp, NPObject* obj, const NPVariant *args, uint32_t argCount, NPVariant *result); + +typedef NPError (*NPP_NewProcPtr)(NPMIMEType pluginType, NPP instance, uint16_t mode, int16_t argc, char* argn[], char* argv[], NPSavedData* saved); +typedef NPError (*NPP_DestroyProcPtr)(NPP instance, NPSavedData** save); +typedef NPError (*NPP_SetWindowProcPtr)(NPP instance, NPWindow* window); +typedef NPError (*NPP_NewStreamProcPtr)(NPP instance, NPMIMEType type, NPStream* stream, NPBool seekable, uint16_t* stype); +typedef NPError (*NPP_DestroyStreamProcPtr)(NPP instance, NPStream* stream, NPReason reason); +typedef void (*NPP_StreamAsFileProcPtr)(NPP instance, NPStream* stream, const char* fname); +typedef int32_t (*NPP_WriteReadyProcPtr)(NPP instance, NPStream* stream); +typedef int32_t (*NPP_WriteProcPtr)(NPP instance, NPStream* stream, int32_t offset, int32_t len, void* buffer); +typedef void (*NPP_PrintProcPtr)(NPP instance, NPPrint* platformPrint); +typedef int16_t (*NPP_HandleEventProcPtr)(NPP instance, void* event); +typedef void (*NPP_URLNotifyProcPtr)(NPP instance, const char* URL, NPReason reason, void* notifyData); +typedef NPError (*NPP_GetValueProcPtr)(NPP instance, NPPVariable variable, void *ret_value); +typedef NPError (*NPP_SetValueProcPtr)(NPP instance, NPNVariable variable, void *value); +typedef NPBool (*NPP_GotFocusPtr)(NPP instance, NPFocusDirection direction); +typedef void (*NPP_LostFocusPtr)(NPP instance); +typedef void (*NPP_URLRedirectNotifyPtr)(NPP instance, const char* url, int32_t status, void* notifyData); +typedef NPError (*NPP_ClearSiteDataPtr)(const char* site, uint64_t flags, uint64_t maxAge); +typedef char** (*NPP_GetSitesWithDataPtr)(void); + +typedef void *(*NPP_GetJavaClassProcPtr)(void); +typedef void* JRIGlobalRef; //not using this right now + +typedef struct _NPNetscapeFuncs { + uint16_t size; + uint16_t version; + + NPN_GetURLProcPtr geturl; + NPN_PostURLProcPtr posturl; + NPN_RequestReadProcPtr requestread; + NPN_NewStreamProcPtr newstream; + NPN_WriteProcPtr write; + NPN_DestroyStreamProcPtr destroystream; + NPN_StatusProcPtr status; + NPN_UserAgentProcPtr uagent; + NPN_MemAllocProcPtr memalloc; + NPN_MemFreeProcPtr memfree; + NPN_MemFlushProcPtr memflush; + NPN_ReloadPluginsProcPtr reloadplugins; + NPN_GetJavaEnvProcPtr getJavaEnv; + NPN_GetJavaPeerProcPtr getJavaPeer; + NPN_GetURLNotifyProcPtr geturlnotify; + NPN_PostURLNotifyProcPtr posturlnotify; + NPN_GetValueProcPtr getvalue; + NPN_SetValueProcPtr setvalue; + NPN_InvalidateRectProcPtr invalidaterect; + NPN_InvalidateRegionProcPtr invalidateregion; + NPN_ForceRedrawProcPtr forceredraw; + + NPN_GetStringIdentifierProcPtr getstringidentifier; + NPN_GetStringIdentifiersProcPtr getstringidentifiers; + NPN_GetIntIdentifierProcPtr getintidentifier; + NPN_IdentifierIsStringProcPtr identifierisstring; + NPN_UTF8FromIdentifierProcPtr utf8fromidentifier; + NPN_IntFromIdentifierProcPtr intfromidentifier; + NPN_CreateObjectProcPtr createobject; + NPN_RetainObjectProcPtr retainobject; + NPN_ReleaseObjectProcPtr releaseobject; + NPN_InvokeProcPtr invoke; + NPN_InvokeDefaultProcPtr invokeDefault; + NPN_EvaluateProcPtr evaluate; + NPN_GetPropertyProcPtr getproperty; + NPN_SetPropertyProcPtr setproperty; + NPN_RemovePropertyProcPtr removeproperty; + NPN_HasPropertyProcPtr hasproperty; + NPN_HasMethodProcPtr hasmethod; + NPN_ReleaseVariantValueProcPtr releasevariantvalue; + NPN_SetExceptionProcPtr setexception; + NPN_PushPopupsEnabledStateProcPtr pushpopupsenabledstate; + NPN_PopPopupsEnabledStateProcPtr poppopupsenabledstate; + NPN_EnumerateProcPtr enumerate; + NPN_PluginThreadAsyncCallProcPtr pluginthreadasynccall; + NPN_ConstructProcPtr construct; + NPN_GetValueForURLProcPtr getvalueforurl; + NPN_SetValueForURLProcPtr setvalueforurl; + NPN_GetAuthenticationInfoProcPtr getauthenticationinfo; + NPN_ScheduleTimerProcPtr scheduletimer; + NPN_UnscheduleTimerProcPtr unscheduletimer; + NPN_PopUpContextMenuProcPtr popupcontextmenu; + NPN_ConvertPointProcPtr convertpoint; +} NPNetscapeFuncs; + +typedef struct _NPPluginFuncs { + uint16_t size; + uint16_t version; + NPP_NewProcPtr newp; + NPP_DestroyProcPtr destroy; + NPP_SetWindowProcPtr setwindow; + NPP_NewStreamProcPtr newstream; + NPP_DestroyStreamProcPtr destroystream; + NPP_StreamAsFileProcPtr asfile; + NPP_WriteReadyProcPtr writeready; + NPP_WriteProcPtr write; + NPP_PrintProcPtr print; + NPP_HandleEventProcPtr event; + NPP_URLNotifyProcPtr urlnotify; + JRIGlobalRef javaClass; + NPP_GetValueProcPtr getvalue; + NPP_SetValueProcPtr setvalue; + NPP_GotFocusPtr gotfocus; + NPP_LostFocusPtr lostfocus; + NPP_URLRedirectNotifyPtr urlredirectnotify; + NPP_ClearSiteDataPtr clearsitedata; + NPP_GetSitesWithDataPtr getsiteswithdata; +} NPPluginFuncs; + +typedef EXPORTED_CALLBACK(NPError, NP_GetEntryPointsFuncPtr)(NPPluginFuncs*); +typedef EXPORTED_CALLBACK(void, NPP_ShutdownProcPtr)(void); + +#if defined(XP_MACOSX) +typedef void (*BP_CreatePluginMIMETypesPreferencesFuncPtr)(void); +typedef NPError (*MainFuncPtr)(NPNetscapeFuncs*, NPPluginFuncs*, NPP_ShutdownProcPtr*); +#endif + +#if defined(XP_UNIX) +typedef EXPORTED_CALLBACK(NPError, NP_InitializeFuncPtr)(NPNetscapeFuncs*, NPPluginFuncs*); +typedef EXPORTED_CALLBACK(char*, NP_GetMIMEDescriptionFuncPtr)(void); +#else +typedef EXPORTED_CALLBACK(NPError, NP_InitializeFuncPtr)(NPNetscapeFuncs*); +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/applications/hbbtvplayer/hbbtvbrowserplugin/src/webkit-plugin-header/npruntime.h b/applications/hbbtvplayer/hbbtvbrowserplugin/src/webkit-plugin-header/npruntime.h new file mode 100644 index 0000000..828a340 --- /dev/null +++ b/applications/hbbtvplayer/hbbtvbrowserplugin/src/webkit-plugin-header/npruntime.h @@ -0,0 +1,393 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * Copyright (c) 2004, Apple Computer, Inc. and The Mozilla Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the names of Apple Computer, Inc. ("Apple") or The Mozilla + * Foundation ("Mozilla") nor the names of their contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE, MOZILLA AND THEIR CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE, MOZILLA OR + * THEIR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef _NP_RUNTIME_H_ +#define _NP_RUNTIME_H_ + +#include "npapi.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + This API is used to facilitate binding code written in C to script + objects. The API in this header does not assume the presence of a + user agent. That is, it can be used to bind C code to scripting + environments outside of the context of a user agent. + + However, the normal use of the this API is in the context of a + scripting environment running in a browser or other user agent. + In particular it is used to support the extended Netscape + script-ability API for plugins (NP-SAP). NP-SAP is an extension + of the Netscape plugin API. As such we have adopted the use of + the "NP" prefix for this API. + + The following NP{N|P}Variables were added to the Netscape plugin + API (in npapi.h): + + NPNVWindowNPObject + NPNVPluginElementNPObject + NPPVpluginScriptableNPObject + + These variables are exposed through NPN_GetValue() and + NPP_GetValue() (respectively) and are used to establish the + initial binding between the user agent and native code. The DOM + objects in the user agent can be examined and manipulated using + the NPN_ functions that operate on NPObjects described in this + header. + + To the extent possible the assumptions about the scripting + language used by the scripting environment have been minimized. +*/ + +#define NP_BEGIN_MACRO do { +#define NP_END_MACRO } while (0) + +/* + Objects (non-primitive data) passed between 'C' and script is + always wrapped in an NPObject. The 'interface' of an NPObject is + described by an NPClass. +*/ +typedef struct NPObject NPObject; +typedef struct NPClass NPClass; + +typedef char NPUTF8; +typedef struct _NPString { + const NPUTF8 *UTF8Characters; + uint32_t UTF8Length; +} NPString; + +typedef enum { + NPVariantType_Void, + NPVariantType_Null, + NPVariantType_Bool, + NPVariantType_Int32, + NPVariantType_Double, + NPVariantType_String, + NPVariantType_Object +} NPVariantType; + +typedef struct _NPVariant { + NPVariantType type; + union { + bool boolValue; + int32_t intValue; + double doubleValue; + NPString stringValue; + NPObject *objectValue; + } value; +} NPVariant; + +/* + NPN_ReleaseVariantValue is called on all 'out' parameters + references. Specifically it is to be called on variants that own + their value, as is the case with all non-const NPVariant* + arguments after a successful call to any methods (except this one) + in this API. + + After calling NPN_ReleaseVariantValue, the type of the variant + will be NPVariantType_Void. +*/ +void NPN_ReleaseVariantValue(NPVariant *variant); + +#define NPVARIANT_IS_VOID(_v) ((_v).type == NPVariantType_Void) +#define NPVARIANT_IS_NULL(_v) ((_v).type == NPVariantType_Null) +#define NPVARIANT_IS_BOOLEAN(_v) ((_v).type == NPVariantType_Bool) +#define NPVARIANT_IS_INT32(_v) ((_v).type == NPVariantType_Int32) +#define NPVARIANT_IS_DOUBLE(_v) ((_v).type == NPVariantType_Double) +#define NPVARIANT_IS_STRING(_v) ((_v).type == NPVariantType_String) +#define NPVARIANT_IS_OBJECT(_v) ((_v).type == NPVariantType_Object) + +#define NPVARIANT_TO_BOOLEAN(_v) ((_v).value.boolValue) +#define NPVARIANT_TO_INT32(_v) ((_v).value.intValue) +#define NPVARIANT_TO_DOUBLE(_v) ((_v).value.doubleValue) +#define NPVARIANT_TO_STRING(_v) ((_v).value.stringValue) +#define NPVARIANT_TO_OBJECT(_v) ((_v).value.objectValue) + +#define VOID_TO_NPVARIANT(_v) \ +NP_BEGIN_MACRO \ + (_v).type = NPVariantType_Void; \ + (_v).value.objectValue = NULL; \ +NP_END_MACRO + +#define NULL_TO_NPVARIANT(_v) \ +NP_BEGIN_MACRO \ + (_v).type = NPVariantType_Null; \ + (_v).value.objectValue = NULL; \ +NP_END_MACRO + +#define BOOLEAN_TO_NPVARIANT(_val, _v) \ +NP_BEGIN_MACRO \ + (_v).type = NPVariantType_Bool; \ + (_v).value.boolValue = !!(_val); \ +NP_END_MACRO + +#define INT32_TO_NPVARIANT(_val, _v) \ +NP_BEGIN_MACRO \ + (_v).type = NPVariantType_Int32; \ + (_v).value.intValue = _val; \ +NP_END_MACRO + +#define DOUBLE_TO_NPVARIANT(_val, _v) \ +NP_BEGIN_MACRO \ + (_v).type = NPVariantType_Double; \ + (_v).value.doubleValue = _val; \ +NP_END_MACRO + +#define STRINGZ_TO_NPVARIANT(_val, _v) \ +NP_BEGIN_MACRO \ + (_v).type = NPVariantType_String; \ + NPString str = { _val, uint32_t(strlen(_val)) }; \ + (_v).value.stringValue = str; \ +NP_END_MACRO + +#define STRINGN_TO_NPVARIANT(_val, _len, _v) \ +NP_BEGIN_MACRO \ + (_v).type = NPVariantType_String; \ + NPString str = { _val, uint32_t(_len) }; \ + (_v).value.stringValue = str; \ +NP_END_MACRO + +#define OBJECT_TO_NPVARIANT(_val, _v) \ +NP_BEGIN_MACRO \ + (_v).type = NPVariantType_Object; \ + (_v).value.objectValue = _val; \ +NP_END_MACRO + + +/* + Type mappings (JavaScript types have been used for illustration + purposes): + + JavaScript to C (NPVariant with type:) + undefined NPVariantType_Void + null NPVariantType_Null + Boolean NPVariantType_Bool + Number NPVariantType_Double or NPVariantType_Int32 + String NPVariantType_String + Object NPVariantType_Object + + C (NPVariant with type:) to JavaScript + NPVariantType_Void undefined + NPVariantType_Null null + NPVariantType_Bool Boolean + NPVariantType_Int32 Number + NPVariantType_Double Number + NPVariantType_String String + NPVariantType_Object Object +*/ + +typedef void *NPIdentifier; + +/* + NPObjects have methods and properties. Methods and properties are + identified with NPIdentifiers. These identifiers may be reflected + in script. NPIdentifiers can be either strings or integers, IOW, + methods and properties can be identified by either strings or + integers (i.e. foo["bar"] vs foo[1]). NPIdentifiers can be + compared using ==. In case of any errors, the requested + NPIdentifier(s) will be NULL. NPIdentifier lifetime is controlled + by the browser. Plugins do not need to worry about memory management + with regards to NPIdentifiers. +*/ +NPIdentifier NPN_GetStringIdentifier(const NPUTF8 *name); +void NPN_GetStringIdentifiers(const NPUTF8 **names, int32_t nameCount, + NPIdentifier *identifiers); +NPIdentifier NPN_GetIntIdentifier(int32_t intid); +bool NPN_IdentifierIsString(NPIdentifier identifier); + +/* + The NPUTF8 returned from NPN_UTF8FromIdentifier SHOULD be freed. +*/ +NPUTF8 *NPN_UTF8FromIdentifier(NPIdentifier identifier); + +/* + Get the integer represented by identifier. If identifier is not an + integer identifier, the behaviour is undefined. +*/ +int32_t NPN_IntFromIdentifier(NPIdentifier identifier); + +/* + NPObject behavior is implemented using the following set of + callback functions. + + The NPVariant *result argument of these functions (where + applicable) should be released using NPN_ReleaseVariantValue(). +*/ +typedef NPObject *(*NPAllocateFunctionPtr)(NPP npp, NPClass *aClass); +typedef void (*NPDeallocateFunctionPtr)(NPObject *npobj); +typedef void (*NPInvalidateFunctionPtr)(NPObject *npobj); +typedef bool (*NPHasMethodFunctionPtr)(NPObject *npobj, NPIdentifier name); +typedef bool (*NPInvokeFunctionPtr)(NPObject *npobj, NPIdentifier name, + const NPVariant *args, uint32_t argCount, + NPVariant *result); +typedef bool (*NPInvokeDefaultFunctionPtr)(NPObject *npobj, + const NPVariant *args, + uint32_t argCount, + NPVariant *result); +typedef bool (*NPHasPropertyFunctionPtr)(NPObject *npobj, NPIdentifier name); +typedef bool (*NPGetPropertyFunctionPtr)(NPObject *npobj, NPIdentifier name, + NPVariant *result); +typedef bool (*NPSetPropertyFunctionPtr)(NPObject *npobj, NPIdentifier name, + const NPVariant *value); +typedef bool (*NPRemovePropertyFunctionPtr)(NPObject *npobj, + NPIdentifier name); +typedef bool (*NPEnumerationFunctionPtr)(NPObject *npobj, NPIdentifier **value, + uint32_t *count); +typedef bool (*NPConstructFunctionPtr)(NPObject *npobj, + const NPVariant *args, + uint32_t argCount, + NPVariant *result); + +/* + NPObjects returned by create, retain, invoke, and getProperty pass + a reference count to the caller. That is, the callee adds a + reference count which passes to the caller. It is the caller's + responsibility to release the returned object. + + NPInvokeFunctionPtr function may return 0 to indicate a void + result. + + NPInvalidateFunctionPtr is called by the scripting environment + when the native code is shutdown. Any attempt to message a + NPObject instance after the invalidate callback has been + called will result in undefined behavior, even if the native code + is still retaining those NPObject instances. (The runtime + will typically return immediately, with 0 or NULL, from an attempt + to dispatch to a NPObject, but this behavior should not be + depended upon.) + + The NPEnumerationFunctionPtr function may pass an array of + NPIdentifiers back to the caller. The callee allocs the memory of + the array using NPN_MemAlloc(), and it's the caller's responsibility + to release it using NPN_MemFree(). +*/ +struct NPClass +{ + uint32_t structVersion; + NPAllocateFunctionPtr allocate; + NPDeallocateFunctionPtr deallocate; + NPInvalidateFunctionPtr invalidate; + NPHasMethodFunctionPtr hasMethod; + NPInvokeFunctionPtr invoke; + NPInvokeDefaultFunctionPtr invokeDefault; + NPHasPropertyFunctionPtr hasProperty; + NPGetPropertyFunctionPtr getProperty; + NPSetPropertyFunctionPtr setProperty; + NPRemovePropertyFunctionPtr removeProperty; + NPEnumerationFunctionPtr enumerate; + NPConstructFunctionPtr construct; +}; + +#define NP_CLASS_STRUCT_VERSION 3 + +#define NP_CLASS_STRUCT_VERSION_ENUM 2 +#define NP_CLASS_STRUCT_VERSION_CTOR 3 + +#define NP_CLASS_STRUCT_VERSION_HAS_ENUM(npclass) \ + ((npclass)->structVersion >= NP_CLASS_STRUCT_VERSION_ENUM) + +#define NP_CLASS_STRUCT_VERSION_HAS_CTOR(npclass) \ + ((npclass)->structVersion >= NP_CLASS_STRUCT_VERSION_CTOR) + +struct NPObject { + NPClass *_class; + uint32_t referenceCount; + /* + * Additional space may be allocated here by types of NPObjects + */ +}; + +/* + If the class has an allocate function, NPN_CreateObject invokes + that function, otherwise a NPObject is allocated and + returned. This method will initialize the referenceCount member of + the NPObject to 1. +*/ +NPObject *NPN_CreateObject(NPP npp, NPClass *aClass); + +/* + Increment the NPObject's reference count. +*/ +NPObject *NPN_RetainObject(NPObject *npobj); + +/* + Decremented the NPObject's reference count. If the reference + count goes to zero, the class's destroy function is invoke if + specified, otherwise the object is freed directly. +*/ +void NPN_ReleaseObject(NPObject *npobj); + +/* + Functions to access script objects represented by NPObject. + + Calls to script objects are synchronous. If a function returns a + value, it will be supplied via the result NPVariant + argument. Successful calls will return true, false will be + returned in case of an error. + + Calls made from plugin code to script must be made from the thread + on which the plugin was initialized. +*/ + +bool NPN_Invoke(NPP npp, NPObject *npobj, NPIdentifier methodName, + const NPVariant *args, uint32_t argCount, NPVariant *result); +bool NPN_InvokeDefault(NPP npp, NPObject *npobj, const NPVariant *args, + uint32_t argCount, NPVariant *result); +bool NPN_Evaluate(NPP npp, NPObject *npobj, NPString *script, + NPVariant *result); +bool NPN_GetProperty(NPP npp, NPObject *npobj, NPIdentifier propertyName, + NPVariant *result); +bool NPN_SetProperty(NPP npp, NPObject *npobj, NPIdentifier propertyName, + const NPVariant *value); +bool NPN_RemoveProperty(NPP npp, NPObject *npobj, NPIdentifier propertyName); +bool NPN_HasProperty(NPP npp, NPObject *npobj, NPIdentifier propertyName); +bool NPN_HasMethod(NPP npp, NPObject *npobj, NPIdentifier methodName); +bool NPN_Enumerate(NPP npp, NPObject *npobj, NPIdentifier **identifier, + uint32_t *count); +bool NPN_Construct(NPP npp, NPObject *npobj, const NPVariant *args, + uint32_t argCount, NPVariant *result); + +/* + NPN_SetException may be called to trigger a script exception upon + return from entry points into NPObjects. Typical usage: + + NPN_SetException (npobj, message); +*/ +void NPN_SetException(NPObject *npobj, const NPUTF8 *message); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/applications/hbbtvplayer/hbbtvbrowserplugin/src/webkit-plugin-header/nptypes.h b/applications/hbbtvplayer/hbbtvbrowserplugin/src/webkit-plugin-header/nptypes.h new file mode 100644 index 0000000..4ebeb97 --- /dev/null +++ b/applications/hbbtvplayer/hbbtvbrowserplugin/src/webkit-plugin-header/nptypes.h @@ -0,0 +1,122 @@ +/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * mozilla.org. + * Portions created by the Initial Developer are Copyright (C) 2004 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Johnny Stenback (Original author) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef nptypes_h_ +#define nptypes_h_ + +/* + * Header file for ensuring that C99 types ([u]int32_t, [u]int64_t and bool) and + * true/false macros are available. + */ + +#if defined(WIN32) || defined(OS2) + /* + * Win32 and OS/2 don't know C99, so define [u]int_16/32/64 here. The bool + * is predefined tho, both in C and C++. + */ + typedef short int16_t; + typedef unsigned short uint16_t; + typedef int int32_t; + typedef unsigned int uint32_t; + typedef long long int64_t; + typedef unsigned long long uint64_t; + +#elif defined(_AIX) || defined(__sun) || defined(__osf__) || defined(IRIX) || defined(HPUX) + /* + * AIX and SunOS ship a inttypes.h header that defines [u]int32_t, + * but not bool for C. + */ + #include + + #ifndef __cplusplus + typedef int bool; + #define true 1 + #define false 0 + #endif +#elif defined(bsdi) || defined(FREEBSD) || defined(OPENBSD) + /* + * BSD/OS, FreeBSD, and OpenBSD ship sys/types.h that define int32_t and + * u_int32_t. + */ + #include + + /* + * BSD/OS ships no header that defines uint32_t, nor bool (for C) + */ + #if defined(bsdi) + typedef u_int32_t uint32_t; + typedef u_int64_t uint64_t; + + #if !defined(__cplusplus) + typedef int bool; + #define true 1 + #define false 0 + #endif + #else + /* + * FreeBSD and OpenBSD define uint32_t and bool. + */ + #include + #include + #endif +#elif defined(BEOS) + #include +#else + /* + * For those that ship a standard C99 stdint.h header file, include + * it. Can't do the same for stdbool.h tho, since some systems ship + * with a stdbool.h file that doesn't compile! + */ + #include + + #ifndef __cplusplus + #if !defined(__GNUC__) || (__GNUC__ > 2 || __GNUC_MINOR__ > 95) + #include + #else + /* + * GCC 2.91 can't deal with a typedef for bool, but a #define + * works. + */ + #define bool int + #define true 1 + #define false 0 + #endif + #endif +#endif + +#endif /* nptypes_h_ */ diff --git a/applications/hbbtvplayer/hbbtvterminal/autogen.sh b/applications/hbbtvplayer/hbbtvterminal/autogen.sh new file mode 100644 index 0000000..cad4e11 --- /dev/null +++ b/applications/hbbtvplayer/hbbtvterminal/autogen.sh @@ -0,0 +1,6 @@ + +#! /bin/sh + +autoreconf --install -v + +./configure diff --git a/applications/hbbtvplayer/hbbtvterminal/configure.ac b/applications/hbbtvplayer/hbbtvterminal/configure.ac new file mode 100644 index 0000000..866a299 --- /dev/null +++ b/applications/hbbtvplayer/hbbtvterminal/configure.ac @@ -0,0 +1,40 @@ +########################################################################################### +## +## Copyright 2011 Telecom Paristech +## Author : Stanislas Selle +## +########################################################################################### + +### Configure.ac for hbbtvterminal + + +AC_INIT([hbbtvterminal], [0.1.0], [stanislas.selle@telecom-paristech.fr]) + +AC_CONFIG_AUX_DIR([build-aux]) + +AC_CONFIG_MACRO_DIR([m4]) +m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) + +AM_INIT_AUTOMAKE([foreign -Wall -Werror]) + +#check programs +AC_PROG_CXX + +#check modules +PKG_CHECK_MODULES([GLIB], [glib-2.0 >= 2.28.0]) +PKG_CHECK_MODULES([GTK], [gtk+-2.0 >= 2.20.1]) +PKG_CHECK_MODULES([WEBKIT], [webkit-1.0 >= 1.5.2]) +PKG_CHECK_MODULES([PIXMAN], [pixman-1 >= 0.16.4]) +PKG_CHECK_MODULES([HBBTVBROWSERPLUGIN], [hbbtvbrowserplugin >= 0.0.3]) + +# Checks for header files. +AC_PATH_X + +# check header +AC_HEADER_STDBOOL + +# makefiles +AC_CONFIG_FILES([makefile src/makefile]) + + +AC_OUTPUT diff --git a/applications/hbbtvplayer/hbbtvterminal/hbbtv_terminal.vcproj b/applications/hbbtvplayer/hbbtvterminal/hbbtv_terminal.vcproj new file mode 100644 index 0000000..987a985 --- /dev/null +++ b/applications/hbbtvplayer/hbbtvterminal/hbbtv_terminal.vcproj @@ -0,0 +1,219 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/applications/hbbtvplayer/hbbtvterminal/makefile.am b/applications/hbbtvplayer/hbbtvterminal/makefile.am new file mode 100644 index 0000000..e21b6f2 --- /dev/null +++ b/applications/hbbtvplayer/hbbtvterminal/makefile.am @@ -0,0 +1,17 @@ + +SUBDIRS = src + +#TESTTS = /home/selle/ressources/mpeg2-ts-hbbtv/TPT/OpenHbb-ch4.ts +TESTTS = /home/selle/ressources/OpenHbbDemo-3ch2app/OpenHbbDemo-3ch2app.ts + + +TESTURL1 = http://cairhblog.free.fr/menu.html +TESTURL2 = http://aquila.enst.fr:8080/subwebsite/hbbtvtest/oha0005web/index.php +TESTLOG = /tmp/test-err.txt + +test : all + ./src/hbbtvterminal -input=$(TESTTS) +test1 : all + ./src/hbbtvterminal -input=$(TESTTS) -url=$(TESTURL1) +test2 : all + ./src/hbbtvterminal -input=$(TESTTS) -url=$(TESTURL2) diff --git a/applications/hbbtvplayer/hbbtvterminal/makefile.old b/applications/hbbtvplayer/hbbtvterminal/makefile.old new file mode 100644 index 0000000..b56fe1b --- /dev/null +++ b/applications/hbbtvplayer/hbbtvterminal/makefile.old @@ -0,0 +1,81 @@ +## +## Copyright : Telecom Paristech - 2011 +## +## Author : Stanislas Selle +## + +############################################################################### +## Programs + +COMPILER = g++ +DELETER = rm -f + + +CFLAGS = -g + +CFLAGS += -DXP_UNIX +CFLAGS += -fPIC + +############################################################################### +## Options, Flags and LinkS + +INCLUDEFLAGS = -I/usr/include/xulrunner-lastest/ \ + -I../../gpac_public/include/ \ + -I../hbbtvbrowserplugin/src/ \ + -I../../gpac_public/ + +LIBRARYPATHS = -L/usr/lib/xulrunner-devel-lastest/sdk/lib/ \ + -L../../gpac_public/bin/gcc/ \ + -L../hbbtvbrowserplugin/bin/ + +LIBRARYNAMES = -lgpac \ + -lmozjs \ + -lxpcom \ + -lxul \ + ../hbbtvbrowserplugin/bin/hbbtvbrowserplugin.so + + +LIBRARYFLAGS = $(LIBRARYPATHS) $(LIBRARYNAMES) + +FROMPKG = webkit-1.0 gtk+-2.0 pixman-1 + +ifneq ($(strip $(FROMPKG)),) +PKGFLAGS = `pkg-config --cflags $(FROMPKG) ` +PKGLIBS = `pkg-config --libs $(FROMPKG) ` +INCLUDEFLAGS += $(PKGFLAGS) +LIBRARYFLAGS += $(PKGLIBS) +endif + + +############################################################################### +## Files + + +MAINTARGET = bin/hbbtvterminal + +OBJ = obj/hbbtvterminal.o \ + obj/hbbtv_demux.o \ + obj/hbbtv_channel.o + +#TESTTS = /home/selle/ressources/OpenHbbDemo-3ch2app/OpenHbbDemo-3ch2app.ts +TESTTS = /home/selle/ressources/mpeg2-ts-hbbtv/TPT/OpenHbb-ch4.ts +TESTURL = http://aquila.enst.fr:8080/subwebsite/hbbtvtest/oha0005web/index.php +TESTLOG = /tmp/test-err.txt + +############################################################################### +## Rules + + +all : $(MAINTARGET) + +$(MAINTARGET) : $(OBJ) + $(COMPILER) $(CFLAGS) $(LIBRARYFLAGS) $(OBJ) -o $(MAINTARGET) + +obj/%.o : src/%.cpp + $(COMPILER) $(CFLAGS) $(INCLUDEFLAGS) -c $^ -o $@ + +test : all + $(MAINTARGET) -input=$(TESTTS) + +clean : + rm -f $(OBJ) $(MAINTARGET) diff --git a/applications/hbbtvplayer/hbbtvterminal/projectmanager/codeblocks/hbbtvterminal/HbbtvTerminal.cbp b/applications/hbbtvplayer/hbbtvterminal/projectmanager/codeblocks/hbbtvterminal/HbbtvTerminal.cbp new file mode 100644 index 0000000..8f32993 --- /dev/null +++ b/applications/hbbtvplayer/hbbtvterminal/projectmanager/codeblocks/hbbtvterminal/HbbtvTerminal.cbp @@ -0,0 +1,35 @@ + + + + + + diff --git a/applications/hbbtvplayer/hbbtvterminal/projectmanager/codeblocks/hbbtvterminal/HbbtvTerminal.layout b/applications/hbbtvplayer/hbbtvterminal/projectmanager/codeblocks/hbbtvterminal/HbbtvTerminal.layout new file mode 100644 index 0000000..c091bfa --- /dev/null +++ b/applications/hbbtvplayer/hbbtvterminal/projectmanager/codeblocks/hbbtvterminal/HbbtvTerminal.layout @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/applications/hbbtvplayer/hbbtvterminal/src/hbbtv_channel.cpp b/applications/hbbtvplayer/hbbtvterminal/src/hbbtv_channel.cpp new file mode 100644 index 0000000..37e4427 --- /dev/null +++ b/applications/hbbtvplayer/hbbtvterminal/src/hbbtv_channel.cpp @@ -0,0 +1,253 @@ +/* + * Copyright (c) 2010-2011 Telecom-Paristech + * All Rights Reserved + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Authors: Jonathan Sillan + * + */ + +#include "hbbtvterminal.h" + +static Bool app_priority_test(GF_M2TS_AIT_APPLICATION*,u8 app_priority,u8 app_transport, u8 MaxPriority); + +/* Constructor */ +Channel::Channel(u32 TSservice_ID, char* TSchannel_name){ + + u8 i; + service_ID = TSservice_ID; + if(TSchannel_name){ + channel_name = gf_strdup(channel_name); + }else{ + channel_name = NULL; + } + + video_ID = 0; + for(i=0 ; i= nb_chan_audio_stream){ + indice = 0; + } + current_audio_index = indice; + return audio_ID[indice]; +} + +u32 Channel::Get_ait_pid(){ + return AIT_PID; +} + +u32 Channel::Get_pmt_pid(){ + return PMT_PID; +} + +Bool Channel::Get_processed(){ + return processed; +} + +u32 Channel::Get_audio_index(){ + return current_audio_index; +} + +u32 Channel::Get_nb_chan_audio_stream(){ + return nb_chan_audio_stream; +} + +GF_M2TS_CHANNEL_APPLICATION_INFO*Channel::Get_App_info(){ + return ChannelApp; +} + +GF_M2TS_AIT_APPLICATION* Channel::App_to_play(Bool isConnected,u8 MaxPriority){ + u32 i; + Bool App_selected; + u32 app_index; + u8 app_priority; + u8 app_transport; + + App_selected = 0; + app_priority = 0; + app_index = 0; + app_transport = 0; + + for(i = 0 ; inb_application; i++){ + GF_M2TS_AIT_APPLICATION* App_info = (GF_M2TS_AIT_APPLICATION*)gf_list_get(ChannelApp->Application,i); + if((isConnected && App_info->broadband) || App_info->broadcast){ + if(App_info->application_control_code == AUTOSTART){ + if(app_priority_test(App_info,app_priority,isConnected,MaxPriority)){ + app_priority = App_info->priority; + app_index = i; + App_selected = 1; + if(!app_transport && App_info->broadband && isConnected){ + app_transport = BROADBAND; + }else{ + app_transport = BROADCAST; + } + + } + } + } + } + if(App_selected){ + GF_M2TS_AIT_APPLICATION* App_info = (GF_M2TS_AIT_APPLICATION*)gf_list_get(ChannelApp->Application,app_index); + return App_info; + } + return NULL; +} + +GF_M2TS_AIT_APPLICATION* Channel::Get_App(u32 application_id){ + u32 i; + + for(i = 0 ; inb_application; i++){ + GF_M2TS_AIT_APPLICATION* App_info = (GF_M2TS_AIT_APPLICATION*)gf_list_get(ChannelApp->Application,i); + if(App_info->application_id == application_id){ + return App_info; + } + } + return NULL; +} + + + +/* Class Fonction */ + +u32 Channel::Add_service_id(u32 service_id){ + if(service_id){ + service_ID = service_id; + return GF_OK; + } + return GF_BAD_PARAM; +} + +u32 Channel::Add_channel_name(char* chan_name){ + if(chan_name != NULL){ + channel_name = gf_strdup(chan_name); + return GF_OK; + } + return GF_BAD_PARAM; +} +u32 Channel::Add_video_ID(u32 video_index){ + if(video_index){ + video_ID = video_index; + return GF_OK; + } + return GF_BAD_PARAM; +} + +u32 Channel::Add_audio_ID(u32 audio_index){ + if(audio_index){ + u32 i; + for(i = 0; ipriority >= MaxPriority && (MaxPriority >0)) && + ((app_priority < App_info->priority) || (app_priority == App_info->priority)) + && ((isConnected && App_info->broadband)||App_info->broadcast)){ + return 1; + } + + return 0; +} diff --git a/applications/hbbtvplayer/hbbtvterminal/src/hbbtv_demux.cpp b/applications/hbbtvplayer/hbbtvterminal/src/hbbtv_demux.cpp new file mode 100644 index 0000000..9bdaebf --- /dev/null +++ b/applications/hbbtvplayer/hbbtvterminal/src/hbbtv_demux.cpp @@ -0,0 +1,388 @@ +/* + * Copyright (c) 2010-2011 Telecom-Paristech + * All Rights Reserved + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Authors: Jonathan Sillan + * + */ + +#include "hbbtvterminal.h" + +static u32 New_ait_received(HbbtvDemuxer* hbbtv_demuxer,GF_M2TS_AIT *ait, char *data, u32 data_size, u32 table_id); +static u32 On_hbbtv_ait_section(sPlayerInterface* player_interf, GF_Event *event); +static u32 On_hbbtv_dsmcc_section(sPlayerInterface* player_interf, GF_Event *event); +static u32 On_hbbtv_get_tsdemuxer(sPlayerInterface* player_interf, GF_Event *event); + + + +/* Constructor */ +HbbtvDemuxer::HbbtvDemuxer(char *input_data, char* url, Bool dsmcc, Bool no_url,sPlayerInterface* player_interf) +{ + + Demuxts = NULL; + + Channels = gf_list_new(); + Input_data = gf_strdup(input_data); + user = player_interf; + player_interf->Demuxer = this; + ait_to_process = 0; + nb_prog_pmt_received = 0; + all_prog_pmt_received =0; + No_URL = 0; + Ignore_TS_URL = 0; + if(dsmcc){ + process_dsmcc = 1; + }else{ + process_dsmcc = 0; + } + + if(no_url){ + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("[HBBTVTerminal] No URL \n")); + No_URL = 1; + }else if(url){ + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("[HBBTVTerminal] Forced URL %s \n",url)); + Force_URL = gf_strdup(url); + Ignore_TS_URL = 1; + } + nb_ait = 0; + ts_demux_mutex = gf_mx_new("HBBTV_TS_Demux_Mutex"); + + ts_demux_thread = gf_th_new("HBBTV_TS_Demux_Thread"); + +} + +/* Destructor */ + +/* Getter */ + +GF_M2TS_Demuxer* HbbtvDemuxer::Get_Ts() +{ + return Demuxts; +} + +GF_List* HbbtvDemuxer::Get_AIT_To_Process_list() +{ + return Ait_To_Process; +} + +char* HbbtvDemuxer::Get_Input_data() +{ + return Input_data; +} + +Bool HbbtvDemuxer::Get_if_dsmcc_process() +{ + return process_dsmcc; +} + +GF_Thread * HbbtvDemuxer::Get_Demux_Thread() +{ + return ts_demux_thread; +} + +GF_Mutex * HbbtvDemuxer::Get_Demux_Mutex() +{ + return ts_demux_mutex; +} + +GF_List * HbbtvDemuxer::Get_ChannelList(){ + return Channels; +} + + +Channel* HbbtvDemuxer::Get_Channel(u32 service_id){ + u32 nb_channel,i; + + nb_channel = gf_list_count(Channels); + + for(i=0;iGet_service_id() == service_id){ + return Chan; + } + } + return NULL; +} + +void* HbbtvDemuxer::Get_User(){ + + return user; +} + + +char* HbbtvDemuxer::Get_Force_URL(){ + return Force_URL; +} + +Bool HbbtvDemuxer::Get_Ignore_TS_URL(){ + return Ignore_TS_URL; +} + +Bool HbbtvDemuxer::Get_ait_to_proces(){ + return ait_to_process; +} + + +/* Setter */ +void HbbtvDemuxer::Set_Ts(GF_M2TS_Demuxer *on_ts){ + Demuxts = on_ts; +} + +void HbbtvDemuxer::Set_ait_to_process(Bool on){ + ait_to_process = on; +} + +/* Class Fonction */ +GF_Err HbbtvDemuxer::Get_application_info(GF_M2TS_CHANNEL_APPLICATION_INFO*ChannelApp) +{ + if(!Ignore_TS_URL && !No_URL){ + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("[HBBTVTerminal] Get application for service %d \n",ChannelApp->service_id)); + return get_app_url((sPlayerInterface*)user,ChannelApp); + }else{ + + if(!No_URL){ + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("[HBBTVTerminal] Forced URL %s \n",Force_URL)); + }else{ + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("[HBBTVTerminal] URL blocked by the user. No Application to play \n"));; + } + return GF_OK; + + } +} + +Channel* HbbtvDemuxer::Zap_channel(u32 service_id,int zap){ + u32 count_list; + u32 i ; + + count_list = gf_list_count(Channels); +// GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("[HBBTVTerminal] list: %d\n",count_list)); + for(i=0;iGet_service_id() == service_id){ + if(zap != 0){ + /* zap is use for changing channel. It could be +1 or -1 in order to take the next/previous service */ + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("[HBBTVTerminal] i: %d zap %d\n",i,zap)); + + i= (i+zap+count_list)%count_list; /* loop on the channels */ + + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("[HBBTVTerminal] i: %d list %d\n",i,count_list)); + } + Channel* chan = ( Channel*)gf_list_get(Channels,i); + return chan; + } + } + + /* If the prog goes here that means Channels list is empty or + no object from this current service_id has already been processed */ +// GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("[HBBTVTerminal] add service_id: %d \n",service_id)); + Channel* chan = new Channel(service_id,NULL); + gf_list_add(Channels,chan); + return chan; +} + +void HbbtvDemuxer::Channel_check(){ + u32 count_list; + u32 i ; + + count_list = gf_list_count(Channels); + for(i=0;iCheck_Info_Done(); + } + } + + +int HbbtvDemuxer::Check_all_channel_info_received(int Timer){ + + if(!Demuxts){ + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("[HBBTV]No PAT received \n")); + return 0; + } + /* Check if all the PMT have been processed or wait until 5 secondes to starts the program */ + if(all_prog_pmt_received || Timer == 5){ + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("[HBBTVTerminal] nb_prog_pmt_received %d\n",nb_prog_pmt_received)); + return 1; + } + return 0; +} + +void HbbtvDemuxer::Create_Channel(GF_M2TS_Program *prog){ + + Channel* chan = new Channel(prog->number,NULL); + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("[HBBTVTerminal]Service number:%d\n",prog->number)); + chan->Add_pmt_pid(prog->pmt_pid); + gf_list_add(Channels,chan); + +} + +void HbbtvDemuxer::Check_PMT_Processing(){ + nb_prog_pmt_received++; + GF_LOG(GF_LOG_DEBUG, GF_LOG_MODULE, ("Demuxts->programs %d\n",gf_list_count(Demuxts->programs))); + if(nb_prog_pmt_received == gf_list_count(Demuxts->programs)){ + all_prog_pmt_received = 1; + } +} + + +/* Global Functions */ + +u32 DemuxThreadStart(HbbtvDemuxer *hbbtv_demuxer){ + + return gf_th_run(hbbtv_demuxer->Get_Demux_Thread(),DemuxStart,(void*)hbbtv_demuxer); +} + +u32 On_hbbtv_received_section(void *ptr, GF_Event *event){ + u32 e; + + e = GF_OK; + HbbtvDemuxer* hbbtv_demuxer = (HbbtvDemuxer*)ptr; + sPlayerInterface* player_interf = (sPlayerInterface*)hbbtv_demuxer->Get_User(); + + + if (event->type == GF_EVENT_FORWARDED) + { + if(event->forwarded_event.service_event_type == GF_M2TS_EVT_PAT_FOUND && hbbtv_demuxer->Get_Ts() == NULL){ + e = On_hbbtv_get_tsdemuxer(player_interf,event); + if(e != 0){ + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("[HBBTVTerminal] Error in receiving the TS Demuxer \n")); + + } + } + if(event->forwarded_event.service_event_type == GF_M2TS_EVT_AIT_FOUND){ + e = On_hbbtv_ait_section(player_interf,event); + } + if(event->forwarded_event.service_event_type == GF_M2TS_EVT_DSMCC_FOUND){ + e = On_hbbtv_dsmcc_section(player_interf,event); + } + if(event->forwarded_event.service_event_type == GF_M2TS_EVT_PMT_FOUND){ + gf_mx_p(hbbtv_demuxer->Get_Demux_Mutex()); + GF_M2TS_Program * prog = (GF_M2TS_Program*)event->forwarded_event.param; + GF_LOG(GF_LOG_DEBUG, GF_LOG_MODULE, ("PMT PID %d \n",prog->pmt_pid)); + hbbtv_demuxer->Create_Channel(prog); + hbbtv_demuxer->Check_PMT_Processing(); + gf_mx_v(hbbtv_demuxer->Get_Demux_Mutex()); + } + } + return e; + + +} + +static u32 On_hbbtv_ait_section(sPlayerInterface* player_interf, GF_Event *event) +{ + HbbtvDemuxer* hbbtv_demuxer = (HbbtvDemuxer*)player_interf->Demuxer; + GF_M2TS_AIT_CARRY* ait_carry; + + + ait_carry = (GF_M2TS_AIT_CARRY*)event->forwarded_event.param; + + /* Make sure we are not modifying the AIT List at the same time */ + gf_mx_p(hbbtv_demuxer->Get_Demux_Mutex()); + if(player_interf->init){ + Get_application_for_channel(hbbtv_demuxer,ait_carry->service_id); + } + + /* Unlock the mutex */ + gf_mx_v(hbbtv_demuxer->Get_Demux_Mutex()); + + return 0; +} + +u32 Get_application_for_channel(HbbtvDemuxer* hbbtv_demuxer,u32 service_id){ + u32 nb_channel; + Channel* Chan; + GF_M2TS_CHANNEL_APPLICATION_INFO* ChanAppInfo; + GF_Err e; + + Chan = hbbtv_demuxer->Get_Channel(service_id); + if(!Chan){ + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("[HBBTVTerminal] service ID %d is not available\n",service_id)); + return GF_BAD_PARAM; + } + + ChanAppInfo = gf_m2ts_get_channel_application_info(hbbtv_demuxer->Get_Ts()->ChannelAppList,service_id); + + if(!ChanAppInfo){ + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("[HBBTVTerminal] service ID %d no application available\n",service_id)); + return GF_BAD_PARAM; + } + + Chan->Add_App_info(ChanAppInfo); + e = hbbtv_demuxer->Get_application_info(ChanAppInfo); + if(e == GF_OK){ + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("[HBBTVTerminal] Application found for the service ID %d is ON\n",ChanAppInfo->service_id)); + }else{ + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("[HBBTVTerminal] This application for this service ID %d does not belong to the current one.\n\n",ChanAppInfo->service_id)); + } + + return GF_OK; + +} + +static u32 On_hbbtv_dsmcc_section(sPlayerInterface* player_interf, GF_Event *event) +{ + HbbtvDemuxer* hbbtv_demuxer = (HbbtvDemuxer*)player_interf->Demuxer; + GF_M2TS_Demuxer* ts = hbbtv_demuxer->Get_Ts(); + GF_M2TS_DSMCC_OVERLORD* dsmcc_overlord; + + GF_M2TS_SL_PCK* pck = (GF_M2TS_SL_PCK*)event->forwarded_event.param; + + dsmcc_overlord = gf_m2ts_get_dmscc_overlord(ts->dsmcc_controler,pck->stream->service_id); + + if (dsmcc_overlord && dsmcc_overlord->get_index){ + GF_M2TS_AIT_APPLICATION* Application; + Channel* Chan; + Chan = (Channel*)hbbtv_demuxer->Get_Channel(dsmcc_overlord->service_id); + //if(!Chan) + Application = Chan->Get_App(dsmcc_overlord->application_id); + Application->url_received = 1; + put_app_url(player_interf); + } + + return 0; +} + +static u32 On_hbbtv_get_tsdemuxer(sPlayerInterface* player_interf, GF_Event *event) +{ + HbbtvDemuxer* hbbtv_demuxer = (HbbtvDemuxer*)player_interf->Demuxer; + GF_M2TS_Demuxer *ts = (GF_M2TS_Demuxer*)event->forwarded_event.param; + hbbtv_demuxer->Set_Ts(ts); + if(hbbtv_demuxer->Get_Ts() != NULL){ + if(hbbtv_demuxer->Get_if_dsmcc_process() && !ts->process_dmscc){ + gf_m2ts_demux_dmscc_init(ts); + } + return GF_OK; + } + + return GF_BAD_PARAM; +} + +u32 DemuxStart(void *par){ + + GF_Err e; + HbbtvDemuxer* hbbtv_demuxer = (HbbtvDemuxer*) par; + e = TSDemux_Demux_Setup(hbbtv_demuxer->Get_Ts(), hbbtv_demuxer->Get_Input_data(), 0); + if(e) + { + GF_LOG(GF_LOG_DEBUG, GF_LOG_MODULE, ("[HBBTV] Error during TS demux \n")); + } + return e; + +} + +Channel* ZapChannel(HbbtvDemuxer *hbbtv_demuxer,u32 service_id,int zap){ + return hbbtv_demuxer->Zap_channel(service_id,zap); +} diff --git a/applications/hbbtvplayer/hbbtvterminal/src/hbbtv_keycontrol.cpp b/applications/hbbtvplayer/hbbtvterminal/src/hbbtv_keycontrol.cpp new file mode 100644 index 0000000..b6108e6 --- /dev/null +++ b/applications/hbbtvplayer/hbbtvterminal/src/hbbtv_keycontrol.cpp @@ -0,0 +1,354 @@ +/* + * Copyright (c) 2010-2011 Telecom-Paristech + * All Rights Reserved + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Authors: Stanislas Selle - Jonathan Sillan + * + */ +#include "hbbtvterminal.h" + + + +void gtksendkey(sPlayerInterface* player_interf,int keycode); + +void gtksendkey(sPlayerInterface* player_interf,int keycode) +{ + TRACEINFO; + GdkEvent *KeyEvent; + + fprintf(stderr, "\x1b[%i;3%imKeyCode\x1b[0m: %d\n",1, 4,keycode); + + KeyEvent = gdk_event_new (GDK_KEY_PRESS); + /* Key Value */ + KeyEvent->key.keyval = gdk_unicode_to_keyval(keycode); + /* GDK_BUTTON1_MASK refers to left mouse button */ + KeyEvent->key.state = GDK_BUTTON1_MASK; + /* Send the key event to Web Window */ + + KeyEvent->key.window = player_interf->ui->pWebWindow->window; + gtk_main_do_event (KeyEvent); + //gdk_event_free(KeyEvent); +} + +/* HBBTV Button */ + +void on_redbuttonclicked(GtkWidget *widget, gpointer data) +{ + TRACEINFO; + fprintf(stderr, "\x1b[%i;3%imBOUTON PRESSED\n\x1b[0m", 1, 5); + sPlayerInterface* player_interf = (sPlayerInterface*)data; + if (player_interf->keyregistered[RK_RED]){ + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("RED is registered by the application : send HBBTV_VK_RED to the Application\n")); + gtksendkey(player_interf,HBBTV_VK_RED); + }else{ + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("RED is not registered by the application\n")); + } + +} + +void on_greenbuttonclicked(GtkWidget *widget, gpointer data) +{ + TRACEINFO; + sPlayerInterface* player_interf = (sPlayerInterface*)data; + if (player_interf->keyregistered[RK_GREEN]){ + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("GREEN is registered by the application : send HBBTV_VK_GREEN to the Application\n")); + gtksendkey(player_interf,HBBTV_VK_GREEN); + }else{ + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("GREEN is not registered by the application\n")); + } +} + +void on_yellowbuttonclicked(GtkWidget *widget, gpointer data) +{ + TRACEINFO; + sPlayerInterface* player_interf = (sPlayerInterface*)data; + if (player_interf->keyregistered[RK_YELLOW]){ + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("YELLOW is registered by the application : send HBBTV_VK_YELLOW to the Application\n")); + gtksendkey(player_interf,HBBTV_VK_YELLOW); + }else{ + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("YELLOW is not registered by the application\n")); + } +} + +void on_bluebuttonclicked(GtkWidget *widget, gpointer data) +{ + TRACEINFO; + sPlayerInterface* player_interf = (sPlayerInterface*)data; + if (player_interf->keyregistered[RK_BLUE]){ + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("BLUE is registered by the application : send HBBTV_VK_BLUE to the Application\n")); + gtksendkey(player_interf,HBBTV_VK_BLUE); + }else{ + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("BLUE is not registered by the application\n")); + } +} + +/* Navigation */ + +void on_returnbuttonclicked(GtkWidget *widget, gpointer data) +{ + TRACEINFO; + sPlayerInterface* player_interf = (sPlayerInterface*)data; + if (player_interf->keyregistered[RK_NAVIGATION]){ + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("NAVIGATION is registered by the application : send HBBTV_VK_ENTER to the Application\n")); + gtksendkey(player_interf,HBBTV_VK_ENTER); + }else{ + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("NAVIGATION is not registered by the application\n")); + } +} + +void on_exitbuttonclicked(GtkWidget *widget, gpointer data) +{ + TRACEINFO; + int posx,posy; + sPlayerInterface* player_interf = (sPlayerInterface*)data; + //~ gtk_window_set_transient_for(GTK_WINDOW(player_interf->ui->pWebWindow),GTK_WINDOW(player_interf->ui->pTVWindow)); + get_window_position(player_interf->ui->pWebWindow, &posx, &posy); + resizevideoplayer(player_interf, HBBTV_VIDEO_WIDTH, HBBTV_VIDEO_HEIGHT); + set_window_position(player_interf->ui->pTVWindow, posx, posy); + gtk_window_set_position(GTK_WINDOW(player_interf->ui->pTVWindow),GTK_WIN_POS_CENTER_ALWAYS); + webkit_web_view_load_uri(player_interf->ui->pWebView, player_interf->url); +} + +void on_upbuttonclicked(GtkWidget *widget, gpointer data) +{ + TRACEINFO; + sPlayerInterface* player_interf = (sPlayerInterface*)data; + if (player_interf->keyregistered[RK_NAVIGATION]){ + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("NAVIGATION is registered by the application : send HBBTV_VK_UP to the Application\n")); + gtksendkey(player_interf,HBBTV_VK_UP); + }else{ + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("NAVIGATION is not registered by the application\n")); + } +} + +void on_downbuttonclicked(GtkWidget *widget, gpointer data) +{ + TRACEINFO; + sPlayerInterface* player_interf = (sPlayerInterface*)data; + if (player_interf->keyregistered[RK_NAVIGATION]){ + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("NAVIGATION is registered by the application : send HBBTV_VK_DOWN to the Application\n")); + gtksendkey(player_interf,HBBTV_VK_DOWN); + }else{ + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("NAVIGATION is not registered by the application\n")); + } +} + +void on_leftbuttonclicked(GtkWidget *widget, gpointer data) +{ + TRACEINFO; + sPlayerInterface* player_interf = (sPlayerInterface*)data; + if (player_interf->keyregistered[RK_NAVIGATION]){ + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("NAVIGATION is registered by the application : send HBBTV_VK_LEFT to the Application\n")); + gtksendkey(player_interf,HBBTV_VK_LEFT); + }else{ + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE,("NAVIGATION is not registered by the application\n")); + } +} + +void on_rightbuttonclicked(GtkWidget *widget, gpointer data) +{ + TRACEINFO; + sPlayerInterface* player_interf = (sPlayerInterface*)data; + if (player_interf->keyregistered[RK_NAVIGATION]){ + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("NAVIGATION is registered by the application : send HBBTV_VK_RIGHT to the Application\n")); + gtksendkey(player_interf,HBBTV_VK_RIGHT); + }else{ + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("NAVIGATION is not registered by the application\n")); + } +} + + + +/* Control */ + +void on_playpausebuttonclicked(GtkWidget *widget, gpointer data) +{ + TRACEINFO; + sPlayerInterface* player_interf = (sPlayerInterface*)data; + set_window_position(player_interf->ui->pTVWindow, 0, 0); +} + +void on_playbuttonclicked(GtkWidget *widget, gpointer data) +{ + TRACEINFO; + sPlayerInterface* player_interf = (sPlayerInterface*)data; + if (player_interf->keyregistered[RK_VCR]){ + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("VCR is registered by the application : send HBBTV_VK_PLAY to the Application\n")); + gtksendkey(player_interf,HBBTV_VK_PLAY); + }else{ + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("VCR is not registered by the application\n")); + } + +} + +void on_pausebuttonclicked(GtkWidget *widget, gpointer data) +{ + TRACEINFO; + sPlayerInterface* player_interf = (sPlayerInterface*)data; + if (player_interf->keyregistered[RK_VCR]){ + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("VCR is registered by the application : send HBBTV_VK_PAUSE to the Application\n")); + gtksendkey(player_interf,HBBTV_VK_PAUSE); + }else{ + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("VCR is not registered by the application\n")); + } +} + +void on_onoffbuttonclicked(GtkWidget *widget, gpointer data) +{ + TRACEINFO; + sPlayerInterface* player_interf; + player_interf = (sPlayerInterface*)data; + + if (player_interf->TVwake == FALSE) { + init_gpac(player_interf); + player_interf->TVwake = TRUE; + } + else { + stop_gpac(player_interf); + player_interf->TVwake = FALSE; + } + TRACEINFO; +} + +void on_langbuttonclicked(GtkWidget *widget, gpointer data) +{ + TRACEINFO; + sPlayerInterface* player_interf; + u32 current_service_id; + HbbtvDemuxer *hbbtvdemuxer; + GF_MediaInfo odi; + u32 index_audio,audio_ID; + + player_interf = (sPlayerInterface*)data; + hbbtvdemuxer = ( HbbtvDemuxer *)player_interf->Demuxer; + current_service_id = index_audio = audio_ID = 0; + + /* Get the current channel struct */ + current_service_id = gf_term_get_current_service_id(player_interf->m_term); + Channel* chan = (Channel*)ZapChannel(hbbtvdemuxer,current_service_id,0); + if(chan->Get_nb_chan_audio_stream() > 1){ + GF_ObjectManager *root_odm = gf_term_get_root_object(player_interf->m_term); + if (root_odm){ + if (gf_term_get_object_info(player_interf->m_term, root_odm, &odi) == GF_OK){ + if (odi.od) { + /* Increment the audio index to get the next audio stream */ + chan->Incr_audio_index(0); + gf_term_select_object(player_interf->m_term, gf_term_get_object(player_interf->m_term, root_odm, chan->Get_audio_ID(chan->Get_audio_index()))); + } + } + } + } +} + +void on_teletextbuttonclicked(GtkWidget *widget, gpointer data) +{ + TRACEINFO; + sPlayerInterface* player_interf; + player_interf = (sPlayerInterface*)data; + gtksendkey(player_interf,HBBTV_VK_TELETEXT); + +} + +void on_channelbuttonclicked(GtkWidget *widget, gpointer data) +{ + TRACEINFO; + sPlayerInterface* player_interf; + GtkButton* button; + int key_code; + char* label; + player_interf = (sPlayerInterface*)data; + button = (GtkButton*) widget; + label = (char*)gtk_button_get_label(button); + + switch(atoi(label)){ + case 0: + key_code = HBBTV_VK_0; + break; + case 1: + key_code = HBBTV_VK_1; + break; + case 2: + key_code = HBBTV_VK_2; + break; + case 3: + key_code = HBBTV_VK_3; + break; + case 4: + key_code = HBBTV_VK_4; + break; + case 5: + key_code = HBBTV_VK_5; + break; + case 6: + key_code = HBBTV_VK_6; + break; + case 7: + key_code = HBBTV_VK_7; + break; + case 8: + key_code = HBBTV_VK_8; + break; + case 9: + key_code = HBBTV_VK_9; + break; + default: + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("Wrong Key Code %d Defaut channel 1 \n",atoi(label))); + key_code = HBBTV_VK_1; + break; + } + if (player_interf->keyregistered[RK_NUMERIC]){ + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("NUMERIC is registered by the application : send HBBTV_VK_%i to the Application\n",atoi(label))); + gtksendkey(player_interf,key_code); + }else{ + hbbtvterm_get_channel_on_air(player_interf,atoi(label),0); + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("NUMERIC is not registered by the application, change channel function\n")); + } + + +} + + +void on_chanupbuttonclicked(GtkWidget *widget, gpointer data) +{ + TRACEINFO; + sPlayerInterface* player_interf; + player_interf = (sPlayerInterface*)data; + + hbbtvterm_channel_zap(player_interf,1); + + TRACEINFO; +} + + +void on_chandownbuttonclicked(GtkWidget *widget, gpointer data) +{ + TRACEINFO; + sPlayerInterface* player_interf; + player_interf = (sPlayerInterface*)data; + + hbbtvterm_channel_zap(player_interf,-1); + + TRACEINFO; +} + +int playpause(sPlayerInterface* player_interf) +{ + int is_pause = gf_term_get_option( player_interf->m_term, GF_OPT_PLAY_STATE); + fprintf(stdout, "[Status: %s]\n", is_pause ? "Playing" : "Paused"); + gf_term_set_option( player_interf->m_term, GF_OPT_PLAY_STATE, is_pause ? GF_STATE_PLAYING : GF_STATE_PAUSED); + return 1; +} + diff --git a/applications/hbbtvplayer/hbbtvterminal/src/hbbtv_tools.cpp b/applications/hbbtvplayer/hbbtvterminal/src/hbbtv_tools.cpp new file mode 100644 index 0000000..ddf8cad --- /dev/null +++ b/applications/hbbtvplayer/hbbtvterminal/src/hbbtv_tools.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2010-2011 Telecom-Paristech + * All Rights Reserved + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Authors: Jonathan Sillan + * + */ +#include "hbbtvterminal.h" + + +GF_Config* check_config_file(){ + Bool firstlaunch; + + firstlaunch = 0; + /*init config and modules*/ + return gf_cfg_init(NULL,&firstlaunch); +} + +Bool is_connected(){ + GF_Err e; + + GF_Socket* sock; + + sock = gf_sk_new(GF_SOCK_TYPE_UDP); + if (sock == NULL) { + return 0; + } + e = gf_sk_connect(sock, "www.google.fr", 4096, NULL); + gf_sk_del(sock); + + if(e){ + return 0; + }else{ + return 1; + } +} + + diff --git a/applications/hbbtvplayer/hbbtvterminal/src/hbbtvterminal.cpp b/applications/hbbtvplayer/hbbtvterminal/src/hbbtvterminal.cpp new file mode 100644 index 0000000..f44a95c --- /dev/null +++ b/applications/hbbtvplayer/hbbtvterminal/src/hbbtvterminal.cpp @@ -0,0 +1,1081 @@ +/* + * Copyright (c) 2010-2011 Telecom-Paristech + * All Rights Reserved + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Authors: Stanislas Selle - Jonathan Sillan + * + */ + +#include "hbbtvterminal.h" +#ifdef XP_UNIX +#include +#include +#else +#include +#include +#endif + +#include +#include + +//static XID handler; +static void activate_uri_entry_cb (GtkWidget* entry, gpointer data); + +sPlayerInterface* player_interf; + +static gboolean on_deleteevent( GtkWidget *widget, GdkEvent *event, gpointer data) +{ + g_print ("delete event occurred\n"); + TRACEINFO; + return TRUE; +} + +static gboolean on_windowconfigure(GtkWidget * widget, GdkEvent *event, gpointer data) +{ + TRACEINFO; + + static int oldx = 0; + static int oldy = 0; + int x, y, olddatax, olddatay; + + sPlayerInterface* player_interf = (sPlayerInterface*)data; + + gtk_window_get_position(GTK_WINDOW(player_interf->ui->pWebWindow), &olddatax, &olddatay); + + x = event->configure.x; + y = event->configure.y; + + int deltax = x - oldx; + int deltay = y - oldy; + + gtk_window_move(GTK_WINDOW(player_interf->ui->pTVWindow),olddatax + deltax, olddatay + deltay); + gtk_window_move(GTK_WINDOW(player_interf->ui->pBackgroundWindow),olddatax + deltax, olddatay + deltay); + + oldx = x; + oldy = y; + return TRUE; +} + + +static void on_destroyevent(GtkWidget *widget, gpointer data) +{ + gtk_main_quit(); + TRACEINFO; +} + + +int ui_init(sPlayerInterface* player_interf) +{ + GtkWidget *pWindow; + GtkWidget *pTVWindow; + GtkWidget *pWebWindow; + GtkWidget *pMainPositionTable; + GtkWidget *pBrowserPositionTable; + GtkWidget *pRCUPositionTable; + GtkWidget *pColorButtonTable; + GtkWidget *pRedButton; + GtkWidget *pGreenButton; + GtkWidget *pYellowButton; + GtkWidget *pBlueButton; + GtkWidget *pArrowButtonTable; + GtkWidget *pUpButton; + GtkWidget *pDownButton; + GtkWidget *pLeftButton; + GtkWidget *pRightButton; + GtkWidget *pUpArrow; + GtkWidget *pDownArrow; + GtkWidget *pLeftArrow; + GtkWidget *pRightArrow; + GtkWidget *pEnterButton; + GtkWidget *pBackButton; + GtkWidget *pEntry; + GtkWidget *pNumPadTable; + GtkWidget *pNumber[10]; + GtkWidget *pPlayButton; + GtkWidget *pStopButton; + GtkWidget *pPauseButton; + GtkWidget *pPlayPauseButton; + GtkWidget *pFastMoveTable; + GtkWidget *pFastForwardButton; + GtkWidget *pFastRewindButton; + GtkWidget *pLangButton; + GtkWidget *pExitButton; + GtkWidget *pOnOffButton; + GtkWidget *pTeletextButton; + GtkWidget *pProgramSelectionTable; + GtkWidget *pPreviousProgramButton; + GtkWidget *pNextProgramButton; + GtkWidget *pQuitButton; + GtkWidget *pTestButton; + GtkWidget *pTest2Button; + GtkWidget *pGoButton; + GtkWidget *pWebScrollWindow; + GtkWidget *pBackgroundWindow; + + GdkColor color; + int Error,posx,posy; + + TRACEINFO; + + sGraphicInterface* ui = player_interf->ui; + + /** Main window **/ + pBackgroundWindow = gtk_window_new(GTK_WINDOW_TOPLEVEL); + pTVWindow = gtk_window_new(GTK_WINDOW_TOPLEVEL); + pWebWindow = gtk_window_new(GTK_WINDOW_TOPLEVEL); + pWindow = gtk_window_new(GTK_WINDOW_TOPLEVEL); + + ui->pMainWindow = pWindow; + ui->pTVWindow = pTVWindow; + ui->pWebWindow = pWebWindow; + ui->pBackgroundWindow = pBackgroundWindow; + + gtk_window_set_title(GTK_WINDOW(pWindow), "RCU"); + gtk_window_set_title(GTK_WINDOW(pTVWindow), "TV"); + gtk_window_set_title(GTK_WINDOW(pWebWindow), "WEB"); + gtk_window_set_title(GTK_WINDOW(pBackgroundWindow), "BACK"); + + gtk_window_set_decorated(GTK_WINDOW(pWebWindow), false); + gtk_window_set_decorated(GTK_WINDOW(pTVWindow), false); + gtk_window_set_decorated(GTK_WINDOW(pBackgroundWindow), false); + gtk_window_set_transient_for(GTK_WINDOW(pTVWindow),GTK_WINDOW(pBackgroundWindow)); + gtk_window_set_transient_for(GTK_WINDOW(pWebWindow),GTK_WINDOW(pTVWindow)); + + gtk_window_set_resizable(GTK_WINDOW(pTVWindow), false); + gtk_window_set_resizable(GTK_WINDOW(pWebWindow), false); + gtk_window_set_resizable(GTK_WINDOW(pBackgroundWindow), false); + + /** Quit button **/ + pQuitButton = gtk_button_new_with_label("Quit"); + + /** Tests button **/ + pTestButton = gtk_button_new_with_label("Test"); + pTest2Button = gtk_button_new_with_label("Test2"); + + /** Entry bar for URL **/ + pEntry = gtk_entry_new(); + ui->pEntry = pEntry; + + /** GO Button **/ + pGoButton = gtk_button_new_with_label("Go"); + + /** Browser Zone **/ + pWebScrollWindow = gtk_scrolled_window_new(NULL, NULL); + //gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(pWebScrollWindow), GTK_POLICY_ALWAYS, GTK_POLICY_ALWAYS); + + ui->pWebView = WEBKIT_WEB_VIEW(webkit_web_view_new()); + + webkit_web_view_set_transparent(ui->pWebView, TRUE); + + if (ui->pWebView) + gtk_container_add(GTK_CONTAINER(pWebScrollWindow), GTK_WIDGET(ui->pWebView)); + + /** Background Zone **/ + ui->pBackgroundView = gtk_drawing_area_new(); + gdk_color_parse("#000044", &color); + gtk_widget_modify_bg ( GTK_WIDGET(ui->pBackgroundView), GTK_STATE_NORMAL, &color); + + /** TV zone **/ + ui->pTVView = gtk_drawing_area_new(); + + /** RCU zone **/ + + /* create Red color */ + gdk_color_parse("#FF0000", &color); + pRedButton = gtk_button_new(); + gtk_widget_modify_bg ( GTK_WIDGET(pRedButton), GTK_STATE_NORMAL, &color); + gtk_button_set_label(GTK_BUTTON(pRedButton)," "); + /* create Green color */ + gdk_color_parse("#00FF00", &color); + pGreenButton = gtk_button_new(); + gtk_widget_modify_bg ( GTK_WIDGET(pGreenButton), GTK_STATE_NORMAL, &color); + gtk_button_set_label(GTK_BUTTON(pGreenButton)," "); + /* create Yellow color */ + gdk_color_parse("#FFFF00", &color); + pYellowButton = gtk_button_new(); + gtk_widget_modify_bg ( GTK_WIDGET(pYellowButton), GTK_STATE_NORMAL, &color); + gtk_button_set_label(GTK_BUTTON(pYellowButton)," "); + /* create Blue color */ + gdk_color_parse("#0000FF", &color); + pBlueButton = gtk_button_new(); + gtk_widget_modify_bg ( GTK_WIDGET(pBlueButton), GTK_STATE_NORMAL, &color); + gtk_button_set_label(GTK_BUTTON(pBlueButton)," "); + + pUpButton = gtk_button_new(); + pDownButton = gtk_button_new(); + pLeftButton = gtk_button_new(); + pRightButton = gtk_button_new(); + + pUpArrow = gtk_arrow_new(GTK_ARROW_UP,GTK_SHADOW_OUT); + pDownArrow = gtk_arrow_new(GTK_ARROW_DOWN,GTK_SHADOW_OUT); + pLeftArrow = gtk_arrow_new(GTK_ARROW_LEFT,GTK_SHADOW_OUT); + pRightArrow = gtk_arrow_new(GTK_ARROW_RIGHT,GTK_SHADOW_OUT); + + gtk_container_add(GTK_CONTAINER(pUpButton), pUpArrow); + gtk_container_add(GTK_CONTAINER(pDownButton), pDownArrow); + gtk_container_add(GTK_CONTAINER(pLeftButton), pLeftArrow); + gtk_container_add(GTK_CONTAINER(pRightButton), pRightArrow); + + pEnterButton = gtk_button_new_with_label("Enter"); + pBackButton = gtk_button_new_with_label("Back"); + + pNumber[0] = gtk_button_new_with_label("0"); + pNumber[1] = gtk_button_new_with_label("1"); + pNumber[2] = gtk_button_new_with_label("2"); + pNumber[3] = gtk_button_new_with_label("3"); + pNumber[4] = gtk_button_new_with_label("4"); + pNumber[5] = gtk_button_new_with_label("5"); + pNumber[6] = gtk_button_new_with_label("6"); + pNumber[7] = gtk_button_new_with_label("7"); + pNumber[8] = gtk_button_new_with_label("8"); + pNumber[9] = gtk_button_new_with_label("9"); + + pPreviousProgramButton = gtk_button_new_with_label("P-"); + pNextProgramButton = gtk_button_new_with_label("P+"); + + pPlayButton = gtk_button_new_with_label("Play"); + pStopButton = gtk_button_new_with_label("Stop"); + pPauseButton = gtk_button_new_with_label("Pause"); + pPlayPauseButton = gtk_button_new_with_label("PlayPause"); + + pFastForwardButton = gtk_button_new_with_label(">>"); + pFastRewindButton = gtk_button_new_with_label("<<"); + pTeletextButton = gtk_button_new_with_label("Teletext"); + pLangButton = gtk_button_new_with_label("Language"); + pExitButton = gtk_button_new_with_label("Exit"); + pOnOffButton = gtk_button_new_with_label("OnOff"); + + pColorButtonTable = gtk_table_new(4,1,false); + gtk_table_attach(GTK_TABLE(pColorButtonTable),pRedButton, 0,1,0,1, GtkAttachOptions(GTK_FILL|GTK_EXPAND),GtkAttachOptions(GTK_FILL),0,0); + gtk_table_attach(GTK_TABLE(pColorButtonTable),pGreenButton, 0,1,1,2, GtkAttachOptions(GTK_FILL|GTK_EXPAND),GtkAttachOptions(GTK_FILL),0,0); + gtk_table_attach(GTK_TABLE(pColorButtonTable),pYellowButton, 0,1,2,3, GtkAttachOptions(GTK_FILL|GTK_EXPAND),GtkAttachOptions(GTK_FILL),0,0); + gtk_table_attach(GTK_TABLE(pColorButtonTable),pBlueButton, 0,1,3,4, GtkAttachOptions(GTK_FILL|GTK_EXPAND),GtkAttachOptions(GTK_FILL),0,0); + + pArrowButtonTable = gtk_table_new(3,3, false); + gtk_table_attach(GTK_TABLE(pArrowButtonTable), pUpButton, 1,2,0,1,GtkAttachOptions(GTK_FILL|GTK_EXPAND), GtkAttachOptions(GTK_FILL|GTK_EXPAND), 0,0 ); + gtk_table_attach(GTK_TABLE(pArrowButtonTable), pDownButton, 1,2,2,3,GtkAttachOptions(GTK_FILL|GTK_EXPAND), GtkAttachOptions(GTK_FILL|GTK_EXPAND), 0,0 ); + gtk_table_attach(GTK_TABLE(pArrowButtonTable), pLeftButton, 0,1,1,2,GtkAttachOptions(GTK_FILL|GTK_EXPAND), GtkAttachOptions(GTK_FILL|GTK_EXPAND), 0,0 ); + gtk_table_attach(GTK_TABLE(pArrowButtonTable), pRightButton, 2,3,1,2,GtkAttachOptions(GTK_FILL|GTK_EXPAND), GtkAttachOptions(GTK_FILL|GTK_EXPAND), 0,0 ); + + pNumPadTable = gtk_table_new(4,3,false); + gtk_table_attach(GTK_TABLE(pNumPadTable), pNumber[0], 1, 2, 3, 4, GtkAttachOptions(GTK_FILL|GTK_EXPAND),GtkAttachOptions(GTK_FILL|GTK_EXPAND),0,0); + gtk_table_attach(GTK_TABLE(pNumPadTable), pNumber[1], 0, 1, 0, 1, GtkAttachOptions(GTK_FILL|GTK_EXPAND),GtkAttachOptions(GTK_FILL|GTK_EXPAND),0,0); + gtk_table_attach(GTK_TABLE(pNumPadTable), pNumber[2], 1, 2, 0, 1, GtkAttachOptions(GTK_FILL|GTK_EXPAND),GtkAttachOptions(GTK_FILL|GTK_EXPAND),0,0); + gtk_table_attach(GTK_TABLE(pNumPadTable), pNumber[3], 2, 3, 0, 1, GtkAttachOptions(GTK_FILL|GTK_EXPAND),GtkAttachOptions(GTK_FILL|GTK_EXPAND),0,0); + gtk_table_attach(GTK_TABLE(pNumPadTable), pNumber[4], 0, 1, 1, 2, GtkAttachOptions(GTK_FILL|GTK_EXPAND),GtkAttachOptions(GTK_FILL|GTK_EXPAND),0,0); + gtk_table_attach(GTK_TABLE(pNumPadTable), pNumber[5], 1, 2, 1, 2, GtkAttachOptions(GTK_FILL|GTK_EXPAND),GtkAttachOptions(GTK_FILL|GTK_EXPAND),0,0); + gtk_table_attach(GTK_TABLE(pNumPadTable), pNumber[6], 2, 3, 1, 2, GtkAttachOptions(GTK_FILL|GTK_EXPAND),GtkAttachOptions(GTK_FILL|GTK_EXPAND),0,0); + gtk_table_attach(GTK_TABLE(pNumPadTable), pNumber[7], 0, 1, 2, 3, GtkAttachOptions(GTK_FILL|GTK_EXPAND),GtkAttachOptions(GTK_FILL|GTK_EXPAND),0,0); + gtk_table_attach(GTK_TABLE(pNumPadTable), pNumber[8], 1, 2, 2, 3, GtkAttachOptions(GTK_FILL|GTK_EXPAND),GtkAttachOptions(GTK_FILL|GTK_EXPAND),0,0); + gtk_table_attach(GTK_TABLE(pNumPadTable), pNumber[9], 2, 3, 2, 3, GtkAttachOptions(GTK_FILL|GTK_EXPAND),GtkAttachOptions(GTK_FILL|GTK_EXPAND),0,0); + + pFastMoveTable = gtk_table_new(1,2,false); + gtk_table_attach(GTK_TABLE(pFastMoveTable), pFastRewindButton, 0,1,0,1, GtkAttachOptions(GTK_FILL|GTK_EXPAND),GtkAttachOptions(GTK_FILL),0,0); + gtk_table_attach(GTK_TABLE(pFastMoveTable), pFastForwardButton, 1,2,0,1, GtkAttachOptions(GTK_FILL|GTK_EXPAND),GtkAttachOptions(GTK_FILL),0,0); + + pProgramSelectionTable = gtk_table_new(1,2, false); + gtk_table_attach(GTK_TABLE(pProgramSelectionTable), pPreviousProgramButton, 0,1,0,1, GtkAttachOptions(GTK_FILL|GTK_EXPAND),GtkAttachOptions(GTK_FILL),0,0); + gtk_table_attach(GTK_TABLE(pProgramSelectionTable), pNextProgramButton, 1,2,0,1, GtkAttachOptions(GTK_FILL|GTK_EXPAND),GtkAttachOptions(GTK_FILL),0,0); + + /** create the toplevel tables **/ + pRCUPositionTable = gtk_table_new(24,1,false); + gtk_table_attach(GTK_TABLE(pRCUPositionTable), pColorButtonTable, 0, 1, 0, 4, GtkAttachOptions(GTK_FILL|GTK_EXPAND),GtkAttachOptions(GTK_FILL|GTK_EXPAND),0,0); + gtk_table_attach(GTK_TABLE(pRCUPositionTable), pArrowButtonTable, 0, 1, 4, 7, GtkAttachOptions(GTK_FILL|GTK_EXPAND),GtkAttachOptions(GTK_FILL|GTK_EXPAND),0,0); + gtk_table_attach(GTK_TABLE(pRCUPositionTable), pEnterButton, 0, 1, 7, 8, GtkAttachOptions(GTK_FILL|GTK_EXPAND),GtkAttachOptions(GTK_FILL|GTK_EXPAND),0,0); + gtk_table_attach(GTK_TABLE(pRCUPositionTable), pBackButton, 0, 1, 8, 9, GtkAttachOptions(GTK_FILL|GTK_EXPAND),GtkAttachOptions(GTK_FILL|GTK_EXPAND),0,0); + gtk_table_attach(GTK_TABLE(pRCUPositionTable), pNumPadTable, 0, 1, 9, 13, GtkAttachOptions(GTK_FILL|GTK_EXPAND),GtkAttachOptions(GTK_FILL|GTK_EXPAND),0,0); + gtk_table_attach(GTK_TABLE(pRCUPositionTable), pPlayButton, 0, 1, 13, 14, GtkAttachOptions(GTK_FILL|GTK_EXPAND),GtkAttachOptions(GTK_FILL|GTK_EXPAND),0,0); + gtk_table_attach(GTK_TABLE(pRCUPositionTable), pStopButton, 0, 1, 14, 15, GtkAttachOptions(GTK_FILL|GTK_EXPAND),GtkAttachOptions(GTK_FILL|GTK_EXPAND),0,0); + gtk_table_attach(GTK_TABLE(pRCUPositionTable), pPauseButton, 0, 1, 15, 16, GtkAttachOptions(GTK_FILL|GTK_EXPAND),GtkAttachOptions(GTK_FILL|GTK_EXPAND),0,0); + gtk_table_attach(GTK_TABLE(pRCUPositionTable), pPlayPauseButton, 0, 1, 16, 17, GtkAttachOptions(GTK_FILL|GTK_EXPAND),GtkAttachOptions(GTK_FILL|GTK_EXPAND),0,0); + gtk_table_attach(GTK_TABLE(pRCUPositionTable), pFastMoveTable, 0, 1, 17, 18, GtkAttachOptions(GTK_FILL|GTK_EXPAND),GtkAttachOptions(GTK_FILL|GTK_EXPAND),0,0); + gtk_table_attach(GTK_TABLE(pRCUPositionTable), pProgramSelectionTable, 0, 1, 18, 19, GtkAttachOptions(GTK_FILL|GTK_EXPAND),GtkAttachOptions(GTK_FILL|GTK_EXPAND),0,0); + gtk_table_attach(GTK_TABLE(pRCUPositionTable), pTeletextButton, 0, 1, 19, 20, GtkAttachOptions(GTK_FILL|GTK_EXPAND),GtkAttachOptions(GTK_FILL|GTK_EXPAND),0,0); + gtk_table_attach(GTK_TABLE(pRCUPositionTable), pLangButton, 0, 1, 20, 21, GtkAttachOptions(GTK_FILL|GTK_EXPAND),GtkAttachOptions(GTK_FILL|GTK_EXPAND),0,0); + gtk_table_attach(GTK_TABLE(pRCUPositionTable), pExitButton, 0, 1, 21, 22, GtkAttachOptions(GTK_FILL|GTK_EXPAND),GtkAttachOptions(GTK_FILL|GTK_EXPAND),0,0); + gtk_table_attach(GTK_TABLE(pRCUPositionTable), pOnOffButton, 0, 1, 22, 23, GtkAttachOptions(GTK_FILL|GTK_EXPAND),GtkAttachOptions(GTK_FILL|GTK_EXPAND),0,0); + gtk_table_attach(GTK_TABLE(pRCUPositionTable), pQuitButton, 0, 1, 23, 24, GtkAttachOptions(GTK_FILL|GTK_EXPAND),GtkAttachOptions(GTK_FILL|GTK_EXPAND),0,0); + + pBrowserPositionTable = gtk_table_new(3,3,false); + //gtk_table_attach(GTK_TABLE(pBrowserPositionTable), pEntry, 0, 2, 0, 1, GtkAttachOptions(GTK_EXPAND | GTK_FILL), GTK_FILL, 0,0); + //gtk_table_attach(GTK_TABLE(pBrowserPositionTable), pGoButton, 2, 3, 0, 1, GtkAttachOptions(GTK_EXPAND | GTK_FILL), GTK_FILL, 0,0); + gtk_table_attach(GTK_TABLE(pBrowserPositionTable), pWebScrollWindow, 0, 3, 1, 3, GtkAttachOptions(GTK_EXPAND | GTK_FILL), GtkAttachOptions(GTK_EXPAND | GTK_FILL), 0,0); + + /** Group toplevel tables in the main table **/ + pMainPositionTable = gtk_table_new(4,7,false); + gtk_table_attach(GTK_TABLE(pMainPositionTable), pRCUPositionTable, 6, 7, 0, 3, GtkAttachOptions(NULL), GtkAttachOptions(NULL), 0,0); + //gtk_table_attach(GTK_TABLE(pMainPositionTable), pBottomBarTable, 0, 7, 3, 4, GtkAttachOptions(GTK_EXPAND | GTK_FILL), GtkAttachOptions(GTK_EXPAND | GTK_FILL), 0,0); + + gtk_container_add(GTK_CONTAINER(pWindow), pMainPositionTable); + gtk_container_add(GTK_CONTAINER(pTVWindow), ui->pTVView); + gtk_container_add(GTK_CONTAINER(pBackgroundWindow), ui->pBackgroundView); + //~ gtk_container_add(GTK_CONTAINER(pWebWindow), ui->pWebView); + gtk_container_add(GTK_CONTAINER(pWebWindow), pBrowserPositionTable); + + /** SET SIZE **/ + gtk_widget_set_size_request(GTK_WIDGET(ui->pTVView), HBBTV_VIDEO_WIDTH,HBBTV_VIDEO_HEIGHT); + gtk_widget_set_size_request(GTK_WIDGET(ui->pBackgroundView), HBBTV_VIDEO_WIDTH,HBBTV_VIDEO_HEIGHT); + gtk_widget_set_size_request(GTK_WIDGET(pBrowserPositionTable), HBBTV_VIDEO_WIDTH,HBBTV_VIDEO_HEIGHT); + + gtk_window_set_position(GTK_WINDOW(pWebWindow), GTK_WIN_POS_CENTER_ALWAYS); + gtk_window_set_position(GTK_WINDOW(pBackgroundWindow), GTK_WIN_POS_CENTER_ALWAYS); + gtk_window_set_position(GTK_WINDOW(pTVWindow), GTK_WIN_POS_CENTER_ALWAYS); + + /** Connect Callback **/ + //** expose drawable + + // connect to deleteevent function + g_signal_connect(G_OBJECT(pWindow), "destroy", G_CALLBACK(on_deleteevent), NULL); + + // connect to destroy function + g_signal_connect(G_OBJECT(pWindow), "destroy", G_CALLBACK(on_destroyevent), NULL); + g_signal_connect(G_OBJECT(pWebWindow), "destroy", G_CALLBACK(on_destroyevent), NULL); + g_signal_connect(G_OBJECT(pQuitButton), "clicked", G_CALLBACK(on_destroyevent), NULL); + + //connect button and entry + /* Control */ + g_signal_connect (G_OBJECT(pEntry), "activate", G_CALLBACK (activate_uri_entry_cb), ui); + g_signal_connect (G_OBJECT(pGoButton), "clicked", G_CALLBACK (activate_uri_entry_cb), ui); + g_signal_connect (G_OBJECT(pOnOffButton), "clicked", G_CALLBACK (on_onoffbuttonclicked), player_interf ); + g_signal_connect (G_OBJECT(pPlayPauseButton), "clicked", G_CALLBACK (on_playpausebuttonclicked), player_interf ); + g_signal_connect (G_OBJECT(pPlayButton), "clicked", G_CALLBACK (on_playbuttonclicked), player_interf ); + g_signal_connect (G_OBJECT(pPauseButton), "clicked", G_CALLBACK (on_pausebuttonclicked), player_interf ); + g_signal_connect (G_OBJECT(pLangButton), "clicked", G_CALLBACK (on_langbuttonclicked), player_interf ); + g_signal_connect (G_OBJECT(pNextProgramButton), "clicked", G_CALLBACK (on_chanupbuttonclicked), player_interf ); + g_signal_connect (G_OBJECT(pPreviousProgramButton), "clicked", G_CALLBACK (on_chandownbuttonclicked), player_interf ); + g_signal_connect (G_OBJECT(pNumber[1]), "clicked", G_CALLBACK (on_channelbuttonclicked), player_interf); + g_signal_connect (G_OBJECT(pNumber[2]), "clicked", G_CALLBACK (on_channelbuttonclicked), player_interf); + g_signal_connect (G_OBJECT(pNumber[3]), "clicked", G_CALLBACK (on_channelbuttonclicked), player_interf); + g_signal_connect (G_OBJECT(pNumber[4]), "clicked", G_CALLBACK (on_channelbuttonclicked), player_interf); + g_signal_connect (G_OBJECT(pNumber[5]), "clicked", G_CALLBACK (on_channelbuttonclicked), player_interf); + g_signal_connect (G_OBJECT(pNumber[6]), "clicked", G_CALLBACK (on_channelbuttonclicked), player_interf); + g_signal_connect (G_OBJECT(pNumber[7]), "clicked", G_CALLBACK (on_channelbuttonclicked), player_interf); + g_signal_connect (G_OBJECT(pNumber[8]), "clicked", G_CALLBACK (on_channelbuttonclicked), player_interf); + g_signal_connect (G_OBJECT(pNumber[9]), "clicked", G_CALLBACK (on_channelbuttonclicked), player_interf); + g_signal_connect (G_OBJECT(pNumber[0]), "clicked", G_CALLBACK (on_channelbuttonclicked), player_interf); + g_signal_connect (G_OBJECT(pTeletextButton), "clicked", G_CALLBACK (on_teletextbuttonclicked), player_interf); + + + /* HBBTV Button */ + g_signal_connect (G_OBJECT(pRedButton), "clicked", G_CALLBACK (on_redbuttonclicked), player_interf); + g_signal_connect (G_OBJECT(pGreenButton), "clicked", G_CALLBACK (on_greenbuttonclicked), player_interf); + g_signal_connect (G_OBJECT(pYellowButton), "clicked", G_CALLBACK (on_yellowbuttonclicked), player_interf); + g_signal_connect (G_OBJECT(pBlueButton), "clicked", G_CALLBACK (on_bluebuttonclicked), player_interf); + + /* Navigation */ + g_signal_connect (G_OBJECT(pUpButton), "clicked", G_CALLBACK (on_upbuttonclicked), player_interf); + g_signal_connect (G_OBJECT(pDownButton), "clicked", G_CALLBACK (on_downbuttonclicked), player_interf); + g_signal_connect (G_OBJECT(pLeftButton), "clicked", G_CALLBACK (on_leftbuttonclicked), player_interf); + g_signal_connect (G_OBJECT(pRightButton), "clicked", G_CALLBACK (on_rightbuttonclicked), player_interf); + g_signal_connect (G_OBJECT(pEnterButton), "clicked", G_CALLBACK (on_returnbuttonclicked), player_interf); + g_signal_connect (G_OBJECT(pExitButton), "clicked", G_CALLBACK (on_exitbuttonclicked), player_interf); + + + // gtk_widget_add_events(GTK_WIDGET(pWebWindow), GDK_CONFIGURE); + // g_signal_connect(G_OBJECT(pTVWindow), "configure-event", G_CALLBACK(on_windowconfigure), pBackgroundWindow); + // g_signal_connect(G_OBJECT(pWebWindow), "configure-event", G_CALLBACK(on_windowconfigure), player_interf); + + /** realisation, map and show **/ + gtk_widget_realize(ui->pBackgroundView); + gtk_widget_realize(ui->pTVView); + + Error = init_gpac(player_interf); + if(Error){ + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("[HBBTVTerminal] Error during gpac intialization \n")); + return GF_IO_ERR; + } + + GdkScreen *screen = gtk_widget_get_screen(GTK_WIDGET(ui->pWebView)); + GdkColormap *rgba = gdk_screen_get_rgba_colormap (screen); + + gtk_widget_set_colormap(GTK_WIDGET(ui->pWebView), rgba); + gtk_widget_set_colormap(GTK_WIDGET(pBrowserPositionTable), rgba); + + /** center the windows **/ + + gtk_widget_set_default_colormap(rgba); + gtk_widget_show_all(pWindow); + gtk_widget_show_all(pBackgroundWindow); + gtk_widget_show_all(pTVWindow); + gtk_widget_show_all(pWebWindow); + gtk_window_present (GTK_WINDOW(pWebWindow)); + + return GF_OK; +} + + +int init_playerinterface(sPlayerInterface* player_interf, char* input_data, char* url, Bool no_url) +{ + + HbbtvDemuxer *hbbtv_demuxer; + int Error; + int ChannelScanTimer; + u32 i,nb_chan; + + hbbtv_demuxer = ( HbbtvDemuxer *)player_interf->Demuxer; + ChannelScanTimer = 0; + + player_interf->ui = (sGraphicInterface*) gf_calloc(1,sizeof(sGraphicInterface)); + + + player_interf->input_data = (char*)gf_calloc(strlen(input_data)+1,sizeof(char)); + sprintf(player_interf->input_data,"%s",input_data); + player_interf->input_data[strlen(input_data)] = 0; + + player_interf->url = url; + player_interf->no_url = no_url; + player_interf->TVwake = FALSE; + player_interf->is_connected = is_connected(); + GF_LOG(GF_LOG_DEBUG, GF_LOG_MODULE, ("player_interf->is_connected %d\n",player_interf->is_connected)); + + + Error = ui_init(player_interf); + if(Error){ + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("[HBBTVTerminal] Initialization error \n")); + return GF_IO_ERR; + } + + if(player_interf->url) { + init_browser(player_interf); + } + + while(!hbbtv_demuxer->Check_all_channel_info_received(ChannelScanTimer)) { + ChannelScanTimer++; + gf_sleep(1000); + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("[HBBTVTerminal] Wainting for the PMTs \n")); + } + + hbbtvterm_scan_channel(player_interf); + + /* The check for init is made inside this function. AIT received have to be processed before getting new ones */ + + nb_chan = gf_list_count(hbbtv_demuxer->Get_ChannelList()); + + for(i=0;iGet_ChannelList(),i); + Get_application_for_channel(hbbtv_demuxer,chan->Get_service_id()); + } + player_interf->init = 1; + + put_app_url(player_interf); + + + + return GF_OK; + +} + + +int init_gpac(sPlayerInterface* player_interf) +{ + TRACEINFO; + char config_path[GF_MAX_PATH]; + const char *str; + const char *gpac_cfg; + sGraphicInterface *ui = player_interf->ui; + + +#ifdef XP_UNIX + gpac_cfg = (char *) ".gpacrc"; + strcpy((char *) config_path, getenv("HOME")); +#else + gpac_cfg = "GPAC.cfg"; + strcpy((char*)config_path,"D:\\code\\trunk\\gpac_public\\bin\\win32\\debug"); +#endif + + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("[HBBTVTerminal] Configuration path : %s/%s \n",config_path,gpac_cfg)); + + player_interf->m_user = (GF_User*)gf_malloc(sizeof(GF_User)); + memset(player_interf->m_user, 0, sizeof(GF_User)); + //~ player_interf->m_user->config = gf_cfg_new((const char *) config_path, gpac_cfg); + + /*init config and modules*/ + player_interf->m_user->config = check_config_file(); + if(!player_interf->m_user->config){ + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("[HBBTVTerminal] Configuration failed. COnfiguration file not found \n")); + return GF_BAD_PARAM; + } + + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("[HBBTVTerminal] Configuration initialized \n")); + + str = gf_cfg_get_key( player_interf->m_user->config, "General", "ModulesDirectory"); + player_interf->m_user->modules = gf_modules_new(str, player_interf->m_user->config); + + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("[HBBTVTerminal] %d modules loaded \n",gf_modules_get_count( player_interf->m_user->modules))); + + player_interf->m_user->opaque = player_interf->Demuxer; + player_interf->m_user->EventProc = On_hbbtv_received_section; + +#ifdef XP_UNIX + player_interf->m_user->os_window_handler = (void*)GDK_WINDOW_XID(gtk_widget_get_window(ui->pTVView)); + player_interf->m_user->os_display = GDK_WINDOW_XDISPLAY(gtk_widget_get_window(ui->pTVView)); + XSynchronize((Display *) player_interf->m_user->os_display, True); +#else + player_interf->m_user->os_window_handler = (void*)GDK_WINDOW_HWND(gtk_widget_get_window(ui->pTVView)); + player_interf->m_user->os_display = gdk_drawable_get_display (gtk_widget_get_window(ui->pTVView)); + gdk_display_sync((GdkDisplay *)player_interf->m_user->os_display); +#endif + + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("[HBBTVTerminal] User initialized \n")); + + if (( player_interf->m_term = gf_term_new(player_interf->m_user))) { + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("[HBBTVTerminal] Terminal created.\n")); + } + else { + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("[HBBTVTerminal] Error when creating terminal\n")); + return GF_IO_ERR; + } + + gf_term_set_size( player_interf->m_term, HBBTV_VIDEO_WIDTH,HBBTV_VIDEO_HEIGHT); + + + if (strnicmp(player_interf->input_data, "udp://", 6) + && strnicmp(player_interf->input_data, "mpegts-udp://", 13) + && strnicmp(player_interf->input_data, "mpegts-tcp://", 13) + && strnicmp(player_interf->input_data, "dvb://", 6) + && strnicmp(player_interf->input_data, "http://", 6)) { + + FILE *test = fopen(player_interf->input_data, "rb"); + if (!test) { + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("[HBBTVTerminal] %s file cannot be oppened \n",player_interf->input_data)); + return GF_BAD_PARAM; + } + else { + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("[HBBTVTerminal] %s file oppened successfully \n", player_interf->input_data)); + fclose(test); + } + + } + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("[HBBTVTerminal] Input URL : %s\n", player_interf->input_data)); + gf_term_connect( player_interf->m_term, (const char*)player_interf->input_data ); + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("[HBBTVTerminal] Terminal connected\n")); + + return GF_OK; +} + + +int stop_gpac(sPlayerInterface* player_interf) +{ + TRACEINFO; + gf_term_disconnect( player_interf->m_term); + gf_term_del( player_interf->m_term); + gf_modules_del( player_interf->m_user->modules); + gf_cfg_del( player_interf->m_user->config); + + return TRUE; +} + + +static void activate_uri_entry_cb (GtkWidget* entry, gpointer data) +{ + TRACEINFO; + sGraphicInterface *pUI; + + pUI = (sGraphicInterface*)data; + const gchar* uri = gtk_entry_get_text (GTK_ENTRY(pUI->pEntry)); + g_assert (uri); + webkit_web_view_load_uri (pUI->pWebView, uri); +} + +int term_play(sPlayerInterface* player_interf) +{ + gf_term_set_option(player_interf->m_term, GF_OPT_PLAY_STATE, GF_STATE_PLAYING); + return 1; +} + + +int term_pause(sPlayerInterface* player_interf) +{ + gf_term_set_option(player_interf->m_term, GF_OPT_PLAY_STATE, GF_STATE_PAUSED); + return 1; +} + + +int init_browser(sPlayerInterface* player_interf) +{ + if(!player_interf->no_url) { + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("[HBBTVTerminal] Initialized the browser with HBBTV Application URL : %s \n",player_interf->url)); + webkit_web_view_load_uri(player_interf->ui->pWebView, player_interf->url); + return GF_OK; + } + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("[HBBTVTerminal] URL blocked by the user. No Application to play \n")); + return GF_OK; +} + + +int init_player(sPlayerInterface* player_interf) +{ + /* Init the player with the fisrt channel of the stream */ + + HbbtvDemuxer* hbbtvdemuxer = (HbbtvDemuxer*)player_interf->Demuxer; + Channel* chan = (Channel*)gf_list_get(hbbtvdemuxer->Get_ChannelList(),0); + hbbtvterm_get_channel_on_air(player_interf,chan->Get_service_id(),0); + + return GF_OK; +} + +GF_Err get_app_url(sPlayerInterface* player_interf, GF_M2TS_CHANNEL_APPLICATION_INFO*ChannelApp) +{ + u32 current_service_id; + + current_service_id = player_interf->m_term->root_scene->selected_service_id; + + if(!ChannelApp){ + return GF_IO_ERR; + } + + if((ChannelApp->service_id == current_service_id)) { + GF_M2TS_AIT_APPLICATION* app_info; + Channel* Chan; + + HbbtvDemuxer* hbbtvdemuxer = (HbbtvDemuxer*)player_interf->Demuxer; + Chan = (Channel*)ZapChannel(hbbtvdemuxer,current_service_id,0); + + app_info = Chan->App_to_play(player_interf->is_connected,0); + + if(!app_info){ + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("[HBBTVTerminal] This service id %d does not have an autostart application. \n",ChannelApp->service_id,current_service_id)); + return GF_IO_ERR; + } + + if(player_interf->init){ + if(app_info->broadband){ + player_interf->url = app_info->http_url; + }else if(app_info->broadcast){ + player_interf->url = app_info->carousel_url; + } + webkit_web_view_load_uri(player_interf->ui->pWebView, player_interf->url); + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("[HBBTVTerminal] HBBTV Application URL : %s \n",player_interf->url)); + return GF_OK; + } + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("[HBBTVTerminal] Cannot start the application until player initialization is finished \n")); + return GF_IO_ERR; + } + GF_LOG(GF_LOG_DEBUG, GF_LOG_MODULE, ("ChannelApp->service_id %d is wrong current_service_id %d !!\n",ChannelApp->service_id,current_service_id)); + return GF_IO_ERR; + + +} + +int put_app_url(sPlayerInterface* player_interf) +{ + u32 current_service_id; + Bool ignore_TS_url; + GF_Err e; + HbbtvDemuxer* hbbtvdemux = (HbbtvDemuxer*)player_interf->Demuxer; + + ignore_TS_url = hbbtvdemux->Get_Ignore_TS_URL(); + e = GF_OK; + + if(!ignore_TS_url && !player_interf->no_url){ + + current_service_id = player_interf->m_term->root_scene->selected_service_id; + Channel* chan = (Channel*)ZapChannel(hbbtvdemux,current_service_id,0); + e = get_app_url(player_interf,chan->Get_App_info()); + if(e == GF_OK) { + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("[HBBTVTerminal] Application started\n")); + return GF_OK; + } + player_interf->url = ""; + webkit_web_view_load_uri(player_interf->ui->pWebView, player_interf->url); + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("[HBBTVTerminal]No Application found for this service ID %d \n",current_service_id)); + return GF_IO_ERR; + + }else{ + + if(!player_interf->no_url){ + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("[HBBTVTerminal] Forced URL %s Reaload Application \n",player_interf->url)); + webkit_web_view_load_uri(player_interf->ui->pWebView, player_interf->url); + }else{ + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("[HBBTVTerminal] URL blocked by the user. \n")); + } + return GF_OK; + } +} + + +int hbbtvterm_scan_channel(sPlayerInterface* player_interf) +{ + + GF_MediaInfo odi; + u32 i, count; + + HbbtvDemuxer* HbbtvDemux = (HbbtvDemuxer*)player_interf->Demuxer; + + GF_ObjectManager *root_odm = gf_term_get_root_object(player_interf->m_term); + if (!root_odm){ + return GF_IO_ERR; + } + + if (gf_term_get_object_info(player_interf->m_term, root_odm, &odi) != GF_OK) return GF_IO_ERR; + if (!odi.od) { + return GF_IO_ERR; + } + count = gf_term_get_object_count(player_interf->m_term, root_odm); + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("[HBBTVTerminal] Count: %d \n",count)); + + for (i=0; im_term, root_odm, i); + if (!odm) break; + if (gf_term_get_object_info(player_interf->m_term, odm, &odi) == GF_OK) { + u32 service_id = odi.od->ServiceID; + Channel* chan = (Channel*)ZapChannel(HbbtvDemux,service_id,0); + + if (odi.od_type==GF_STREAM_VISUAL && !chan->Get_processed()) { + chan->Add_video_ID(i); + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("[HBBTVTerminal] Service: %d chan->video_index : %d \n",service_id,chan->Get_video_ID())); + }else if(odi.od_type==GF_STREAM_AUDIO && !chan->Get_processed()) { + chan->Add_audio_ID(i); + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("[HBBTVTerminal]Service:%d chan->current_audio_index : %d \n",service_id,chan->Get_audio_ID(0)));; + } + + } + } + HbbtvDemux->Channel_check(); + + return GF_OK; + +} + + +int hbbtvterm_channel_zap(sPlayerInterface* player_interf,int up_down) +{ + + u32 current_service_id; + + current_service_id = gf_term_get_current_service_id(player_interf->m_term); + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("[HBBTVTerminal] Current_service_id : %d \n",current_service_id)); + hbbtvterm_get_channel_on_air(player_interf,current_service_id,up_down); + + return GF_OK; + +} + + +int hbbtvterm_get_channel_on_air(sPlayerInterface* player_interf, u32 service_id, int zap) +{ + + GF_MediaInfo odi; + + HbbtvDemuxer* hbbtvdemuxer = (HbbtvDemuxer*)player_interf->Demuxer; + Channel* chan = (Channel*)ZapChannel(hbbtvdemuxer,service_id,zap); + GF_ObjectManager *root_odm = gf_term_get_root_object(player_interf->m_term); + if (!root_odm) return GF_IO_ERR; + + if (gf_term_get_object_info(player_interf->m_term, root_odm, &odi) != GF_OK) return GF_IO_ERR; + if (!odi.od) { + return GF_IO_ERR; + } + + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("[HBBTVTerminal]service_id:%d video pid : %d audio pid:%d\n",chan->Get_service_id(),chan->Get_video_ID(),chan->Get_audio_ID(0))); + + gf_term_select_object(player_interf->m_term, gf_term_get_object(player_interf->m_term, root_odm, chan->Get_video_ID())); + gf_term_select_object(player_interf->m_term, gf_term_get_object(player_interf->m_term, root_odm, chan->Get_audio_ID(0))); + + put_app_url(player_interf); + + return GF_OK; + +} + +/** On Windows Functions **/ + +void resizevideoplayer(sPlayerInterface* player_interf, int width, int height) +{ + TRACEINFO; + gf_term_set_size(player_interf->m_term,width,height); + gtk_widget_set_size_request(GTK_WIDGET(player_interf->ui->pTVWindow),width,height); + +} + +void get_window_position(GtkWidget* Widget, int* x, int* y) +{ + //TRACEINFO; + gtk_window_get_position(GTK_WINDOW(Widget), &(*x), &(*y)); +} + +void set_window_position(GtkWidget* Widget, int x, int y) +{ + //~ //TRACEINFO; + gtk_window_move(GTK_WINDOW(Widget), x, y); +} + + +void OnAPPLICATION_Show() +{ + TRACEINFO; + gtk_window_set_opacity(GTK_WINDOW(player_interf->ui->pWebWindow),1); +} + +void OnAPPLICATION_Hide() +{ + TRACEINFO; + gtk_window_set_opacity(GTK_WINDOW(player_interf->ui->pWebWindow),0); +} + + +void OnVIBRC_SetChannel(int channel_number){ + //~ hbbtvterm_get_channel_on_air(player_interf, service_id, 0); +} + +void OnKEYSET_SetValue(double param) +{ + TRACEINFO; + fprintf(stderr, "\t param transmitted : %i\n", (int)param); + + int KEYMASK_RED = 0x1; + int KEYMASK_GREEN = 0x2; + int KEYMASK_YELLOW = 0x4; + int KEYMASK_BLUE = 0x8; + int KEYMASK_NAVIGATION = 0x10; + int KEYMASK_VCR = 0x20; + int KEYMASK_SCROLL = 0x40; + int KEYMASK_INFO = 0x80; + int KEYMASK_NUMERIC = 0x100; + int KEYMASK_ALPHA = 0x200; + int KEYMASK_OTHER = 0x400; + + player_interf->keyregistered[RK_RED] = (((int)param & KEYMASK_RED)) ? true : false; + player_interf->keyregistered[RK_GREEN] = (((int)param & KEYMASK_GREEN)) ? true : false; + player_interf->keyregistered[RK_YELLOW] = (((int)param & KEYMASK_YELLOW)) ? true : false; + player_interf->keyregistered[RK_BLUE] = (((int)param & KEYMASK_BLUE)) ? true : false; + player_interf->keyregistered[RK_NAVIGATION] = (((int)param & KEYMASK_NAVIGATION)) ? true : false; + player_interf->keyregistered[RK_VCR] = (((int)param & KEYMASK_VCR)) ? true : false; + player_interf->keyregistered[RK_SCROLL] = (((int)param & KEYMASK_SCROLL)) ? true : false; + player_interf->keyregistered[RK_INFO] = (((int)param & KEYMASK_INFO)) ? true : false; + player_interf->keyregistered[RK_NUMERIC] = (((int)param & KEYMASK_NUMERIC)) ? true : false; + player_interf->keyregistered[RK_ALPHA] = (((int)param & KEYMASK_ALPHA)) ? true : false; + player_interf->keyregistered[RK_OTHER] = (((int)param & KEYMASK_OTHER)) ? true : false; +} + +void OnNoFullscreenSetWindow(int x, int y, int width, int height) +{ + TRACEINFO; + OnVIDBRC_SetFullScreen(false); +} +void OnVIDBRC_SetFullScreen(int fullscreenparam) +{ + TRACEINFO; + fprintf(stderr,"SET FULLSCREEN OnVIDBRC_SetFullScreen Param : %d\n",fullscreenparam); + + ///Getting the videobroadcast element. + WebKitDOMDocument *pDOMdoc = webkit_web_view_get_dom_document(player_interf->ui->pWebView); + WebKitDOMNodeList *objectslist = webkit_dom_document_get_elements_by_tag_name(pDOMdoc,"object"); + gulong l = webkit_dom_node_list_get_length(objectslist); + gulong i = 0; + bool videofound = false; + WebKitDOMNode* videonode; + char *videonodetype; + while ((i < l) && !videofound) + { + videonode = webkit_dom_node_list_item(objectslist,i); + videonodetype = webkit_dom_element_get_attribute(WEBKIT_DOM_ELEMENT(videonode),"type"); + if (!(strcmp(videonodetype, "video/broadcast"))) + videofound = true; + else + i++; + } + + if (!videofound) + { + fprintf(stderr,"object video broadcast not found \n"); + return; + } + else + { + fprintf(stderr,"object video broadcast found \n"); + WebKitDOMElement* videoelt = WEBKIT_DOM_ELEMENT(videonode); + + ///Getting the position of the WebView + gint posx, posy; + get_window_position(player_interf->ui->pWebWindow, &posx, &posy); + fprintf(stderr,"WebWindow Left : %d , WebWindow Top : %d \n", posx, posy); + + ///Getting the videobroadcast geometry + glong left, top; + glong eltwidth, eltheight ; + ///Setting the new size and position of the TVWindow broadcast + int newx; + int newy; + + if (fullscreenparam){ + left = 0; + top = 0; + eltwidth = HBBTV_VIDEO_WIDTH; + eltheight = HBBTV_VIDEO_HEIGHT; + get_window_position(player_interf->ui->pWebWindow, &newx, &newy); + }else{ + gtk_window_set_position(GTK_WINDOW(player_interf->ui->pTVWindow), GTK_WIN_POS_NONE); + ///Getting the top and left values of the video/broadcast element . + left = webkit_dom_element_get_offset_left(videoelt); + top = webkit_dom_element_get_offset_top(videoelt); + ///Getting the width and height values of the video/broadcast element . + eltwidth = webkit_dom_element_get_offset_width(videoelt); + eltheight = webkit_dom_element_get_offset_height(videoelt); + newx = posx + left; + newy = posy + top; + } + + fprintf(stderr,"Videoelt offset : Left : %d , Top : %d, ", left, top); + fprintf(stderr,"EltWidth : %d , EltHeight : %d \n", eltwidth, eltheight); + set_window_position(player_interf->ui->pTVWindow, newx, newy); + fprintf(stderr,"Supposed TVWindow new position : %d x %d \n", newx, newy); + resizevideoplayer(player_interf, eltwidth, eltheight); + if(fullscreenparam){ + gtk_window_set_position(GTK_WINDOW(player_interf->ui->pTVWindow), GTK_WIN_POS_CENTER_ALWAYS); + } + fprintf(stderr,"Checking position : \n "); + gint checkx, checky; + get_window_position(player_interf->ui->pTVWindow, &checkx, &checky); + fprintf(stderr,"CHECK TVWindow Left : %d , CHECK TVWindow Top : %d \n", checkx, checky); + } + +} + +void OnKEYSET_Allocate() +{ + webkit_web_view_execute_script(player_interf->ui->pWebView, + " var KeyEvent = new Object(); \ + KeyEvent.VK_RED = 403; \ + KeyEvent.VK_YELLOW = 405;\ + KeyEvent.VK_GREEN = 404;\ + KeyEvent.VK_BLUE = 406;\ + KeyEvent.VK_UP = 38;\ + KeyEvent.VK_DOWN = 40;\ + KeyEvent.VK_LEFT = 37;\ + KeyEvent.VK_RIGHT = 39;\ + KeyEvent.VK_PLAY = 415;\ + KeyEvent.VK_STOP = 413;\ + KeyEvent.VK_PAUSE = 19;\ + KeyEvent.VK_FAST_FWD = 417;\ + KeyEvent.VK_REWIND = 412;\ + KeyEvent.VK_TELETEXT = 459;\ + KeyEvent.VK_ESCAPE = 27;\ + KeyEvent.VK_ENTER = 13;\ + KeyEvent.VK_0 = 48;\ + KeyEvent.VK_1 = 49;\ + KeyEvent.VK_2 = 50;\ + KeyEvent.VK_3 = 51;\ + KeyEvent.VK_4 = 52;\ + KeyEvent.VK_5 = 53;\ + KeyEvent.VK_6 = 54;\ + KeyEvent.VK_7 = 55;\ + KeyEvent.VK_8 = 56;\ + KeyEvent.VK_9 = 57; "); +} + + +static void usage() +{ + printf("\nUsage: hbbtvterminal -input=input_data [-url=url] [-no_url]\n"); + printf("-input=input_data : input data to process (dvb://, udp://, or file)\n"); + printf("-url=url : force the player to connect to an url. Ignore the url(s) found in the input data\n"); + printf("-no_url : the player will not connect to HBBTV services \n"); + printf("-dsmcc : enable the DSMCC data carousel processing \n"); +} + + +/****************************************************************************/ +/** getargs */ +/** @author Stanislas Selle */ +/** @date 2011/06/15 */ +/** gets args from agrv and set the options into the arguments */ +/****************************************************************************/ + +static void getargs(int argc, char *argv[], char* &input_data, Bool* dsmcc, char* &url, Bool* no_url) +{ + u32 i; + + no_url = 0; + i = 0; + input_data = NULL; + url = NULL; + + for (i=1; i<(u32) argc; i++) { + char *arg = argv[i]; + if (arg[0]=='-') { + if (!strnicmp(arg, "-input=", 7)) { + input_data = arg+7; + }else if (!strnicmp(arg, "-url=", 5)) { + url = arg+5; + }else if (!strnicmp(arg, "-no_url", 7)) { + *no_url = 1; + }else if (!strnicmp(arg, "-dsmcc", 6)) { + *dsmcc = 1; + }else { + usage(); + exit(0); + } + }else { + usage(); + exit(0); + } + } + + if( !input_data && (!url || *no_url)) { + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("[HBBTVTerminal] Input data is needed if no URL is given.\n")); + usage(); + exit(0); + } + if( !*dsmcc) { + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("[HBBTVTerminal] Ignoring DSMCC data !! \n")); + } + + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("[HBBTVTerminal] Starting the processing of the TS file %s \n",input_data)); + +} + +int main (int argc, char* argv[]) +{ + char* input_data; + char* url; + Bool dsmcc; + Bool no_url; + int Error; + + dsmcc = no_url = 0; + + url = input_data = NULL; + + //TRACEINFO; + gf_sys_init(1); + gf_log_set_tool_level(GF_LOG_MODULE,GF_LOG_INFO); + printf("GPAC HBBTV Terminal (c) Telecom ParisTech 2011\n"); + + getargs(argc, argv, input_data , &dsmcc, url, &no_url); + + GF_SAFEALLOC(player_interf, sPlayerInterface); + + HbbtvDemuxer* hbbtv_demuxer = new HbbtvDemuxer(input_data, url,dsmcc, no_url, player_interf); + + gtk_init(&argc, &argv); + + if (!g_thread_supported()) { + g_thread_init(NULL); + } + + Error = init_playerinterface(player_interf,input_data,url,no_url); + if(Error){ + GF_LOG(GF_LOG_INFO, GF_LOG_MODULE, ("[HBBTVTerminal] Error - Aborting the processing \n")); + return 0; + } + //~ gf_term_set_option(player_interf->m_term, GF_OPT_PLAY_STATE, GF_STATE_PLAYING); + + gtk_main(); + //free(ui); +} + diff --git a/applications/hbbtvplayer/hbbtvterminal/src/hbbtvterminal.h b/applications/hbbtvplayer/hbbtvterminal/src/hbbtvterminal.h new file mode 100644 index 0000000..a258002 --- /dev/null +++ b/applications/hbbtvplayer/hbbtvterminal/src/hbbtvterminal.h @@ -0,0 +1,350 @@ +/* + * Copyright (c) 2010-2011 Telecom-Paristech + * All Rights Reserved + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Authors: Stanislas Selle - Jonathan Sillan + * + */ + +#ifndef __HBBTVTERMINAL__ +#define __HBBTVTERMINAL__ + +#define _WIN32_WINNT 0x0510 + +#ifdef __cplusplus +extern "C" { +#endif + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +} +#endif + +#include + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define DEBUG true +#define PROJECTNAME "HbbTVTerminal" +#define TRACEINFO if (DEBUG) { fprintf(stderr, " BOB \x1b[%i;3%im%s\x1b[0m : call %s\n", 1, 4, PROJECTNAME, __FUNCTION__); } +#define NOTIMPLEMENTED if (DEBUG) { printf("SEGAR %s : %s is NOT IMPLEMENTED : TODO \n", PROJECTNAME, __FUNCTION__); } + +#define TRANSPARENCE true + +#define HBBTV_VIDEO_WIDTH 1280 +#define HBBTV_VIDEO_HEIGHT 720 + +typedef struct GraphicInterface +{ + WebKitWebView *pWebView; + GtkWidget *pTVView; + GtkWidget *pBackgroundView; + GtkWidget *pMainWindow; + GtkWidget *pTVWindow; + GtkWidget *pWebWindow; + GtkWidget *pBackgroundWindow; + GtkWidget *pEntry; +} sGraphicInterface; + + +enum listHBBTVKeys{ + HBBTV_VK_ENTER = 13, + HBBTV_VK_PAUSE = 19, + HBBTV_VK_ESCAPE = 27, + HBBTV_VK_LEFT = 37, + HBBTV_VK_RIGHT = 39, + HBBTV_VK_UP = 38, + HBBTV_VK_DOWN = 40, + HBBTV_VK_0 = 48, + HBBTV_VK_1 = 49, + HBBTV_VK_2 = 50, + HBBTV_VK_3 = 51, + HBBTV_VK_4 = 52, + HBBTV_VK_5 = 53, + HBBTV_VK_6 = 54, + HBBTV_VK_7 = 55, + HBBTV_VK_8 = 56, + HBBTV_VK_9 = 57, + HBBTV_VK_RED = 403, + HBBTV_VK_YELLOW = 405, + HBBTV_VK_GREEN = 404, + HBBTV_VK_BLUE = 406, + HBBTV_VK_REWIND = 412, + HBBTV_VK_STOP = 413, + HBBTV_VK_PLAY = 415, + HBBTV_VK_FAST_FWD = 417, + HBBTV_VK_TELETEXT = 459 +}; + + +enum listRegisteredKeys { + RK_OTHER = 0, + RK_RED = 1, + RK_GREEN = 2, + RK_YELLOW = 3, + RK_BLUE = 4, + RK_NAVIGATION = 5, + RK_VCR = 6, + RK_SCROLL = 7, + RK_INFO = 8, + RK_NUMERIC = 9, + RK_ALPHA = 10 +}; + +typedef struct PlayerInterface +{ + sGraphicInterface* ui; + Bool no_url; + char* input_data; + char* url; + GF_Terminal *m_term; + GF_User *m_user; + void* Demuxer; + GF_Mutex *Get_demux_info_mutex; + int TVwake; + Bool keyregistered[11]; + Bool init; + Bool is_connected; + Bool app_in_action; + +} sPlayerInterface; + +int ui_init(sGraphicInterface *ui); + +int init_gpac(sPlayerInterface* player_interf); +int init_browser(sPlayerInterface* player_interf); +int stop_gpac(sPlayerInterface *player_interf); +int playpause(sPlayerInterface *player_interf); +int term_play(sPlayerInterface *player_interf); +int term_pause(sPlayerInterface *player_interf); +int init_player(sPlayerInterface* player_interf); + + +typedef struct +{ + GF_M2TS_AIT* ait; + + char* data; + u32 table_id; + u32 data_size; +} AIT_TO_PROCESS; + + +typedef struct +{ + GF_M2TS_DSMCC_SECTION* dsmcc_sections; + char* buff; + + /*added not in the spec*/ + u8 first_section_received; +}GF_M2TS_GATHER_DSMCC_SECTION; + +#define MAX_audio_index 16 + +class Channel +{ +public: + /* Constructor */ + Channel(u32 TSservice_ID, char* TSchannel_name); + /* Destructor */ + void Destroy_Channel(); + /* Fonctions */ + u32 Add_service_id(u32 service_id); + u32 Add_channel_name(char* chan_name); + u32 Add_video_ID(u32 video_index); + u32 Add_audio_ID(u32 current_audio_index); + u32 Add_ait_pid(u32 ait_pid); + u32 Add_pmt_pid(u32 pmt_pid); + u32 Add_App_info(GF_M2TS_CHANNEL_APPLICATION_INFO*add_ait); + void Check_Info_Done(); + void Incr_audio_index(int index); + void Set_audio_index(u32 index); + /* Getter */ + u32 Get_service_id(); + char* Get_channel_name(); + u32 Get_video_ID(); + u32 Get_audio_ID(u32 indice); + u32 Get_ait_pid(); + u32 Get_pmt_pid(); + Bool Get_processed(); + u32 Get_audio_index(); + u32 Get_nb_chan_audio_stream(); + GF_M2TS_CHANNEL_APPLICATION_INFO* Get_App_info(); + GF_M2TS_AIT_APPLICATION* App_to_play(Bool isConnected,u8 MaxPriority); + GF_M2TS_AIT_APPLICATION* Get_App(u32 application_id); + +private: + u32 service_ID; + u32 number; + char* channel_name; + u32 video_ID; + u32 audio_ID[MAX_audio_index]; + u32 AIT_PID; + u32 PMT_PID; + Bool processed; + u32 current_audio_index; + u32 nb_chan_audio_stream; + GF_M2TS_CHANNEL_APPLICATION_INFO* ChannelApp; + +}; + +class HbbtvDemuxer +{ +public: + /* Constructeur */ + HbbtvDemuxer(char *input_data, char* url, Bool dsmcc, Bool no_url,sPlayerInterface* player_interf); + + /* Destructeur */ + + /* Fonction */ + u32 HbbtvDemuxer_DemuxStart(); + Bool ait_already_received(char *data,u32 pid); + void GetAppInfoFromAit(GF_M2TS_AIT* ait); + Channel* Zap_channel(u32 service_id,int zap); + void Create_Channel(GF_M2TS_Program* pmt); + void Check_PMT_Processing(); + void Channel_check(); + u32 Check_application_priority(Channel* chan, GF_M2TS_AIT* ait); + /* Getter */ + GF_M2TS_Demuxer* Get_Ts(); + GF_List* Get_ChannelList(); + GF_List* Get_AIT_To_Process_list(); + char* Get_Input_data(); + Bool Get_if_dsmcc_process(); + GF_Thread * Get_Demux_Thread(); + GF_Mutex * Get_Demux_Mutex(); + void* Get_User(); + int Check_all_channel_info_received(int Timer); + char* Get_Force_URL(); + Bool Get_Ignore_TS_URL(); + Bool Get_ait_to_proces(); + Channel* Get_Channel(u32 service_id); + GF_Err Get_application_info(GF_M2TS_CHANNEL_APPLICATION_INFO*app_info); + /* Setter */ + void Set_Ts(GF_M2TS_Demuxer* ts); + void Set_ait_to_process(Bool on); + +private: + /* Fonction */ + + u32 GetDemuxStartFunction(); + + /* Attribut */ + GF_M2TS_Demuxer *Demuxts; + GF_List* Channels; + GF_List* Ait_To_Process; + + /* Thread for Demux */ + GF_Thread *ts_demux_thread; + /* Mutex for Demux */ + GF_Mutex *ts_demux_mutex; + + /*local file playing*/ + char *Input_data; + char *Service_URL; + char *Force_URL; + Bool process_dsmcc; + Bool Ignore_TS_URL; + Bool No_URL; + u32 nb_ait; + u32 file_size; + Bool ait_to_process; + u32 nb_prog_pmt_received; + Bool all_prog_pmt_received; + + /*callback to push AIT information when a AIT is received*/ + void (*on_ait_event)(void *player_interf, GF_M2TS_CHANNEL_APPLICATION_INFO*app_info); + /*private user data - To the PlayerInterface*/ + void *user; + +}; + + +/* Global Functions */ + +u32 On_hbbtv_received_section(void *ptr, GF_Event *event); +u32 DemuxThreadStart(HbbtvDemuxer *hbbtv_demuxer); +u32 DemuxStart(void *par); +GF_Err get_app_url(sPlayerInterface* player_interf, GF_M2TS_CHANNEL_APPLICATION_INFO*app_info); +int put_app_url(sPlayerInterface* player_interf); +u32 Get_application_for_channel(HbbtvDemuxer* hbbtv_demuxer,u32 service_id); + +int change_geometry( int width, int height); +int hbbtvterm_scan_channel(sPlayerInterface* player_interf); +Channel* ZapChannel(HbbtvDemuxer *hbbtv_demuxer,u32 service_id,int zap); +int hbbtvterm_channel_zap(sPlayerInterface* player_interf,int up_down); +int hbbtvterm_get_channel_on_air(sPlayerInterface* player_interf, u32 service_id, int zap); +void resizevideoplayer(sPlayerInterface* player_interf, int width, int height); + +void get_window_position(GtkWidget* Widget, int* x, int* y); +void set_window_position(GtkWidget* Widget, int x, int y); + +GF_Config* check_config_file(); +Bool is_connected(); + +/* HBBTV Functions */ + + +/* HBBTV Button */ +void on_redbuttonclicked(GtkWidget *widget, gpointer data); +void on_greenbuttonclicked(GtkWidget *widget, gpointer data); +void on_yellowbuttonclicked(GtkWidget *widget, gpointer data); +void on_bluebuttonclicked(GtkWidget *widget, gpointer data); + +/*Navigation */ +void on_upbuttonclicked(GtkWidget *widget, gpointer data); +void on_downbuttonclicked(GtkWidget *widget, gpointer data); +void on_leftbuttonclicked(GtkWidget *widget, gpointer data); +void on_rightbuttonclicked(GtkWidget *widget, gpointer data); +void on_returnbuttonclicked(GtkWidget *widget, gpointer data); +void on_exitbuttonclicked(GtkWidget *widget, gpointer data); +void on_2buttonclicked(GtkWidget *widget, gpointer data); + +/* Control */ +void on_onoffbuttonclicked(GtkWidget *widget, gpointer data); +void on_playpausebuttonclicked(GtkWidget *widget, gpointer data); +void on_playbuttonclicked(GtkWidget *widget, gpointer data); +void on_pausebuttonclicked(GtkWidget *widget, gpointer data); +void on_langbuttonclicked(GtkWidget *widget, gpointer data); +void on_chanupbuttonclicked(GtkWidget *widget, gpointer data); +void on_chandownbuttonclicked(GtkWidget *widget, gpointer data); +void on_channelbuttonclicked(GtkWidget *widget, gpointer data); +void on_teletextbuttonclicked(GtkWidget *widget, gpointer data); + + + +#ifdef __cplusplus +} +#endif + +#endif // __HBBTVTERMINAL__ diff --git a/applications/hbbtvplayer/hbbtvterminal/src/makefile.am b/applications/hbbtvplayer/hbbtvterminal/src/makefile.am new file mode 100644 index 0000000..65c35f3 --- /dev/null +++ b/applications/hbbtvplayer/hbbtvterminal/src/makefile.am @@ -0,0 +1,25 @@ +########################################################################################### +## +## Copyright 2011 Telecom Paristech +## Author : Stanislas Selle +## +########################################################################################### + +bin_PROGRAMS = hbbtvterminal +hbbtvterminal_SOURCES = hbbtvterminal.cpp hbbtv_demux.cpp hbbtv_channel.cpp hbbtv_keycontrol.cpp hbbtv_tools.cpp hbbtvterminal.h + +SPECIALCFLAGS = -g -DXP_UNIX=1 -DMOZ_X11=1 -fPIC + +hbbtvterminal_CPPFLAGS = $(SPECIALCFLAGS) $(HBBTVBROWSERPLUGIN_CFLAGS) $(GLIB_CFLAGS) $(GTK_CFLAGS) $(WEBKIT_CFLAGS) $(PIXMAN_CFLAGS) $(NPAPI_CFLAGS)\ + -g -DXP_UNIX -fPIC \ + -I /usr/include\ + -I /usr/include/gpac/\ + -I /usr/local/include\ + -I /usr/local/include/gpac/ + + +hbbtvterminal_LDFLAGS = $(SPECIALCFLAGS) + +hbbtvterminal_LDADD = $(GLIB_LIBS) $(GTK_LIBS) $(WEBKIT_LIBS) $(PIXMAN_LIBS) $(NPAPI_LIBS) $(MOZILLAJS_LIBS) $(HBBTVBROWSERPLUGIN_LIBS) \ + -L/usr/local/lib -lgpac + diff --git a/applications/hbbtvplayer/install.sh b/applications/hbbtvplayer/install.sh new file mode 100644 index 0000000..a5c9883 --- /dev/null +++ b/applications/hbbtvplayer/install.sh @@ -0,0 +1,78 @@ +#!/bin/sh -e + +PLAYER=0 +DEPENDENCIES=0 +WEBKIT=0 +GPAC=0 + +if [ -z $1 ] ; then + echo "\nUsage: You must choose options :" + echo "\n\033[31m full - build the whole package (gpac+webkit+hbbtvplayer) Recommanded for computer without gpac \033[0m" + echo "\n\033[32m player - build the HBBTV player \033[0m" + echo "\n\033[33m webkit - download Webkit sources and install it\033[0m" + echo "\n\033[33m gpac - download gpac sources and install it\033[0m" + echo "\n\033[34m dependencies - get the dependencies needed to build the HBBTVPlayer \033[0m" + exit 1 +fi + +for i in $* ; do + if [ "$i" = "full" ] ; then + echo -e "\033[31m Usage: $0 Full Building : Activated \033[0m" + PLAYER=1 + DEPENDENCIES=1 + WEBKIT=1 + GPAC=1 + fi + + if [ "$i" = "player" ] ; then + echo -e "\033[32m Usage: $0 Player Building : Activated \033[0m" + PLAYER=1 + fi + + if [ "$i" = "dependencies" ] ; then + echo -e "\033[34m Usage: $0 Dependecies Building : Activated \033[0m" + DEPENDENCIES=1 + fi + + if [ "$i" = "webkit" ] ; then + echo -e "\033[33m Usage: $0 Webkit Building : Activated \033[0m" + WEBKIT=1 + fi + + if [ "$i" = "gpac" ] ; then + echo -e "\033[33m Usage: $0 gpac Building : Activated \033[0m" + WEBKIT=1 + fi +done + +if [ $DEPENDENCIES -eq 1 ] ; then + sudo apt-get install `cat listdependencies` +fi + +if [ $GPAC -eq 1 ] ; then + cd gpac + ./configure --use-js=no + make -j2 + sudo make install + sudo make install-lib + cd .. +fi + +if [ $WEBKIT -eq 1 ] ; then + ./WebKit/Tools/Scripts/build-webkit --gtk --with-gtk=2.0 --no-webkit2 --makeargs="-j2 install" +fi + + +if [ $PLAYER -eq 1 ] ; then + cd hbbtvbrowserplugin + ./autogen.sh + sudo make install + cd .. + + cd hbbtvterminal + ./autogen.sh + sudo make install + cd .. +fi + + diff --git a/applications/hbbtvplayer/listdependencies b/applications/hbbtvplayer/listdependencies new file mode 100644 index 0000000..58c74a3 --- /dev/null +++ b/applications/hbbtvplayer/listdependencies @@ -0,0 +1,79 @@ +autoconf +automake +autotools-dev +bison +build-essential +dvb-apps +flex +gail-3.0 +glib-networking +gperf +gtk-doc-tools +liba52-0.7.4-dev +libasound2-dev +libatk1.0-0 +libavcodec52 +libavcodec-dev +libavformat52 +libavformat-dev +libavutil-dev +libc6 +libcairo2 +libenchant1c2a +libenchant-dev +libfaad2 +libfaad-dev +libfontconfig1 +libfreetype6 +libfreetype6-dev +libgail18 +libgail-3-dev +libgail-dev +libgcc1 +libgdk-pixbuf2.0-0 +libgl1-mesa-dev +libglib2.0-0 +libglib2.0-dev +libgpac0.4.5 +libgstreamer0.10-0 +libgstreamer-plugins-base0.10-0 +libgtk2.0-dev +libicu44 +libjpeg62-dev +libmad0-dev +libogg-dev +libopenjpeg-dev +libpango1.0-0 +libpng12-dev +libpulse-dev +libsdl1.2debian +libsdl1.2-dev +libsoup2.4-dev +libsqlite3-dev +libssl-dev +libstdc++6 +libswscale-dev +libtheora-dev +libtool +libvorbis-dev +libwxbase2.8-dev +libwxgtk2.8-dev +libx11-6 +libxml2 +libxslt-dev +libxt6 +libxv-dev +libxvidcore-dev +linux-sound-base +subversion +wx2.8-headers +x11proto-gl-dev +x11proto-video-dev +xulrunner-2.0-dev +zlib1g-dev +libgeoclue-dev +icu-doc +libicu-dev +libgstreamer0.10-dev +libgstreamer-plugins-base0.10-dev + diff --git a/applications/hbbtvplayer/projectmanager/codeblocks/hbbtvplayer.workspace b/applications/hbbtvplayer/projectmanager/codeblocks/hbbtvplayer.workspace new file mode 100644 index 0000000..ab6eb5a --- /dev/null +++ b/applications/hbbtvplayer/projectmanager/codeblocks/hbbtvplayer.workspace @@ -0,0 +1,7 @@ + + + + + + + diff --git a/applications/m3u82mpd/m3u82mpd.vcproj b/applications/m3u82mpd/m3u82mpd.vcproj new file mode 100644 index 0000000..1887aca --- /dev/null +++ b/applications/m3u82mpd/m3u82mpd.vcproj @@ -0,0 +1,183 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/applications/m3u82mpd/main.c b/applications/m3u82mpd/main.c index 114c4d4..cb8c8e2 100644 --- a/applications/m3u82mpd/main.c +++ b/applications/m3u82mpd/main.c @@ -22,95 +22,101 @@ * */ -#include "../modules/mpd_in/m3u8.h" + +#include #include #include + int main(int argc, char **argv) { - GF_Err e; - VariantPlaylist * pl = NULL; - char *url = argv[1]; - char *cache_m3u8_file; + GF_Err e; + VariantPlaylist * pl = NULL; + char *url = argv[1]; + //char *cache_m3u8_file; u32 i, count; - FILE *fmpd; - Bool verbose = 0; - u32 update_interval = 0; - char *m3u8_local_name = "file.m3u8"; - Bool is_local = 0; + FILE *fmpd; + Bool verbose = 0; + u32 update_interval = 0; + char *m3u8_local_name = "file.m3u8"; + Bool is_local = 0; - gf_sys_init(0); + gf_sys_init(0); - gf_log_set_level(verbose ? GF_LOG_DEBUG : GF_LOG_INFO); - gf_log_set_tools(GF_LOG_NETWORK); + gf_log_set_tool_level(GF_LOG_NETWORK, verbose ? GF_LOG_DEBUG : GF_LOG_INFO); - while (1) { + while (1) { - if (gf_url_is_local(url)) { - m3u8_local_name = url; - is_local = 1; - } else { - e = gf_dm_wget(url, m3u8_local_name); - if (e != GF_OK) return -1; - } + if (gf_url_is_local(url)) { + m3u8_local_name = url; + is_local = 1; + } else { + e = gf_dm_wget(url, m3u8_local_name); + if (e != GF_OK) return -1; + } - e = parse_root_playlist(m3u8_local_name, &pl, "."); - if (e != GF_OK) return -1; - - fmpd = fopen(argv[2], "wt"); - - fprintf(fmpd, "\n"); - fprintf(fmpd, " \n"); - fprintf(fmpd, " Media Presentation Description for file %s\n", url); - fprintf(fmpd, " Generated by GPAC %s\n", GPAC_FULL_VERSION); + e = parse_root_playlist(m3u8_local_name, &pl, "."); + if (e != GF_OK) return -1; + + fmpd = fopen(argv[2], "wt"); + + fprintf(fmpd, "\n"); + fprintf(fmpd, " \n"); + fprintf(fmpd, " Media Presentation Description for file %s\n", url); + fprintf(fmpd, " Generated by GPAC %s\n", GPAC_FULL_VERSION); - fprintf(fmpd, " \n"); - fprintf(fmpd, " \n"); + fprintf(fmpd, " \n"); + fprintf(fmpd, " \n"); - count = gf_list_count(pl->programs); - for (i=0; iprograms, i); - count2 = gf_list_count(prog->bitrates); - for (j = 0; jbitrates, j); - fprintf(stdout, "%d, %d, %s, %s, %d\n", pe->durationInfo, pe->bandwidth, pe->title, pe->url, pe->elementType); - if (pe->elementType == TYPE_PLAYLIST) { - u32 k, count3; - char *tmp; - char c; - char baseURL[GF_MAX_PATH]; - tmp = strrchr(url, '/'); - tmp++; - c = tmp[0]; - tmp[0] = 0; - strcpy(baseURL, url); - tmp[0] = c; - fprintf(fmpd, " \n"); - fprintf(fmpd, " \n", pe->durationInfo, baseURL); - count3 = gf_list_count(pe->element.playlist.elements); - update_interval = (count3 - 1) * pe->durationInfo * 1000; - for (k=0; kelement.playlist.elements, k); - if (k) fprintf(fmpd, " \n", elt->url); - else fprintf(fmpd, " \n", elt->url); - } - fprintf(fmpd, " \n"); - fprintf(fmpd, " \n"); - } else if (pe->elementType == TYPE_STREAM) { - fprintf(stdout, "Stream\n"); - } - } - } - fprintf(fmpd, " \n"); - fprintf(fmpd, ""); - fclose(fmpd); - variant_playlist_del(pl); - if (is_local) break; - gf_sleep(update_interval); - - } + count = gf_list_count(pl->programs); + for (i=0; iprograms, i); + count2 = gf_list_count(prog->bitrates); + for (j = 0; jbitrates, j); + fprintf(stdout, "%d, %d, %s, %s, %d\n", pe->durationInfo, pe->bandwidth, pe->title, pe->url, pe->elementType); + if (pe->elementType == TYPE_PLAYLIST) { + u32 k, count3; + char *tmp; + char c; + char baseURL[GF_MAX_PATH]; + tmp = strrchr(url, '/'); + if (tmp) { + tmp++; + c = tmp[0]; + tmp[0] = 0; + strcpy(baseURL, url); + tmp[0] = c; + } else { + baseURL[0] = 0; + } + fprintf(fmpd, " \n"); + fprintf(fmpd, " durationInfo); + if (baseURL[0]) fprintf(fmpd, "baseURL=\"%s\"", baseURL); + fprintf(fmpd, ">\n"); + count3 = gf_list_count(pe->element.playlist.elements); + update_interval = (count3 - 1) * pe->durationInfo * 1000; + for (k=0; kelement.playlist.elements, k); + if (k) fprintf(fmpd, " \n", elt->url); + else fprintf(fmpd, " \n", elt->url); + } + fprintf(fmpd, " \n"); + fprintf(fmpd, " \n"); + } else if (pe->elementType == TYPE_STREAM) { + fprintf(stdout, "Stream\n"); + } + } + } + fprintf(fmpd, " \n"); + fprintf(fmpd, ""); + fclose(fmpd); + variant_playlist_del(pl); + if (is_local) break; + gf_sleep(update_interval); + } - gf_sys_close(); - return 0; + gf_sys_close(); + return 0; } diff --git a/applications/mp42avi/main.c b/applications/mp42avi/main.c index ad7def4..dd31812 100644 --- a/applications/mp42avi/main.c +++ b/applications/mp42avi/main.c @@ -164,17 +164,17 @@ void write_bmp(GF_VideoSurface *fb, char *rad_name, u32 img_num) fi.biSizeImage = fb->pitch * fb->height; /*NOT ALIGNED!!*/ - fwrite(&fh.bfType, 2, 1, fout); - fwrite(&fh.bfSize, 4, 1, fout); - fwrite(&fh.bfReserved1, 2, 1, fout); - fwrite(&fh.bfReserved2, 2, 1, fout); - fwrite(&fh.bfOffBits, 4, 1, fout); + gf_fwrite(&fh.bfType, 2, 1, fout); + gf_fwrite(&fh.bfSize, 4, 1, fout); + gf_fwrite(&fh.bfReserved1, 2, 1, fout); + gf_fwrite(&fh.bfReserved2, 2, 1, fout); + gf_fwrite(&fh.bfOffBits, 4, 1, fout); - fwrite(&fi, 1, 40, fout); + gf_fwrite(&fi, 1, 40, fout); for (j=fb->height; j>0; j--) { ptr = fb->video_buffer + (j-1)*fb->pitch; - //fwrite(ptr, 1, fb->width * 3, fout); + //gf_fwrite(ptr, 1, fb->width * 3, fout); for (i=0;iwidth; i++) { fputc(ptr[2], fout); fputc(ptr[1], fout); @@ -201,7 +201,7 @@ void write_raw(GF_VideoSurface *fb, char *rad_name, u32 img_num) fout = fopen(str, "wb"); if (!fout) return; - fwrite(fb->video_buffer , fb->height*fb->pitch, 1, fout); + gf_fwrite(fb->video_buffer , fb->height*fb->pitch, 1, fout); fclose(fout); } diff --git a/applications/mp42ts/main.c b/applications/mp42ts/main.c index 4f9261a..dc4a10a 100644 --- a/applications/mp42ts/main.c +++ b/applications/mp42ts/main.c @@ -52,7 +52,8 @@ static GFINLINE void usage(const char * progname) "\t-psi-rate=V sets PSI refresh rate V in ms (default 100ms). If 0, PSI data is only send once at the begining\n" "\t-time=n request the program to stop after n ms\n" "\t-single-au forces 1 PES = 1 AU (disabled by default)\n" - + "\t-rap forces RAP/IDR to be aligned with PES start for video streams (disabled by default)\n" + " in this mode, PAT, PMT and PCR will be inserted before the first TS packet of the RAP PES\n" "\t-prog=filename specifies an input file used for a TS service\n" "\t * currently only supports ISO files and SDP files\n" "\t * can be used several times, once for each program\n" @@ -76,9 +77,8 @@ static GFINLINE void usage(const char * progname) "\t-bifs-pes carries BIFS over PES instead of sections\n" "\t-bifs-pes-ex carries BIFS over PES without writing timestamps in SL\n" "\t\n" - "\t-ll=LogLevel specifies log level to use (by default error)\n" - "\t-lt=LogTools specifies log tools to use (by default all)\n" - "\t-h or -help Print this screen\n" + "\t-logs set log tools and levels, formatted as a ':'-separated list of toolX[:toolZ]@levelX\n" + "\t-h or -help print this screen\n" "\n", progname, DEFAULT_PCR_OFFSET ); } @@ -166,7 +166,9 @@ static GF_Err mp4_input_ctrl(GF_ESInterface *ifce, u32 act_type, void *param) if (!priv->sample) priv->sample = gf_isom_get_sample(priv->mp4, priv->track, priv->sample_number+1, NULL); - if (!priv->sample) return GF_IO_ERR; + if (!priv->sample) { + return GF_IO_ERR; + } pck.flags = 0; pck.flags = GF_ESI_DATA_AU_START | GF_ESI_DATA_HAS_CTS; @@ -184,14 +186,6 @@ static GF_Err mp4_input_ctrl(GF_ESInterface *ifce, u32 act_type, void *param) pck.flags |= GF_ESI_DATA_HAS_DTS; } - if (priv->sample->IsRAP && priv->dsi && priv->dsi_size) { - pck.data = priv->dsi; - pck.data_len = priv->dsi_size; - ifce->output_ctrl(ifce, GF_ESI_OUTPUT_DATA_DISPATCH, &pck); - pck.flags = 0; - } - - if (priv->nalu_size) { Bool nalu_delim_sent = 0; u32 remain = priv->sample->dataLength; @@ -224,15 +218,23 @@ static GF_Err mp4_input_ctrl(GF_ESInterface *ifce, u32 act_type, void *param) pck.data_len = 6; ifce->output_ctrl(ifce, GF_ESI_OUTPUT_DATA_DISPATCH, &pck); pck.flags &= ~GF_ESI_DATA_AU_START; - } + /*and send SPD / PPS if RAP - it is not clear in the specs whether SPS/PPS should be inserted after + the AU delimiter NALU*/ + if (priv->sample->IsRAP && priv->dsi && priv->dsi_size) { + pck.data = priv->dsi; + pck.data_len = priv->dsi_size; + ifce->output_ctrl(ifce, GF_ESI_OUTPUT_DATA_DISPATCH, &pck); + pck.flags &= ~GF_ESI_DATA_AU_START; + } + } pck.data = sc; pck.data_len = 4; ifce->output_ctrl(ifce, GF_ESI_OUTPUT_DATA_DISPATCH, &pck); if (!remain) pck.flags |= GF_ESI_DATA_AU_END; - pck.flags &= ~GF_ESI_DATA_AU_START; + pck.flags &= ~GF_ESI_DATA_AU_START; pck.data = ptr; pck.data_len = size; @@ -241,6 +243,14 @@ static GF_Err mp4_input_ctrl(GF_ESInterface *ifce, u32 act_type, void *param) } } else { + + if (priv->sample->IsRAP && priv->dsi && priv->dsi_size) { + pck.data = priv->dsi; + pck.data_len = priv->dsi_size; + ifce->output_ctrl(ifce, GF_ESI_OUTPUT_DATA_DISPATCH, &pck); + pck.flags &= ~GF_ESI_DATA_AU_START; + } + pck.flags |= GF_ESI_DATA_AU_END; pck.data = priv->sample->data; pck.data_len = priv->sample->dataLength; @@ -1418,7 +1428,7 @@ static GFINLINE GF_Err parse_args(int argc, char **argv, u32 *mux_rate, u32 *car Bool *real_time, u32 *run_time, char **video_buffer, u32 *video_buffer_size, u32 *audio_input_type, char **audio_input_ip, u16 *audio_input_port, u32 *output_type, char **ts_out, char **udp_out, char **rtp_out, u16 *output_port, - char** segment_dir, u32 *segment_duration, char **segment_manifest, u32 *segment_number, char **segment_http_prefix) + char** segment_dir, u32 *segment_duration, char **segment_manifest, u32 *segment_number, char **segment_http_prefix, Bool *split_rap) { Bool rate_found=0, mpeg4_carousel_found=0, time_found=0, src_found=0, dst_found=0, audio_input_found=0, video_input_found=0, seg_dur_found=0, seg_dir_found=0, seg_manifest_found=0, seg_number_found=0, seg_http_found = 0, real_time_found=0; @@ -1520,7 +1530,7 @@ static GFINLINE GF_Err parse_args(int argc, char **argv, u32 *mux_rate, u32 *car #ifdef GPAC_MEMORY_TRACKING gf_sys_close(); gf_sys_init(1); - gf_log_set_tools(gf_log_get_tools()|GF_LOG_MEMORY); + gf_log_set_tool_level(GF_LOG_MEMORY, GF_LOG_DEBUG); #else fprintf(stdout, "WARNING - GPAC not compiled with Memory Tracker - ignoring \"-mem-track\"\n"); #endif @@ -1556,6 +1566,8 @@ static GFINLINE GF_Err parse_args(int argc, char **argv, u32 *mux_rate, u32 *car *run_time = atoi(arg+6); } else if (!stricmp(arg, "-single-au")) { *single_au_pes = 1; + } else if (!stricmp(arg, "-rap")) { + *split_rap = 1; } } if (*real_time) force_real_time = 1; @@ -1565,14 +1577,9 @@ static GFINLINE GF_Err parse_args(int argc, char **argv, u32 *mux_rate, u32 *car for (i=1; istart_pes_at_rap = 1; } cur_pid += progs[i].nb_streams; @@ -2039,7 +2052,7 @@ int main(int argc, char **argv) /*flush all packets*/ while ((ts_pck = gf_m2ts_mux_process(muxer, &status)) != NULL) { if (ts_output_file != NULL) { - fwrite(ts_pck, 1, 188, ts_output_file); + gf_fwrite(ts_pck, 1, 188, ts_output_file); if (segment_duration && (muxer->time.sec > prev_seg_time.sec + segment_duration)) { prev_seg_time = muxer->time; fclose(ts_output_file); @@ -2138,7 +2151,8 @@ int main(int argc, char **argv) { u64 bits = muxer->tot_pck_sent*8*188; u32 dur_sec = gf_m2ts_get_ts_clock(muxer) / 1000; - fprintf(stdout, "Done muxing - %d sec - average rate %d kbps "LLD" packets written ("LLD" padding)\n", dur_sec, (u32) (bits/dur_sec/1000), muxer->tot_pck_sent, muxer->tot_pad_sent); + fprintf(stdout, "Done muxing - %d sec - average rate %d kbps "LLD" packets written\n", dur_sec, (u32) (bits/dur_sec/1000), muxer->tot_pck_sent); + fprintf(stdout, "\tPadding: "LLD" packets - "LLD" PES padded bytes (%g kbps)\n", muxer->tot_pad_sent, muxer->tot_pes_pad_bytes, (Double) (muxer->tot_pes_pad_bytes*8.0/dur_sec/1000) ); } exit: diff --git a/applications/mp42ts/mp42ts.dsp b/applications/mp42ts/mp42ts.dsp deleted file mode 100644 index 61f3b4b..0000000 --- a/applications/mp42ts/mp42ts.dsp +++ /dev/null @@ -1,99 +0,0 @@ -# Microsoft Developer Studio Project File - Name="mp42ts" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Console Application" 0x0103 - -CFG=mp42ts - Win32 Debug -!MESSAGE This is not a valid makefile. To build this project using NMAKE, -!MESSAGE use the Export Makefile command and run -!MESSAGE -!MESSAGE NMAKE /f "mp42ts.mak". -!MESSAGE -!MESSAGE You can specify a configuration when running NMAKE -!MESSAGE by defining the macro CFG on the command line. For example: -!MESSAGE -!MESSAGE NMAKE /f "mp42ts.mak" CFG="mp42ts - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "mp42ts - Win32 Release" (based on "Win32 (x86) Console Application") -!MESSAGE "mp42ts - Win32 Debug" (based on "Win32 (x86) Console Application") -!MESSAGE - -# Begin Project -# PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "" -# PROP Scc_LocalPath "" -CPP=cl.exe -RSC=rc.exe - -!IF "$(CFG)" == "mp42ts - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "Release" -# PROP BASE Intermediate_Dir "Release" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "Release" -# PROP Intermediate_Dir "Release" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c -# ADD CPP /nologo /MD /W3 /GX /O2 /I "../../../include" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c -# ADD BASE RSC /l 0x40c /d "NDEBUG" -# ADD RSC /l 0x40c /d "NDEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 -# ADD LINK32 /nologo /subsystem:console /machine:I386 /out:"../../../bin/w32_rel/mp42ts.exe" /libpath:"../../../extra_lib/lib/w32_rel" - -!ELSEIF "$(CFG)" == "mp42ts - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "Debug" -# PROP BASE Intermediate_Dir "Debug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "Debug" -# PROP Intermediate_Dir "Debug" -# PROP Ignore_Export_Lib 0 -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c -# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "../../../include" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FR /YX /FD /GZ /c -# ADD BASE RSC /l 0x40c /d "_DEBUG" -# ADD RSC /l 0x40c /d "_DEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept -# ADD LINK32 /nologo /subsystem:console /debug /machine:I386 /out:"../../../bin/w32_deb/mp42ts.exe" /pdbtype:sept /libpath:"../../../extra_lib/lib/w32_deb" -# SUBTRACT LINK32 /nodefaultlib - -!ENDIF - -# Begin Target - -# Name "mp42ts - Win32 Release" -# Name "mp42ts - Win32 Debug" -# Begin Source File - -SOURCE=.\main.c -# End Source File -# Begin Source File - -SOURCE=.\mp42ts.c -# End Source File -# Begin Source File - -SOURCE=.\mp42ts.h -# End Source File -# End Target -# End Project diff --git a/applications/mp42ts/mp42ts.vcproj b/applications/mp42ts/mp42ts.vcproj deleted file mode 100644 index 7702b04..0000000 --- a/applications/mp42ts/mp42ts.vcproj +++ /dev/null @@ -1,231 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/applications/mp4box/filedump.c b/applications/mp4box/filedump.c index 0a17d6a..dc7f8d7 100644 --- a/applications/mp4box/filedump.c +++ b/applications/mp4box/filedump.c @@ -34,6 +34,7 @@ #include #include #include +#include /*for asctime and gmtime*/ #include /*ISO 639 languages*/ @@ -113,7 +114,7 @@ GF_Err dump_cover_art(GF_ISOFile *file, char *inName) sprintf(szName, "%s.%s", inName, (tag_len>>31) ? "png" : "jpg"); t = gf_f64_open(szName, "wb"); - fwrite(tag, tag_len & 0x7FFFFFFF, 1, t); + gf_fwrite(tag, tag_len & 0x7FFFFFFF, 1, t); fclose(t); return GF_OK; @@ -153,8 +154,6 @@ GF_Err dump_file_text(char *file, char *inName, u32 dump_mode, Bool do_log) GF_SceneGraph *sg; GF_SceneLoader load; u32 ftype; - u32 prev_level = gf_log_get_level(); - u32 prev_tools = gf_log_get_tools(); gf_log_cbk prev_logs = NULL; FILE *logs = NULL; e = GF_OK; @@ -198,16 +197,14 @@ GF_Err dump_file_text(char *file, char *inName, u32 dump_mode, Bool do_log) sprintf(szLog, "%s_dec.logs", inName); logs = gf_f64_open(szLog, "wt"); - gf_log_set_tools(GF_LOG_CODING); - gf_log_set_level(GF_LOG_DEBUG); + gf_log_set_tool_level(GF_LOG_CODING, GF_LOG_DEBUG); prev_logs = gf_log_set_callback(logs, scene_coding_log); } e = gf_sm_load_init(&load); if (!e) e = gf_sm_load_run(&load); gf_sm_load_done(&load); if (logs) { - gf_log_set_tools(prev_tools); - gf_log_set_level(prev_level); + gf_log_set_tool_level(GF_LOG_CODING, GF_LOG_ERROR); gf_log_set_callback(NULL, prev_logs); fclose(logs); } @@ -1581,6 +1578,13 @@ void DumpTrackInfo(GF_ISOFile *file, u32 trackID, Bool full_dump) } else { fprintf(stdout, "\tAverage rate %d bps - Max Rate %d bps\n", rate, max_rate); } + + { + u32 dmin, dmax, davg, smin, smax, savg; + gf_isom_get_chunks_infos(file, trackNum, &dmin, &davg, &dmax, &smin, &savg, &smax); + fprintf(stdout, "\tChunk durations: min %d ms - max %d ms - average %d ms\n", (1000*dmin)/ts, (1000*dmax)/ts, (1000*davg)/ts); + fprintf(stdout, "\tChunk sizes (bytes): min %d - max %d - average %d\n", smin, smax, savg); + } fprintf(stdout, "\n"); count = gf_isom_get_chapter_count(file, trackNum); @@ -1654,6 +1658,9 @@ void DumpMovieInfo(GF_ISOFile *file) fprintf(stdout, "* Movie Info *\n\tTimescale %d - Duration %s\n\tFragmented File %s - %d track(s)\n", timescale, format_duration(gf_isom_get_duration(file), timescale, szDur), gf_isom_is_fragmented(file) ? "yes" : "no", gf_isom_get_track_count(file)); + if (gf_isom_moov_first(file)) + fprintf(stdout, "\tFile suitable for progressive download (moov before mdat)\n"); + if (gf_isom_get_brand_info(file, &brand, &min, NULL) == GF_OK) { fprintf(stdout, "\tFile Brand %s - version %d\n", gf_4cc_to_str(brand), min); } @@ -1768,9 +1775,12 @@ typedef struct Bool segment_at_rap; u32 subsegs_per_segment; char *seg_name; + char *seg_ext; Bool use_url_template; char *init_seg_name; - Bool use_index_segment; + Bool single_segment; + + u32 segment_index; FILE *index_file; char index_file_name[100]; @@ -1781,9 +1791,12 @@ typedef struct /* temporary file to store the MPD segment description before writing the header */ FILE *mpd_segs; + u32 represantation_idx; + u32 reference_pid; GF_M2TS_PES *reference_stream; + u32 nb_pes_in_segment; /* earliest presentation time for the whole segment */ u64 first_PTS; @@ -1793,6 +1806,8 @@ typedef struct u32 base_offset; /* last presentation time for the subsegment being processed (before the next subsegment is started) */ u64 last_PTS; + /* last decoding time for the subsegment being processed */ + u64 last_DTS; /* byte offset for the last PES packet for the subsegment being processed */ u32 last_offset; @@ -1805,16 +1820,18 @@ typedef struct /* byte offset for the last PES packet for the previous subsegment */ u32 prev_last_offset; - /* boolean indicating if the current subsegment contains a RAP */ - Bool has_rap; + /* indicates if the current subsegment contains a SAP and its SAP type*/ + u32 SAP_type; + /* indicates if the first PES in the current subsegment is a SAP*/ + Bool first_pes_sap; /* Presentation time for the first RAP encountered in the subsegment */ - u64 first_RAP_PTS; + u64 first_SAP_PTS; /* byte offset for the first RAP encountered in the subsegment */ - u32 first_RAP_offset; - u64 prev_last_RAP_PTS; - u32 prev_last_RAP_offset; - u64 last_RAP_PTS; - u32 last_RAP_offset; + u32 first_SAP_offset; + u64 prev_last_SAP_PTS; + u32 prev_last_SAP_offset; + u64 last_SAP_PTS; + u32 last_SAP_offset; /* information about the first PAT found in the subsegment */ u32 last_pat_position; @@ -1852,10 +1869,12 @@ typedef struct /* when writing to file */ FILE *pes_out; char dump[100]; +#if 0 FILE *pes_out_nhml; char nhml[100]; FILE *pes_out_info; char info[100]; +#endif Bool is_info_dumped; u32 prog_number; @@ -1884,18 +1903,19 @@ static GF_SegmentIndexBox *m2ts_sidx_new(u32 pid, u64 PTS, u64 position) return sidx; } -static void m2ts_sidx_add_entry(GF_SegmentIndexBox *sidx, Bool type, - u32 size, u32 duration, Bool has_rap, u32 RAP_delta_time) +static void m2ts_sidx_add_entry(GF_SegmentIndexBox *sidx, Bool ref_type, + u32 size, u32 duration, Bool first_is_SAP, u32 sap_type, u32 RAP_delta_time) { GF_SIDXReference *ref; sidx->nb_refs++; sidx->refs = gf_realloc(sidx->refs, sidx->nb_refs*sizeof(GF_SIDXReference)); ref = &(sidx->refs[sidx->nb_refs-1]); - ref->reference_type = type; - ref->contains_RAP = has_rap; + ref->reference_type = ref_type; ref->reference_size = size; ref->subsegment_duration = duration; - ref->RAP_delta_time = (has_rap ? RAP_delta_time: 0); + ref->starts_with_SAP = first_is_SAP; + ref->SAP_type = sap_type; + ref->SAP_delta_time = (sap_type ? RAP_delta_time: 0); } static void m2ts_sidx_update_prev_entry_duration(GF_SegmentIndexBox *sidx, u32 duration) @@ -1912,22 +1932,21 @@ static void m2ts_sidx_finalize_size(GF_M2TS_IndexingInfo *index_info, u64 file_s if (index_info->sidx->nb_refs == 0) return; ref = &(index_info->sidx->refs[index_info->sidx->nb_refs-1]); ref->reference_size = (u32)(file_size - index_info->prev_base_offset); - fprintf(stderr, "Subsegment: position-range ajdustment:%d-%d (%d bytes)\n", - index_info->prev_base_offset, (u32)file_size, ref->reference_size); + + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("Subsegment: position-range ajdustment:%d-%d (%d bytes)\n", index_info->prev_base_offset, (u32)file_size, ref->reference_size)); } static void m2ts_sidx_flush_entry(GF_M2TS_IndexingInfo *index_info) { u32 size; u32 duration, prev_duration; - u32 RAP_delta_time; - u32 RAP_offset; + u32 SAP_delta_time; + u32 SAP_offset; u32 end_offset; if (!index_info->sidx) { - fprintf(stderr, "Segment: Reference PID: %d, EPTime: "LLU", Start Offset: %d bytes\n", - index_info->reference_pid, index_info->base_PTS, index_info->base_offset); + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("Segment: Reference PID: %d, EPTime: "LLU", Start Offset: %d bytes\n", index_info->reference_pid, index_info->base_PTS, index_info->base_offset)); index_info->sidx = m2ts_sidx_new(index_info->reference_pid, index_info->base_PTS, index_info->base_offset); } @@ -1943,56 +1962,55 @@ static void m2ts_sidx_flush_entry(GF_M2TS_IndexingInfo *index_info) /* close the current index */ size = (u32)(end_offset - index_info->base_offset); duration = (u32)(index_info->last_PTS - index_info->base_PTS); - RAP_delta_time= (u32)(index_info->first_RAP_PTS - index_info->base_PTS); - RAP_offset = (u32)(index_info->first_RAP_offset - index_info->base_offset); - m2ts_sidx_add_entry(index_info->sidx, 0, size, duration, index_info->has_rap, RAP_delta_time); + SAP_delta_time= (u32)(index_info->first_SAP_PTS - index_info->base_PTS); + SAP_offset = (u32)(index_info->first_SAP_offset - index_info->base_offset); + m2ts_sidx_add_entry(index_info->sidx, 0, size, duration, index_info->first_pes_sap, index_info->SAP_type, SAP_delta_time); /* adjust the previous index duration */ if (index_info->sidx->nb_refs > 0 && (index_info->base_PTS < index_info->prev_last_PTS) ) { prev_duration = (u32)(index_info->base_PTS-index_info->prev_base_PTS); - fprintf(stderr, " time-range adj.: %.03f-%.03f / %.03f sec.\n", + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, (" time-range adj.: %.03f-%.03f / %.03f sec.\n", (index_info->prev_base_PTS - index_info->first_PTS)/90000.0, (index_info->base_PTS - index_info->first_PTS)/90000.0, prev_duration/90000.0); - m2ts_sidx_update_prev_entry_duration(index_info->sidx, prev_duration); + m2ts_sidx_update_prev_entry_duration(index_info->sidx, prev_duration)); } /* Printing result */ - fprintf(stderr, "Subsegment:"); + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("Subsegment:")); //time-range:position-range: - //fprintf(stderr, LLD"-"LLD,index_info->base_PTS, index_info->last_PTS); - fprintf(stderr, " %.03f-%0.3f / %.03f sec., %d-%d / %d bytes, ", + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, (" %.03f-%0.3f / %.03f sec., %d-%d / %d bytes, ", (index_info->base_PTS - index_info->first_PTS)/90000.0, (index_info->last_PTS - index_info->first_PTS)/90000.0, duration/90000.0, - index_info->base_offset, end_offset, size); - if (index_info->has_rap) { - fprintf(stderr, "RAP @ %.03f sec. / %d bytes", (index_info->first_RAP_PTS - index_info->first_PTS)/90000.0, - RAP_offset); + index_info->base_offset, end_offset, size)); + if (index_info->SAP_type) { + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("RAP @ %.03f sec. / %d bytes", (index_info->first_SAP_PTS - index_info->first_PTS)/90000.0, + SAP_offset)); } if (index_info->first_pat_position_valid) { - fprintf(stderr, ", PAT @ %d bytes", (u32)(index_info->first_pat_position - index_info->base_offset)); + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, (", PAT @ %d bytes", (u32)(index_info->first_pat_position - index_info->base_offset))); } else { - fprintf(stderr, ", No PAT"); + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, (", No PAT")); } if (index_info->first_cat_position_valid) { - fprintf(stderr, ", CAT @ %d bytes", (u32)(index_info->first_cat_position - index_info->base_offset)); + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, (", CAT @ %d bytes", (u32)(index_info->first_cat_position - index_info->base_offset))); } else { - fprintf(stderr, ", No CAT"); + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, (", No CAT")); } if (index_info->first_pmt_position_valid) { - fprintf(stderr, ", PMT @ %d bytes", (u32)(index_info->first_pmt_position - index_info->base_offset)); + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, (", PMT @ %d bytes", (u32)(index_info->first_pmt_position - index_info->base_offset))); } else { - fprintf(stderr, ", No PMT"); + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, (", No PMT")); } if (index_info->first_pcr_position_valid) { - fprintf(stderr, ", PCR @ %d bytes", (u32)(index_info->first_pcr_position - index_info->base_offset)); + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, (", PCR @ %d bytes", (u32)(index_info->first_pcr_position - index_info->base_offset))); } else { - fprintf(stderr, ", No PCR"); + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, (", No PCR")); } - fprintf(stderr, "\n"); + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("\n")); /* save the current values for later adjustments */ - index_info->prev_last_RAP_PTS = index_info->last_RAP_PTS; - index_info->prev_last_RAP_offset = index_info->last_RAP_offset; + index_info->prev_last_SAP_PTS = index_info->last_SAP_PTS; + index_info->prev_last_SAP_offset = index_info->last_SAP_offset; index_info->prev_last_PTS = index_info->last_PTS; index_info->prev_last_offset = index_info->last_offset; index_info->prev_base_PTS = index_info->base_PTS; @@ -2005,9 +2023,13 @@ static void m2ts_sidx_flush_entry(GF_M2TS_IndexingInfo *index_info) /* update the values for the new index*/ index_info->base_offset = end_offset; - index_info->has_rap = 0; - index_info->first_RAP_PTS = 0; - index_info->first_RAP_offset = 0; + index_info->SAP_type = 0; + index_info->first_SAP_PTS = 0; + index_info->first_SAP_offset = 0; + index_info->first_pes_sap = 0; + index_info->nb_pes_in_segment = 0; + index_info->last_DTS = 0; + if (index_info->last_pat_position >= index_info->base_offset) { index_info->first_pat_position_valid = 1; index_info->first_pat_position = index_info->last_pat_position; @@ -2058,7 +2080,6 @@ static void on_m2ts_dump_event(GF_M2TS_Demuxer *ts, u32 evt_type, void *par) switch (evt_type) { case GF_M2TS_EVT_PAT_FOUND: - fprintf(stdout, "Initial PAT found - %d programs\n", gf_list_count(ts->programs) ); if (index_info->start_indexing) { if (!index_info->first_pat_position_valid) { index_info->first_pat_position_valid = 1; @@ -2071,7 +2092,6 @@ static void on_m2ts_dump_event(GF_M2TS_Demuxer *ts, u32 evt_type, void *par) } break; case GF_M2TS_EVT_PAT_UPDATE: - fprintf(stdout, "PAT updated - %d programs\n", gf_list_count(ts->programs) ); if (index_info->start_indexing) { if (!index_info->first_pat_position_valid) { index_info->first_pat_position_valid = 1; @@ -2100,7 +2120,6 @@ static void on_m2ts_dump_event(GF_M2TS_Demuxer *ts, u32 evt_type, void *par) // fprintf(stdout, "Repeated PAT found - %d programs\n", gf_list_count(ts->programs) ); break; case GF_M2TS_EVT_CAT_FOUND: - fprintf(stdout, "Initial CAT found\n"); if (index_info->start_indexing) { if (!index_info->first_cat_position_valid) { index_info->first_cat_position_valid = 1; @@ -2113,7 +2132,6 @@ static void on_m2ts_dump_event(GF_M2TS_Demuxer *ts, u32 evt_type, void *par) } break; case GF_M2TS_EVT_CAT_UPDATE: - fprintf(stdout, "CAT updated\n"); if (index_info->start_indexing) { if (!index_info->first_cat_position_valid) { index_info->first_cat_position_valid = 1; @@ -2149,17 +2167,18 @@ static void on_m2ts_dump_event(GF_M2TS_Demuxer *ts, u32 evt_type, void *par) index_info->last_pmt_position = (ts->pck_number-1)*188; } count = gf_list_count(prog->streams); - fprintf(stdout, "Program number %d found - %d streams:\n", prog->number, count); + + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("Program number %d found - %d streams:\n", prog->number, count)); for (i=0; istreams, i); if (es->pid == prog->pmt_pid) { - fprintf(stdout, "\tPID %d: Program Map Table\n", es->pid); + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("\tPID %d: Program Map Table\n", es->pid)); } else { GF_M2TS_PES *pes = (GF_M2TS_PES *)es; - gf_m2ts_set_pes_framing(pes, GF_M2TS_PES_FRAMING_DEFAULT); - fprintf(stdout, "\tPID %d: %s ", pes->pid, gf_m2ts_get_stream_name(pes->stream_type) ); - if (pes->mpeg4_es_id) fprintf(stdout, " - MPEG-4 ES ID %d", pes->mpeg4_es_id); - fprintf(stdout, "\n"); + gf_m2ts_set_pes_framing(pes, dumper->pes_out ? GF_M2TS_PES_FRAMING_RAW : GF_M2TS_PES_FRAMING_DEFAULT); + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("\tPID %d: %s ", pes->pid, gf_m2ts_get_stream_name(pes->stream_type) )); + if (pes->mpeg4_es_id) GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, (" - MPEG-4 ES ID %d", pes->mpeg4_es_id)); + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("\n")); } if (es->pid == prog->pcr_pid) { /* we create indexing information on the stream used for carrying the PCR */ @@ -2175,7 +2194,6 @@ static void on_m2ts_dump_event(GF_M2TS_Demuxer *ts, u32 evt_type, void *par) prog = (GF_M2TS_Program*)par; if (gf_list_count(ts->programs)>1 && prog->number!=dumper->prog_number) break; - fprintf(stdout, "Program list updated - %d streams\n", gf_list_count( ((GF_M2TS_Program*)par)->streams) ); if (index_info->start_indexing) { if (!index_info->first_pmt_position_valid) { index_info->first_pmt_position_valid = 1; @@ -2198,29 +2216,27 @@ static void on_m2ts_dump_event(GF_M2TS_Demuxer *ts, u32 evt_type, void *par) } index_info->last_pmt_position = (ts->pck_number-1)*188; } -// fprintf(stdout, "Repeated Program list found - %d streams\n", gf_list_count( ((GF_M2TS_Program*)par)->streams) ); if (dumper->timestamps_info_file) { fprintf(dumper->timestamps_info_file, "%u\t%d\n", ts->pck_number, prog->pmt_pid); } break; case GF_M2TS_EVT_SDT_FOUND: count = gf_list_count(ts->SDTs) ; - fprintf(stdout, "Program Description found - %d desc:\n", count); + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("Program Description found - %d desc:\n", count)); for (i=0; iSDTs, i); - fprintf(stdout, "\tServiceID %d - Provider %s - Name %s\n", sdt->service_id, sdt->provider, sdt->service); + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("\tServiceID %d - Provider %s - Name %s\n", sdt->service_id, sdt->provider, sdt->service)); } break; case GF_M2TS_EVT_SDT_UPDATE: count = gf_list_count(ts->SDTs) ; - fprintf(stdout, "Program Description updated - %d desc\n", count); + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("Program Description updated - %d desc\n", count)); for (i=0; iSDTs, i); - fprintf(stdout, "\tServiceID %d - Provider %s - Name %s\n", sdt->service_id, sdt->provider, sdt->service); + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("\tServiceID %d - Provider %s - Name %s\n", sdt->service_id, sdt->provider, sdt->service)); } break; case GF_M2TS_EVT_SDT_REPEAT: -// fprintf(stdout, "Repeated Program Description - %d desc\n", gf_list_count(ts->SDTs) ); break; case GF_M2TS_EVT_PES_TIMING: pck = par; @@ -2258,16 +2274,25 @@ static void on_m2ts_dump_event(GF_M2TS_Demuxer *ts, u32 evt_type, void *par) if (pck->stream->pid != index_info->reference_pid) { break; } else { + if (index_info->last_DTS != pck->DTS) { + index_info->last_DTS = pck->DTS; + index_info->nb_pes_in_segment++; + } + /* we store the fact that there is at least a RAP for the index and we store the PTS of the first encountered RAP in the index*/ if (pck->flags & GF_M2TS_PES_PCK_RAP) { - index_info->has_rap = 1; - if (!index_info->first_RAP_PTS || (index_info->first_RAP_PTS > pck->PTS)) { - index_info->first_RAP_PTS = pck->PTS; - index_info->first_RAP_offset = (pck->stream->pes_start_packet_number-1)*188; + index_info->SAP_type = 1; + if (!index_info->first_SAP_PTS || (index_info->first_SAP_PTS > pck->PTS)) { + index_info->first_SAP_PTS = pck->PTS; + index_info->first_SAP_offset = (pck->stream->pes_start_packet_number-1)*188; + } + index_info->last_SAP_PTS = pck->PTS; + index_info->last_SAP_offset = (pck->stream->pes_start_packet_number-1)*188; + + if (index_info->nb_pes_in_segment==1) { + index_info->first_pes_sap = 1; } - index_info->last_RAP_PTS = pck->PTS; - index_info->last_RAP_offset = (pck->stream->pes_start_packet_number-1)*188; } /* we need to know the earliest PTS value (RAP or not) in the index*/ if (!index_info->base_PTS || (index_info->base_PTS > pck->PTS)) { @@ -2282,11 +2307,12 @@ static void on_m2ts_dump_event(GF_M2TS_Demuxer *ts, u32 evt_type, void *par) index_info->last_PTS = pck->PTS; index_info->last_offset = (index_info->reference_stream->pes_start_packet_number-1)*188; } + m2ts_check_indexing(index_info); } } - if (dumper->pes_out && (dumper->dump_pid == pck->stream->pid)) { - fwrite(pck->data, pck->data_len, 1, dumper->pes_out); + if (dumper->has_seen_pat && dumper->pes_out && (dumper->dump_pid == pck->stream->pid)) { + gf_fwrite(pck->data, pck->data_len, 1, dumper->pes_out); } break; case GF_M2TS_EVT_PES_PCR: @@ -2314,7 +2340,7 @@ static void on_m2ts_dump_event(GF_M2TS_Demuxer *ts, u32 evt_type, void *par) if (sl_pck->stream->mpeg4_es_id) { GF_ESD *esd = ((GF_M2TS_PES*)sl_pck->stream)->esd; if (!dumper->is_info_dumped) { - if (esd->decoderConfig->decoderSpecificInfo) fwrite(esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength, 1, dumper->pes_out_info); + if (esd->decoderConfig->decoderSpecificInfo) gf_fwrite(esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength, 1, dumper->pes_out_info); dumper->is_info_dumped = 1; fprintf(dumper->pes_out_nhml, "pes_out_nhml, "timeScale=\"%d\" ", esd->slConfig->timestampResolution); @@ -2325,7 +2351,7 @@ static void on_m2ts_dump_event(GF_M2TS_Demuxer *ts, u32 evt_type, void *par) fprintf(dumper->pes_out_nhml, "inRootOD=\"yes\">\n"); } gf_sl_depacketize(esd->slConfig, &header, sl_pck->data, sl_pck->data_len, &header_len); - fwrite(sl_pck->data+header_len, sl_pck->data_len-header_len, 1, dumper->pes_out); + gf_fwrite(sl_pck->data+header_len, sl_pck->data_len-header_len, 1, dumper->pes_out); fprintf(dumper->pes_out_nhml, "\n", LLD_CAST header.decodingTimeStamp, sl_pck->data_len-header_len, (header.randomAccessPointFlag?"yes":"no")); } } @@ -2349,117 +2375,266 @@ static void mpd_duration(Double duration, char *duration_string) else sprintf(duration_string, "PT0S"); } -void mpd_start(FILE *mpd, Bool on_demand, const char *media_file_name, - Double file_duration, const char *media_mime, const char *media_codecs, - u32 width, u32 height, u32 sample_rate, u32 nb_channels, char *langCode, - Bool split_seg_at_rap, u64 file_size, Double bufferTime) +static void mpd_start(GF_M2TS_IndexingInfo *index_info, Bool on_demand, const char *media_file_name, + Double file_duration, Bool split_seg_at_rap, u64 file_size, Double bufferTime) { char duration_string[100]; char buffer_string[100]; - u32 bandwidth; + u32 bandwidth, i; + GF_MediaImporter import; + char szCodecs[1000]; + u32 width, height, sample_rate, nb_channels, langCode; + GF_Err e; + FILE *mpd = index_info->mpd_file; + + /*get codecs*/ + memset(&import, 0, sizeof(GF_MediaImporter)); + import.trackID = 0; + import.flags = GF_IMPORT_PROBE_ONLY; + import.in_name = (char *)media_file_name; + e = gf_media_import(&import); + strcpy(szCodecs, ""); + width = height = sample_rate = nb_channels = langCode = 0; + + if (!e) { + for (i=0; i\n", (on_demand ? "OnDemand": "Live")); - fprintf(mpd, " \n"); - fprintf(mpd, " Media Presentation Description for file %s generated with GPAC \n", media_file_name); - fprintf(mpd, " \n"); mpd_duration(file_duration, duration_string); mpd_duration(bufferTime, buffer_string); - fprintf(mpd, " \n", duration_string, buffer_string); - fprintf(mpd, " represantation_idx) { + fprintf(mpd, "\n", (on_demand ? "static": "dynamic"), buffer_string); + fprintf(mpd, " \n"); + fprintf(mpd, " MPD for file %s generated with GPAC %s\n", media_file_name, GPAC_FULL_VERSION); + fprintf(mpd, " \n"); + fprintf(mpd, " \n", duration_string, buffer_string); + fprintf(mpd, " \n"); + } + + fprintf(mpd, " represantation_idx+1, szCodecs); if (width && height) fprintf(mpd, " width=\"%d\" height=\"%d\"", width, height); if (sample_rate && nb_channels) fprintf(mpd, " sampleRate=\"%d\" numChannels=\"%d\"", sample_rate, nb_channels); - if (langCode[0]) fprintf(mpd, " lang=\"%s\"", langCode); - fprintf(mpd, " startWithRAP=\"%s\"", split_seg_at_rap ? "true" : "false"); + if (langCode) fprintf(mpd, " lang=\"%s\"", gf_4cc_to_str(langCode) ); + fprintf(mpd, " startWithRAP=\"%s\"", split_seg_at_rap ? "1" : "false"); fprintf(mpd, " bandwidth=\"%d\"", bandwidth); fprintf(mpd, ">\n"); } -void mpd_end(FILE *mpd) +void mpd_end(FILE *mpd, Bool is_last_rep) { - fprintf(mpd, " \n"); - fprintf(mpd, " \n"); - fprintf(mpd, ""); + fprintf(mpd, " \n"); + if (is_last_rep) { + fprintf(mpd, " \n"); + fprintf(mpd, " \n"); + fprintf(mpd, ""); + } } static void write_mpd_segment_info(GF_M2TS_IndexingInfo *index_info, char *media_file_name) { + char *sep, SegName[GF_MAX_PATH]; u32 i; u64 start; - char duration_string[100]; - mpd_duration(index_info->segment_duration, duration_string); - fprintf(index_info->mpd_file, " \n", duration_string); + if (!index_info->seg_name) { + fprintf(index_info->mpd_file, " %s\n", media_file_name); + } + + if (index_info->single_segment) { + fprintf(index_info->mpd_file, " \n"); + fprintf(index_info->mpd_file, " %s\n", index_info->index_file_name); + fprintf(index_info->mpd_file, " \n"); + return; + } + + if (index_info->seg_name) { + char *mfile = strrchr(media_file_name, '/'); + if (!mfile) mfile = strrchr(media_file_name, '\\'); + if (mfile) mfile += 1; + else mfile = media_file_name; + + if (strstr(index_info->seg_name, "%s")) { + sprintf(SegName, index_info->seg_name, mfile); + } else { + strcpy(SegName, index_info->seg_name); + } + sep = strrchr(SegName, '.'); + if (sep) sep[0] = 0; + } + + if (index_info->seg_name && index_info->use_url_template) { + fprintf(index_info->mpd_file, " \n", (u32) (1000*index_info->segment_duration), SegName, index_info->seg_ext); + } else { + fprintf(index_info->mpd_file, " \n", (u32) (1000*index_info->segment_duration)); + } /* add startIndex for live scenarios */ if (index_info->init_seg_name) { - fprintf(index_info->mpd_file, " \n", index_info->init_seg_name); + fprintf(index_info->mpd_file, " \n", index_info->init_seg_name); } - if (!index_info->use_index_segment) { - fprintf(index_info->mpd_file, " %s\n", media_file_name); + + fprintf(index_info->mpd_file, " %s\n", index_info->index_file_name); + + if (!index_info->seg_name) { + start=index_info->sidx->first_offset; + for (i=0; isidx->nb_refs; i++) { + GF_SIDXReference *ref = &index_info->sidx->refs[i]; + fprintf(index_info->mpd_file, " \n", start, start+ref->reference_size-1); + start += ref->reference_size; + } + } else { + FILE *src, *dst; + u64 pos, end; + src= gf_f64_open(media_file_name, "rb"); start=index_info->sidx->first_offset; for (i=0; isidx->nb_refs; i++) { + char szFile[GF_MAX_PATH], buf[4096]; GF_SIDXReference *ref = &index_info->sidx->refs[i]; - fprintf(index_info->mpd_file, " \n", start, start+ref->reference_size-1); + + strcpy(szFile, SegName); + sprintf(buf, "%d", index_info->segment_index); + strcat(szFile, buf); + strcat(szFile, ".ts"); + + if (index_info->use_url_template!=2) { + dst = gf_f64_open(szFile, "wb"); + + gf_f64_seek(src, start, SEEK_SET); + pos = start; + end = start+ref->reference_size; + while (pos= end) { + to_read = (u32) (end-pos); + } + res = fread(buf, 1, to_read, src); + if (res==to_read) { + res = fwrite(buf, 1, to_read, dst); + } + if (res!=to_read) { + fprintf(stderr, "IO error while Extracting segment %03d / %03d\r", i+1, index_info->sidx->nb_refs); + break; + } + pos += res; + } + fclose(dst); + } start += ref->reference_size; - } + index_info->segment_index++; + + if (!index_info->use_url_template) + fprintf(index_info->mpd_file, " \n", szFile); + + fprintf(stdout, "Extracting segment %03d / %03d\r", i+1, index_info->sidx->nb_refs); + } + fclose(src); + } +// fprintf(index_info->mpd_file, " \n", media_file_name, index_info->index_file_name); + if (index_info->seg_name && index_info->use_url_template) { + fprintf(index_info->mpd_file, " \n"); } else { - fprintf(index_info->mpd_file, " \n", media_file_name); - fprintf(index_info->mpd_file, " \n", index_info->index_file_name); + fprintf(index_info->mpd_file, " \n"); } - fprintf(index_info->mpd_file, " \n"); } -void dump_mpeg2_ts(char *mpeg2ts_file, char *pes_out_name, Bool prog_num, +void dump_mpeg2_ts(char *mpeg2ts_file, char *out_name, Bool prog_num, Double dash_duration, Bool seg_at_rap, u32 subseg_per_seg, - char *seg_name, char *seg_ext, Bool use_url_template, Bool use_index_segment) + char *seg_name, char *seg_ext, Bool use_url_template, Bool single_segment, u32 representation_idx, Bool is_last_rep) { char data[188]; GF_M2TS_Dump dumper; + u32 size; u64 fsize, fdone; GF_M2TS_Demuxer *ts; FILE *src; src = gf_f64_open(mpeg2ts_file, "rb"); - + if (!src) { + fprintf(stderr, "Cannot open %s: no such file\n", mpeg2ts_file); + return; + } ts = gf_m2ts_demux_new(); ts->on_event = on_m2ts_dump_event; memset(&dumper, 0, sizeof(GF_M2TS_Dump)); ts->user = &dumper; dumper.prog_number = prog_num; - + + if (dash_duration) { - char *c; + char *c, *f; dumper.index_info.segment_duration = dash_duration; dumper.index_info.segment_at_rap = seg_at_rap; dumper.index_info.subsegs_per_segment = subseg_per_seg; - dumper.index_info.seg_name = seg_name ? gf_strdup(seg_name) : NULL; dumper.index_info.use_url_template = use_url_template; dumper.index_info.init_seg_name = NULL; - dumper.index_info.use_index_segment = use_index_segment; + dumper.index_info.single_segment = single_segment; + dumper.index_info.seg_name = seg_name; + dumper.index_info.seg_ext = seg_ext ? seg_ext : "ts"; + c = strrchr(mpeg2ts_file, '.'); if (c) *c = 0; - sprintf(dumper.index_info.index_file_name, "%s_index.%s", mpeg2ts_file, (seg_ext?seg_ext:"m4s")); + f = strrchr(mpeg2ts_file, '/'); + if (!f) f = strrchr(mpeg2ts_file, '\\'); + sprintf(dumper.index_info.index_file_name, "%s_index.%s", f ? f+1 : mpeg2ts_file, (seg_ext?seg_ext:"didx")); if (c) *c = '.'; - dumper.index_info.index_file = gf_f64_open(dumper.index_info.index_file_name, "wb"); - dumper.index_info.index_bs = gf_bs_from_file(dumper.index_info.index_file, GF_BITSTREAM_WRITE); - { - GF_SegmentTypeBox *styp = (GF_SegmentTypeBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_STYP); - styp->majorBrand = GF_4CC('i','s','s','s'); - styp->minorVersion = 0; - styp->altBrand = (u32*)gf_malloc(sizeof(u32)); - styp->altBrand[0] = styp->majorBrand; - styp->altCount = 1; - gf_isom_box_size((GF_Box *)styp); - gf_isom_box_write((GF_Box *)styp, dumper.index_info.index_bs); - gf_isom_box_del((GF_Box *)styp); + + dumper.index_info.index_file = NULL; + dumper.index_info.index_bs = NULL; + if (dumper.index_info.use_url_template!=2) { + dumper.index_info.index_file = gf_f64_open(dumper.index_info.index_file_name, "wb"); + dumper.index_info.index_bs = gf_bs_from_file(dumper.index_info.index_file, GF_BITSTREAM_WRITE); + { + GF_SegmentTypeBox *styp = (GF_SegmentTypeBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_STYP); + styp->majorBrand = GF_4CC('s','i','s','x'); + styp->minorVersion = 0; + styp->altBrand = (u32*)gf_malloc(sizeof(u32)); + styp->altBrand[0] = styp->majorBrand; + styp->altCount = 1; + gf_isom_box_size((GF_Box *)styp); + gf_isom_box_write((GF_Box *)styp, dumper.index_info.index_bs); + gf_isom_box_del((GF_Box *)styp); + } + } + } + /*PES dumping*/ + else if (out_name) { + char *pid = strrchr(out_name, '#'); + if (pid) { + dumper.dump_pid = atoi(pid+1); + pid[0] = 0; + sprintf(dumper.dump, "%s_%d.raw", out_name, dumper.dump_pid); + dumper.pes_out = gf_f64_open(dumper.dump, "wb"); +#if 0 + sprintf(dumper.nhml, "%s_%d.nhml", pes_out_name, dumper.dump_pid); + dumper.pes_out_nhml = gf_f64_open(dumper.nhml, "wt"); + sprintf(dumper.info, "%s_%d.info", pes_out_name, dumper.dump_pid); + dumper.pes_out_info = gf_f64_open(dumper.info, "wb"); +#endif + pid[0] = '#'; } } + gf_f64_seek(src, 0, SEEK_END); fsize = gf_f64_tell(src); gf_f64_seek(src, 0, SEEK_SET); @@ -2484,29 +2659,29 @@ void dump_mpeg2_ts(char *mpeg2ts_file, char *pes_out_name, Bool prog_num, fprintf(dumper.timestamps_info_file, "PCK#\tPID\tPCR\tDTS\tPTS\tRAP\tDiscontinuity\n"); } - if (pes_out_name) { - char *pid = strrchr(pes_out_name, '#'); - if (pid) { - dumper.dump_pid = atoi(pid+1); - pid[0] = 0; - sprintf(dumper.dump, "%s_%d.media", pes_out_name, dumper.dump_pid); - dumper.pes_out = gf_f64_open(dumper.dump, "wb"); - sprintf(dumper.nhml, "%s_%d.nhml", pes_out_name, dumper.dump_pid); - dumper.pes_out_nhml = gf_f64_open(dumper.nhml, "wt"); - sprintf(dumper.info, "%s_%d.info", pes_out_name, dumper.dump_pid); - dumper.pes_out_info = gf_f64_open(dumper.info, "wb"); - pid[0] = '#'; - } - } - gf_m2ts_reset_parsers(ts); gf_f64_seek(src, 0, SEEK_SET); fdone = 0; if (dumper.index_info.segment_duration) { + char *sep; dumper.index_info.start_indexing = 1; fprintf(stderr, "Starting indexing ...\n"); - sprintf(dumper.index_info.mpd_file_name, "%s.mpd", mpeg2ts_file); - dumper.index_info.mpd_file = gf_f64_open(dumper.index_info.mpd_file_name, "wt"); + if (out_name) { + strcpy(dumper.index_info.mpd_file_name, out_name); + } else { + sep = strrchr(mpeg2ts_file, '/'); + if (!sep) sep = strrchr(mpeg2ts_file, '\\'); + strcpy(dumper.index_info.mpd_file_name, sep ? sep+1 : mpeg2ts_file); + sep = strrchr(dumper.index_info.mpd_file_name, '.'); + if (sep) sep[0] = 0; + strcat(dumper.index_info.mpd_file_name, ".mpd"); + } + if (!representation_idx) { + dumper.index_info.mpd_file = gf_f64_open(dumper.index_info.mpd_file_name, "wt"); + } else { + dumper.index_info.mpd_file = gf_f64_open(dumper.index_info.mpd_file_name, "a+t"); + } + dumper.index_info.represantation_idx = representation_idx; } while (!feof(src)) { size = fread(data, 1, 188, src); @@ -2527,27 +2702,27 @@ void dump_mpeg2_ts(char *mpeg2ts_file, char *pes_out_name, Bool prog_num, m2ts_sidx_finalize_size(&dumper.index_info, file_size); fprintf(stderr, "Indexing done (1 sidx, %d entries).\n", dumper.index_info.sidx->nb_refs); - mpd_start(dumper.index_info.mpd_file, 1, mpeg2ts_file, (dumper.index_info.last_PTS-dumper.index_info.first_PTS)/90000.0, - "video/mp2t", NULL, 0, 0, 0, 0, "", dumper.index_info.segment_at_rap, file_size, dumper.index_info.segment_duration); + mpd_start(&dumper.index_info, 1, mpeg2ts_file, (dumper.index_info.last_PTS-dumper.index_info.first_PTS)/90000.0, dumper.index_info.segment_at_rap, file_size, dumper.index_info.segment_duration/4); write_mpd_segment_info(&dumper.index_info, mpeg2ts_file); - mpd_end(dumper.index_info.mpd_file); + mpd_end(dumper.index_info.mpd_file, is_last_rep); } fclose(src); gf_m2ts_demux_del(ts); if (dumper.pes_out) fclose(dumper.pes_out); +#if 0 if (dumper.pes_out_nhml) { if (dumper.is_info_dumped) fprintf(dumper.pes_out_nhml, "\n"); fclose(dumper.pes_out_nhml); fclose(dumper.pes_out_info); } +#endif if (dumper.timestamps_info_file) fclose(dumper.timestamps_info_file); if (dumper.index_info.sidx) { gf_isom_box_size((GF_Box *)dumper.index_info.sidx); if (dumper.index_info.index_bs) gf_isom_box_write((GF_Box *)dumper.index_info.sidx, dumper.index_info.index_bs); gf_isom_box_del((GF_Box *)dumper.index_info.sidx); } - if (dumper.index_info.seg_name) gf_free(dumper.index_info.seg_name); if (dumper.index_info.mpd_file) fclose(dumper.index_info.mpd_file); if (dumper.index_info.index_file) fclose(dumper.index_info.index_file); if (dumper.index_info.index_bs) gf_bs_del(dumper.index_info.index_bs); diff --git a/applications/mp4box/fileimport.c b/applications/mp4box/fileimport.c index b45d922..97fb414 100644 --- a/applications/mp4box/fileimport.c +++ b/applications/mp4box/fileimport.c @@ -75,7 +75,7 @@ void convert_file_info(char *inName, u32 trackID) case GF_ISOM_MEDIA_AUDIO: fprintf(stdout, "Audio (%s)", gf_4cc_to_str(import.tk_info[i].media_type)); break; case GF_ISOM_MEDIA_TEXT: fprintf(stdout, "Text (%s)", gf_4cc_to_str(import.tk_info[i].media_type)); break; case GF_ISOM_MEDIA_SCENE: fprintf(stdout, "Scene (%s)", gf_4cc_to_str(import.tk_info[i].media_type)); break; - case GF_ISOM_MEDIA_OD: fprintf(stdout, "ObjectDescriptor (%s)", gf_4cc_to_str(import.tk_info[i].media_type)); break; + case GF_ISOM_MEDIA_OD: fprintf(stdout, "OD (%s)", gf_4cc_to_str(import.tk_info[i].media_type)); break; default: fprintf(stdout, "Other (4CC: %s)", gf_4cc_to_str(import.tk_info[i].type)); break; } @@ -142,7 +142,7 @@ GF_Err import_file(GF_ISOFile *dest, char *inName, u32 import_flags, Double forc const char *szLan; GF_Err e; GF_MediaImporter import; - char *ext, szName[1000], *fmt, *handler_name, *rvc_config; + char *ext, szName[1000], *handler_name, *rvc_config; rvc_predefined = 0; memset(&import, 0, sizeof(GF_MediaImporter)); @@ -173,7 +173,6 @@ GF_Err import_file(GF_ISOFile *dest, char *inName, u32 import_flags, Double forc handler_name = NULL; rvc_config = NULL; - fmt = NULL; while (ext) { char *ext2 = strchr(ext+1, ':'); if (ext2 && !strncmp(ext2, "://", 3)) ext2 = strchr(ext2+1, ':'); @@ -206,6 +205,7 @@ GF_Err import_file(GF_ISOFile *dest, char *inName, u32 import_flags, Double forc else if (!stricmp(ext+1, "svc")) import_flags |= GF_IMPORT_SVC_EXPLICIT; else if (!stricmp(ext+1, "nosvc")) import_flags |= GF_IMPORT_SVC_NONE; else if (!stricmp(ext+1, "subsamples")) import_flags |= GF_IMPORT_SET_SUBSAMPLES; + else if (!stricmp(ext+1, "forcesync")) import_flags |= GF_IMPORT_FORCE_SYNC; else if (!stricmp(ext+1, "mpeg4")) import_flags |= GF_IMPORT_FORCE_MPEG4; else if (!strnicmp(ext+1, "agg=", 4)) frames_per_sample = atoi(ext+5); else if (!strnicmp(ext+1, "dur=", 4)) import.duration = (u32) (atof(ext+5) * 1000); @@ -299,6 +299,10 @@ GF_Err import_file(GF_ISOFile *dest, char *inName, u32 import_flags, Double forc } } } + else if (!strnicmp(ext, "prog_id=", 8)) { + prog_id = atoi(ext+8); + do_all = 0; + } else track_id = atoi(ext); } if (do_audio || do_video || track_id) do_all = 0; @@ -357,7 +361,7 @@ GF_Err import_file(GF_ISOFile *dest, char *inName, u32 import_flags, Double forc if (delay>0) { gf_isom_append_edit_segment(import.dest, i+1, (timescale*delay)/1000, 0, GF_ISOM_EDIT_EMPTY); gf_isom_append_edit_segment(import.dest, i+1, tk_dur, 0, GF_ISOM_EDIT_NORMAL); - } else { + } else if (delay<0) { u64 to_skip = (timescale*(-delay))/1000; if (to_skipDTS; last_rap_sample_time /= tki->time_scale; e = gf_isom_get_sample_for_media_time(mp4, tki->tk, samp->DTS+tki->firstDTS+2, &sdi, GF_ISOM_SEARCH_SYNC_FORWARD, &next_rap, &next_rap_num); - if (e==GF_EOS) is_last = 1; + if (e==GF_EOS) + is_last = 1; if (next_rap) { if (!next_rap->IsRAP) is_last = 1; @@ -872,8 +889,8 @@ GF_Err split_isomedia_file(GF_ISOFile *mp4, Double split_dur, u32 split_size_kb, ) { nb_over++; tki->stop_state = 1; - if (tki->last_samplesample_count) is_last = 0; - if ((!tki->can_duplicate || all_duplicatable) && (tki->last_sample==tki->sample_count)) is_last = 1; + if (tki->last_samplesample_count) + is_last = 0; if (rap_split && tki->next_sample_is_rap) { file_split_dur = (Double) ( gf_isom_get_sample_dts(mp4, tki->tk, tki->last_sample+1) - tki->firstDTS); @@ -920,7 +937,15 @@ GF_Err split_isomedia_file(GF_ISOFile *mp4, Double split_dur, u32 split_size_kb, fprintf(stdout, "Cannot split file (duration too small or size too small)\n"); goto err_exit; } - if (chunk_extraction) file_split_dur = split_dur; + if (chunk_extraction) { + if (adjust_split_end) { + fprintf(stdout, "Adjusting chunk end time to previous random access at %02.2f sec\n", chunk_start + last_rap_sample_time); + file_split_dur = last_rap_sample_time; + sprintf(szFile, "%s_%d_%d%s", szName, (u32) chunk_start, (u32) (chunk_start+file_split_dur), ext); + gf_isom_set_final_name(dest, szFile); + } + else file_split_dur = split_dur; + } /*don't split if eq to copy...*/ if (is_last && !cur_file && !chunk_start) { @@ -1157,7 +1182,6 @@ GF_Err cat_isomedia_file(GF_ISOFile *dest, char *fileName, u32 import_flags, Dou for (i=0; istreamType != GF_STREAM_SCENE) continue; - delete_desc = 0; esd = NULL; - is_in_iod = 1; if (iod) { - is_in_iod = 0; for (j=0; jESDescriptors); j++) { esd = gf_list_get(iod->ESDescriptors, j); if (esd->decoderConfig && esd->decoderConfig->streamType == GF_STREAM_SCENE) { if (!sc->ESID) sc->ESID = esd->ESID; if (sc->ESID == esd->ESID) { - is_in_iod = 1; break; } } /*special BIFS direct import from NHNT*/ else if (gf_list_count(iod->ESDescriptors)==1) { sc->ESID = esd->ESID; - is_in_iod = 1; break; } esd = NULL; @@ -1747,7 +1761,6 @@ GF_Err EncodeBIFSChunk(GF_SceneManager *ctx, char *bifsOutputFile, GF_Err (*AUCa } if (!esd) { - delete_desc = 1; esd = gf_odf_desc_esd_new(2); gf_odf_desc_del((GF_Descriptor *) esd->decoderConfig->decoderSpecificInfo); esd->decoderConfig->decoderSpecificInfo = NULL; @@ -1811,7 +1824,7 @@ GF_Err EncodeBIFSChunk(GF_SceneManager *ctx, char *bifsOutputFile, GF_Err (*AUCa if (data) { sprintf(szName, "%s%02d.bifs", szRad, j); f = gf_f64_open(szName, "wb"); - fwrite(data, data_len, 1, f); + gf_fwrite(data, data_len, 1, f); fclose(f); gf_free(data); } diff --git a/applications/mp4box/live.c b/applications/mp4box/live.c index ef26ac2..aafaf5b 100644 --- a/applications/mp4box/live.c +++ b/applications/mp4box/live.c @@ -89,8 +89,7 @@ int stream_file_rtp(int argc, char **argv) gf_sys_init(0); - gf_log_set_tools(GF_LOG_RTP); - gf_log_set_level(GF_LOG_WARNING); //set to debug to have packet list + gf_log_set_tool_level(GF_LOG_RTP, GF_LOG_WARNING); //set to debug to have packet list file_streamer = gf_isom_streamer_new(inName, ip_dest, port, loop, force_mpeg4, path_mtu, ttl, ifce_addr); if (!file_streamer) { @@ -131,6 +130,7 @@ void PrintLiveUsage() "-sdp=Name ouput SDP file - default: session.sdp\n" "\n" "-dims turns on DIMS mode for SVG input - default: off\n" + "-no-rap disabled RAP sending - this also disables carousel generation. Default: off\n" "-src=file source of updates - default: null\n" "-rap=time duration in ms of base carousel - default: 0 (off)\n" " you can specify the RAP period of a single ESID (not in DIMS):\n" @@ -428,7 +428,7 @@ int live_session(int argc, char **argv) s32 next_time; u64 last_src_modif, mod_time; char *src_name = NULL; - Bool run, has_carousel, skip_init_scene; + Bool run, has_carousel, no_rap; Bool udp = 0; u16 sk_port=0; GF_Socket *sk = NULL; @@ -445,13 +445,12 @@ int live_session(int argc, char **argv) /* souchay : needs to initialize those two vars... */ aggregate_au = 1; es_id = 0; - skip_init_scene = 0; + no_rap = 0; gf_sys_init(0); memset(&livesess, 0, sizeof(LiveSession)); - gf_log_set_level(GF_LOG_INFO); - gf_log_set_tools(0xFFFFFFFF); + gf_log_set_tool_level(GF_LOG_ALL, GF_LOG_INFO); for (i=1; iend\" expressed in seconds\n"); @@ -1914,6 +1954,7 @@ int mp4boxMain(int argc, char **argv) sscanf(argv[i+1], "%lf:%lf", &split_start, &split_duration); split_duration -= split_start; split_size = 0; + if (!stricmp(arg, "-splitz")) adjust_split_end = 1; i++; } /*meta*/ @@ -1992,7 +2033,9 @@ int mp4boxMain(int argc, char **argv) nb_tsel_acts++; open_edit=1; } - + else if (!stricmp(arg, "-group-single")) { + single_group = 1; + } else if (!stricmp(arg, "-package")) { CHECK_NEXT_ARG pack_file = argv[i+1]; @@ -2026,13 +2069,11 @@ int mp4boxMain(int argc, char **argv) i++; } else if (!stricmp(arg, "-rb")) { - char *b = argv[i+1]; CHECK_NEXT_ARG if (nb_alt_brand_rem>=MAX_CUMUL_OPS) { fprintf(stdout, "Sorry - no more than %d brand remove operations allowed\n", MAX_CUMUL_OPS); return 1; } - brand_rem[nb_alt_brand_rem] = GF_4CC(b[0], b[1], b[2], b[3]); nb_alt_brand_rem++; open_edit = 1; i++; @@ -2116,7 +2157,7 @@ int mp4boxMain(int argc, char **argv) done = 0; while (1) { u32 nb_bytes = fread(chunk, 1, 4096, fin); - fwrite(chunk, 1, nb_bytes, fout); + gf_fwrite(chunk, 1, nb_bytes, fout); done += nb_bytes; fprintf(stdout, "Appending file %s - %02.2f done\r", raw_cat, 100.0*done/to_copy); if (done >= to_copy) break; @@ -2126,34 +2167,33 @@ int mp4boxMain(int argc, char **argv) return 0; } - { - u32 logtools = GF_LOG_CONTAINER|GF_LOG_SCENE|GF_LOG_PARSER|GF_LOG_AUTHOR|GF_LOG_CODING; + /*init libgpac*/ + gf_sys_init(enable_mem_tracker); + + if (gf_logs) { + gf_log_set_tools_levels(gf_logs); + } else { + u32 level = verbose ? GF_LOG_DEBUG : GF_LOG_INFO; + gf_log_set_tool_level(GF_LOG_CONTAINER, level); + gf_log_set_tool_level(GF_LOG_SCENE, level); + gf_log_set_tool_level(GF_LOG_PARSER, level); + gf_log_set_tool_level(GF_LOG_AUTHOR, level); + gf_log_set_tool_level(GF_LOG_CODING, level); #ifdef GPAC_MEMORY_TRACKING - if (enable_mem_tracker) logtools |= GF_LOG_MEMORY; + if (enable_mem_tracker) + gf_log_set_tool_level(GF_LOG_MEMORY, level); #endif - - //gf_log_set_level((verbose>1) ? GF_LOG_DEBUG : (verbose ? GF_LOG_INFO : GF_LOG_WARNING) ); - gf_log_set_level(verbose ? GF_LOG_DEBUG : GF_LOG_INFO); - gf_log_set_tools(logtools); if (quiet) { - if (quiet==2) gf_log_set_level(0); + if (quiet==2) gf_log_set_tool_level(GF_LOG_ALL, GF_LOG_QUIET); gf_set_progress_callback(NULL, progress_quiet); } - } - /*init libgpac*/ -#ifdef GPAC_MEMORY_TRACKING - gf_sys_init(enable_mem_tracker); -#else - gf_sys_init(0); -#endif - if (do_mpd) { Bool remote = 0; char *mpd_base_url = gf_strdup(inName); - if (strcmp(inName, "http://")) { + if (!strnicmp(inName, "http://", 7)) { e = gf_dm_wget(inName, "tmp_main.m3u8"); if (e != GF_OK) { fprintf(stdout, "Cannot retrieve M3U8 (%s): %s\n", inName, gf_error_to_string(e)); @@ -2163,7 +2203,7 @@ int mp4boxMain(int argc, char **argv) inName = "tmp_main.m3u8"; remote = 1; } - e = gf_m3u8_to_mpd(NULL, inName, mpd_base_url, (outName ? outName : inName), 0, "video/mp2t"); + e = gf_m3u8_to_mpd(inName, mpd_base_url, (outName ? outName : inName), 0, "video/mp2t", NULL, 1, use_url_template); gf_free(mpd_base_url); if (remote) { //gf_delete_file("tmp_main.m3u8"); @@ -2172,10 +2212,17 @@ int mp4boxMain(int argc, char **argv) fprintf(stdout, "Error converting M3U8 (%s) to MPD (%s): %s\n", inName, outName, gf_error_to_string(e)); return 1; } else { + fprintf(stdout, "Done converting M3U8 (%s) to MPD (%s)\n", inName, outName); return 0; } } + if (dash_duration && !nb_dash_inputs) { + dash_inputs[nb_dash_inputs] = inName; + nb_dash_inputs++; + } + + if (do_saf && !encode) { switch (get_file_type_by_ext(inName)) { case 2: case 3: case 4: @@ -2385,7 +2432,7 @@ int mp4boxMain(int argc, char **argv) file = gf_isom_open(inName, (u8) (open_edit ? GF_ISOM_OPEN_EDIT : ( (dump_isom>0) ? GF_ISOM_OPEN_READ_DUMP : GF_ISOM_OPEN_READ) ), tmpdir); if (!file && (gf_isom_last_error(NULL) == GF_ISOM_INCOMPLETE_FILE) && !open_edit) { u64 missing_bytes; - e = gf_isom_open_progressive(inName, &file, &missing_bytes); + e = gf_isom_open_progressive(inName, 0, 0, &file, &missing_bytes); fprintf(stdout, "Truncated file - missing "LLD" bytes\n", missing_bytes); } @@ -2441,18 +2488,20 @@ int mp4boxMain(int argc, char **argv) } if (dash_duration) { - if (frags_per_sidx<0) frags_per_sidx = 0; + if (subsegs_per_sidx<0) subsegs_per_sidx = 0; #ifndef GPAC_DISABLE_MPEG2TS - dump_mpeg2_ts(inName, NULL, program_number, dash_duration, seg_at_rap, frags_per_sidx, - seg_name, seg_ext, use_url_template, dash_ts_use_index); + for (i=0; idelay_ms) { u64 tk_dur; + gf_isom_remove_edit_segments(file, track); tk_dur = gf_isom_get_track_duration(file, track); + if (gf_isom_get_edit_segment_count(file, track)) + needSave = 1; if (tka->delay_ms>0) { gf_isom_append_edit_segment(file, track, (timescale*tka->delay_ms)/1000, 0, GF_ISOM_EDIT_EMPTY); gf_isom_append_edit_segment(file, track, tk_dur, 0, GF_ISOM_EDIT_NORMAL); @@ -2907,8 +2960,9 @@ int mp4boxMain(int argc, char **argv) } else { u64 to_skip = (timescale*(-tka->delay_ms))/1000; if (to_skipdelay_ms)*gf_isom_get_media_timescale(file, track) / 1000; - gf_isom_append_edit_segment(file, track, tk_dur-to_skip, seg_dur, GF_ISOM_EDIT_NORMAL); + u64 media_time = (-tka->delay_ms)*gf_isom_get_media_timescale(file, track) / 1000; + gf_isom_append_edit_segment(file, track, tk_dur-to_skip, media_time, GF_ISOM_EDIT_NORMAL); + needSave = 1; } else { fprintf(stdout, "Warning: request negative delay longer than track duration - ignoring\n"); } @@ -3078,20 +3132,172 @@ int mp4boxMain(int argc, char **argv) /*split file*/ if (dash_duration) { - - fprintf(stdout, "DASH-ing file with %.3f secs segments - fragments: %.3f secs - ", dash_duration, InterleavingTime); - if (frags_per_sidx<0) fprintf(stdout, "no sidx"); - else if (frags_per_sidx) fprintf(stdout, "%d per sidx", frags_per_sidx); - else fprintf(stdout, "single sidx"); - if (seg_at_rap) fprintf(stdout, " at GOP boundaries"); + char szMPD[GF_MAX_PATH]; + char szInit[GF_MAX_PATH]; + GF_ISOFile *init_seg; + Bool sps_merge_failed = 0; + Double period_duration = 0; + + if (single_segment) { + fprintf(stdout, "DASH-ing file%s with single segment\nSubsegment duration %.3f - Fragment duration: %.3f secs\n", (nb_dash_inputs>1) ? "s" : "", dash_duration, InterleavingTime); + subsegs_per_sidx = 0; + seg_name = seg_ext = NULL; + } else { + if (!seg_ext) seg_ext = "m4s"; + fprintf(stdout, "DASH-ing file with %.3f secs segments - fragments: %.3f secs\n", dash_duration, InterleavingTime); + if (subsegs_per_sidx<0) fprintf(stdout, "No sidx used"); + else if (subsegs_per_sidx) fprintf(stdout, "%d subsegments per sidx", subsegs_per_sidx); + else fprintf(stdout, "Single sidx used"); + } fprintf(stdout, "\n"); + if (seg_at_rap) fprintf(stdout, "Spliting segments at GOP boundaries\n"); strcpy(outfile, outName ? outName : inName); while (outfile[strlen(outfile)-1] != '.') outfile[strlen(outfile)-1] = 0; outfile[strlen(outfile)-1] = 0; if (!outName) strcat(outfile, "_dash"); - e = gf_media_fragment_file(file, outfile, InterleavingTime, seg_at_rap ? 2 : 1, dash_duration, seg_name, seg_ext, frags_per_sidx, daisy_chain_sidx, use_url_template, dash_ctx); - if (e) fprintf(stdout, "Error while DASH-ing file: %s\n", gf_error_to_string(e)); + + strcpy(szInit, outfile); + strcat(szInit, "_init.mp4"); + strcpy(szMPD, outfile); + strcat(szMPD, ".mpd"); + + init_seg = gf_isom_open(szInit, GF_ISOM_OPEN_WRITE, tmpdir); + for (i=0; isequenceParameterSets); k++) { + GF_AVCConfigSlot *slc = gf_list_get(avccfg2->sequenceParameterSets, k); + gf_avc_get_sps_info(slc->data, slc->size, &sps_id1, NULL, NULL, NULL, NULL); + for (l=0; lsequenceParameterSets); l++) { + GF_AVCConfigSlot *slc_orig = gf_list_get(avccfg1->sequenceParameterSets, l); + gf_avc_get_sps_info(slc_orig->data, slc_orig->size, &sps_id2, NULL, NULL, NULL, NULL); + if (sps_id2==sps_id1) { + do_merge = 0; + break; + } + } + } + /*no conflicts in SPS ids, merge all SPS in a single sample desc*/ + if (do_merge) { + while (gf_list_count(avccfg1->sequenceParameterSets)) { + GF_AVCConfigSlot *slc = gf_list_get(avccfg1->sequenceParameterSets, 0); + gf_list_rem(avccfg1->sequenceParameterSets, 0); + gf_list_add(avccfg2->sequenceParameterSets, slc); + } + while (gf_list_count(avccfg1->pictureParameterSets)) { + GF_AVCConfigSlot *slc = gf_list_get(avccfg1->pictureParameterSets, 0); + gf_list_rem(avccfg1->pictureParameterSets, 0); + gf_list_add(avccfg2->pictureParameterSets, slc); + } + gf_isom_avc_config_update(init_seg, track, 1, avccfg2); + } else { + sps_merge_failed = 1; + } + gf_odf_avc_cfg_del(avccfg1); + gf_odf_avc_cfg_del(avccfg2); + } + + /*cannot merge, clone*/ + if (!do_merge) + gf_isom_clone_sample_description(init_seg, track, in, j+1, 1, NULL, NULL, &outDescIndex); + } + } else { + gf_isom_clone_track(in, j+1, init_seg, 0, &track); + } + dur = (Double) gf_isom_get_track_duration(in, j+1); + dur /= gf_isom_get_timescale(in); + if (dur>period_duration) period_duration = dur; + } + + if (i) gf_isom_close(in); + } + if (sps_merge_failed) { + fprintf(stdout, "Couldnt merge AVC|H264 SPS from different files (same SPS ID used) - different sample descriptions will be used\n"); + } + if (!seg_name) use_url_template = 0; + + gf_media_mpd_start(szMPD, (char *)gf_isom_get_filename(file), use_url_template, single_segment, dash_ctx, szInit, period_duration); + + for (i=0; i1) { + char *sep = strrchr(dash_inputs[i], '/'); + if (!sep) sep = strrchr(dash_inputs[i], '\\'); + if (sep) strcpy(outfile, sep+1); + else strcpy(outfile, dash_inputs[i]); + sep = strrchr(outfile, '.'); + if (sep) sep[0] = 0; + + if (seg_name) { + if (strstr(seg_name, "%s")) sprintf(szSegName, seg_name, outfile); + else strcpy(szSegName, seg_name); + segment_name = szSegName; + } + strcat(outfile, "_dash"); + } + if (nb_dash_inputs>1) { + fprintf(stdout, "DASHing file %s\n", dash_inputs[i]); + } + e = gf_media_fragment_file(in, outfile, szMPD, InterleavingTime, seg_at_rap ? 2 : 1, dash_duration, segment_name, seg_ext, subsegs_per_sidx, daisy_chain_sidx, use_url_template, single_segment, dash_ctx, init_seg, i+1); + if (e) { + fprintf(stdout, "Error while DASH-ing file: %s\n", gf_error_to_string(e)); + break; + } + if (i) gf_isom_close(in); + } + /*close MPD*/ + gf_media_mpd_end(szMPD); + + /*if init segment shared, write to file*/ + if (nb_dash_inputs>1) { + gf_isom_close(init_seg); + } else { + gf_isom_delete(init_seg); + gf_delete_file(szInit); + } + gf_isom_delete(file); gf_sys_close(); return (e!=GF_OK) ? 1 : 0; @@ -3099,7 +3305,7 @@ int mp4boxMain(int argc, char **argv) if (!InterleavingTime) InterleavingTime = 0.5; if (HintIt) fprintf(stdout, "Warning: cannot hint and fragment - ignoring hint\n"); fprintf(stdout, "Fragmenting file (%.3f seconds fragments)\n", InterleavingTime); - e = gf_media_fragment_file(file, outfile, InterleavingTime, 0, 0, NULL, NULL, 0, 0, 0, NULL); + e = gf_media_fragment_file(file, outfile, NULL, InterleavingTime, 0, 0, NULL, NULL, 0, 0, 0, 0, NULL, NULL, 0); if (e) fprintf(stdout, "Error while fragmenting file: %s\n", gf_error_to_string(e)); gf_isom_delete(file); if (!e && !outName && !force_new) { @@ -3115,7 +3321,7 @@ int mp4boxMain(int argc, char **argv) if (force_ocr) SetupClockReferences(file); fprintf(stdout, "Hinting file with Path-MTU %d Bytes\n", MTUSize); MTUSize -= 12; - e = HintFile(file, MTUSize, max_ptime, rtp_rate, hint_flags, HintCopy, HintInter, regular_iod); + e = HintFile(file, MTUSize, max_ptime, rtp_rate, hint_flags, HintCopy, HintInter, regular_iod, single_group); if (e) goto err_exit; needSave = 1; if (print_sdp) DumpSDP(file, dump_std ? NULL : outfile); diff --git a/applications/mp4client/extract.c b/applications/mp4client/extract.c index 2458dcf..2ce04e3 100644 --- a/applications/mp4client/extract.c +++ b/applications/mp4client/extract.c @@ -183,13 +183,13 @@ void write_bmp(GF_VideoSurface *fb, char *rad_name, u32 img_num) fi.biSizeImage = fb->pitch_y * fb->height; /*NOT ALIGNED!!*/ - fwrite(&fh.bfType, 2, 1, fout); - fwrite(&fh.bfSize, 4, 1, fout); - fwrite(&fh.bfReserved1, 2, 1, fout); - fwrite(&fh.bfReserved2, 2, 1, fout); - fwrite(&fh.bfOffBits, 4, 1, fout); + gf_fwrite(&fh.bfType, 2, 1, fout); + gf_fwrite(&fh.bfSize, 4, 1, fout); + gf_fwrite(&fh.bfReserved1, 2, 1, fout); + gf_fwrite(&fh.bfReserved2, 2, 1, fout); + gf_fwrite(&fh.bfOffBits, 4, 1, fout); - fwrite(&fi, 1, 40, fout); + gf_fwrite(&fi, 1, 40, fout); //#ifndef GPAC_USE_TINYGL for (j=fb->height; j>0; j--) { ptr = fb->video_buffer + (j-1)*fb->pitch_y; @@ -243,7 +243,7 @@ void write_png(GF_VideoSurface *fb, char *rad_name, u32 img_num) if (fout) { GF_Err e = gf_img_png_enc(fb->video_buffer, fb->width, fb->height, fb->pitch_y, fb->pixel_format, dst, &dst_size); if (!e) { - fwrite(dst, dst_size, 1, fout); + gf_fwrite(dst, dst_size, 1, fout); fclose(fout); } } diff --git a/applications/mp4client/main.c b/applications/mp4client/main.c index 5de99c2..7185f69 100644 --- a/applications/mp4client/main.c +++ b/applications/mp4client/main.c @@ -58,6 +58,7 @@ void PrintGPACConfig(); static Bool gui_mode = 0; static Bool restart = 0; +static Bool reload = 0; #if defined(__DARWIN__) || defined(__APPLE__) //we keep no decoder thread because of JS_GC deadlocks between threads ... static u32 threading_flags = GF_TERM_NO_COMPOSITOR_THREAD | GF_TERM_NO_DECODER_THREAD; @@ -133,13 +134,14 @@ void PrintUsage() "\t-strict-error: exit when the player reports its first error\n" "\t-opt option: Overrides an option in the configuration file. String format is section:key=value\n" "\t-log-file file: sets output log file. Also works with -lf\n" - "\t-log-level lev: sets log level. Also works with -ll. Possible values are:\n" + "\t-logs log_args: sets log tools and levels, formatted as a ':'-separated list of toolX[:toolZ]@levelX\n" + "\t levelX can be one of:\n" + "\t \"quiet\" : skip logs\n" "\t \"error\" : logs only error messages\n" "\t \"warning\" : logs error+warning messages\n" "\t \"info\" : logs error+warning+info messages\n" "\t \"debug\" : logs all messages\n" - "\n" - "\t-log-tools lt: sets tool(s) to log. Also works with -lt. List of \':\'-separated values:\n" + "\t toolX can be one of:\n" "\t \"core\" : libgpac core\n" "\t \"coding\" : bitstream formats (audio, video, scene)\n" "\t \"container\" : container formats (ISO File, MPEG-2 TS, AVI, ...)\n" @@ -153,11 +155,18 @@ void PrintUsage() "\t \"scene\" : scene graph and scene manager\n" "\t \"script\" : scripting engine messages\n" "\t \"interact\" : interaction engine (events, scripts, etc)\n" + "\t \"smil\" : SMIL timing engine\n" "\t \"compose\" : composition engine (2D, 3D, etc)\n" - "\t \"service\" : network service management\n" "\t \"mmio\" : Audio/Video HW I/O management\n" - "\t \"none\" : no tool logged\n" - "\t \"all\" : all tools logged\n" + "\t \"rti\" : various run-time stats\n" + "\t \"cache\" : HTTP cache subsystem\n" + "\t \"audio\" : Audio renderer and mixers\n" +#ifdef GPAC_MEMORY_TRACKING + "\t \"mem\" : GPAC memory tracker\n" +#endif + "\t \"module\" : GPAC modules debugging\n" + "\t \"mutex\" : mutex\n" + "\t \"all\" : all tools logged - other tools can be specified afterwards.\n" "\n" "\t-size WxH: specifies visual size (default: scene size)\n" #if defined(__DARWIN__) || defined(__APPLE__) @@ -204,10 +213,12 @@ void PrintUsage() "\n" "\t-help: show this screen\n" "\n" - "MP4Client - GPAC command line player and dumper - version %s\n" - "GPAC Written by Jean Le Feuvre (c) 2001-2005 - ENST (c) 2005-200X\n", + "MP4Client - GPAC command line player and dumper - version "GPAC_FULL_VERSION"\n" + "GPAC Written by Jean Le Feuvre (c) 2001-2005 - ENST (c) 2005-200X\n" + "GPAC Configuration: " GPAC_CONFIGURATION "\n" + "Features: %s\n", GF_IMPORT_DEFAULT_FPS, - GPAC_FULL_VERSION + gpac_features() ); } @@ -285,7 +296,7 @@ static void PrintTime(u64 time) m = (u32) (time / 1000 / 60 - h*60); s = (u32) (time / 1000 - h*3600 - m*60); ms = (u32) (time - (h*3600 + m*60 + s) * 1000); - fprintf(stdout, "%02d:%02d:%02d.%02d", h, m, s, ms); + fprintf(stdout, "%02d:%02d:%02d.%03d", h, m, s, ms); } @@ -451,7 +462,7 @@ Bool GPAC_EventProc(void *ptr, GF_Event *evt) { if (!term) return 0; - if (gui_mode) { + if (gui_mode==1) { if (evt->type==GF_EVENT_QUIT) Run = 0; return 0; } @@ -475,14 +486,15 @@ Bool GPAC_EventProc(void *ptr, GF_Event *evt) if (!evt->message.message) return 0; - if (evt->message.error==GF_SCRIPT_INFO) { - GF_LOG(GF_LOG_INFO, GF_LOG_SCRIPT, ("[Script] %s\n", evt->message.message)); - } else if (evt->message.error) { + if (evt->message.error) { if (!is_connected) last_error = evt->message.error; - GF_LOG(GF_LOG_ERROR, GF_LOG_ALL, ("%s %s: %s\n", evt->message.message, servName, gf_error_to_string(evt->message.error))); + if (evt->message.error==GF_SCRIPT_INFO) { + GF_LOG(GF_LOG_INFO, GF_LOG_CONSOLE, ("%s\n", evt->message.message)); + } else { + GF_LOG(GF_LOG_ERROR, GF_LOG_CONSOLE, ("%s %s: %s\n", servName, evt->message.message, gf_error_to_string(evt->message.error))); + } } else if (!be_quiet) - /*TODO: put a GF_LOG here*/ - fprintf(stdout, "%s %s\r", evt->message.message, servName); + GF_LOG(GF_LOG_INFO, GF_LOG_CONSOLE, ("%s %s\n", servName, evt->message.message)); } break; case GF_EVENT_PROGRESS: @@ -606,6 +618,10 @@ Bool GPAC_EventProc(void *ptr, GF_Event *evt) if ((evt->key.flags & GF_KEY_MOD_CTRL) && is_connected) gf_term_switch_quality(term, 0); break; + case GF_KEY_F5: + if (is_connected) + reload = 1; + break; } break; @@ -624,7 +640,7 @@ Bool GPAC_EventProc(void *ptr, GF_Event *evt) ResetCaption(); break; case GF_EVENT_EOS: - if (loop_at_end) restart = 1; + if (!playlist && loop_at_end) restart = 1; break; case GF_EVENT_SIZE: if (user.init_flags & GF_TERM_WINDOWLESS) { @@ -844,8 +860,7 @@ static void init_rti_logs(char *rti_file, char *url, Bool use_rtix) /*turn on RTI loging*/ if (use_rtix) { gf_log_set_callback(NULL, on_gpac_log); - gf_log_set_level(GF_LOG_DEBUG); - gf_log_set_tools(GF_LOG_RTI); + gf_log_set_tool_level(GF_LOG_RTI, GF_LOG_DEBUG); GF_LOG(GF_LOG_DEBUG, GF_LOG_RTI, ("[RTI] System state when enabling log\n")); } else if (log_time_start) { @@ -876,7 +891,7 @@ int main (int argc, char **argv) Bool enable_mem_tracker = 0; #endif Double fps = GF_IMPORT_DEFAULT_FPS; - Bool ret, fill_ar, visible; + Bool fill_ar, visible; char *url_arg, *the_cfg, *rti_file, *views; FILE *logfile = NULL; Float scale = 1; @@ -924,10 +939,17 @@ int main (int argc, char **argv) cfg_file = gf_cfg_init(the_cfg, NULL); if (!cfg_file) { fprintf(stdout, "Error: Configuration File not found\n"); - if (logfile) fclose(logfile); + return 1; + } + /*if logs are specified, use them*/ + if (gf_log_set_tools_levels( gf_cfg_get_key(cfg_file, "General", "Logs") ) != GF_OK) { return 1; } + if( gf_cfg_get_key(cfg_file, "General", "Logs") != NULL ){ + logs_set = 1; + } + for (i=1; i<(u32) argc; i++) { char *arg = argv[i]; // if (isalnum(arg[0]) || (arg[0]=='/') || (arg[0]=='.') || (arg[0]=='\\') ) { @@ -1001,16 +1023,10 @@ int main (int argc, char **argv) logfile = gf_f64_open(argv[i+1], "wt"); gf_log_set_callback(logfile, on_gpac_log); i++; - } else if (!strcmp(arg, "-log-level") || !strcmp(arg, "-ll")) { - u32 flags = gf_log_parse_level(argv[i+1]); - if (!flags) return 1; - gf_log_set_level(flags); - logs_set = 1; - i++; - } else if (!strcmp(arg, "-log-tools") || !strcmp(arg, "-lt")) { - u32 flags = gf_log_parse_tools(argv[i+1]); - if (!flags) return 1; - gf_log_set_tools(flags); + } else if (!strcmp(arg, "-logs") ) { + if (gf_log_set_tools_levels(argv[i+1]) != GF_OK) { + return 1; + } logs_set = 1; i++; } else if (!strcmp(arg, "-log-clock") || !strcmp(arg, "-lc")) { @@ -1106,8 +1122,7 @@ int main (int argc, char **argv) if (dump_mode) rti_file = NULL; if (!logs_set) { - gf_log_set_level(GF_LOG_ERROR); - gf_log_set_tools(0xFFFFFFFF); + //gf_log_set_tool_level(GF_LOG_ALL, GF_LOG_ERROR); } if (rti_file) init_rti_logs(rti_file, url_arg, use_rtix); @@ -1193,14 +1208,13 @@ int main (int argc, char **argv) } Run = 1; - ret = 1; if (dump_mode) { if (!nb_times) { times[0] = 0; nb_times++; } - ret = dump_file(url_arg, dump_mode, fps, forced_width, forced_height, scale, times, nb_times); + dump_file(url_arg, dump_mode, fps, forced_width, forced_height, scale, times, nb_times); Run = 0; } else @@ -1256,6 +1270,11 @@ int main (int argc, char **argv) while (Run) { /*we don't want getchar to block*/ if (gui_mode || !gf_prompt_has_input()) { + if (reload) { + reload = 0; + gf_term_disconnect(term); + gf_term_connect(term, startup_file ? gf_cfg_get_key(cfg_file, "General", "StartupFile") : the_url); + } if (restart) { restart = 0; gf_term_play_from_time(term, 0, 0); @@ -1326,9 +1345,15 @@ force_input: case '\n': case 'N': if (playlist) { + int res; gf_term_disconnect(term); - if (fscanf(playlist, "%s", the_url) == EOF) { + res = fscanf(playlist, "%s", the_url); + if ((res == EOF) && loop_at_end) { + fseek(playlist, 0, SEEK_SET); + res = fscanf(playlist, "%s", the_url); + } + if (res == EOF) { fprintf(stdout, "No more items - exiting\n"); Run = 0; } else { @@ -1357,10 +1382,8 @@ force_input: } break; case 'r': - if (is_connected) { - gf_term_disconnect(term); - gf_term_connect(term, startup_file ? gf_cfg_get_key(cfg_file, "General", "StartupFile") : the_url); - } + if (is_connected) + reload = 1; break; case 'D': @@ -1572,36 +1595,18 @@ force_input: case 'L': { - u32 flags; - char szLog[1024]; - fprintf(stdout, "Enter new log level:\n"); - if (1 > scanf("%s", szLog)){ + char szLog[1024], *cur_logs; + cur_logs = gf_log_get_tools_levels(); + fprintf(stdout, "Enter new log level (current tools %s):\n", cur_logs); + gf_free(cur_logs); + if (scanf("%s", szLog) < 1) { fprintf(stderr, "Cannot read new log level, aborting.\n"); break; } - flags = gf_log_parse_level(szLog); - if (!flags) - fprintf(stderr, "Wrong log level specified, aborting.\n"); - else - gf_log_set_level(flags); - } - break; - case 'T': - { - u32 flags; - char szLog[1024]; - fprintf(stdout, "Enter new log tools:\n"); - if (1 > scanf("%s", szLog)) { - fprintf(stderr, "Cannot read new log tools, aborting.\n"); - break; - } - flags = gf_log_parse_tools(szLog); - if (!flags) - fprintf(stderr, "Wrong log tools specified, aborting.\n"); - else - gf_log_set_tools(flags); + gf_log_modify_tools_levels(szLog); } break; + case 'g': { GF_SystemRTInfo rti; @@ -1677,7 +1682,7 @@ force_input: fprintf(stdout, "Error writing file %s\n", szFileName); nb_pass = 0; } else { - fwrite(dst, dst_size, 1, png); + gf_fwrite(dst, dst_size, 1, png); fclose(png); fprintf(stdout, "Dump to %s\n", szFileName); } diff --git a/applications/osmo4_ios/Resources/icon.png b/applications/osmo4_ios/Resources/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..13f1dee1cbed855335c048d2321c4f60a5598f0c GIT binary patch literal 7682 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 literal 0 HcmV?d00001 diff --git a/applications/osmo4_ios/Resources/osmo.icns b/applications/osmo4_ios/Resources/osmo.icns new file mode 100644 index 0000000000000000000000000000000000000000..da7226f64f8939dfd5fddf847699e6f2ba13250b GIT binary patch literal 147235 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