From: Andres Mejia Date: Sun, 1 May 2011 01:29:43 +0000 (-0400) Subject: Imported Upstream version 0.4.5+svn3042 X-Git-Tag: archive/raspbian/1.0.1+dfsg1-4+rpi1~1^2~15^2~31 X-Git-Url: https://dgit.raspbian.org/?a=commitdiff_plain;h=4f45506d7980cd65418bef69fedea526c30571f1;p=gpac.git Imported Upstream version 0.4.5+svn3042 --- diff --git a/INSTALLME b/INSTALLME index 2dc4abd..7f548e4 100644 --- a/INSTALLME +++ b/INSTALLME @@ -28,6 +28,8 @@ are provided per library in the package. * Installing GPAC +/!\ GPAC won't compile if the 'svnversion' command (shipped with every Subversion package) is not in your path /!\ + Detailed instruction for Win32 MSVC Compilation are available in gpac/doc/INSTALL.w32 Detailed instruction for WinCE eVC Compilation are available in gpac/doc/INSTALL.wCE diff --git a/Makefile b/Makefile index ee078e4..3bfe4ce 100644 --- a/Makefile +++ b/Makefile @@ -5,9 +5,15 @@ include config.mak vpath %.c $(SRC_PATH) -all: lib apps mods +all: version + $(MAKE) -C src all + $(MAKE) -C applications all + $(MAKE) -C modules all + +version: + @if which svnversion >/dev/null; then echo "#define GPAC_SVN_REVISION \"$(shell svnversion \"$(SRC_PATH)\")\"" > $(SRC_PATH)/include/gpac/version.h ; else echo "No SVN Version found"; fi -lib: +lib: version $(MAKE) -C src all apps: @@ -65,9 +71,17 @@ install: $(INSTALL) $(INSTFLAGS) -m 644 doc/man/gpac.1 $(DESTDIR)$(mandir)/man1/ ; \ $(INSTALL) -d "$(DESTDIR)$(prefix)/share/gpac" ; \ $(INSTALL) $(INSTFLAGS) -m 644 doc/gpac.mp4 $(DESTDIR)$(prefix)/share/gpac/ ; \ + fi + if [ -d gui ] ; then \ $(INSTALL) -d "$(DESTDIR)$(prefix)/share/gpac/gui" ; \ + $(INSTALL) $(INSTFLAGS) -m 644 gui/gui.bt "$(DESTDIR)$(prefix)/share/gpac/gui/" ; \ + $(INSTALL) $(INSTFLAGS) -m 644 gui/gui.js "$(DESTDIR)$(prefix)/share/gpac/gui/" ; \ + $(INSTALL) $(INSTFLAGS) -m 644 gui/gwlib.js "$(DESTDIR)$(prefix)/share/gpac/gui/" ; \ + $(INSTALL) $(INSTFLAGS) -m 644 gui/mpegu-core.js "$(DESTDIR)$(prefix)/share/gpac/gui/" ; \ $(INSTALL) -d "$(DESTDIR)$(prefix)/share/gpac/gui/icons" ; \ - $(INSTALL) $(INSTFLAGS) -m 644 gui/icons/*.svg "$(DESTDIR)$(prefix)/share/gpac/gui/icons" ; \ + $(INSTALL) $(INSTFLAGS) -m 644 gui/icons/*.svg "$(DESTDIR)$(prefix)/share/gpac/gui/icons/" ; \ + cp -R gui/extensions "$(DESTDIR)$(prefix)/share/gpac/gui/" ; \ + rm -rf "$(DESTDIR)$(prefix)/share/gpac/gui/extensions/*.svn" ; \ fi uninstall: @@ -122,6 +136,23 @@ uninstall-lib: rm -rf "$(prefix)/include/gpac/enst" rm -rf "$(prefix)/include/gpac" +ifeq ($(CONFIG_DARWIN),yes) +dmg: + rm "bin/gcc/MP4Client" + $(MAKE) -C applications/mp4client + ./mkdmg.sh +endif + +ifeq ($(CONFIG_LINUX),yes) +deb: + fakeroot debian/rules clean + sed -i "s/.DEV/.DEV-r`svnversion \"$(SRC_PATH)\"`/" debian/changelog + fakeroot debian/rules configure + fakeroot debian/rules binary + svn cleanup + svn revert debian/changelog +endif + help: @echo "Input to GPAC make:" @echo "depend/dep: builds dependencies (dev only)" @@ -138,6 +169,12 @@ help: @echo @echo "install: install applications and modules on system" @echo "uninstall: uninstall applications and modules" +ifeq ($(CONFIG_DARWIN),yes) + @echo "dmg: creates DMG package file for OSX" +endif +ifeq ($(CONFIG_LINUX),yes) + @echo "dmg: creates DEB package file for debian based systems" +endif @echo @echo "install-lib: install gpac library (dyn and static) and headers , and " @echo "uninstall-lib: uninstall gpac library (dyn and static) and headers" diff --git a/applications/GPAX/GPAXPlugin.cpp b/applications/GPAX/GPAXPlugin.cpp index a72bf40..98e3ff6 100644 --- a/applications/GPAX/GPAXPlugin.cpp +++ b/applications/GPAX/GPAXPlugin.cpp @@ -384,7 +384,7 @@ LRESULT CGPAXPlugin::OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHa gf_cfg_set_key(m_user.config, "Audio", "NumBuffers", "2"); gf_cfg_set_key(m_user.config, "Audio", "TotalDuration", "120"); gf_cfg_set_key(m_user.config, "Video", "DriverName", "dx_hw"); - gf_cfg_set_key(m_user.config, "Video", "UseHardwareMemory", "yes"); + gf_cfg_set_key(m_user.config, "Video", "HardwareMemory", "Auto"); #ifdef _WIN32_WCE strcpy((char*)cfg_file, "\\windows"); diff --git a/applications/Makefile b/applications/Makefile index 45801ad..a5f449b 100644 --- a/applications/Makefile +++ b/applications/Makefile @@ -1,24 +1,25 @@ include ../config.mak APPDIRS= mp4client +APPDIRS+=mp42ts ifeq ($(DISABLE_ISOFF), no) APPDIRS += mp4box endif - V4STUDIODIR= INSTDIRS=mp4client ifeq ($(CONFIG_XUL),no) else -INSTDIRS+=osmozilla -APPDIRS+=osmozilla +#INSTDIRS+=osmozilla +#APPDIRS+=osmozilla endif +#disable due to version incompatibilities ifeq ($(USE_WXWIDGETS), yes) -APPDIRS+=osmo4_wx -V4STUDIODIR=V4Studio -INSTDIRS+=osmo4_wx +#APPDIRS+=osmo4_wx +#V4STUDIODIR=V4Studio +#INSTDIRS+=osmo4_wx endif ALLDIRS=$(APPDIRS) diff --git a/applications/generators/MPEG4/Makefile b/applications/generators/MPEG4/Makefile index 3f38945..373acab 100644 --- a/applications/generators/MPEG4/Makefile +++ b/applications/generators/MPEG4/Makefile @@ -2,7 +2,7 @@ include ../../../config.mak vpath %.c $(SRC_PATH)/applications/generators/MPEG4 -CFLAGS= $(OPTFLAGS) -I$(SRC_PATH)/include +CFLAGS= $(OPTFLAGS) -I"$(SRC_PATH)/include" ifeq ($(DEBUGBUILD), yes) CFLAGS+=-g diff --git a/applications/generators/MPEG4/main.c b/applications/generators/MPEG4/main.c index 2dcac27..de59bfd 100644 --- a/applications/generators/MPEG4/main.c +++ b/applications/generators/MPEG4/main.c @@ -26,8 +26,12 @@ #include #include +#undef _DEBUG +#undef DEBUG + #include + #include @@ -1570,8 +1574,112 @@ void parse_profile(GF_List *nodes, FILE *prof) } } +char *format_bit_string(char *str, u32 val, u32 nb_bits) +{ + u32 i, len; + strcpy(str, ""); + while (nb_bits) { + strcat(str, (val%2) ? "1" : "0"); + val>>=1; + nb_bits--; + } + len = strlen(str); + for (i=0; i ((1<\n"\ +"\n"\ +"\n"\ +"NdtListV%d.html\n"\ +"\n"\ +"\n"\ +"Node Coding Tables for BIFS Version %d group\n" + ,GPAC_FULL_VERSION, i+1, i+1); + + for (j=0; jversion != i+1) continue; + if (!IsNodeInTable(n, ndt)) continue; + nb_in_ndt ++; + } + + if (!nb_in_ndt) continue; + + fprintf(f, "
\n"\ +"\n"\ +"\n"\ +"\n", ndt, ndt, nb_in_ndt); + + nb_bits = GetBitsCount(nb_in_ndt); + fprintf(f, "\n", format_bit_string(szStr, 0, nb_bits)); + + idx = 1; + for (k=0;kversion != i+1) continue; + if (!IsNodeInTable(n, ndt)) continue; + + + n->hasDef = n->hasIn = n->hasOut = n->hasDyn = 0; + for (l=0;lFields); l++) { + BField *bf = gf_list_get(n->Fields, l); + if (!strcmp(bf->type, "field") || !strcmp(bf->type, "exposedField")) { + n->hasDef += 1; + } + if (!strcmp(bf->type, "eventIn") || !strcmp(bf->type, "exposedField")) { + n->hasIn += 1; + //check for anim + if (bf->hasAnim) n->hasDyn += 1; + } + if (!strcmp(bf->type, "eventOut") || !strcmp(bf->type, "exposedField")) { + n->hasOut += 1; + } + } + + fprintf(f, "\n", n->name, format_bit_string(szStr, idx, nb_bits), get_nb_bits(n->hasDef), get_nb_bits(n->hasIn), get_nb_bits(n->hasOut), get_nb_bits(n->hasDyn)); + idx++; + } + + fprintf(f, "
%s%u nodes
reserved

%s

 

 

 

 

%s

%s

%d DEF bits

%d IN bits

%d OUT bits

%d DYN bits

\n"); + } + fclose(f); + } + +} + int main (int argc, char **argv) { + Bool generate_ndt = 0; + char szTempFile[1024]; FILE *nodes, *ndt_c, *ndt_h, *fskip; GF_List *BNodes, *NDTs; u32 i, j, nbVersion; @@ -1590,7 +1698,9 @@ int main (int argc, char **argv) fskip = NULL; i=1; - if (argv[i][0]=='-') { + if (!strcmp(argv[i], "-ndt")) { + generate_ndt = 1; + } else if (argv[i][0]=='-') { fskip = fopen(argv[i+1], "rt"); if (!fskip) { printf("file %s not found\n", argv[i+1]); @@ -1598,9 +1708,16 @@ int main (int argc, char **argv) } i+=2; } - nbVersion = 1; - for (; i<(u32)argc; i++) { - nodes = fopen(argv[i], "rt"); + nbVersion=1; + while (1) { + sprintf(szTempFile, "templates%u.txt", nbVersion); + nodes = fopen(szTempFile, "rt"); + if (!nodes) { + sprintf(szTempFile, "template%u.txt", nbVersion); + nodes = fopen(szTempFile, "rt"); + } + if (!nodes) break; + //all nodes are in the same list but we keep version info ParseTemplateFile(nodes, BNodes, NDTs, nbVersion); @@ -1612,7 +1729,12 @@ int main (int argc, char **argv) } nbVersion--; printf("BIFS tables parsed: %d versions\n", nbVersion); - + + if (generate_ndt) { + generate_ndts(NDTs, BNodes, nbVersion); + goto exit; + } + if (fskip) { parse_profile(BNodes, fskip); fclose(fskip); @@ -1691,6 +1813,8 @@ int main (int argc, char **argv) EndFile(ndt_h, "NDT", 0); EndFile(ndt_c, "", 1); + +exit: //free NDTs while (gf_list_count(NDTs)) { char *tmp = gf_list_get(NDTs, 0); diff --git a/applications/generators/SVG/Makefile b/applications/generators/SVG/Makefile index e55a40c..f7ccf3a 100644 --- a/applications/generators/SVG/Makefile +++ b/applications/generators/SVG/Makefile @@ -2,7 +2,7 @@ include ../../../config.mak vpath %.c $(SRC_PATH)/applications/generators/SVG -CFLAGS= $(OPTFLAGS) -I$(SRC_PATH)/include +CFLAGS= $(OPTFLAGS) -I"$(SRC_PATH)/include" ifeq ($(DEBUGBUILD), yes) CFLAGS+=-g diff --git a/applications/generators/X3D/Makefile b/applications/generators/X3D/Makefile index 4274cb5..fd0f71e 100644 --- a/applications/generators/X3D/Makefile +++ b/applications/generators/X3D/Makefile @@ -2,7 +2,7 @@ include ../../../config.mak vpath %.c $(SRC_PATH)/applications/generators/X3D -CFLAGS= $(OPTFLAGS) -I$(SRC_PATH)/include +CFLAGS= $(OPTFLAGS) -I"$(SRC_PATH)/include" ifeq ($(DEBUGBUILD), yes) CFLAGS+=-g diff --git a/applications/m3u82mpd/main.c b/applications/m3u82mpd/main.c index c378f67..e3732e2 100644 --- a/applications/m3u82mpd/main.c +++ b/applications/m3u82mpd/main.c @@ -22,8 +22,8 @@ * */ -#include "../modules/m3u8_in/playlist.h" -#include "../modules/m3u8_in/m3u8_parser.h" +#include "../modules/mpd_in/m3u8.h" +#include #include int main(int argc, char **argv) diff --git a/applications/mp42avi/Makefile b/applications/mp42avi/Makefile index 77295e4..2226793 100644 --- a/applications/mp42avi/Makefile +++ b/applications/mp42avi/Makefile @@ -2,7 +2,7 @@ include ../../config.mak vpath %.c $(SRC_PATH)/applications/mp42avi -CFLAGS= $(OPTFLAGS) -I$(SRC_PATH)/include +CFLAGS= $(OPTFLAGS) -I"$(SRC_PATH)/include" ifeq ($(DEBUGBUILD), yes) CFLAGS+=-g diff --git a/applications/mp42ts/Makefile b/applications/mp42ts/Makefile new file mode 100644 index 0000000..9b99262 --- /dev/null +++ b/applications/mp42ts/Makefile @@ -0,0 +1,61 @@ +include ../../config.mak + +vpath %.c $(SRC_PATH)/applications/mp42ts + +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= main.o + +LINKFLAGS=-L../../bin/gcc +ifeq ($(CONFIG_WIN32),yes) +EXE=.exe +PROG=mp42ts$(EXE) +else +EXT= +PROG=mp42ts +endif +LINKFLAGS+=-lgpac + + +SRCS := $(OBJS:.o=.c) + +all: $(PROG) + +$(PROG): $(OBJS) + $(CC) $(LDFLAGS) -o ../../bin/gcc/$@ $(OBJS) $(LINKFLAGS) + + +%.o: %.c + $(CC) $(CFLAGS) -c -o $@ $< + + +clean: + rm -f $(OBJS) ../../bin/gcc/$(PROG) + +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/applications/mp42ts/main.c b/applications/mp42ts/main.c new file mode 100644 index 0000000..5a6c12d --- /dev/null +++ b/applications/mp42ts/main.c @@ -0,0 +1,1955 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) ENST 2000-200X + * All rights reserved + * + * This file is part of GPAC / mp4-to-ts (mp42ts) application + * + * 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 + +#define TDMB_PSI_REFRESH_RATE 500 /*repeat PAT/PMT every TDMB_PSI_REFRESH_RATE ms*/ + +#define UDP_BUFFER_SIZE 0x40000 + +#define MP42TS_PRINT_FREQ 634 /*refresh printed info every CLOCK_REFRESH ms*/ +#define MP42TS_VIDEO_FREQ 1000 /*meant to send AVC IDR only every CLOCK_REFRESH ms*/ + +static GFINLINE void usage(const char * progname) +{ + fprintf(stderr, "USAGE: %s -rate=R [[-prog=prog1]..[-prog=progn]] [-audio=url] [-video=url] [-mpeg4-carousel=n] [-mpeg4] [-time=n] [-src=file] DST [[DST]]\n" + "\n" + "\t-rate=R specifies target rate in kbps of the multiplex (mandatory)\n" + /* "\t If not set transport stream will be of variable bitrate\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" + "\t-audio=url may be mp3/udp or aac/http (shoutcast/icecast)\n" + "\t-video=url shall be raw h264\n" + "\t-mpeg4-carousel=n carousel period in ms\n" + "\t-mpeg4 forces usage of MPEG-4 signaling (IOD and SL Config)\n" + "\t-time=n request the program to stop after n ms\n" + "\t-src=filename update file: must be either an .sdp or a .bt file\n\n" + "\tDST : Destinations, at least one is mandatory\n" + "\t -dst-udp UDP_address:port (multicast or unicast)\n" + "\t -dst-rtp RTP_address:port\n" + "\t -dst-file Supports the following arguments:\n" + "\t -segment-dir=dir server local directory to store segments\n" + "\t -segment-duration=dur segment duration in seconds\n" + "\t -segment-manifest=file m3u8 file basename\n" + "\t -segment-http-prefix=p client address for accessing server segments\n" + "\t -segment-number=n only n segments are used using a cyclic pattern\n" + "\n", progname + ); +} + + +#define MAX_MUX_SRC_PROG 100 +typedef struct +{ + GF_ISOFile *mp4; + u32 nb_streams, pcr_idx; + GF_ESInterface streams[40]; + GF_Descriptor *iod; + GF_SceneEngine *seng; + GF_Thread *th; + char *src_name; + u32 rate; + Bool repeat; + u32 mpeg4_signaling; + Bool audio_configured; +} M2TSProgram; + +typedef struct +{ + GF_ISOFile *mp4; + u32 track, sample_number, sample_count; + GF_ISOSample *sample; + /*refresh rate for images*/ + u32 image_repeat_ms, nb_repeat_last; + void *dsi; + u32 dsi_size; + u32 nalu_size; + void *dsi_and_rap; + Bool loop; + u64 ts_offset; +} GF_ESIMP4; + +typedef struct +{ + u32 carousel_period, ts_delta; + u16 aggregate_on_stream; + Bool adjust_carousel_time; + Bool discard; + Bool rap; + Bool critical; + Bool vers_inc; +} GF_ESIStream; + +typedef struct +{ + u32 size; + char *data; +} GF_SimpleDataDescriptor; + +//TODO: find a clean way to save this data +static u32 audio_OD_stream_id = (u32)-1; +#define AUDIO_OD_ESID 100 +#define AUDIO_DATA_ESID 101 +#define VIDEO_DATA_ESID 105 + +/*output types*/ +enum +{ + GF_MP42TS_FILE, /*open mpeg2ts file*/ + GF_MP42TS_UDP, /*open udp socket*/ + GF_MP42TS_RTP, /*open rtp socket*/ + GF_MP42TS_HTTP, /*open http downloader*/ +}; + +static GF_Err mp4_input_ctrl(GF_ESInterface *ifce, u32 act_type, void *param) +{ + GF_ESIMP4 *priv = (GF_ESIMP4 *)ifce->input_udta; + if (!priv) return GF_BAD_PARAM; + + switch (act_type) { + case GF_ESI_INPUT_DATA_FLUSH: + { + GF_ESIPacket pck; + 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; + + pck.flags = 0; + pck.flags = GF_ESI_DATA_AU_START | GF_ESI_DATA_HAS_CTS; + if (priv->sample->IsRAP) pck.flags |= GF_ESI_DATA_AU_RAP; + pck.cts = priv->sample->DTS + priv->ts_offset; + + if (priv->nb_repeat_last) { + pck.cts += priv->nb_repeat_last*ifce->timescale * priv->image_repeat_ms / 1000; + } + + if (priv->sample->CTS_Offset) { + pck.dts = pck.cts; + pck.cts += priv->sample->CTS_Offset; + pck.flags |= GF_ESI_DATA_HAS_DTS; + } + + if (priv->sample->IsRAP && priv->dsi) { + 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) { + u32 remain = priv->sample->dataLength; + char *ptr = priv->sample->data; + u32 v, size; + char sc[4]; + sc[0] = sc[1] = sc[2] = 0; sc[3] = 1; + + while (remain) { + size = 0; + v = priv->nalu_size; + while (v) { + size |= (u8) *ptr; + ptr++; + remain--; + v-=1; + if (v) size<<=8; + } + remain -= size; + + pck.data = sc; + pck.data_len = 4; + ifce->output_ctrl(ifce, GF_ESI_OUTPUT_DATA_DISPATCH, &pck); + pck.flags &= ~GF_ESI_DATA_AU_START; + + if (!remain) pck.flags |= GF_ESI_DATA_AU_END; + + pck.data = ptr; + pck.data_len = size; + ifce->output_ctrl(ifce, GF_ESI_OUTPUT_DATA_DISPATCH, &pck); + ptr += size; + } + + } else { + pck.flags |= GF_ESI_DATA_AU_END; + pck.data = priv->sample->data; + pck.data_len = priv->sample->dataLength; + ifce->output_ctrl(ifce, GF_ESI_OUTPUT_DATA_DISPATCH, &pck); + } + + gf_isom_sample_del(&priv->sample); + priv->sample_number++; + if (priv->sample_number==priv->sample_count) { + if (priv->loop) { + Double scale; + u64 duration; + /*increment ts offset*/ + scale = gf_isom_get_media_timescale(priv->mp4, priv->track); + scale /= gf_isom_get_timescale(priv->mp4); + duration = (u64) (gf_isom_get_duration(priv->mp4) * scale); + priv->ts_offset += duration; + priv->sample_number = 0; + } + else if (priv->image_repeat_ms) { + priv->nb_repeat_last++; + priv->sample_number--; + } else { + ifce->caps |= GF_ESI_STREAM_IS_OVER; + } + } + } + return GF_OK; + + case GF_ESI_INPUT_DESTROY: + if (priv->dsi) gf_free(priv->dsi); + if (ifce->decoder_config) { + gf_free(ifce->decoder_config); + ifce->decoder_config = NULL; + } + gf_free(priv); + ifce->input_udta = NULL; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + +static void fill_isom_es_ifce(GF_ESInterface *ifce, GF_ISOFile *mp4, u32 track_num) +{ + GF_ESIMP4 *priv; + char _lan[4]; + GF_DecoderConfig *dcd; + u64 avg_rate, duration; + + GF_SAFEALLOC(priv, GF_ESIMP4); + + priv->mp4 = mp4; + priv->track = track_num; + priv->loop = 1; + priv->sample_count = gf_isom_get_sample_count(mp4, track_num); + + memset(ifce, 0, sizeof(GF_ESInterface)); + ifce->stream_id = gf_isom_get_track_id(mp4, track_num); + dcd = gf_isom_get_decoder_config(mp4, track_num, 1); + ifce->stream_type = dcd->streamType; + ifce->object_type_indication = dcd->objectTypeIndication; + if (dcd->decoderSpecificInfo && dcd->decoderSpecificInfo->dataLength) { + switch (dcd->objectTypeIndication) { + case GPAC_OTI_AUDIO_AAC_MPEG4: + ifce->decoder_config = gf_malloc(sizeof(char)*dcd->decoderSpecificInfo->dataLength); + ifce->decoder_config_size = dcd->decoderSpecificInfo->dataLength; + memcpy(ifce->decoder_config, dcd->decoderSpecificInfo->data, dcd->decoderSpecificInfo->dataLength); + break; + case GPAC_OTI_VIDEO_MPEG4_PART2: + priv->dsi = gf_malloc(sizeof(char)*dcd->decoderSpecificInfo->dataLength); + priv->dsi_size = dcd->decoderSpecificInfo->dataLength; + memcpy(priv->dsi, dcd->decoderSpecificInfo->data, dcd->decoderSpecificInfo->dataLength); + break; + case GPAC_OTI_VIDEO_AVC: + { +#ifndef GPAC_DISABLE_AV_PARSERS + GF_AVCConfigSlot *slc; + u32 i; + GF_BitStream *bs; + GF_AVCConfig *avccfg = gf_isom_avc_config_get(mp4, track_num, 1); + priv->nalu_size = avccfg->nal_unit_size; + bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + for (i=0; isequenceParameterSets);i++) { + slc = gf_list_get(avccfg->sequenceParameterSets, i); + gf_bs_write_u32(bs, 1); + gf_bs_write_data(bs, slc->data, slc->size); + } + for (i=0; ipictureParameterSets);i++) { + slc = gf_list_get(avccfg->pictureParameterSets, i); + gf_bs_write_u32(bs, 1); + gf_bs_write_data(bs, slc->data, slc->size); + } + gf_bs_get_content(bs, (char **) &priv->dsi, &priv->dsi_size); + gf_bs_del(bs); +#endif + } + break; + } + } + gf_odf_desc_del((GF_Descriptor *)dcd); + gf_isom_get_media_language(mp4, track_num, _lan); + ifce->lang = GF_4CC(_lan[0],_lan[1],_lan[2],' '); + + ifce->timescale = gf_isom_get_media_timescale(mp4, track_num); + ifce->duration = gf_isom_get_media_timescale(mp4, track_num); + avg_rate = gf_isom_get_media_data_size(mp4, track_num); + avg_rate *= ifce->timescale * 8; + if (0!=(duration=gf_isom_get_media_duration(mp4, track_num))) + avg_rate /= duration; + + if (gf_isom_has_time_offset(mp4, track_num)) ifce->caps |= GF_ESI_SIGNAL_DTS; + + ifce->bit_rate = (u32) avg_rate; + ifce->duration = (Double) (s64) gf_isom_get_media_duration(mp4, track_num); + ifce->duration /= ifce->timescale; + + ifce->input_ctrl = mp4_input_ctrl; + if (priv != ifce->input_udta){ + if (ifce->input_udta) + gf_free(ifce->input_udta); + ifce->input_udta = priv; + } +} + + +static GF_Err seng_input_ctrl(GF_ESInterface *ifce, u32 act_type, void *param) +{ + if (act_type==GF_ESI_INPUT_DESTROY) { + //TODO: free my data + if (ifce->input_udta) + gf_free(ifce->input_udta); + ifce->input_udta = NULL; + return GF_OK; + } + + return GF_OK; +} + +typedef struct +{ + /*RTP channel*/ + GF_RTPChannel *rtp_ch; + + /*depacketizer*/ + GF_RTPDepacketizer *depacketizer; + + GF_ESIPacket pck; + + GF_ESInterface *ifce; + + Bool cat_dsi; + void *dsi_and_rap; + + Bool use_carousel; + u32 au_sn; +} GF_ESIRTP; + +static GF_Err rtp_input_ctrl(GF_ESInterface *ifce, u32 act_type, void *param) +{ + u32 size, PayloadStart; + GF_Err e; + GF_RTPHeader hdr; + char buffer[8000]; + GF_ESIRTP *rtp = (GF_ESIRTP*)ifce->input_udta; + + if (!ifce->input_udta) return GF_BAD_PARAM; + + switch (act_type) { + case GF_ESI_INPUT_DATA_FLUSH: + /*flush rtp channel*/ + while (1) { + size = gf_rtp_read_rtp(rtp->rtp_ch, buffer, 8000); + if (!size) break; + e = gf_rtp_decode_rtp(rtp->rtp_ch, buffer, size, &hdr, &PayloadStart); + if (e) return e; + gf_rtp_depacketizer_process(rtp->depacketizer, &hdr, buffer + PayloadStart, size - PayloadStart); + } + /*flush rtcp channel*/ + while (1) { + size = gf_rtp_read_rtcp(rtp->rtp_ch, buffer, 8000); + if (!size) break; + e = gf_rtp_decode_rtcp(rtp->rtp_ch, buffer, size, NULL); + if (e == GF_EOS) ifce->caps |= GF_ESI_STREAM_IS_OVER; + } + return GF_OK; + case GF_ESI_INPUT_DESTROY: + gf_rtp_depacketizer_del(rtp->depacketizer); + if (rtp->dsi_and_rap) gf_free(rtp->dsi_and_rap); + gf_rtp_del(rtp->rtp_ch); + gf_free(rtp); + ifce->input_udta = NULL; + return GF_OK; + } + return GF_OK; +} + +static GF_Err void_input_ctrl(GF_ESInterface *ifce, u32 act_type, void *param) +{ + return GF_OK; +} + +/*AAC import features*/ +void *audio_prog = NULL; +static void SampleCallBack(void *calling_object, u16 ESID, char *data, u32 size, u64 ts); +#define DONT_USE_TERMINAL_MODULE_API +#include "../../modules/aac_in/aac_in.c" +AACReader *aac_reader = NULL; +u64 audio_discontinuity_offset = 0; + +/*create an OD codec and encode the descriptor*/ +static GF_Err encode_audio_desc(GF_ESD *esd, GF_SimpleDataDescriptor *audio_desc) +{ + GF_Err e; + GF_ODCodec *odc = gf_odf_codec_new(); + GF_ODUpdate *od_com = (GF_ODUpdate*)gf_odf_com_new(GF_ODF_OD_UPDATE_TAG); + GF_ObjectDescriptor *od = (GF_ObjectDescriptor*)gf_odf_desc_new(GF_ODF_OD_TAG); + assert( esd ); + assert( audio_desc ); + gf_list_add(od->ESDescriptors, esd); + od->objectDescriptorID = AUDIO_DATA_ESID; + gf_list_add(od_com->objectDescriptors, od); + + e = gf_odf_codec_add_com(odc, (GF_ODCom*)od_com); + if (e) { + fprintf(stderr, "Audio input error add the command to be encoded\n"); + return e; + } + e = gf_odf_codec_encode(odc, 0); + if (e) { + fprintf(stderr, "Audio input error encoding the descriptor\n"); + return e; + } + e = gf_odf_codec_get_au(odc, &audio_desc->data, &audio_desc->size); + if (e) { + fprintf(stderr, "Audio input error getting the descriptor\n"); + return e; + } + e = gf_odf_com_del((GF_ODCom**)&od_com); + if (e) { + fprintf(stderr, "Audio input error deleting the command\n"); + return e; + } + gf_odf_codec_del(odc); + + return GF_OK; +} + +static void SampleCallBack(void *calling_object, u16 ESID, char *data, u32 size, u64 ts) +{ + u32 i; + //fprintf(stderr, "update: ESID=%d - size=%d - ts="LLD"\n", ESID, size, ts); + + if (calling_object) { + M2TSProgram *prog = (M2TSProgram *)calling_object; + + if (ESID == AUDIO_DATA_ESID) { + if (audio_OD_stream_id != (u32)-1) { + /*this is the first time we get some audio data. Therefore we are sure we can retrieve the audio descriptor. Then we'll + send it by calling this callback recursively so that a player gets the audio descriptor before audio data. + Hack: the descriptor is carried thru the input_udta, you shall delete it*/ + GF_SimpleDataDescriptor *audio_desc = prog->streams[audio_OD_stream_id].input_udta; + if (audio_desc && !audio_desc->data) /*intended for HTTP/AAC: an empty descriptor was set (vs already filled for RTP/UDP MP3)*/ + { + /*get the audio descriptor and encode it*/ + GF_ESD *esd = AAC_GetESD(aac_reader); + assert(esd->slConfig->timestampResolution); + esd->slConfig->useAccessUnitStartFlag = 1; + esd->slConfig->useAccessUnitEndFlag = 1; + esd->slConfig->useTimestampsFlag = 1; + esd->slConfig->timestampLength = 33; + /*audio stream, all samples are RAPs*/ + esd->slConfig->useRandomAccessPointFlag = 0; + esd->slConfig->hasRandomAccessUnitsOnlyFlag = 1; + for (i=0; inb_streams; i++) { + if (prog->streams[i].stream_id == AUDIO_DATA_ESID) { + GF_Err e; + prog->streams[i].timescale = esd->slConfig->timestampResolution; + e = gf_m2ts_program_stream_update_ts_scale(&prog->streams[i], esd->slConfig->timestampResolution); + assert(!e); + gf_m2ts_program_stream_update_sl_config(&prog->streams[i], esd->slConfig); + break; + } + } + esd->ESID = AUDIO_DATA_ESID; + assert(audio_OD_stream_id != (u32)-1); + encode_audio_desc(esd, audio_desc); + + /*build the ESI*/ + { + /*audio OD descriptor: rap=1 and vers_inc=0*/ + GF_SAFEALLOC(prog->streams[audio_OD_stream_id].input_udta, GF_ESIStream); + ((GF_ESIStream*)prog->streams[audio_OD_stream_id].input_udta)->rap = 1; + + /*we have the descriptor; now call this callback recursively so that a player gets the audio descriptor before audio data.*/ + prog->repeat = 1; + SampleCallBack(prog, AUDIO_OD_ESID, audio_desc->data, audio_desc->size, 0/*gf_m2ts_get_sys_clock(muxer)*/); + prog->repeat = 0; + + /*clean*/ + gf_free(audio_desc->data); + gf_free(audio_desc); + gf_free(prog->streams[audio_OD_stream_id].input_udta); + prog->streams[audio_OD_stream_id].input_udta = NULL; + } + } + } + /*update the timescale if needed*/ + else if (!prog->audio_configured) { + GF_ESD *esd = AAC_GetESD(aac_reader); + assert(esd->slConfig->timestampResolution); + for (i=0; inb_streams; i++) { + if (prog->streams[i].stream_id == AUDIO_DATA_ESID) { + GF_Err e; + prog->streams[i].timescale = esd->slConfig->timestampResolution; + prog->streams[i].decoder_config = esd->decoderConfig->decoderSpecificInfo->data; + prog->streams[i].decoder_config_size = esd->decoderConfig->decoderSpecificInfo->dataLength; + esd->decoderConfig->decoderSpecificInfo->data = NULL; + esd->decoderConfig->decoderSpecificInfo->dataLength = 0; + e = gf_m2ts_program_stream_update_ts_scale(&prog->streams[i], esd->slConfig->timestampResolution); + if (!e) + prog->audio_configured = 1; + break; + } + } + gf_odf_desc_del((GF_Descriptor *)esd); + } + + /*overwrite timing as it is flushed to 0 on discontinuities*/ + ts += audio_discontinuity_offset; + } + + i=0; + while (inb_streams){ + if (prog->streams[i].output_ctrl==NULL) { + fprintf(stderr, "MULTIPLEX NOT YET CREATED\n"); + return; + } + if (prog->streams[i].stream_id == ESID) { + GF_ESIStream *priv = (GF_ESIStream *)prog->streams[i].input_udta; + GF_ESIPacket pck; + memset(&pck, 0, sizeof(GF_ESIPacket)); + pck.data = data; + pck.data_len = size; + pck.flags |= GF_ESI_DATA_HAS_CTS; + pck.flags |= GF_ESI_DATA_HAS_DTS; + pck.flags |= GF_ESI_DATA_AU_START; + pck.flags |= GF_ESI_DATA_AU_END; + if (ts) pck.cts = pck.dts = ts; + + if (priv->rap) + pck.flags |= GF_ESI_DATA_AU_RAP; + if (prog->repeat || !priv->vers_inc) { + pck.flags |= GF_ESI_DATA_REPEAT; + fprintf(stderr, "RAP carousel from scene engine sent: ESID=%d - size=%d - ts="LLD"\n", ESID, size, ts); + } else { + if (ESID != AUDIO_DATA_ESID && ESID != VIDEO_DATA_ESID) /*don't log A/V inputs*/ + fprintf(stderr, "Update from scene engine sent: ESID=%d - size=%d - ts="LLD"\n", ESID, size, ts); + } + prog->streams[i].output_ctrl(&prog->streams[i], GF_ESI_OUTPUT_DATA_DISPATCH, &pck); + return; + } + i++; + } + } + return; +} + +//static gf_seng_callback * SampleCallBack = &mySampleCallBack; + + +static volatile Bool run = 1; + +static GF_ESIStream * set_broadcast_params(M2TSProgram *prog, u16 esid, u32 period, u32 ts_delta, u16 aggregate_on_stream, Bool adjust_carousel_time, Bool force_rap, Bool aggregate_au, Bool discard_pending, Bool signal_rap, Bool signal_critical, Bool version_inc) +{ + u32 i=0; + GF_ESIStream *priv=NULL; + GF_ESInterface *esi=NULL; + + /*locate our stream*/ + if (esid) { + while (inb_streams) { + if (prog->streams[i].stream_id == esid){ + priv = (GF_ESIStream *)prog->streams[i].input_udta; + esi = &prog->streams[i]; + break; + } + else{ + i++; + } + } + /*TODO: stream not found*/ + } + + /*TODO - set/reset the ESID for the parsers*/ + if (!priv) return NULL; + + /*TODO - if discard is set, abort current carousel*/ + if (discard_pending) { + } + + /*remember RAP flag*/ + priv->rap = signal_rap; + priv->critical = signal_critical; + priv->vers_inc = version_inc; + + priv->ts_delta = ts_delta; + priv->adjust_carousel_time = adjust_carousel_time; + + /*change stream aggregation mode*/ + if ((aggregate_on_stream != (u16)-1) && (priv->aggregate_on_stream != aggregate_on_stream)) { + gf_seng_enable_aggregation(prog->seng, esid, aggregate_on_stream); + priv->aggregate_on_stream = aggregate_on_stream; + } + /*change stream aggregation mode*/ + if (priv->aggregate_on_stream==esi->stream_id) { + if (priv->aggregate_on_stream && (period!=(u32)-1) && (esi->repeat_rate != period)) { + esi->repeat_rate = period; + } + } else { + esi->repeat_rate = 0; + } + + return priv; +} + +static Bool seng_output(void *param) +{ + GF_Err e; + u64 last_src_modif, mod_time; + Bool has_carousel=0; + M2TSProgram *prog = (M2TSProgram *)param; + GF_SceneEngine *seng = prog->seng; + GF_SimpleDataDescriptor *audio_desc; + Bool update_context=0; + Bool force_rap, adjust_carousel_time, discard_pending, signal_rap, signal_critical, version_inc, aggregate_au; + u32 period, ts_delta; + u16 es_id, aggregate_on_stream; + e = GF_OK; + if (prog->rate){ + has_carousel = 1; + } + gf_sleep(2000); /*TODO: events instead? What are we waiting for?*/ + gf_seng_encode_context(seng, SampleCallBack); + + last_src_modif = prog->src_name ? gf_file_modification_time(prog->src_name) : 0; + + /*send the audio descriptor*/ + if (prog->mpeg4_signaling==GF_M2TS_MPEG4_SIGNALING_FULL && audio_OD_stream_id!=(u32)-1) { + audio_desc = prog->streams[audio_OD_stream_id].input_udta; + if (audio_desc && audio_desc->data) /*RTP/UDP + MP3 case*/ + { + assert(audio_OD_stream_id != (u32)-1); + assert(!aac_reader); /*incompatible with AAC*/ + prog->repeat = 1; + SampleCallBack(prog, AUDIO_OD_ESID, audio_desc->data, audio_desc->size, 0/*gf_m2ts_get_sys_clock(muxer)*/); + prog->repeat = 0; + gf_free(audio_desc->data); + gf_free(audio_desc); + prog->streams[audio_OD_stream_id].input_udta = NULL; + } + } + + while (run) { + if (!gf_prompt_has_input()) { + if (prog->src_name) { + mod_time = gf_file_modification_time(prog->src_name); + if (mod_time != last_src_modif) { + FILE *srcf; + char flag_buf[201], *flag; + fprintf(stderr, "Update file modified - processing\n"); + last_src_modif = mod_time; + + srcf = gf_f64_open(prog->src_name, "rt"); + if (!srcf) continue; + + /*checks if we have a broadcast config*/ + if (!fgets(flag_buf, 200, srcf)) + flag_buf[0] = '\0'; + fclose(srcf); + + aggregate_au = force_rap = adjust_carousel_time = discard_pending = signal_rap = signal_critical = 0; + version_inc = 1; + period = -1; + aggregate_on_stream = -1; + ts_delta = 0; + es_id = 0; + + /*find our keyword*/ + flag = strstr(flag_buf, "gpac_broadcast_config "); + if (flag) { + flag += strlen("gpac_broadcast_config "); + /*move to next word*/ + while (flag && (flag[0]==' ')) flag++; + + while (1) { + char *sep = strchr(flag, ' '); + if (sep) sep[0] = 0; + if (!strnicmp(flag, "esid=", 5)) { + /*ESID on which the update is applied*/ + es_id = atoi(flag+5); + } else if (!strnicmp(flag, "period=", 7)) { + /*TODO: target period carousel for ESID ??? (ESID/carousel)*/ + period = atoi(flag+7); + } else if (!strnicmp(flag, "ts=", 3)) { + /*TODO: */ + ts_delta = atoi(flag+3); + } else if (!strnicmp(flag, "carousel=", 9)) { + /*TODO: why? => sends the update on carousel id specified by this argument*/ + aggregate_on_stream = atoi(flag+9); + } else if (!strnicmp(flag, "restamp=", 8)) { + /*CTS is updated when carouselled*/ + adjust_carousel_time = atoi(flag+8); + } else if (!strnicmp(flag, "discard=", 8)) { + /*when we receive several updates during a single carousel period, this attribute specifies whether the current update discard pending ones*/ + discard_pending = atoi(flag+8); + } else if (!strnicmp(flag, "aggregate=", 10)) { + /*Boolean*/ + aggregate_au = atoi(flag+10); + } else if (!strnicmp(flag, "force_rap=", 10)) { + /*TODO: */ + force_rap = atoi(flag+10); + } else if (!strnicmp(flag, "rap=", 4)) { + /*TODO: */ + signal_rap = atoi(flag+4); + } else if (!strnicmp(flag, "critical=", 9)) { + /*TODO: */ + signal_critical = atoi(flag+9); + } else if (!strnicmp(flag, "vers_inc=", 9)) { + /*Boolean to increment m2ts section version number*/ + version_inc = atoi(flag+9); + } + if (sep) { + sep[0] = ' '; + flag = sep+1; + } else { + break; + } + } + + set_broadcast_params(prog, es_id, period, ts_delta, aggregate_on_stream, adjust_carousel_time, force_rap, aggregate_au, discard_pending, signal_rap, signal_critical, version_inc); + } + + e = gf_seng_encode_from_file(seng, es_id, aggregate_au ? 0 : 1, prog->src_name, SampleCallBack); + if (e){ + fprintf(stderr, "Processing command failed: %s\n", gf_error_to_string(e)); + } else + gf_seng_aggregate_context(seng, 0); + + update_context=1; + + + + } + } + if (update_context) { + prog->repeat = 1; + e = gf_seng_encode_context(seng, SampleCallBack); + prog->repeat = 0; + update_context = 0; + } + + gf_sleep(10); + } else { /*gf_prompt_has_input()*/ + char c = gf_prompt_get_char(); + switch (c) { + case 'u': + { + GF_Err e; + char szCom[8192]; + fprintf(stderr, "Enter command to send:\n"); + fflush(stdin); + szCom[0] = 0; + if (1 > scanf("%[^\t\n]", szCom)){ + fprintf(stderr, "No command has been properly entered, aborting.\n"); + break; + } + e = gf_seng_encode_from_string(seng, 0, 0, szCom, SampleCallBack); + if (e) { + fprintf(stderr, "Processing command failed: %s\n", gf_error_to_string(e)); + } + update_context=1; + } + break; + case 'p': + { + char rad[GF_MAX_PATH]; + fprintf(stderr, "Enter output file name - \"std\" for stdout: "); + if (1 > scanf("%s", rad)){ + fprintf(stderr, "No outfile name has been entered, aborting.\n"); + break; + } + e = gf_seng_save_context(seng, !strcmp(rad, "std") ? NULL : rad); + fprintf(stderr, "Dump done (%s)\n", gf_error_to_string(e)); + } + break; + case 'q': + { + run = 0; + } + } + e = GF_OK; + } + } + + + return e ? 1 : 0; +} + + +static void rtp_sl_packet_cbk(void *udta, char *payload, u32 size, GF_SLHeader *hdr, GF_Err e) +{ + GF_ESIRTP *rtp = (GF_ESIRTP*)udta; + rtp->pck.data = payload; + rtp->pck.data_len = size; + rtp->pck.dts = hdr->decodingTimeStamp; + rtp->pck.cts = hdr->compositionTimeStamp; + rtp->pck.flags = 0; + if (hdr->compositionTimeStampFlag) rtp->pck.flags |= GF_ESI_DATA_HAS_CTS; + if (hdr->decodingTimeStampFlag) rtp->pck.flags |= GF_ESI_DATA_HAS_DTS; + if (hdr->randomAccessPointFlag) rtp->pck.flags |= GF_ESI_DATA_AU_RAP; + if (hdr->accessUnitStartFlag) rtp->pck.flags |= GF_ESI_DATA_AU_START; + if (hdr->accessUnitEndFlag) rtp->pck.flags |= GF_ESI_DATA_AU_END; + + if (rtp->use_carousel) { + if ((hdr->AU_sequenceNumber==rtp->au_sn) && hdr->randomAccessPointFlag) rtp->pck.flags |= GF_ESI_DATA_REPEAT; + rtp->au_sn = hdr->AU_sequenceNumber; + } + + if (rtp->cat_dsi && hdr->randomAccessPointFlag && hdr->accessUnitStartFlag) { + if (rtp->dsi_and_rap) gf_free(rtp->dsi_and_rap); + rtp->pck.data_len = size + rtp->depacketizer->sl_map.configSize; + rtp->dsi_and_rap = gf_malloc(sizeof(char)*(rtp->pck.data_len)); + memcpy(rtp->dsi_and_rap, rtp->depacketizer->sl_map.config, rtp->depacketizer->sl_map.configSize); + memcpy((char *) rtp->dsi_and_rap + rtp->depacketizer->sl_map.configSize, payload, size); + rtp->pck.data = rtp->dsi_and_rap; + } + + + rtp->ifce->output_ctrl(rtp->ifce, GF_ESI_OUTPUT_DATA_DISPATCH, &rtp->pck); +} + +static void fill_rtp_es_ifce(GF_ESInterface *ifce, GF_SDPMedia *media, GF_SDPInfo *sdp) +{ + u32 i; + GF_Err e; + GF_X_Attribute*att; + GF_ESIRTP *rtp; + GF_RTPMap*map; + GF_SDPConnection *conn; + GF_RTSPTransport trans; + + /*check connection*/ + conn = sdp->c_connection; + if (!conn) conn = (GF_SDPConnection*)gf_list_get(media->Connections, 0); + + /*check payload type*/ + map = (GF_RTPMap*)gf_list_get(media->RTPMaps, 0); + GF_SAFEALLOC(rtp, GF_ESIRTP); + + memset(ifce, 0, sizeof(GF_ESInterface)); + rtp->rtp_ch = gf_rtp_new(); + i=0; + while ((att = (GF_X_Attribute*)gf_list_enum(media->Attributes, &i))) { + if (!stricmp(att->Name, "mpeg4-esid") && att->Value) ifce->stream_id = atoi(att->Value); + } + + memset(&trans, 0, sizeof(GF_RTSPTransport)); + trans.Profile = media->Profile; + trans.source = conn ? conn->host : sdp->o_address; + trans.IsUnicast = gf_sk_is_multicast_address(trans.source) ? 0 : 1; + if (!trans.IsUnicast) { + trans.port_first = media->PortNumber; + trans.port_last = media->PortNumber + 1; + trans.TTL = conn ? conn->TTL : 0; + } else { + trans.client_port_first = media->PortNumber; + trans.client_port_last = media->PortNumber + 1; + } + + if (gf_rtp_setup_transport(rtp->rtp_ch, &trans, NULL) != GF_OK) { + gf_rtp_del(rtp->rtp_ch); + fprintf(stderr, "Cannot initialize RTP transport\n"); + return; + } + /*setup depacketizer*/ + rtp->depacketizer = gf_rtp_depacketizer_new(media, rtp_sl_packet_cbk, rtp); + if (!rtp->depacketizer) { + gf_rtp_del(rtp->rtp_ch); + fprintf(stderr, "Cannot create RTP depacketizer\n"); + return; + } + /*setup channel*/ + gf_rtp_setup_payload(rtp->rtp_ch, map); + ifce->input_udta = rtp; + ifce->input_ctrl = rtp_input_ctrl; + rtp->ifce = ifce; + + ifce->object_type_indication = rtp->depacketizer->sl_map.ObjectTypeIndication; + ifce->stream_type = rtp->depacketizer->sl_map.StreamType; + ifce->timescale = gf_rtp_get_clockrate(rtp->rtp_ch); + if (rtp->depacketizer->sl_map.config) { + switch (ifce->object_type_indication) { + case GPAC_OTI_VIDEO_MPEG4_PART2: + rtp->cat_dsi = 1; + break; + } + } + if (rtp->depacketizer->sl_map.StreamStateIndication) { + rtp->use_carousel = 1; + rtp->au_sn=0; + } + + /*DTS signaling is only supported for MPEG-4 visual*/ + if (rtp->depacketizer->sl_map.DTSDeltaLength) ifce->caps |= GF_ESI_SIGNAL_DTS; + + gf_rtp_depacketizer_reset(rtp->depacketizer, 1); + e = gf_rtp_initialize(rtp->rtp_ch, 0x100000ul, 0, 0, 10, 200, NULL); + if (e!=GF_OK) { + gf_rtp_del(rtp->rtp_ch); + fprintf(stderr, "Cannot initialize RTP channel: %s\n", gf_error_to_string(e)); + return; + } + fprintf(stderr, "RTP interface initialized\n"); +} + +void fill_seng_es_ifce(GF_ESInterface *ifce, u32 i, GF_SceneEngine *seng, u32 period) +{ + GF_Err e = GF_OK; + u32 len; + GF_ESIStream *stream; + char *config_buffer = NULL; + + memset(ifce, 0, sizeof(GF_ESInterface)); + e = gf_seng_get_stream_config(seng, i, (u16*) &(ifce->stream_id), &config_buffer, &len, (u32*) &(ifce->stream_type), (u32*) &(ifce->object_type_indication), &(ifce->timescale)); + if (e) { + fprintf(stderr, "Cannot set the stream config for stream %d to %d: %s\n", ifce->stream_id, period, gf_error_to_string(e)); + } + + ifce->repeat_rate = period; + GF_SAFEALLOC(stream, GF_ESIStream); + memset(stream, 0, sizeof(GF_ESIStream)); + stream->rap = 1; + if (ifce->input_udta) + gf_free(ifce->input_udta); + ifce->input_udta = stream; + + //fprintf(stderr, "Caroussel period: %d\n", period); +// e = gf_seng_set_carousel_time(seng, ifce->stream_id, period); + if (e) { + fprintf(stderr, "Cannot set carousel time on stream %d to %d: %s\n", ifce->stream_id, period, gf_error_to_string(e)); + } + ifce->input_ctrl = seng_input_ctrl; + +} + +static Bool open_program(M2TSProgram *prog, char *src, u32 carousel_rate, Bool force_mpeg4, char *update, char *audio_input_ip, u16 audio_input_port, char *video_buffer) +{ + GF_SDPInfo *sdp; + u32 i; + GF_Err e; + + memset(prog, 0, sizeof(M2TSProgram)); + prog->mpeg4_signaling = force_mpeg4 ? GF_M2TS_MPEG4_SIGNALING_FULL : GF_M2TS_MPEG4_SIGNALING_NONE; + + /*open ISO file*/ + if (gf_isom_probe_file(src)) { + u32 nb_tracks; + u32 first_audio = 0; + prog->mp4 = gf_isom_open(src, GF_ISOM_OPEN_READ, 0); + prog->nb_streams = 0; + /*on MPEG-2 TS, carry 3GPP timed text as MPEG-4 Part17*/ + gf_isom_text_set_streaming_mode(prog->mp4, 1); + nb_tracks = gf_isom_get_track_count(prog->mp4); + for (i=0; imp4, i+1) == GF_ISOM_MEDIA_HINT) + continue; + fill_isom_es_ifce(&prog->streams[i], prog->mp4, i+1); + switch(prog->streams[i].stream_type) { + case GF_STREAM_OD: + case GF_STREAM_SCENE: + prog->mpeg4_signaling = GF_M2TS_MPEG4_SIGNALING_FULL; + prog->streams[i].repeat_rate = carousel_rate; + break; + case GF_STREAM_VISUAL: + /*turn on image repeat*/ + switch (prog->streams[i].object_type_indication) { + case GPAC_OTI_IMAGE_JPEG: + case GPAC_OTI_IMAGE_PNG: + ((GF_ESIMP4 *)prog->streams[i].input_udta)->image_repeat_ms = carousel_rate; + break; + } + break; + default: + /*log not supported stream type: %s*/ + break; + } + prog->nb_streams++; + /*get first visual stream as PCR*/ + if (!prog->pcr_idx && + (gf_isom_get_media_type(prog->mp4, i+1) == GF_ISOM_MEDIA_VISUAL) && + (gf_isom_get_sample_count(prog->mp4, i+1)>1) ) { + prog->pcr_idx = i+1; + } + if (!first_audio && (gf_isom_get_media_type(prog->mp4, i+1) == GF_ISOM_MEDIA_AUDIO) ) { + first_audio = i+1; + } + } + /*if no visual PCR found, use first audio*/ + if (!prog->pcr_idx) prog->pcr_idx = first_audio; + if (prog->pcr_idx) { + GF_ESIMP4 *priv; + prog->pcr_idx-=1; + priv = prog->streams[prog->pcr_idx].input_udta; + gf_isom_set_default_sync_track(prog->mp4, priv->track); + } + prog->iod = gf_isom_get_root_od(prog->mp4); + if (!prog->iod) + fprintf(stderr, "NULL IOD for program !\n"); + return 1; + } + + /*open SDP file*/ + if (strstr(src, ".sdp")) { + GF_X_Attribute *att; + char *sdp_buf; + u32 sdp_size; + FILE *_sdp = fopen(src, "rt"); + if (!_sdp) { + fprintf(stderr, "Error opening %s - no such file\n", src); + return 0; + } + gf_f64_seek(_sdp, 0, SEEK_END); + sdp_size = (u32)gf_f64_tell(_sdp); + gf_f64_seek(_sdp, 0, SEEK_SET); + sdp_buf = (char*)gf_malloc(sizeof(char)*sdp_size); + memset(sdp_buf, 0, sizeof(char)*sdp_size); + sdp_size = fread(sdp_buf, 1, sdp_size, _sdp); + fclose(_sdp); + + sdp = gf_sdp_info_new(); + e = gf_sdp_info_parse(sdp, sdp_buf, sdp_size); + gf_free(sdp_buf); + if (e) { + fprintf(stderr, "Error opening %s : %s\n", src, gf_error_to_string(e)); + gf_sdp_info_del(sdp); + return 0; + } + + i=0; + while ((att = (GF_X_Attribute*)gf_list_enum(sdp->Attributes, &i))) { + char buf[2000]; + u32 size; + char *buf64; + u32 size64; + char *iod_str; + if (strcmp(att->Name, "mpeg4-iod") ) continue; + iod_str = att->Value + 1; + if (strnicmp(iod_str, "data:application/mpeg4-iod;base64", strlen("data:application/mpeg4-iod;base64"))) continue; + + buf64 = strstr(iod_str, ","); + if (!buf64) break; + buf64 += 1; + size64 = strlen(buf64) - 1; + size = gf_base64_decode(buf64, size64, buf, 2000); + + gf_odf_desc_read(buf, size, &prog->iod); + break; + } + + prog->nb_streams = gf_list_count(sdp->media_desc); + for (i=0; inb_streams; i++) { + GF_SDPMedia *media = gf_list_get(sdp->media_desc, i); + fill_rtp_es_ifce(&prog->streams[i], media, sdp); + switch(prog->streams[i].stream_type) { + case GF_STREAM_OD: + case GF_STREAM_SCENE: + prog->mpeg4_signaling = GF_M2TS_MPEG4_SIGNALING_FULL; + prog->streams[i].repeat_rate = carousel_rate; + break; + } + if (!prog->pcr_idx && (prog->streams[i].stream_type == GF_STREAM_VISUAL)) { + prog->pcr_idx = i+1; + } + } + + if (prog->pcr_idx) prog->pcr_idx-=1; + gf_sdp_info_del(sdp); + + return 2; + } + else if (strstr(src, ".bt")) //open .bt file + { + u32 load_type=0; + prog->seng = gf_seng_init(prog, src, load_type, NULL, (load_type == GF_SM_LOAD_DIMS) ? 1 : 0); + if (!prog->seng) { + fprintf(stderr, "Cannot create scene engine\n"); + exit(1); + } + else{ + fprintf(stderr, "Scene engine created.\n"); + } + assert( prog ); + assert( prog->seng); + prog->iod = gf_seng_get_iod(prog->seng); + if (! prog->iod){ + fprintf(stderr, __FILE__": No IOD\n"); + } + + prog->nb_streams = gf_seng_get_stream_count(prog->seng); + prog->rate = carousel_rate; + prog->mpeg4_signaling = GF_M2TS_MPEG4_SIGNALING_FULL; + + for (i=0; inb_streams; i++) { + fill_seng_es_ifce(&prog->streams[i], i, prog->seng, prog->rate); + //fprintf(stderr, "Fill interface\n"); + if (!prog->pcr_idx && (prog->streams[i].stream_type == GF_STREAM_AUDIO)) { + prog->pcr_idx = i+1; + } + } + + /*when an audio input is present, declare it and store OD + ESD_U*/ + if (audio_input_ip) { + /*add the audio program*/ + prog->pcr_idx = prog->nb_streams; + prog->streams[prog->nb_streams].stream_type = GF_STREAM_AUDIO; + /*hack: http urls are not decomposed therefore audio_input_port remains null*/ + if (audio_input_port) { /*UDP/RTP*/ + prog->streams[prog->nb_streams].object_type_indication = GPAC_OTI_AUDIO_MPEG1; + } else { /*HTTP*/ + aac_reader->oti = prog->streams[prog->nb_streams].object_type_indication = GPAC_OTI_AUDIO_AAC_MPEG4; + } + prog->streams[prog->nb_streams].input_ctrl = void_input_ctrl; + prog->streams[prog->nb_streams].stream_id = AUDIO_DATA_ESID; + prog->streams[prog->nb_streams].timescale = 1000; + + GF_SAFEALLOC(prog->streams[prog->nb_streams].input_udta, GF_ESIStream); + ((GF_ESIStream*)prog->streams[prog->nb_streams].input_udta)->vers_inc = 1; /*increment version number at every audio update*/ + assert( prog ); + //assert( prog->iod); + if (prog->iod && ((prog->iod->tag!=GF_ODF_IOD_TAG) || ((GF_InitialObjectDescriptor*)prog->iod)->OD_profileAndLevel!=GPAC_MAGIC_OD_PROFILE_FOR_MPEG4_SIGNALING)) { + /*create the descriptor*/ + GF_ESD *esd; + GF_SimpleDataDescriptor *audio_desc; + GF_SAFEALLOC(audio_desc, GF_SimpleDataDescriptor); + if (audio_input_port) { /*UDP/RTP*/ + esd = gf_odf_desc_esd_new(0); + esd->decoderConfig->streamType = prog->streams[prog->nb_streams].stream_type; + esd->decoderConfig->objectTypeIndication = prog->streams[prog->nb_streams].object_type_indication; + } else { /*HTTP*/ + esd = AAC_GetESD(aac_reader); /*in case of AAC, we have to wait the first ADTS chunk*/ + } + assert( esd ); + esd->ESID = prog->streams[prog->nb_streams].stream_id; + if (esd->slConfig->timestampResolution) /*in case of AAC, we have to wait the first ADTS chunk*/ + encode_audio_desc(esd, audio_desc); + else + gf_odf_desc_del((GF_Descriptor *)esd); + + /*find the audio OD stream and attach its descriptor*/ + for (i=0; inb_streams; i++) { + if (prog->streams[i].stream_id == AUDIO_OD_ESID) { + if (prog->streams[i].input_udta) + gf_free(prog->streams[i].input_udta); + prog->streams[i].input_udta = (void*)audio_desc; /*Hack: the real input_udta type (for our SampleCallBack function) is GF_ESIStream*/ + audio_OD_stream_id = i; + break; + } + } + if (audio_OD_stream_id == (u32)-1) { + fprintf(stderr, "Error: could not find an audio OD stream with ESID=100 in '%s'\n", src); + return 0; + } + } else { + prog->mpeg4_signaling = GF_M2TS_MPEG4_SIGNALING_SCENE; + } + prog->nb_streams++; + } + + /*when an audio input is present, declare it and store OD + ESD_U*/ + if (video_buffer) { + /*add the video program*/ + prog->streams[prog->nb_streams].stream_type = GF_STREAM_VISUAL; + prog->streams[prog->nb_streams].object_type_indication = GPAC_OTI_VIDEO_AVC; + prog->streams[prog->nb_streams].input_ctrl = void_input_ctrl; + prog->streams[prog->nb_streams].stream_id = VIDEO_DATA_ESID; + prog->streams[prog->nb_streams].timescale = 1000; + + GF_SAFEALLOC(prog->streams[prog->nb_streams].input_udta, GF_ESIStream); + ((GF_ESIStream*)prog->streams[prog->nb_streams].input_udta)->vers_inc = 1; /*increment version number at every video update*/ + assert(prog); + + if (prog->iod && ((prog->iod->tag!=GF_ODF_IOD_TAG) || ((GF_InitialObjectDescriptor*)prog->iod)->OD_profileAndLevel!=GPAC_MAGIC_OD_PROFILE_FOR_MPEG4_SIGNALING)) { + assert(0); /*TODO*/ +#if 0 + /*create the descriptor*/ + GF_ESD *esd; + GF_SimpleDataDescriptor *video_desc; + GF_SAFEALLOC(video_desc, GF_SimpleDataDescriptor); + esd = gf_odf_desc_esd_new(0); + esd->decoderConfig->streamType = prog->streams[prog->nb_streams].stream_type; + esd->decoderConfig->objectTypeIndication = prog->streams[prog->nb_streams].object_type_indication; + esd->ESID = prog->streams[prog->nb_streams].stream_id; + + /*find the audio OD stream and attach its descriptor*/ + for (i=0; inb_streams; i++) { + if (prog->streams[i].stream_id == 103/*TODO: VIDEO_OD_ESID*/) { + if (prog->streams[i].input_udta) + gf_free(prog->streams[i].input_udta); + prog->streams[i].input_udta = (void*)video_desc; + audio_OD_stream_id = i; + break; + } + } + if (audio_OD_stream_id == (u32)-1) { + fprintf(stderr, "Error: could not find an audio OD stream with ESID=100 in '%s'\n", src); + return 0; + } +#endif + } else { + assert (prog->mpeg4_signaling == GF_M2TS_MPEG4_SIGNALING_SCENE); + } + + prog->nb_streams++; + } + + if (!prog->pcr_idx) prog->pcr_idx=1; + prog->th = gf_th_new("Carousel"); + prog->src_name = update; + gf_th_run(prog->th, seng_output, prog); + return 1; + } else { + fprintf(stderr, "Error opening %s - not a supported input media, skipping.\n", src); + return 0; + } +} + +/*parse MP42TS arguments*/ +static GFINLINE GF_Err parse_args(int argc, char **argv, u32 *mux_rate, u32 *carrousel_rate, + M2TSProgram *progs, u32 *nb_progs, char **src_name, + 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) +{ + Bool rate_found=0, mpeg4_carousel_found=0, prog_found=0, mpeg4_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; + char *prog_name, *arg = NULL, *error_msg = "no argument found"; + Bool mpeg4_signaling = 0; + s32 i; + + /*first pass: find audio*/ + for (i=1; iiod = progs[i].iod; + + for (j=0; jdnload)) { + GF_ESD *esd; + aac_download_file(aac_reader, audio_input_ip); + esd = AAC_GetESD(aac_reader); + if (!esd) + break; + assert(esd->slConfig->timestampResolution); /*if we don't have this value we won't be able to adjust the timestamps within the MPEG2-TS*/ + if (esd->slConfig->timestampResolution) + audio_discontinuity_offset = gf_m2ts_get_sys_clock(muxer) * (u64)esd->slConfig->timestampResolution / 1000; + gf_odf_desc_del((GF_Descriptor *)esd); + } + break; + default: + assert(0); + } + } + + /*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); + if (segment_duration && (muxer->time.sec > prev_seg_time.sec + segment_duration)) { + prev_seg_time = muxer->time; + fclose(ts_output_file); + segment_index++; + if (segment_dir) { + if (strchr("\\/", segment_name[strlen(segment_name)-1])) { + sprintf(segment_name, "%s%s_%d.ts", segment_dir, segment_prefix, segment_index); + } else { + sprintf(segment_name, "%s/%s_%d.ts", segment_dir, segment_prefix, segment_index); + } + } else { + sprintf(segment_name, "%s_%d.ts", segment_prefix, segment_index); + } + ts_output_file = fopen(segment_name, "wb"); + if (!ts_output_file) { + fprintf(stderr, "Error opening %s\n", segment_name); + goto exit; + } + /* delete the oldest segment */ + if (segment_number && ((s32) (segment_index - segment_number - 1) >= 0)){ + char old_segment_name[GF_MAX_PATH]; + if (segment_dir) { + if (strchr("\\/", segment_name[strlen(segment_name)-1])) { + sprintf(old_segment_name, "%s%s_%d.ts", segment_dir, segment_prefix, segment_index - segment_number - 1); + } else { + sprintf(old_segment_name, "%s/%s_%d.ts", segment_dir, segment_prefix, segment_index - segment_number - 1); + } + } else { + sprintf(old_segment_name, "%s_%d.ts", segment_prefix, segment_index - segment_number - 1); + } + gf_delete_file(old_segment_name); + } + write_manifest(segment_manifest, segment_dir, segment_duration, segment_prefix, segment_http_prefix, +// (segment_index >= segment_number/2 ? segment_index - segment_number/2 : 0), segment_index >1 ? segment_index-1 : 0, 0); + ( (segment_index > segment_number ) ? segment_index - segment_number : 0), segment_index >1 ? segment_index-1 : 0, 0); + } + } + + if (ts_output_udp_sk != NULL) { + e = gf_sk_send(ts_output_udp_sk, (char*)ts_pck, 188); + if (e) { + fprintf(stderr, "Error %s sending UDP packet\n", gf_error_to_string(e)); + } + } + if (ts_output_rtp != NULL) { + hdr.SequenceNumber++; + /*muxer clock at 90k*/ + ts = muxer->time.sec*90000 + muxer->time.nanosec*9/100000; + /*FIXME - better discontinuity check*/ + hdr.Marker = (ts < hdr.TimeStamp) ? 1 : 0; + hdr.TimeStamp = ts; + e = gf_rtp_send_packet(ts_output_rtp, &hdr, (char*)ts_pck, 188, 0); + if (e) { + fprintf(stderr, "Error %s sending RTP packet\n", gf_error_to_string(e)); + } + } + if (status>=GF_M2TS_STATE_PADDING) { + break; + } + } + + /*push video*/ + { + u32 now=gf_sys_clock(); + if (now/MP42TS_VIDEO_FREQ != last_video_time/MP42TS_VIDEO_FREQ) { + /*should use carrousel behaviour instead of being pushed manually*/ + if (video_buffer) + SampleCallBack((void*)&progs[nb_progs-1], VIDEO_DATA_ESID, video_buffer, video_buffer_size, gf_m2ts_get_sys_clock(muxer)+1000/*try buffering due to VLC msg*/); + last_video_time = now; + } + } + + if (real_time) { + /*refresh every MP42TS_PRINT_FREQ ms*/ + u32 now=gf_sys_clock(); + if (now/MP42TS_PRINT_FREQ != last_print_time/MP42TS_PRINT_FREQ) { + last_print_time = now; + fprintf(stderr, "M2TS: time %d - TS time %d - avg bitrate %d\r", gf_m2ts_get_sys_clock(muxer), gf_m2ts_get_ts_clock(muxer), muxer->avg_br); + } + } + + /*cpu load regulation*/ + gf_sleep(1); + + if (run_time) { + if (gf_m2ts_get_ts_clock(muxer) > run_time) { + fprintf(stderr, "Stopping multiplex at %d ms (requested runtime %d ms)\n", gf_m2ts_get_ts_clock(muxer), run_time); + break; + } + } + if (status==GF_M2TS_STATE_EOS) { + break; + } + } + + +exit: + run = 0; + if (segment_duration) { + write_manifest(segment_manifest, segment_dir, segment_duration, segment_prefix, segment_http_prefix, segment_index - segment_number, segment_index, 1); + } + if (ts_output_file) fclose(ts_output_file); + if (ts_output_udp_sk) gf_sk_del(ts_output_udp_sk); + if (ts_output_rtp) gf_rtp_del(ts_output_rtp); + if (ts_out) gf_free(ts_out); + if (audio_input_udp_sk) gf_sk_del(audio_input_udp_sk); + if (audio_input_buffer) gf_free (audio_input_buffer); + if (video_buffer) gf_free(video_buffer); + if (udp_out) gf_free(udp_out); + if (rtp_out) gf_free(rtp_out); + if (aac_reader) AAC_Reader_del(aac_reader); + if (muxer) gf_m2ts_mux_del(muxer); + + for (i=0; i +# 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.h b/applications/mp42ts/mp42ts.h new file mode 100644 index 0000000..5ed59a1 --- /dev/null +++ b/applications/mp42ts/mp42ts.h @@ -0,0 +1,201 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) ENST 2000-200X + * All rights reserved + * + * This file is part of GPAC / mp42ts application + * + * 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 __m2ts_mux_program M2TS_Mux_Program; +typedef struct __m2ts_mux M2TS_Mux; + +enum { + LOG_NO_LOG = 0, + LOG_PES = 1, + LOG_SECTION = 2, + LOG_TS = 3 +}; + + +typedef struct __m2ts_section { + struct __m2ts_section *next; + u8 *data; + u32 length; +} M2TS_Mux_Section; + +typedef struct __m2ts_table { + struct __m2ts_table *next; + u8 table_id; + u8 version_number; + struct __m2ts_section *section; +} M2TS_Mux_Table; + +typedef struct +{ + u32 sec; + u32 nanosec; +} M2TS_Time; + + +typedef struct __m2ts_mux_pck +{ + struct __m2ts_mux_pck *next; + char *data; + u32 data_len; + u32 flags; + u64 cts, dts; +} M2TS_Packet; + + +typedef struct __m2ts_mux_stream { + struct __m2ts_mux_stream *next; + + u32 pid; + u8 continuity_counter; + struct __m2ts_mux_program *program; + + /*average stream bit-rate in bit/sec*/ + u32 bit_rate; + + /*multiplexer time - NOT THE PCR*/ + M2TS_Time time; + + + /* MPEG-4 SL Config */ + GF_SLConfig sl_config; + + /*table tools*/ + M2TS_Mux_Table *tables; + /*total table sizes for bitrate estimation (PMT/PAT/...)*/ + u32 total_table_size; + /* used for on-the-fly packetization of sections */ + M2TS_Mux_Table *current_table; + M2TS_Mux_Section *current_section; + u32 current_section_offset; + u32 refresh_rate_ms; + Bool table_needs_update; + + Bool (*process)(struct __m2ts_mux *muxer, struct __m2ts_mux_stream *stream); + + /*PES tools*/ + void *pes_packetizer; + u32 mpeg2_stream_type; + u32 mpeg2_stream_id; + + GF_ESIPacket pck; + u32 pck_offset; + Bool force_new; + + struct __elementary_stream_ifce *ifce; + Double ts_scale; + u64 initial_ts; + + /*packet fifo*/ + M2TS_Packet *pck_first, *pck_last; + /*packet reassembler (PES packets are most of the time full frames)*/ + M2TS_Packet *pck_reassembler; + GF_Mutex *mx; + /*avg bitrate compute*/ + u64 last_br_time; + u32 bytes_since_last_time; + + /*MPEG-4 over MPEG-2*/ + u8 table_id; + GF_SLHeader sl_header; + //GF_SLConfig sl_config; + + u32 last_aac_time; +} M2TS_Mux_Stream; + + +struct __m2ts_mux_program { + struct __m2ts_mux_program *next; + + struct __m2ts_mux *mux; + u16 number; + /*all streams but PMT*/ + M2TS_Mux_Stream *streams; + /*PMT*/ + M2TS_Mux_Stream *pmt; + /*pointer to PCR stream*/ + M2TS_Mux_Stream *pcr; + + /*TS time at pcr init*/ + M2TS_Time ts_time_at_pcr_init; + u64 pcr_init_time, num_pck_at_pcr_init; + u64 last_pcr; + u32 last_sys_clock; + + GF_Descriptor *iod; +}; + +struct __m2ts_mux { + M2TS_Mux_Program *programs; + M2TS_Mux_Stream *pat; + + u16 ts_id; + + Bool needs_reconfig; + + /* used to indicate that the input data is pushed to the muxer (i.e. not read from a file) + or that the output data is sent on sockets (not written to a file) */ + Bool real_time; + + /* indicates if the multiplexer shall target a fix bit rate (monitoring timing and produce padding packets) + or if the output stream will contain only input data*/ + Bool fixed_rate; + + /*output bit-rate in bit/sec*/ + u32 bit_rate; + + char dst_pck[188], null_pck[188]; + + /*multiplexer time, incremented each time a packet is sent + used to monitor the sending of muxer related data (PAT, ...) */ + M2TS_Time time; + + /* Time of the muxer when the first call to process is made (first packet sent?) */ + M2TS_Time init_ts_time; + + /* System time when the muxer is started */ + u32 init_sys_time; + + Bool eos_found; + u32 pck_sent_over_br_window, last_br_time, avg_br; + u64 tot_pck_sent, tot_pad_sent; + + Bool mpeg4_signaling; +}; + + +enum +{ + GF_M2TS_STATE_IDLE, + GF_M2TS_STATE_DATA, + GF_M2TS_STATE_PADDING, + GF_M2TS_STATE_EOS, +}; + diff --git a/applications/mp42ts/mp42ts.vcproj b/applications/mp42ts/mp42ts.vcproj new file mode 100644 index 0000000..7702b04 --- /dev/null +++ b/applications/mp42ts/mp42ts.vcproj @@ -0,0 +1,231 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/applications/mp4box/Makefile b/applications/mp4box/Makefile index 2cce51a..ded9d7b 100644 --- a/applications/mp4box/Makefile +++ b/applications/mp4box/Makefile @@ -2,7 +2,7 @@ include ../../config.mak vpath %.c $(SRC_PATH)/applications/mp4box -CFLAGS= $(OPTFLAGS) -I$(SRC_PATH)/include +CFLAGS= $(OPTFLAGS) -I"$(SRC_PATH)/include" ifeq ($(DEBUGBUILD), yes) CFLAGS+=-g @@ -43,6 +43,17 @@ EXT= PROG=MP4Box ifeq ($(MP4BOX_STATIC),yes) LINKFLAGS+=-lgpac_static $(EXTRALIBS) $(GPAC_SH_FLAGS) -lz + +# spidermonkey support +ifeq ($(CONFIG_JS),no) +else +SCENEGRAPH_CFLAGS+=$(JS_FLAGS) +ifeq ($(CONFIG_JS),local) +NEED_LOCAL_LIB="yes" +endif +LINKFLAGS+=$(JS_LIBS) +endif + else LINKFLAGS+=-lgpac endif diff --git a/applications/mp4box/filedump.c b/applications/mp4box/filedump.c index bd9df65..0c09810 100644 --- a/applications/mp4box/filedump.c +++ b/applications/mp4box/filedump.c @@ -1064,8 +1064,9 @@ static void DumpMetaItem(GF_ISOFile *file, Bool root_meta, u32 tk_num, char *nam void DumpTrackInfo(GF_ISOFile *file, u32 trackID, Bool full_dump) { Float scale; - u32 trackNum, i, j, size, max_rate, rate, ts, mtype, msub_type, timescale, sr, nb_ch, count, alt_group, nb_groups; - u64 time_slice, dur; + Bool is_od_track = 0; + u32 trackNum, i, j, max_rate, rate, ts, mtype, msub_type, timescale, sr, nb_ch, count, alt_group, nb_groups; + u64 time_slice, dur, size; u8 bps; GF_ESD *esd; char sType[5], szDur[50]; @@ -1100,6 +1101,13 @@ void DumpTrackInfo(GF_ISOFile *file, u32 trackID, Bool full_dump) fprintf(stdout, "Handler name: %s\n", handler_name); } + if (mtype==GF_ISOM_MEDIA_VISUAL) { + s32 tx, ty; + u32 w, h; + gf_isom_get_track_layout_info(file, trackNum, &w, &h, &tx, &ty, NULL); + fprintf(stdout, "Visual Track layout: x=%d y=%d width=%d height=%d\n", tx, ty, w, h); + } + gf_isom_get_audio_info(file, trackNum, 1, &sr, &nb_ch, &bps); msub_type = gf_isom_get_media_subtype(file, trackNum, 1); @@ -1122,6 +1130,9 @@ void DumpTrackInfo(GF_ISOFile *file, u32 trackID, Bool full_dump) fprintf(stdout, "MPEG-4 Config%sStream Type 0x%02x - ObjectTypeIndication 0x%02x\n", full_dump ? "\n\t" : ": ", esd->decoderConfig->streamType, esd->decoderConfig->objectTypeIndication); } + if (esd->decoderConfig->streamType==GF_STREAM_OD) + is_od_track=1; + if (esd->decoderConfig->streamType==GF_STREAM_VISUAL) { u32 w, h; w = h = 0; @@ -1152,36 +1163,41 @@ void DumpTrackInfo(GF_ISOFile *file, u32 trackID, Bool full_dump) gf_isom_get_visual_info(file, trackNum, 1, &w, &h); if (full_dump) fprintf(stdout, "\t"); - fprintf(stdout, "AVC/H264 Video - Visual Size %d x %d", w, h); + fprintf(stdout, "AVC/H264 Video - Visual Size %d x %d\n", w, h); #ifndef GPAC_DISABLE_AV_PARSERS avccfg = gf_isom_avc_config_get(file, trackNum, 1); svccfg = gf_isom_svc_config_get(file, trackNum, 1); if (!avccfg && !svccfg) { fprintf(stdout, "\n\n\tNon-compliant AVC track: SPS/PPS not found in sample description\n"); } else if (avccfg) { + fprintf(stdout, "\tAVC Info: %d SPS - %d PPS", gf_list_count(avccfg->sequenceParameterSets) , gf_list_count(avccfg->pictureParameterSets) ); fprintf(stdout, " - Profile %s @ Level %g\n", gf_avc_get_profile_name(avccfg->AVCProfileIndication), ((Double)avccfg->AVCLevelIndication)/10.0 ); - fprintf(stdout, "NAL Unit length bits: %d\n", 8*avccfg->nal_unit_size); + fprintf(stdout, "\tNAL Unit length bits: %d\n", 8*avccfg->nal_unit_size); slc = gf_list_get(avccfg->sequenceParameterSets, 0); if (slc) { - gf_avc_get_sps_info(slc->data, slc->size, NULL, NULL, &par_n, &par_d); + gf_avc_get_sps_info(slc->data, slc->size, NULL, NULL, NULL, &par_n, &par_d); if ((par_n>0) && (par_d>0)) { u32 tw, th; gf_isom_get_track_layout_info(file, trackNum, &tw, &th, NULL, NULL, NULL); - fprintf(stdout, "Pixel Aspect Ratio %d:%d - Indicated track size %d x %d\n", par_n, par_d, tw, th); + fprintf(stdout, "\tPixel Aspect Ratio %d:%d - Indicated track size %d x %d\n", par_n, par_d, tw, th); } } gf_odf_avc_cfg_del(avccfg); } if (svccfg) { - fprintf(stdout, "\nSVC Profile %s @ Level %g\n", gf_avc_get_profile_name(svccfg->AVCProfileIndication), ((Double)svccfg->AVCLevelIndication)/10.0 ); - fprintf(stdout, "SVC NAL Unit length bits: %d\n", 8*svccfg->nal_unit_size); - slc = gf_list_get(svccfg->sequenceParameterSets, 0); - if (slc) { - gf_avc_get_sps_info(slc->data, slc->size, NULL, NULL, &par_n, &par_d); - if ((par_n>0) && (par_d>0)) { - u32 tw, th; - gf_isom_get_track_layout_info(file, trackNum, &tw, &th, NULL, NULL, NULL); - fprintf(stdout, "Pixel Aspect Ratio %d:%d - Indicated track size %d x %d\n", par_n, par_d, tw, th); + fprintf(stdout, "\n\tSVC Info: %d SPS - %d PPS - Profile %s @ Level %g\n", gf_list_count(svccfg->sequenceParameterSets) , gf_list_count(svccfg->pictureParameterSets), gf_avc_get_profile_name(svccfg->AVCProfileIndication), ((Double)svccfg->AVCLevelIndication)/10.0 ); + fprintf(stdout, "\tSVC NAL Unit length bits: %d\n", 8*svccfg->nal_unit_size); + for (i=0; isequenceParameterSets); i++) { + slc = gf_list_get(svccfg->sequenceParameterSets, i); + if (slc) { + u32 s_w, s_h, sps_id; + gf_avc_get_sps_info(slc->data, slc->size, &sps_id, &s_w, &s_h, &par_n, &par_d); + fprintf(stdout, "\t\tSSPS ID %d - Visual Size %d x %d\n", sps_id, s_w, s_h); + if ((par_n>0) && (par_d>0)) { + u32 tw, th; + gf_isom_get_track_layout_info(file, trackNum, &tw, &th, NULL, NULL, NULL); + fprintf(stdout, "\tPixel Aspect Ratio %d:%d - Indicated track size %d x %d\n", par_n, par_d, tw, th); + } } } gf_odf_avc_cfg_del(svccfg); @@ -1197,6 +1213,18 @@ void DumpTrackInfo(GF_ISOFile *file, u32 trackID, Bool full_dump) else szName = "Unknown"; fprintf(stdout, "Ogg/%s video / GPAC Mux - Visual Size %d x %d\n", szName, w, h); } + else if (esd->decoderConfig->objectTypeIndication==GPAC_OTI_IMAGE_JPEG) { + gf_isom_get_visual_info(file, trackNum, 1, &w, &h); + fprintf(stdout, "JPEG Stream - Visual Size %d x %d\n", w, h); + } + else if (esd->decoderConfig->objectTypeIndication==GPAC_OTI_IMAGE_PNG) { + gf_isom_get_visual_info(file, trackNum, 1, &w, &h); + fprintf(stdout, "PNG Stream - Visual Size %d x %d\n", w, h); + } + else if (esd->decoderConfig->objectTypeIndication==GPAC_OTI_IMAGE_JPEG_2000) { + gf_isom_get_visual_info(file, trackNum, 1, &w, &h); + fprintf(stdout, "JPEG2000 Stream - Visual Size %d x %d\n", w, h); + } if (!w || !h) { gf_isom_get_visual_info(file, trackNum, 1, &w, &h); if (full_dump) fprintf(stdout, "\t"); @@ -1216,7 +1244,10 @@ void DumpTrackInfo(GF_ISOFile *file, u32 trackID, Bool full_dump) is_mp2 = 1; case GPAC_OTI_AUDIO_AAC_MPEG4: #ifndef GPAC_DISABLE_AV_PARSERS - e = gf_m4a_get_config(esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength, &a_cfg); + if (!esd->decoderConfig->decoderSpecificInfo) + e = GF_NON_COMPLIANT_BITSTREAM; + else + e = gf_m4a_get_config(esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength, &a_cfg); if (full_dump) fprintf(stdout, "\t"); if (e) fprintf(stdout, "Corrupted AAC Config\n"); else { @@ -1236,15 +1267,19 @@ void DumpTrackInfo(GF_ISOFile *file, u32 trackID, Bool full_dump) } else { #ifndef GPAC_DISABLE_AV_PARSERS GF_ISOSample *samp = gf_isom_get_sample(file, trackNum, 1, &oti); - oti = GF_4CC((u8)samp->data[0], (u8)samp->data[1], (u8)samp->data[2], (u8)samp->data[3]); - if (full_dump) fprintf(stdout, "\t"); - fprintf(stdout, "%s Audio - %d Channel(s) - SampleRate %d - Layer %d\n", - gf_mp3_version_name(oti), - gf_mp3_num_channels(oti), - gf_mp3_sampling_rate(oti), - gf_mp3_layer(oti) - ); - gf_isom_sample_del(&samp); + if (samp) { + oti = GF_4CC((u8)samp->data[0], (u8)samp->data[1], (u8)samp->data[2], (u8)samp->data[3]); + if (full_dump) fprintf(stdout, "\t"); + fprintf(stdout, "%s Audio - %d Channel(s) - SampleRate %d - Layer %d\n", + gf_mp3_version_name(oti), + gf_mp3_num_channels(oti), + gf_mp3_sampling_rate(oti), + gf_mp3_layer(oti) + ); + gf_isom_sample_del(&samp); + } else { + fprintf(stdout, "\n\tError fetching sample: %s\n", gf_error_to_string(gf_isom_last_error(file)) ); + } #else fprintf(stdout, "MPEG-1/2 Audio - %d Channels - SampleRate %d\n", nb_ch, sr); #endif @@ -1276,17 +1311,29 @@ void DumpTrackInfo(GF_ISOFile *file, u32 trackID, Bool full_dump) } } else if (esd->decoderConfig->streamType==GF_STREAM_SCENE) { - if (esd->decoderConfig->objectTypeIndication<=6) { + if (esd->decoderConfig->objectTypeIndication<=4) { GF_BIFSConfig *b_cfg = gf_odf_get_bifs_config(esd->decoderConfig->decoderSpecificInfo, esd->decoderConfig->objectTypeIndication); fprintf(stdout, "BIFS Scene description - %s stream\n", b_cfg->elementaryMasks ? "Animation" : "Command"); if (full_dump && !b_cfg->elementaryMasks) { fprintf(stdout, "\tWidth %d Height %d Pixel Metrics %s\n", b_cfg->pixelWidth, b_cfg->pixelHeight, b_cfg->pixelMetrics ? "yes" : "no"); } gf_odf_desc_del((GF_Descriptor *)b_cfg); + } else if (esd->decoderConfig->objectTypeIndication==GPAC_OTI_SCENE_AFX) { + u8 tag = esd->decoderConfig->decoderSpecificInfo ? esd->decoderConfig->decoderSpecificInfo->data[0] : 0xFF; + const char *afxtype = gf_afx_get_type_description(tag); + fprintf(stdout, "AFX Stream - type %s (%d)\n", afxtype, tag); + } else if (esd->decoderConfig->objectTypeIndication==GPAC_OTI_FONT) { + fprintf(stdout, "Font Data stream\n"); } else if (esd->decoderConfig->objectTypeIndication==GPAC_OTI_SCENE_LASER) { GF_LASERConfig l_cfg; gf_odf_get_laser_config(esd->decoderConfig->decoderSpecificInfo, &l_cfg); fprintf(stdout, "LASER Stream - %s\n", l_cfg.newSceneIndicator ? "Full Scene" : "Scene Segment"); + } else if (esd->decoderConfig->objectTypeIndication==GPAC_OTI_TEXT_MPEG4) { + fprintf(stdout, "MPEG-4 Streaming Text stream\n"); + } else if (esd->decoderConfig->objectTypeIndication==GPAC_OTI_SCENE_SYNTHESIZED_TEXTURE) { + fprintf(stdout, "Synthetized Texture stream stream\n"); + } else { + fprintf(stdout, "Unknown Systems stream OTI %d\n", esd->decoderConfig->objectTypeIndication); } } @@ -1495,7 +1542,12 @@ void DumpTrackInfo(GF_ISOFile *file, u32 trackID, Bool full_dump) time_slice = 0; ts = gf_isom_get_media_timescale(file, trackNum); for (j=0; jDTS+samp->CTS_Offset; size += samp->dataLength; rate += samp->dataLength; @@ -1510,12 +1562,13 @@ void DumpTrackInfo(GF_ISOFile *file, u32 trackID, Bool full_dump) scale = 1000; scale /= ts; dur = (u64) (scale * (s64)dur); - fprintf(stdout, "\tTotal size %d bytes - Total samples duration %d ms\n", size, (u32) dur); + fprintf(stdout, "\tTotal size "LLU" bytes - Total samples duration "LLU" ms\n", size, dur); if (!dur) { fprintf(stdout, "\n"); return; } - rate = (u32) (size * 8 / (dur/1000)); + /*rate in byte, dur is in ms*/ + rate = (u32) ((size * 8 * 1000) / dur); max_rate *= 8; if (rate >= 1500) { rate /= 1000; @@ -1609,14 +1662,15 @@ void DumpMovieInfo(GF_ISOFile *file) iod = (GF_InitialObjectDescriptor *) gf_isom_get_root_od(file); if (iod) { + u32 desc_size = gf_odf_desc_size((GF_Descriptor *)iod); if (iod->tag == GF_ODF_IOD_TAG) { - fprintf(stdout, "File has root IOD\n"); + fprintf(stdout, "File has root IOD (%d bytes)\n", desc_size); fprintf(stdout, "Scene PL 0x%02x - Graphics PL 0x%02x - OD PL 0x%02x\n", iod->scene_profileAndLevel, iod->graphics_profileAndLevel, iod->OD_profileAndLevel); fprintf(stdout, "Visual PL: %s (0x%02x)\n", gf_m4v_get_profile_name(iod->visual_profileAndLevel), iod->visual_profileAndLevel); fprintf(stdout, "Audio PL: %s (0x%02x)\n", gf_m4a_get_profile_name(iod->audio_profileAndLevel), iod->audio_profileAndLevel); //fprintf(stdout, "inline profiles included %s\n", iod->inlineProfileFlag ? "yes" : "no"); } else { - fprintf(stdout, "File has root OD\n"); + fprintf(stdout, "File has root OD (%d bytes)\n", desc_size); } if (!gf_list_count(iod->ESDescriptors)) fprintf(stdout, "No streams included in root OD\n"); gf_odf_desc_del((GF_Descriptor *) iod); @@ -1666,7 +1720,7 @@ void DumpMovieInfo(GF_ISOFile *file) } } if (gf_isom_apple_get_tag(file, GF_ISOM_ITUNE_COMPILATION, &tag, &tag_len)==GF_OK) fprintf(stdout, "\tCompilation: %s\n", tag[0] ? "Yes" : "No"); - if (gf_isom_apple_get_tag(file, GF_ISOM_ITUNE_GAPELESS, &tag, &tag_len)==GF_OK) fprintf(stdout, "\tGapeless album: %s\n", tag[0] ? "Yes" : "No"); + if (gf_isom_apple_get_tag(file, GF_ISOM_ITUNE_GAPLESS, &tag, &tag_len)==GF_OK) fprintf(stdout, "\tGapless album: %s\n", tag[0] ? "Yes" : "No"); if (gf_isom_apple_get_tag(file, GF_ISOM_ITUNE_CREATED, &tag, &tag_len)==GF_OK) fprintf(stdout, "\tCreated: %s\n", tag); if (gf_isom_apple_get_tag(file, GF_ISOM_ITUNE_DISK, &tag, &tag_len)==GF_OK) fprintf(stdout, "\tDisk: %d / %d\n", tag[3], tag[5]); @@ -1699,6 +1753,95 @@ void DumpMovieInfo(GF_ISOFile *file) #ifndef GPAC_DISABLE_MPEG2TS +#include + +typedef struct +{ + Bool start_indexing; + + /* For indexing the TS*/ + Double segment_duration; + Bool segment_at_rap; + u32 subsegs_per_segment; + char *seg_name; + Bool use_url_template; + char *init_seg_name; + Bool use_index_segment; + + FILE *index_file; + char index_file_name[100]; + GF_BitStream *index_bs; + + char mpd_file_name[100]; + FILE *mpd_file; + /* temporary file to store the MPD segment description before writing the header */ + FILE *mpd_segs; + + u32 reference_pid; + GF_M2TS_PES *reference_stream; + + /* earliest presentation time for the whole segment */ + u64 first_PTS; + + /* earliest presentation time for the subsegment being processed */ + u64 base_PTS; + /* byte offset for the start of subsegment being processed */ + u32 base_offset; + /* last presentation time for the subsegment being processed (before the next subsegment is started) */ + u64 last_PTS; + /* byte offset for the last PES packet for the subsegment being processed */ + u32 last_offset; + + /* earliest presentation time for the previous subsegment */ + u64 prev_base_PTS; + /* byte offset for the start of the previous subsegment */ + u32 prev_base_offset; + /* last presentation time for the previous subsegment */ + u64 prev_last_PTS; + /* 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; + /* Presentation time for the first RAP encountered in the subsegment */ + u64 first_RAP_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; + + /* information about the first PAT found in the subsegment */ + u32 last_pat_position; + u32 first_pat_position; + u32 prev_last_pat_position; + Bool first_pat_position_valid; + u32 pat_version; + + /* information about the first CAT found in the subsegment */ + u32 last_cat_position; + u32 first_cat_position; + u32 prev_last_cat_position; + Bool first_cat_position_valid; + u32 cat_version; + + /* information about the first PMT found in the subsegment */ + u32 last_pmt_position; + u32 first_pmt_position; + u32 prev_last_pmt_position; + Bool first_pmt_position_valid; + u32 pmt_version; + + /* information about the first PCR found in the subsegment */ + u32 last_pcr_position; + u32 first_pcr_position; + Bool first_pcr_position_valid; + u32 prev_last_pcr_position; + + //GF_List *sidxs; + GF_SegmentIndexBox *sidx; +} GF_M2TS_IndexingInfo; typedef struct { @@ -1710,50 +1853,348 @@ typedef struct FILE *pes_out_info; char info[100]; Bool is_info_dumped; + + u32 prog_number; + /* For logging timing information (PCR, PTS/DTS) */ + FILE *timestamps_info_file; + char timestamps_info_name[100]; /* when dumping TS information */ u32 dump_pid; Bool has_seen_pat; + + GF_M2TS_IndexingInfo index_info; + } GF_M2TS_Dump; +/* Initializes an SIDX */ +static GF_SegmentIndexBox *m2ts_sidx_new(u32 pid, u64 PTS, u64 position) +{ + GF_SegmentIndexBox *sidx = (GF_SegmentIndexBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_SIDX); + sidx->reference_ID = pid; + /* timestamps in MPEG-2 are expressed in 90 kHz timescale */ + sidx->timescale = 90000; + /* first encountered PTS on the PID for this subsegment */ + sidx->earliest_presentation_time = PTS; + sidx->first_offset = 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) +{ + 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_offset = size; + ref->subsegment_duration = duration; + ref->RAP_delta_time = (has_rap ? RAP_delta_time: 0); +} + +static void m2ts_sidx_update_prev_entry_duration(GF_SegmentIndexBox *sidx, u32 duration) +{ + GF_SIDXReference *ref; + if (sidx->nb_refs == 0) return; + ref = &(sidx->refs[sidx->nb_refs-1]); + ref->subsegment_duration = duration; +} + +static void m2ts_sidx_finalize_size(GF_M2TS_IndexingInfo *index_info, u64 file_size) +{ + GF_SIDXReference *ref; + if (index_info->sidx->nb_refs == 0) return; + ref = &(index_info->sidx->refs[index_info->sidx->nb_refs-1]); + ref->reference_offset = (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_offset); +} + +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 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); + index_info->sidx = m2ts_sidx_new(index_info->reference_pid, index_info->base_PTS, index_info->base_offset); + } + + /* determine the end of the current index */ + if (index_info->segment_at_rap) { + /*split at PAT*/ + end_offset = index_info->last_pat_position; + } else { + /* split at PES header */ + end_offset = index_info->last_offset; + } + + /* 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); + + /* 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", + (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); + } + + /* Printing result */ + fprintf(stderr, "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, ", + (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); + } + if (index_info->first_pat_position_valid) { + fprintf(stderr, ", PAT @ %d bytes", (u32)(index_info->first_pat_position - index_info->base_offset)); + } else { + fprintf(stderr, ", No PAT"); + } + if (index_info->first_cat_position_valid) { + fprintf(stderr, ", CAT @ %d bytes", (u32)(index_info->first_cat_position - index_info->base_offset)); + } else { + fprintf(stderr, ", No CAT"); + } + if (index_info->first_pmt_position_valid) { + fprintf(stderr, ", PMT @ %d bytes", (u32)(index_info->first_pmt_position - index_info->base_offset)); + } else { + fprintf(stderr, ", No PMT"); + } + if (index_info->first_pcr_position_valid) { + fprintf(stderr, ", PCR @ %d bytes", (u32)(index_info->first_pcr_position - index_info->base_offset)); + } else { + fprintf(stderr, ", No PCR"); + } + fprintf(stderr, "\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_PTS = index_info->last_PTS; + index_info->prev_last_offset = index_info->last_offset; + index_info->prev_base_PTS = index_info->base_PTS; + index_info->base_PTS = index_info->last_PTS; + index_info->prev_base_offset = index_info->base_offset; + index_info->prev_last_pat_position = index_info->last_pat_position; + index_info->prev_last_cat_position = index_info->last_cat_position; + index_info->prev_last_pmt_position = index_info->last_pmt_position; + index_info->prev_last_pcr_position = index_info->last_pcr_position; + + /* 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; + 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; + } else { + index_info->first_pat_position_valid = 0; + index_info->first_pat_position = 0; + } + if (index_info->last_cat_position >= index_info->base_offset) { + index_info->first_cat_position_valid = 1; + index_info->first_cat_position = index_info->last_cat_position; + } else { + index_info->first_cat_position_valid = 0; + index_info->first_cat_position = 0; + } + if (index_info->last_pmt_position >= index_info->base_offset) { + index_info->first_pmt_position_valid = 1; + index_info->first_pmt_position = index_info->last_pmt_position; + } else { + index_info->first_pmt_position_valid = 0; + index_info->first_pmt_position = 0; + } + if (index_info->last_pcr_position >= index_info->base_offset) { + index_info->first_pcr_position_valid = 1; + index_info->first_pcr_position = index_info->last_pcr_position; + } else { + index_info->first_pcr_position_valid = 0; + index_info->first_pcr_position = 0; + } +} + +static void m2ts_check_indexing(GF_M2TS_IndexingInfo *index_info) +{ + u32 delta_time = (u32)(index_info->last_PTS - index_info->base_PTS); + u32 segment_duration = (u32)(index_info->segment_duration*90000); + /* we need to create an SIDX entry when the duration of the previous entry is too big */ + if (delta_time >= segment_duration) { + m2ts_sidx_flush_entry(index_info); + } +} + static void on_m2ts_dump_event(GF_M2TS_Demuxer *ts, u32 evt_type, void *par) { u32 i, count; GF_M2TS_Program *prog; GF_M2TS_PES_PCK *pck; GF_M2TS_Dump *dumper = (GF_M2TS_Dump *)ts->user; + GF_M2TS_IndexingInfo *index_info = &dumper->index_info; 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; + index_info->first_pat_position = (ts->pck_number-1)*188; + } + index_info->last_pat_position = (ts->pck_number-1)*188; + } + if (dumper->timestamps_info_file) { + fprintf(dumper->timestamps_info_file, "%u\t%d\n", ts->pck_number, 0); + } 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; + index_info->first_pat_position = (ts->pck_number-1)*188; + } + index_info->last_pat_position = (ts->pck_number-1)*188; + } + if (dumper->timestamps_info_file) { + fprintf(dumper->timestamps_info_file, "%u\t%d\n", ts->pck_number, 0); + } break; case GF_M2TS_EVT_PAT_REPEAT: + /* WARNING: We detect the pat on a repetition, probably to ensure that we also have seen all the PMT + To be checked */ dumper->has_seen_pat = 1; + if (index_info->start_indexing) { + if (!index_info->first_pat_position_valid) { + index_info->first_pat_position_valid = 1; + index_info->first_pat_position = (ts->pck_number-1)*188; + } + index_info->last_pat_position = (ts->pck_number-1)*188; + } + if (dumper->timestamps_info_file) { + fprintf(dumper->timestamps_info_file, "%u\t%d\n", ts->pck_number, 0); + } // 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; + index_info->first_cat_position = (ts->pck_number-1)*188; + } + index_info->last_cat_position = (ts->pck_number-1)*188; + } + if (dumper->timestamps_info_file) { + fprintf(dumper->timestamps_info_file, "%u\t%d\n", ts->pck_number, 0); + } + 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; + index_info->first_cat_position = (ts->pck_number-1)*188; + } + index_info->last_cat_position = (ts->pck_number-1)*188; + } + if (dumper->timestamps_info_file) { + fprintf(dumper->timestamps_info_file, "%u\t%d\n", ts->pck_number, 0); + } + break; + case GF_M2TS_EVT_CAT_REPEAT: + if (index_info->start_indexing) { + if (!index_info->first_cat_position_valid) { + index_info->first_cat_position_valid = 1; + index_info->first_cat_position = (ts->pck_number-1)*188; + } + index_info->last_cat_position = (ts->pck_number-1)*188; + } + if (dumper->timestamps_info_file) { + fprintf(dumper->timestamps_info_file, "%u\t%d\n", ts->pck_number, 0); + } + break; case GF_M2TS_EVT_PMT_FOUND: prog = (GF_M2TS_Program*)par; + if (prog->number != dumper->prog_number) break; + if (index_info->start_indexing) { + if (!index_info->first_pmt_position_valid) { + index_info->first_pmt_position_valid = 1; + index_info->first_pmt_position = (ts->pck_number-1)*188; + } + 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); for (i=0; istreams, i); - if (es->pid == prog->pmt_pid) fprintf(stdout, "\tPID %d: Program Map Table\n", es->pid); - else { + if (es->pid == prog->pmt_pid) { + fprintf(stdout, "\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"); + } + if (es->pid == prog->pcr_pid) { + /* we create indexing information on the stream used for carrying the PCR */ + index_info->reference_pid = prog->pcr_pid; + index_info->reference_stream = (GF_M2TS_PES *)es; } } + 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_PMT_UPDATE: + prog = (GF_M2TS_Program*)par; + if (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; + index_info->first_pmt_position = (ts->pck_number-1)*188; + } + index_info->last_pmt_position = (ts->pck_number-1)*188; + } + 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_PMT_REPEAT: + prog = (GF_M2TS_Program*)par; + if (prog->number != dumper->prog_number) break; + if (index_info->start_indexing) { + if (!index_info->first_pmt_position_valid) { + index_info->first_pmt_position_valid = 1; + index_info->first_pmt_position = (ts->pck_number-1)*188; + } + 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) ; @@ -1773,14 +2214,86 @@ static void on_m2ts_dump_event(GF_M2TS_Demuxer *ts, u32 evt_type, void *par) 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; + if (pck->stream->program->number != dumper->prog_number) break; + break; case GF_M2TS_EVT_PES_PCK: pck = par; - //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); + if (pck->stream->program->number != dumper->prog_number) break; + if (dumper->has_seen_pat) { + if (dumper->timestamps_info_file) { + GF_M2TS_PES *pes = pck->stream; + /*FIXME : not used GF_M2TS_Program *prog = pes->program; */ + /* Interpolated PCR value for the TS packet containing the PES header start */ + u64 interpolated_pcr_value = 0; + if (pes->last_pcr_value && pes->before_last_pcr_value && pes->last_pcr_value > pes->before_last_pcr_value) { + u32 delta_pcr_pck_num = pes->last_pcr_value_pck_number - pes->before_last_pcr_value_pck_number; + u32 delta_pts_pcr_pck_num = pes->pes_start_packet_number - pes->last_pcr_value_pck_number; + u64 delta_pcr_value = pes->last_pcr_value - pes->before_last_pcr_value; + /* we can compute the interpolated pcr value for the packet containing the PES header */ + interpolated_pcr_value = pes->last_pcr_value + (u64)((delta_pcr_value*delta_pts_pcr_pck_num*1.0)/delta_pcr_pck_num); + } + + fprintf(dumper->timestamps_info_file, "%u\t%d\t", pck->stream->pes_start_packet_number, pck->stream->pid); + if (interpolated_pcr_value) fprintf(dumper->timestamps_info_file, "%f", interpolated_pcr_value/(300.0 * 90000)); + fprintf(dumper->timestamps_info_file, "\t"); + if (pck->DTS) fprintf(dumper->timestamps_info_file, "%f", (pck->DTS / 90000.0)); + fprintf(dumper->timestamps_info_file, "\t%f\t%d\t%d\n", pck->PTS / 90000.0, (pck->flags & GF_M2TS_PES_PCK_RAP ? 1 : 0), (pck->flags & GF_M2TS_PES_PCK_DISCONTINUITY ? 1 : 0)); + } + } + if (index_info->start_indexing) { + /* we process packets only for the given PID */ + if (pck->stream->pid != index_info->reference_pid) { + break; + } else { + /* 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->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)) { + index_info->base_PTS = pck->PTS; + } + /* we need to know the earliest PTS value for the whole file (segment) */ + if (!index_info->first_PTS || (index_info->first_PTS > pck->PTS)) { + index_info->first_PTS = pck->PTS; + } + if (pck->PTS > index_info->last_PTS) { + /* we use the last PTS for first approximation of the duration */ + 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); } break; + case GF_M2TS_EVT_PES_PCR: + pck = par; + if (pck->stream->program->number != dumper->prog_number) break; + if (dumper->timestamps_info_file) { + fprintf(dumper->timestamps_info_file, "%u\t%d\t%f\t\t\t\t%d\n", pck->stream->program->last_pcr_value_pck_number, pck->stream->pid, pck->PTS / (300*90000.0), (pck->flags & GF_M2TS_PES_PCK_DISCONTINUITY ? 1 : 0)); + } + if (index_info->start_indexing) { + if (!index_info->first_pcr_position_valid) { + index_info->first_pcr_position_valid = 1; + index_info->first_pcr_position = (ts->pck_number-1)*188; + } + index_info->last_pcr_position = (ts->pck_number-1)*188; + } + break; case GF_M2TS_EVT_SL_PCK: #if 0 { @@ -1812,26 +2325,155 @@ static void on_m2ts_dump_event(GF_M2TS_Demuxer *ts, u32 evt_type, void *par) } } -void dump_mpeg2_ts(char *mpeg2ts_file, char *pes_out_name) +static void mpd_duration(Double duration, char *duration_string) +{ + u32 h, m; + Double s; + + h = (u32) (duration/3600); + m = (u32) (duration-h*60)/60; + s = (duration - h*3600 - m*60); + if (h) sprintf(duration_string, "PT%dH%dM%.2fS", h, m, s); + else if (m) sprintf(duration_string, "PT%dM%.2fS", m, s); + else if (s) sprintf(duration_string, "PT%.2fS", s); + 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) +{ + char duration_string[100]; + char buffer_string[100]; + u32 bandwidth; + bandwidth = (u32) (file_size * 8 / file_duration); + fprintf(mpd, "\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, " \n"); + +} + +void mpd_end(FILE *mpd) +{ + fprintf(mpd, " \n"); + fprintf(mpd, " \n"); + fprintf(mpd, ""); +} + +static void write_mpd_segment_info(GF_M2TS_IndexingInfo *index_info, char *media_file_name) +{ + u32 i; + u64 start; + char duration_string[100]; + + mpd_duration(index_info->segment_duration, duration_string); + fprintf(index_info->mpd_file, " \n", duration_string); + /* add startIndex for live scenarios */ + + if (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); + 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_offset-1); + start += ref->reference_offset; + } + } 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"); +} + +void dump_mpeg2_ts(char *mpeg2ts_file, char *pes_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 data[188]; GF_M2TS_Dump dumper; u32 size; u64 fsize, fdone; GF_M2TS_Demuxer *ts; + FILE *src; - FILE *src = gf_f64_open(mpeg2ts_file, "rb"); + src = gf_f64_open(mpeg2ts_file, "rb"); 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; + 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 = gf_strdup(seg_name); + 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; + 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")); + 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); + } + } + gf_f64_seek(src, 0, SEEK_END); fsize = gf_f64_tell(src); gf_f64_seek(src, 0, SEEK_SET); fdone = 0; + /* first loop to process all packets between two PAT, and assume all signaling was found between these 2 PATs */ + while (!feof(src)) { + size = fread(data, 1, 188, src); + if (size<188) break; + + gf_m2ts_process_data(ts, data, size); + if (dumper.has_seen_pat) break; + } + + if (prog_num && !dash_duration) { + sprintf(dumper.timestamps_info_name, "%s_prog_%d_timestamps.txt", mpeg2ts_file, prog_num/*, mpeg2ts_file*/); + dumper.timestamps_info_file = gf_f64_open(dumper.timestamps_info_name, "wt"); + if (!dumper.timestamps_info_file) { + fprintf(stderr, "Cannot open file %s\n", dumper.timestamps_info_name); + return; + } + 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) { @@ -1847,17 +2489,15 @@ void dump_mpeg2_ts(char *mpeg2ts_file, char *pes_out_name) } } - while (!feof(src)) { - size = fread(data, 1, 188, src); - if (size<188) break; - - gf_m2ts_process_data(ts, data, size); - if (dumper.has_seen_pat) break; - } - gf_m2ts_reset_parsers(ts); gf_f64_seek(src, 0, SEEK_SET); fdone = 0; + if (dumper.index_info.segment_duration) { + 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"); + } while (!feof(src)) { size = fread(data, 1, 188, src); if (size<188) break; @@ -1868,6 +2508,21 @@ void dump_mpeg2_ts(char *mpeg2ts_file, char *pes_out_name) gf_set_progress("MPEG-2 TS Parsing", fdone, fsize); } + if (dumper.index_info.segment_duration) { + u64 file_size; + /* flush SIDX entry for the last packets */ + m2ts_sidx_flush_entry(&dumper.index_info); + gf_f64_seek(src, 0, SEEK_END); + file_size = gf_f64_tell(src); + 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); + write_mpd_segment_info(&dumper.index_info, mpeg2ts_file); + mpd_end(dumper.index_info.mpd_file); + } + fclose(src); gf_m2ts_demux_del(ts); if (dumper.pes_out) fclose(dumper.pes_out); @@ -1876,6 +2531,16 @@ void dump_mpeg2_ts(char *mpeg2ts_file, char *pes_out_name) fclose(dumper.pes_out_nhml); fclose(dumper.pes_out_info); } + 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); } #endif /*GPAC_DISABLE_MPEG2TS*/ diff --git a/applications/mp4box/fileimport.c b/applications/mp4box/fileimport.c index 220b330..6c5b81c 100644 --- a/applications/mp4box/fileimport.c +++ b/applications/mp4box/fileimport.c @@ -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; + char *ext, szName[1000], *fmt, *handler_name, *rvc_config; memset(&import, 0, sizeof(GF_MediaImporter)); @@ -171,6 +171,7 @@ GF_Err import_file(GF_ISOFile *dest, char *inName, u32 import_flags, Double forc if (ext && ext[1]=='\\') ext = strchr(szName+2, ':'); handler_name = NULL; + rvc_config = NULL; fmt = NULL; while (ext) { char *ext2 = strchr(ext+1, ':'); @@ -185,7 +186,7 @@ GF_Err import_file(GF_ISOFile *dest, char *inName, u32 import_flags, Double forc if (!strcmp(ext+5, "auto")) force_fps = 10000.0; else if (strchr(ext+5, '-')) { u32 ticks, dts_inc; - sscanf(ext+5, "%d-%d", &ticks, &dts_inc); + sscanf(ext+5, "%u-%u", &ticks, &dts_inc); if (!dts_inc) dts_inc=1; force_fps = ticks; force_fps /= dts_inc; @@ -217,6 +218,7 @@ GF_Err import_file(GF_ISOFile *dest, char *inName, u32 import_flags, Double forc } } else if (!strnicmp(ext+1, "name=", 5)) handler_name = gf_strdup(ext+6); + else if (!strnicmp(ext+1, "rvc=", 4)) rvc_config = gf_strdup(ext+5); else if (!strnicmp(ext+1, "font=", 5)) import.fontName = gf_strdup(ext+6); else if (!strnicmp(ext+1, "size=", 5)) import.fontSize = atoi(ext+6); else if (!strnicmp(ext+1, "fmt=", 4)) import.streamFormat = gf_strdup(ext+5); @@ -230,8 +232,12 @@ GF_Err import_file(GF_ISOFile *dest, char *inName, u32 import_flags, Double forc handler = GF_4CC(ext[6], ext[7], ext[8], ext[9]); else if (!strnicmp(ext+1, "layout=", 7)) { - sscanf(ext+8, "%dx%dx%dx%d", &tw, &th, &tx, &ty); - track_layout = 1; + if ( sscanf(ext+8, "%dx%dx%dx%d", &tw, &th, &tx, &ty)==4) { + track_layout = 1; + } else if ( sscanf(ext+8, "%dx%d", &tw, &th)==2) { + track_layout = 1; + tx = ty = 0; + } } else if (!strnicmp(ext+1, "stype=", 6)) { stype = GF_4CC(ext[7], ext[8], ext[9], ext[10]); @@ -239,7 +245,6 @@ GF_Err import_file(GF_ISOFile *dest, char *inName, u32 import_flags, Double forc else if (!strnicmp(ext+1, "profile=", 8)) profile = atoi(ext+9); else if (!strnicmp(ext+1, "level=", 6)) level = atoi(ext+7); - /*unrecognized, assume name has colon in it*/ else { ext = ext2; @@ -464,6 +469,26 @@ GF_Err import_file(GF_ISOFile *dest, char *inName, u32 import_flags, Double forc if (gf_isom_get_mpeg4_subtype(import.dest, track, 1)) keep_sys_tracks = 1; + + if (rvc_config) { + FILE *f = gf_f64_open(rvc_config, "rb"); + if (f) { + char *data; + u32 size; + gf_f64_seek(f, 0, SEEK_END); + size = (u32) gf_f64_tell(f); + gf_f64_seek(f, 0, SEEK_SET); + data = gf_malloc(sizeof(char)*size); + fread(data, 1, size, f); + fclose(f); + gf_gz_compress_payload(&data, size, &size); + + e = gf_isom_set_meta_type(import.dest, 0, track, GF_4CC('r','v','c','z')); + gf_isom_modify_alternate_brand(import.dest, GF_ISOM_BRAND_ISO2, 1); + gf_isom_set_meta_xml_memory(import.dest, 0, track, data, size, 1); + gf_free(data); + } + } } if (track_id) fprintf(stdout, "WARNING: Track ID %d not found in file\n", track_id); else if (do_video) fprintf(stdout, "WARNING: Video track not found\n"); @@ -475,6 +500,7 @@ exit: if (import.fontName) gf_free(import.fontName); if (import.streamFormat) gf_free(import.streamFormat); if (import.force_ext) gf_free(import.force_ext); + if (rvc_config) gf_free(rvc_config); return e; } @@ -1120,13 +1146,25 @@ GF_Err cat_isomedia_file(GF_ISOFile *dest, char *fileName, u32 import_flags, Dou /*we only support cat with the same number of sample descriptions*/ if (gf_isom_get_sample_description_count(orig, i+1) != gf_isom_get_sample_description_count(dest, dst_tk)) dst_tk = 0; /*if not forcing cat, check the media codec config is the same*/ - if (!force_cat && !gf_isom_is_same_sample_description(orig, i+1, dest, dst_tk)) dst_tk = 0; + if (!gf_isom_is_same_sample_description(orig, i+1, dest, dst_tk)) { + if (!force_cat) { + dst_tk = 0; + } else { + fprintf(stdout, "WARNING: Concatenating track ID %d even though sample descriptions do not match\n", tk_id); + } + } /*we force the same visual resolution*/ else if (mtype==GF_ISOM_MEDIA_VISUAL) { u32 w, h, ow, oh; gf_isom_get_visual_info(orig, i+1, 1, &ow, &oh); gf_isom_get_visual_info(dest, dst_tk, 1, &w, &h); - if ((ow!=w) || (oh!=h)) dst_tk = 0; + if ((ow!=w) || (oh!=h)) { + if (!force_cat) { + dst_tk = 0; + } else { + fprintf(stdout, "WARNING: Concatenating track ID %d even though visual sizes do not match\n", tk_id); + } + } } if (!dst_tk && ((stype == GF_ISOM_SUBTYPE_AVC_H264) || (stype == GF_ISOM_SUBTYPE_AVC2_H264)) ) { diff --git a/applications/mp4box/live.c b/applications/mp4box/live.c index 6ef9343..b0c1d63 100644 --- a/applications/mp4box/live.c +++ b/applications/mp4box/live.c @@ -60,7 +60,7 @@ int stream_file_rtp(int argc, char **argv) char *inName = NULL; u16 port = 7000; u32 ttl = 1; - Bool loop = 1, stream_rtp = 0; + Bool loop = 1; Bool force_mpeg4 = 0; u32 path_mtu = 1450; u32 i; @@ -234,9 +234,11 @@ static void live_session_callback(void *calling_object, u16 ESID, char *data, u3 } /*send data*/ else { + Bool rap = rtpch->rap; + if (livesess->carousel_generation) rap = 1; ts += rtpch->timescale*(gf_sys_clock()-rtpch->init_time + rtpch->ts_delta)/1000; - gf_rtp_streamer_send_au_with_sn(rtpch->rtp, data, size, ts, ts, rtpch->rap ? 1 : 0, (livesess->critical || rtpch->critical) ? 1 : 0 ); - fprintf(stdout, "Stream %d: Sending update at TS "LLD", %d bytes - RAP %d - critical %d\n", ESID, ts, size, rtpch->rap, (livesess->critical || rtpch->critical) ? 1 : 0); + gf_rtp_streamer_send_au_with_sn(rtpch->rtp, data, size, ts, ts, rap, (livesess->critical || rtpch->critical) ? 1 : 0 ); + fprintf(stdout, "Stream %d: Sending update at TS "LLD", %d bytes - RAP %d - critical %d\n", ESID, ts, size, rap, (livesess->critical || rtpch->critical) ? 1 : 0); rtpch->rap = rtpch->critical = 0; if (rtpch->manual_rtcp) gf_rtp_streamer_send_rtcp(rtpch->rtp, 0, 0); @@ -265,7 +267,7 @@ static void live_session_send_carousel(LiveSession *livesess, RTPChannel *ch) } } else { u32 i=0; - while (ch = gf_list_enum(livesess->streams, &i)) { + while (NULL != (ch = gf_list_enum(livesess->streams, &i))) { if (ch->carousel_size) { if (ch->adjust_carousel_time) { ts = ch->carousel_ts + ch->timescale*(gf_sys_clock()-ch->init_time + ch->ts_delta)/1000; @@ -297,7 +299,7 @@ static void live_session_setup(LiveSession *livesess, char *ip, u16 port, u32 pa for (i=0; iseng, i, &ESID, &config, &config_len, &st, &oti, &ts); @@ -418,7 +420,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; + Bool run, has_carousel, skip_init_scene; Bool udp = 0; u16 sk_port=0; GF_Socket *sk = NULL; @@ -431,7 +433,11 @@ int live_session(int argc, char **argv) Bool update_context; u32 period, ts_delta; u16 es_id; - + e = GF_OK; + /* souchay : needs to initialize those two vars... */ + aggregate_au = 1; + es_id = 0; + skip_init_scene = 0; gf_sys_init(0); memset(&livesess, 0, sizeof(LiveSession)); @@ -449,6 +455,10 @@ int live_session(int argc, char **argv) else if (!strnicmp(arg, "-ttl=", 5)) ttl = atoi(arg+5); else if (!strnicmp(arg, "-ifce=", 6)) ifce_addr = arg+6; + else if (!strnicmp(arg, "-dims-stream", 12)) { + load_type = GF_SM_LOAD_DIMS; + skip_init_scene = 1; + } else if (!strnicmp(arg, "-dims", 5)) load_type = GF_SM_LOAD_DIMS; else if (!strnicmp(arg, "-src=", 5)) src_name = arg+5; else if (!strnicmp(arg, "-udp=", 5)) { sk_port = atoi(arg+5); udp = 1; } @@ -492,18 +502,18 @@ int live_session(int argc, char **argv) RTPChannel *ch; period = id = 0; if (strchr(arg, ':')) { - sscanf(arg, "-rap=ESID=%d:%d", &id, &period); + sscanf(arg, "-rap=ESID=%u:%u", &id, &period); e = gf_seng_enable_aggregation(livesess.seng, id, 1); if (e) { - fprintf(stdout, "Cannot enable aggregation on stream %d: %s\n", id, gf_error_to_string(e)); + fprintf(stdout, "Cannot enable aggregation on stream %u: %s\n", id, gf_error_to_string(e)); goto exit; } } else { - sscanf(arg, "-rap=%d", &period); + sscanf(arg, "-rap=%u", &period); } j=0; - while (ch = gf_list_enum(livesess.streams, &j)) { + while (NULL != (ch = gf_list_enum(livesess.streams, &j))) { if (!id || (ch->ESID==id)) ch->carousel_period = period; } @@ -512,16 +522,20 @@ int live_session(int argc, char **argv) } i=0; - while (ch = gf_list_enum(livesess.streams, &i)) { + while (NULL != (ch = gf_list_enum(livesess.streams, &i))) { if (ch->carousel_period) { has_carousel = 1; break; } } + update_context = 0; - livesess.carousel_generation = 1; - gf_seng_encode_context(livesess.seng, live_session_callback); - livesess.carousel_generation = 0; + + if (has_carousel || !skip_init_scene) { + livesess.carousel_generation = 1; + gf_seng_encode_context(livesess.seng, live_session_callback); + livesess.carousel_generation = 0; + } live_session_send_carousel(&livesess, NULL); @@ -545,7 +559,10 @@ int live_session(int argc, char **argv) char szCom[8192]; fprintf(stdout, "Enter command to send:\n"); szCom[0] = 0; - scanf("%[^\t\n]", szCom); + if (1 > scanf("%[^\t\n]", szCom)){ + fprintf(stderr, "No command entered properly, aborting.\n"); + break; + } /*stdin flush bug*/ while (getchar()!='\n') {} e = gf_seng_encode_from_string(livesess.seng, 0, 0, szCom, live_session_callback); @@ -563,7 +580,10 @@ int live_session(int argc, char **argv) char szCom[8192]; fprintf(stdout, "Enter command to send:\n"); szCom[0] = 0; - scanf("%[^\t\n]", szCom); + if (1 > scanf("%[^\t\n]", szCom)){ + printf("No command entered properly, aborting.\n"); + break; + } /*stdin flush bug*/ while (getchar()!='\n') {} e = gf_seng_encode_from_string(livesess.seng, 0, 1, szCom, live_session_callback); @@ -578,7 +598,10 @@ int live_session(int argc, char **argv) { char rad[GF_MAX_PATH]; fprintf(stdout, "Enter output file name - \"std\" for stdout: "); - scanf("%s", rad); + if (1 > scanf("%s", rad)){ + fprintf(stderr, "No ouput file name entered, aborting.\n"); + break; + } e = gf_seng_save_context(livesess.seng, !strcmp(rad, "std") ? NULL : rad); fprintf(stdout, "Dump done (%s)\n", gf_error_to_string(e)); } @@ -606,7 +629,8 @@ int live_session(int argc, char **argv) if (!srcf) continue; /*checks if we have a broadcast config*/ - fgets(flag_buf, 200, srcf); + if (!fgets(flag_buf, 200, srcf)) + flag_buf[0] = '\0'; fclose(srcf); aggregate_on_stream = (u16) -1; diff --git a/applications/mp4box/main.c b/applications/mp4box/main.c index eddc1ac..0af8789 100644 --- a/applications/mp4box/main.c +++ b/applications/mp4box/main.c @@ -24,6 +24,7 @@ #include +#include #ifdef GPAC_DISABLE_ISOM @@ -37,6 +38,8 @@ #include #include +#include + #include #define BUFFSIZE 8192 @@ -97,7 +100,9 @@ void PrintLanguages(); const char *GetLanguageCode(char *lang); #ifndef GPAC_DISABLE_MPEG2TS -void dump_mpeg2_ts(char *mpeg2ts_in, char *pes_out_name); +void dump_mpeg2_ts(char *mpeg2ts_file, char *pes_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); #endif @@ -110,7 +115,7 @@ int live_session(int argc, char **argv); void PrintLiveUsage(); -Bool quiet = 0; +u32 quiet = 0; Bool dvbhdemux =0; Bool keep_sys_tracks = 0; @@ -131,7 +136,7 @@ static const itunes_tag itags[] = { {GF_ISOM_ITUNE_COMMENT, "comment", "usage: comment=any comment"}, {GF_ISOM_ITUNE_COMPILATION, "compilation", "usage: compilation=yes,no"}, {GF_ISOM_ITUNE_COMPOSER, "composer", "usage: composer=name"}, - {GF_ISOM_ITUNE_CREATED, "created", ""}, + {GF_ISOM_ITUNE_CREATED, "created", "usage: created=time"}, {GF_ISOM_ITUNE_DISK, "disk", "usage: disk=x/N"}, {GF_ISOM_ITUNE_TOOL, "tool", "usage: tool=name"}, {GF_ISOM_ITUNE_GENRE, "genre", "usage: genre=name"}, @@ -139,9 +144,9 @@ static const itunes_tag itags[] = { {GF_ISOM_ITUNE_TEMPO, "tempo", "usage: tempo=integer"}, {GF_ISOM_ITUNE_WRITER, "writer", "usage: writer=name"}, {GF_ISOM_ITUNE_GROUP, "group", "usage: group=name"}, - {GF_ISOM_ITUNE_COVER_ART, "cover", "usage: covber=file.jpg,file.png"}, + {GF_ISOM_ITUNE_COVER_ART, "cover", "usage: cover=file.jpg,file.png"}, {GF_ISOM_ITUNE_ENCODER, "encoder", "usage: encoder=name"}, - {GF_ISOM_ITUNE_GAPELESS, "gapeless", "usage: artist=yes,no"}, + {GF_ISOM_ITUNE_GAPLESS, "gapless", "usage: gapless=yes,no"}, }; u32 nb_itunes_tags = sizeof(itags) / sizeof(itunes_tag); @@ -191,7 +196,6 @@ void PrintGeneralUsage() " -enable trackID enables track\n" " -disable trackID disables track\n" " -new forces creation of a new destination file\n" - " -rem trackID removes track from file\n" " -lang [tkID=]LAN sets track language. LAN is the ISO 639-2 code (eng, und)\n" " -delay tkID=TIME sets track start delay in ms.\n" " -par tkID=PAR sets visual track pixel aspect ratio (PAR=N:D or \"none\")\n" @@ -224,6 +228,18 @@ void PrintGeneralUsage() " -group-rem ID removes the track's group\n" " -group-clean removes all group information from all tracks\n" " -ref id:XXXX:refID adds a reference of type 4CC from track ID to track refID\n" + "\n" + " -dash dur enables DASH-ing of the file with a segment duration of DUR\n" + " Note: the duration of a fragment (subsegment) is set\n" + " using the interleaver (-inter) switch.\n" + " Note: You can specify -rap switch to split segments at RAP boundaries\n" + " -frags-per-sidx N sets the number of segments to be written in each SIDX box\n" + " If 0, SIDX box is not used\n" + " -segment-name name sets the segment name for generated segments\n" + " If not set (default), segments are concatenated in output file\n" + " -segment-ext name sets the segment extension. Default is m4s\n" + " -url-template uses UrlTemplate instead of explicit sources in segments.\n" + " Ignored if segments are stored in the output file.\n" "\n"); } @@ -308,6 +324,7 @@ void PrintImportUsage() " the tracks in the file are used.\n" " - if Y=-1, the layout is moved to the bottom of the\n" " track area\n" + " - X and Y can be omitted (:layout=WxH)\n" "\n" " -add file add file tracks to (new) output file\n" " -cat file concatenates file samples to (new) output file\n" @@ -571,6 +588,7 @@ void PrintUsage() " -languages lists supported ISO 639 languages\n" "\n" "-quiet quiet mode\n" + "-noprog disables progress\n" " -v verbose mode\n" " -version gets build version\n" ); @@ -921,7 +939,7 @@ static Bool can_convert_to_isma(GF_ISOFile *file) } #endif -static void progress_quiet(void *cbck, char *title, u64 done, u64 total) { } +static void progress_quiet(const void *cbck, const char *title, u64 done, u64 total) { } typedef struct @@ -980,7 +998,7 @@ static Bool parse_meta_args(MetaAction *meta, char *opts) if (next) next[0] = 0; if (!strnicmp(szSlot, "tk=", 3)) { - sscanf(szSlot, "tk=%d", &meta->trackID); + sscanf(szSlot, "tk=%u", &meta->trackID); meta->root_meta = 0; ret = 1; } @@ -1162,15 +1180,17 @@ int main(int argc, char **argv) TSELAction tsel_acts[MAX_CUMUL_OPS]; u64 movie_time; u32 brand_add[MAX_CUMUL_OPS], brand_rem[MAX_CUMUL_OPS]; - u32 i, MTUSize, stat_level, hint_flags, info_track_id, import_flags, nb_add, nb_cat, ismaCrypt, agg_samples, nb_sdp_ex, max_ptime, raw_sample_num, split_size, nb_meta_act, nb_track_act, rtp_rate, major_brand, nb_alt_brand_add, nb_alt_brand_rem, old_interleave, car_dur, minor_version, conv_type, nb_tsel_acts, frags_per_sidx; + u32 i, MTUSize, stat_level, hint_flags, info_track_id, import_flags, nb_add, nb_cat, ismaCrypt, agg_samples, nb_sdp_ex, max_ptime, raw_sample_num, split_size, nb_meta_act, nb_track_act, rtp_rate, major_brand, nb_alt_brand_add, nb_alt_brand_rem, old_interleave, car_dur, minor_version, conv_type, nb_tsel_acts, frags_per_sidx, program_number; Bool HintIt, needSave, FullInter, Frag, HintInter, dump_std, dump_rtp, dump_mode, regular_iod, trackID, HintCopy, remove_sys_tracks, remove_hint, force_new, remove_root_od, import_subtitle, dump_chap; - Bool print_sdp, print_info, open_edit, track_dump_type, dump_isom, dump_cr, force_ocr, encode, do_log, do_flat, dump_srt, dump_ttxt, x3d_info, chunk_mode, dump_ts, do_saf, dump_m2ts, dump_cart, do_hash, verbose, force_cat, pack_wgt; + Bool print_sdp, print_info, open_edit, track_dump_type, dump_isom, dump_cr, force_ocr, encode, do_log, do_flat, dump_srt, dump_ttxt, x3d_info, chunk_mode, dump_ts, do_saf, do_mpd, dump_m2ts, dump_cart, do_hash, verbose, force_cat, pack_wgt, dash_ts_use_index; char *inName, *outName, *arg, *mediaSource, *tmpdir, *input_ctx, *output_ctx, *drm_file, *avi2raw, *cprt, *chap_file, *pes_dump, *itunes_tags, *pack_file, *raw_cat, *seg_name; GF_ISOFile *file; Bool stream_rtp=0; Bool live_scene=0; Bool dump_iod=0; + Bool use_url_template=0; Bool seg_at_rap =0; + char *seg_ext = "m4s"; if (argc < 2) { PrintUsage(); @@ -1183,12 +1203,13 @@ int main(int argc, char **argv) split_start = -1.0; InterleavingTime = 0.5; dash_duration = 0.0; + dash_ts_use_index = 0; import_fps = 0; import_flags = 0; split_size = 0; movie_time = 0; MTUSize = 1450; - HintCopy = FullInter = HintInter = encode = do_log = old_interleave = do_saf = do_hash = verbose = 0; + HintCopy = FullInter = HintInter = encode = do_log = old_interleave = do_saf = do_mpd = do_hash = verbose = 0; chunk_mode = dump_mode = Frag = force_ocr = remove_sys_tracks = agg_samples = remove_hint = keep_sys_tracks = remove_root_od = 0; x3d_info = conv_type = HintIt = needSave = print_sdp = print_info = regular_iod = dump_std = open_edit = dump_isom = dump_rtp = dump_cr = dump_chap = dump_srt = dump_ttxt = force_new = dump_ts = dump_m2ts = dump_cart = import_subtitle = force_cat = pack_wgt = 0; frags_per_sidx = 1; @@ -1203,6 +1224,7 @@ int main(int argc, char **argv) #endif trackID = stat_level = hint_flags = 0; + program_number = 0; info_track_id = 0; do_flat = 0; inName = outName = mediaSource = input_ctx = output_ctx = drm_file = avi2raw = cprt = chap_file = pack_file = raw_cat = NULL; @@ -1223,12 +1245,13 @@ int main(int argc, char **argv) else if (!stricmp(arg, "-?")) { PrintUsage(); return 0; } else if (!stricmp(arg, "-version")) { PrintVersion(); return 0; } else if (!stricmp(arg, "-sdp")) print_sdp = 1; - else if (!stricmp(arg, "-quiet")) quiet = 1; + else if (!stricmp(arg, "-quiet")) quiet = 2; + else if (!stricmp(arg, "-noprog")) quiet = 1; else if (!stricmp(arg, "-info")) { print_info = 1; - if ((i+1<(u32) argc) && (sscanf(argv[i+1], "%d", &info_track_id)==1)) { + if ((i+1<(u32) argc) && (sscanf(argv[i+1], "%u", &info_track_id)==1)) { char szTk[20]; - sprintf(szTk, "%d", info_track_id); + sprintf(szTk, "%u", info_track_id); if (!strcmp(szTk, argv[i+1])) i++; else info_track_id=0; } else { @@ -1268,7 +1291,7 @@ int main(int argc, char **argv) CHECK_NEXT_ARG track_dump_type = GF_EXPORT_RAW_SAMPLES; if (strchr(argv[i+1], ':')) { - sscanf(argv[i+1], "%d:%d", &trackID, &raw_sample_num); + sscanf(argv[i+1], "%u:%u", &trackID, &raw_sample_num); } else { trackID = atoi(argv[i+1]); } @@ -1345,10 +1368,15 @@ int main(int argc, char **argv) fprintf(stdout, "WARNING: \"-dmp4\" is deprecated - use \"-diso\" option\n"); } else if (!stricmp(arg, "-drtp")) dump_rtp = 1; - else if (!stricmp(arg, "-dts")) dump_ts = 1; - else if (!stricmp(arg, "-dcr")) dump_cr = 1; + else if (!stricmp(arg, "-dts")) { + dump_ts = 1; + if ( ((i+1<(u32) argc) && inName) || (i+2<(u32) argc) ) { + if (argv[i+1][0] != '-') program_number = atoi(argv[i+1]); + i++; + } + } else if (!stricmp(arg, "-dcr")) dump_cr = 1; else if (!stricmp(arg, "-ttxt") || !stricmp(arg, "-srt")) { - if ((i+1<(u32) argc) && (sscanf(argv[i+1], "%d", &trackID)==1)) { + if ((i+1<(u32) argc) && (sscanf(argv[i+1], "%u", &trackID)==1)) { char szTk[20]; sprintf(szTk, "%d", trackID); if (!strcmp(szTk, argv[i+1])) i++; @@ -1419,6 +1447,12 @@ int main(int argc, char **argv) dash_duration = atof(argv[i+1]) / 1000; needSave = 1; i++; + } else if (!stricmp(arg, "-dash-ts-prog")) { + CHECK_NEXT_ARG + program_number = atoi(argv[i+1]); + i++; + } else if (!stricmp(arg, "-dash-ts-use-index")) { + dash_ts_use_index = 1; } else if (!stricmp(arg, "-frags-per-sidx")) { CHECK_NEXT_ARG frags_per_sidx = atoi(argv[i+1]); @@ -1427,7 +1461,13 @@ int main(int argc, char **argv) CHECK_NEXT_ARG seg_name = argv[i+1]; i++; - } + } else if (!stricmp(arg, "-segment-ext")) { + CHECK_NEXT_ARG + seg_ext = argv[i+1]; + i++; + } else if (!stricmp(arg, "-url-template")) { + use_url_template = 1; + } else if (!stricmp(arg, "-itags")) { CHECK_NEXT_ARG itunes_tags = argv[i+1]; i++; open_edit = 1; } #ifndef GPAC_DISABLE_ISOM_HINTING else if (!stricmp(arg, "-hint")) { open_edit = 1; HintIt = 1; } @@ -1449,9 +1489,9 @@ int main(int argc, char **argv) else if (!stricmp(arg, "-static")) hint_flags |= GP_RTP_PCK_USE_STATIC_ID; else if (!stricmp(arg, "-multi")) { hint_flags |= GP_RTP_PCK_USE_MULTI; - if ((i+1<(u32) argc) && (sscanf(argv[i+1], "%d", &max_ptime)==1)) { + if ((i+1<(u32) argc) && (sscanf(argv[i+1], "%u", &max_ptime)==1)) { char szPt[20]; - sprintf(szPt, "%d", max_ptime); + sprintf(szPt, "%u", max_ptime); if (!strcmp(szPt, argv[i+1])) i++; else max_ptime=0; } @@ -1479,7 +1519,7 @@ int main(int argc, char **argv) id = strchr(argv[i+1], ':'); if (id) { id[0] = 0; - if (sscanf(argv[i+1], "%d", &sdp_lines[0].trackID)==1) { + if (sscanf(argv[i+1], "%u", &sdp_lines[0].trackID)==1) { id[0] = ':'; sdp_lines[nb_sdp_ex].line = id+1; } else { @@ -1689,7 +1729,7 @@ int main(int argc, char **argv) if (!strcmp(argv[i+1], "auto")) import_fps = 10000.0; else if (strchr(argv[i+1], '-')) { u32 ticks, dts_inc; - sscanf(argv[i+1], "%d-%d", &ticks, &dts_inc); + sscanf(argv[i+1], "%u-%u", &ticks, &dts_inc); if (!dts_inc) dts_inc=1; import_fps = ticks; import_fps /= dts_inc; @@ -1703,7 +1743,13 @@ int main(int argc, char **argv) else if (!stricmp(arg, "-ms")) { CHECK_NEXT_ARG mediaSource = argv[i+1]; i++; } else if (!stricmp(arg, "-mp4")) { encode = 1; open_edit = 1; } else if (!stricmp(arg, "-saf")) { do_saf = 1; } - else if (!stricmp(arg, "-log")) do_log = 1; + else if (!stricmp(arg, "-log")) { do_log = 1; } + else if (!stricmp(arg, "-mpd")) { + do_mpd = 1; + CHECK_NEXT_ARG + outName = argv[i+1]; + i++; + } #ifndef GPAC_DISABLE_SCENE_ENCODER else if (!stricmp(arg, "-def")) opts.flags |= GF_SM_ENCODE_USE_NAMES; else if (!stricmp(arg, "-sync")) { @@ -2028,13 +2074,38 @@ int main(int argc, char **argv) gf_log_set_level(verbose ? GF_LOG_DEBUG : GF_LOG_INFO); gf_log_set_tools(GF_LOG_CONTAINER|GF_LOG_SCENE|GF_LOG_PARSER|GF_LOG_AUTHOR|GF_LOG_CODING); if (quiet) { - gf_log_set_level(0); + if (quiet==2) gf_log_set_level(0); gf_set_progress_callback(NULL, progress_quiet); } /*init libgpac*/ - gf_sys_init(0); - + gf_sys_init(1); + + if (do_mpd) { + Bool remote = 0; + char *mpd_base_url = gf_strdup(inName); + if (strcmp(inName, "http://")) { + 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)); + gf_free(mpd_base_url); + return 1; + } + inName = "tmp_main.m3u8"; + remote = 1; + } + e = gf_m3u8_to_mpd(NULL, inName, mpd_base_url, (outName ? outName : inName), 0, "video/mp2t"); + gf_free(mpd_base_url); + if (remote) { + //gf_delete_file("tmp_main.m3u8"); + } + if (e != GF_OK) { + fprintf(stdout, "Error converting M3U8 (%s) to MPD (%s): %s\n", inName, outName, gf_error_to_string(e)); + return 1; + } else { + return 0; + } + } if (do_saf && !encode) { switch (get_file_type_by_ext(inName)) { @@ -2294,9 +2365,18 @@ int main(int argc, char **argv) } } - if (dump_m2ts) { + if (dash_duration) { +#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); +#endif + } else if (dump_m2ts) { +#ifndef GPAC_DISABLE_MPEG2TS + dump_mpeg2_ts(inName, pes_dump, program_number, 0, 0, 0, NULL, NULL, 0, 0); +#endif + } else if (dump_ts) { /* dump_ts means dump time stamp information */ #ifndef GPAC_DISABLE_MPEG2TS - dump_mpeg2_ts(inName, pes_dump); + dump_mpeg2_ts(inName, pes_dump, program_number, 0, 0, 0, NULL, NULL, 0, 0); #endif } else { convert_file_info(inName, info_track_id); @@ -2427,7 +2507,7 @@ int main(int argc, char **argv) fwrite(desc, 1, size, iodf); gf_free(desc); } else { - fprintf(stdout, "Error writing IOD\n", szName); + fprintf(stdout, "Error writing IOD %s\n", szName); } fclose(iodf); } @@ -2505,20 +2585,32 @@ int main(int argc, char **argv) needSave = 1; break; case 6: - e = gf_isom_remove_meta_xml(file, meta->root_meta, tk); - needSave = 1; + if (gf_isom_get_meta_item_count(file, meta->root_meta, tk)) { + e = gf_isom_remove_meta_xml(file, meta->root_meta, tk); + needSave = 1; + } else { + fprintf(stdout, "No meta box in input file\n"); + } break; case 8: - e = gf_isom_extract_meta_item(file, meta->root_meta, tk, meta->item_id, strlen(meta->szPath) ? meta->szPath : NULL); + if (gf_isom_get_meta_item_count(file, meta->root_meta, tk)) { + e = gf_isom_extract_meta_item(file, meta->root_meta, tk, meta->item_id, strlen(meta->szPath) ? meta->szPath : NULL); + } else { + fprintf(stdout, "No meta box in input file\n"); + } break; #endif case 7: - e = gf_isom_extract_meta_xml(file, meta->root_meta, tk, meta->szPath, NULL); + if (gf_isom_get_meta_item_count(file, meta->root_meta, tk)) { + e = gf_isom_extract_meta_xml(file, meta->root_meta, tk, meta->szPath, NULL); + } else { + fprintf(stdout, "No meta box in input file\n"); + } break; } if (e) goto err_exit; } - if (!open_edit) { + if (!open_edit && !needSave) { if (file) gf_isom_delete(file); gf_sys_close(); return 0; @@ -2869,13 +2961,13 @@ int main(int argc, char **argv) n = t = 0; memset(_t, 0, sizeof(char)*8); tlen = (itag==GF_ISOM_ITUNE_DISK) ? 6 : 8; - if (sscanf(val, "%d/%d", &n, &t) == 2) { _t[3]=n; _t[5]=t;} - else if (sscanf(val, "%d", &n) == 1) { _t[3]=n;} + if (sscanf(val, "%u/%u", &n, &t) == 2) { _t[3]=n; _t[5]=t;} + else if (sscanf(val, "%u", &n) == 1) { _t[3]=n;} else tlen = 0; if (tlen) gf_isom_apple_set_tag(file, itag, _t, tlen); } break; - case GF_ISOM_ITUNE_GAPELESS: + case GF_ISOM_ITUNE_GAPLESS: case GF_ISOM_ITUNE_COMPILATION: { char _t[1]; @@ -2917,7 +3009,7 @@ int main(int argc, char **argv) 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, frags_per_sidx, 0); + e = gf_media_fragment_file(file, outfile, InterleavingTime, seg_at_rap ? 2 : 1, dash_duration, seg_name, seg_ext, frags_per_sidx, 0, use_url_template); if (e) fprintf(stdout, "Error while DASH-ing file: %s\n", gf_error_to_string(e)); gf_isom_delete(file); gf_sys_close(); @@ -2926,7 +3018,7 @@ int main(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, 0, 0); + e = gf_media_fragment_file(file, outfile, InterleavingTime, 0, 0, NULL, NULL, 0, 0, 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) { diff --git a/applications/mp4client/Makefile b/applications/mp4client/Makefile index 058a563..68bac02 100644 --- a/applications/mp4client/Makefile +++ b/applications/mp4client/Makefile @@ -2,7 +2,7 @@ include ../../config.mak vpath %.c $(SRC_PATH)/applications/mp4client -CFLAGS= $(OPTFLAGS) -I$(SRC_PATH)/include -I../../ +CFLAGS= $(OPTFLAGS) -I"$(SRC_PATH)/include" -I../../ LINKLIBS=$(OGL_LIBS) @@ -41,30 +41,26 @@ PROG=MP4Client endif ifneq ($(CONFIG_JPEG), no) -LINKLIBS+= -L$(prefix)/lib +#LINKLIBS+= -L$(prefix)/lib else ifneq ($(CONFIG_PNG), no) -LINKLIBS+= -L$(prefix)/lib +#LINKLIBS+= -L$(prefix)/lib else ifeq ($(CONFIG_JS),prefix) -LINKLIBS+= -L$(prefix)/lib +#LINKLIBS+= -L$(prefix)/lib endif endif endif ifeq ($(CONFIG_JPEG), no) else -LINKLIBS+= -ljpeg +#LINKLIBS+= -ljpeg endif -ifeq ($(CONFIG_JS), prefix) -LINKLIBS+= -ljs -else -endif ifeq ($(CONFIG_PNG), no) else -LINKLIBS+= -lpng +#LINKLIBS+= -lpng endif @@ -73,7 +69,7 @@ SRCS := $(OBJS:.o=.c) all: $(PROG) MP4Client$(EXE): $(OBJS) - $(CC) $(LDFLAGS) -o ../../bin/gcc/$@ $(OBJS) -L../../bin/gcc -L../../extra_lib/lib/gcc -lgpac -lz $(LINKLIBS) + $(CC) $(LDFLAGS) -o ../../bin/gcc/$@ $(OBJS) -L../../bin/gcc -lgpac $(LINKLIBS) %.o: %.c $(CC) $(CFLAGS) $(INSTALL_FLAGS) -c -o $@ $< diff --git a/applications/mp4client/extract.c b/applications/mp4client/extract.c index c3d0310..6aac57e 100644 --- a/applications/mp4client/extract.c +++ b/applications/mp4client/extract.c @@ -213,6 +213,45 @@ void write_bmp(GF_VideoSurface *fb, char *rad_name, u32 img_num) fclose(fout); } +#include + +void write_png(GF_VideoSurface *fb, char *rad_name, u32 img_num) +{ + char str[GF_MAX_PATH]; + FILE *fout; + u32 dst_size; + char *dst; + char *prev = strrchr(rad_name, '.'); + if (prev) prev[0] = '\0'; + sprintf(str, "%s_%d.png", rad_name, img_num); + if (prev) prev[0] = '.'; + + switch (fb->pixel_format) { + case GF_PIXEL_ARGB: + case GF_PIXEL_RGBA: + dst_size = fb->width*fb->height*4; + break; + default: + dst_size = fb->width*fb->height*3; + break; + } + dst = (char*)gf_malloc(sizeof(char)*dst_size); + + + fout = gf_f64_open(str, "wb"); + 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); + fclose(fout); + } + } + + gf_free(dst); +} + + + /*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) @@ -380,6 +419,9 @@ void dump_depth (GF_Terminal *term, char *rad_name, u32 dump_type, u32 frameNum, case 2: write_bmp(&fb, rad_name, frameNum); break; + case 11: + write_png(&fb, rad_name, frameNum); + break; case 3: write_raw(&fb, rad_name, frameNum); break; @@ -408,11 +450,6 @@ void dump_frame(GF_Terminal *term, char *rad_name, u32 dump_type, u32 frameNum, 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: @@ -508,6 +545,11 @@ void dump_frame(GF_Terminal *term, char *rad_name, u32 dump_type, u32 frameNum, break; } } + if (dump_type!=5 && dump_type!= 10 && dump_type!= 11) { + out_size = fb.height*fb.width*3; + } else { + out_size = fb.height*fb.width*4; + } #ifndef GPAC_DISABLE_AVILIB if (dump_type!=5 && dump_type!= 10) { if (AVI_write_frame(avi_out, conv_buf, out_size, 1) <0) @@ -521,6 +563,9 @@ void dump_frame(GF_Terminal *term, char *rad_name, u32 dump_type, u32 frameNum, case 2: write_bmp(&fb, rad_name, frameNum); break; + case 11: + write_png(&fb, rad_name, frameNum); + break; case 6: case 9: write_texture_file(&fb, rad_name, frameNum, dump_type); diff --git a/applications/mp4client/main.c b/applications/mp4client/main.c index ff4e448..08cecb7 100644 --- a/applications/mp4client/main.c +++ b/applications/mp4client/main.c @@ -34,8 +34,13 @@ #include #ifndef WIN32 +#include #include #include +#if defined(__DARWIN__) || defined(__APPLE__) +#include +#include +#endif #else #include /*for GetModuleFileName*/ @@ -45,17 +50,6 @@ #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*/ @@ -66,13 +60,17 @@ void PrintODList(GF_Terminal *term, GF_ObjectManager *root_odm, u32 num, u32 ind void ViewODs(GF_Terminal *term, Bool show_timing); void PrintGPACConfig(); +static Bool gui_mode = 0; + static Bool restart = 0; #if defined(__DARWIN__) || defined(__APPLE__) -static Bool not_threaded = 1; +//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; #else -static Bool not_threaded = 0; +static u32 threading_flags = 0; #endif static Bool no_audio = 0; +static Bool term_step = 0; static Bool no_regulation = 0; static Bool bench_mode = 0; Bool is_connected = 0; @@ -90,13 +88,12 @@ 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 Bool loop_at_end = 0; static u32 forced_width=0; static u32 forced_height=0; @@ -110,6 +107,22 @@ 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 hide_shell(u32 cmd_type) +{ +#if defined(WIN32) && !defined(_WIN32_WCE) + typedef HWND (WINAPI *GetConsoleWindowT)(void); + HMODULE hk32 = GetModuleHandle("kernel32.dll"); + GetConsoleWindowT GetConsoleWindow = (GetConsoleWindowT ) GetProcAddress(hk32,"GetConsoleWindow"); + if (cmd_type==0) ShowWindow( GetConsoleWindow(), SW_SHOW); + else if (cmd_type==1) ShowWindow( GetConsoleWindow(), SW_HIDE); + else if (cmd_type==2) PostMessage(GetConsoleWindow(), WM_CLOSE, 0, 0); +#endif +} + + + + void PrintUsage() { fprintf(stdout, "Usage MP4Client [options] [filename]\n" @@ -160,10 +173,12 @@ void PrintUsage() " default alignment is top-left\n" " default alignment is top-left\n" "\t-pause: pauses at first frame\n" + "\t-loop: loops presentation\n" "\n" "Dumper Options:\n" "\t-bmp [times]: dumps given frames to bmp\n" - "\t-raw [times]: dumps given frames to bmp\n" + "\t-png [times]: dumps given frames to png\n" + "\t-raw [times]: dumps given frames to raw\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" @@ -240,11 +255,14 @@ GF_Config *create_default_config(char *file_path, char *file_name) { GF_Config *cfg; char szPath[GF_MAX_PATH]; + char gui_path[GF_MAX_PATH]; #ifdef WIN32 FILE *f; Bool write_access = 0; + strcpy(gui_path, file_path); + /*following code is highly inspired by Osmo4*/ /*do we have the write privileges on this dir ? if not, use user local data directory*/ strcpy(szPath, file_path); @@ -275,6 +293,8 @@ GF_Config *create_default_config(char *file_path, char *file_name) } #else FILE *f; + strcpy(gui_path, ""); + sprintf(szPath, "%s%c%s", file_path, GF_PATH_SEPARATOR, file_name); f = gf_f64_open(szPath, "wt"); fprintf(stdout, "create %s: %s\n", szPath, (f==NULL) ? "Error" : "OK"); @@ -285,16 +305,17 @@ GF_Config *create_default_config(char *file_path, char *file_name) cfg = gf_cfg_new(file_path, file_name); if (!cfg) return NULL; -#ifdef GPAC_MODULES_PATH +#if defined(WIN32) + //szPath still contains the executable directory +#elif defined(GPAC_MODULES_PATH) /* Mac OS and UNIX */ fprintf(stdout, "Using module directory %s \n", GPAC_MODULES_PATH); strcpy(szPath, GPAC_MODULES_PATH); -#elif defined(WIN32) - //szPath still contains the executable directory #else fprintf(stdout, "Please enter full path to GPAC modules directory:\n"); - scanf("%s", szPath); + while ( 1 > scanf("%s", szPath)); #endif gf_cfg_set_key(cfg, "General", "ModulesDirectory", szPath); + gf_cfg_set_key(cfg, "Compositor", "Raster2D", "GPAC 2D Raster"); gf_cfg_set_key(cfg, "Audio", "ForceConfig", "yes"); gf_cfg_set_key(cfg, "Audio", "NumBuffers", "2"); gf_cfg_set_key(cfg, "Audio", "TotalDuration", "120"); @@ -306,32 +327,19 @@ GF_Config *create_default_config(char *file_path, char *file_name) if (szPath[strlen((char*)szPath)-1] != '\\') strcat((char*)szPath, "\\"); strcat((char *)szPath, "Fonts"); #elif defined(__DARWIN__) || defined(__APPLE__) - fprintf(stdout, "Please enter full path to a TrueType font directory (.ttf, .ttc) - enter to default:\n"); - scanf("%s", szPath); + strcpy(szPath, "/Library/Fonts"); #else - /*these fonts seems installed by default on many systems...*/ - gf_cfg_set_key(cfg, "FontEngine", "FontSerif", "Bitstream Vera Serif"); - gf_cfg_set_key(cfg, "FontEngine", "FontSans", "Bitstream Vera Sans"); - gf_cfg_set_key(cfg, "FontEngine", "FontFixed", "Bitstream Vera Monospace"); strcpy(szPath, "/usr/share/fonts/truetype/"); #endif fprintf(stdout, "Using default font directory %s\n", szPath); gf_cfg_set_key(cfg, "FontEngine", "FontDirectory", szPath); -#ifdef WIN32 -/* fprintf(stdout, "Please enter full path to a cache directory for HTTP downloads:\n"); - scanf("%s", szPath); -*/ - GetWindowsDirectory((char*)szPath, MAX_PATH); - if (szPath[strlen((char*)szPath)-1] != '\\') strcat((char*)szPath, "\\"); - strcat((char *)szPath, "Temp"); - - gf_cfg_set_key(cfg, "General", "CacheDirectory", szPath); - fprintf(stdout, "Using default cache directory %s\n", szPath); -#else - fprintf(stdout, "Using /tmp as a cache directory for HTTP downloads:\n"); - gf_cfg_set_key(cfg, "General", "CacheDirectory", "/tmp"); -#endif + { + char * tmp = gf_get_default_cache_directory(); + gf_cfg_set_key(cfg, "General", "CacheDirectory", tmp); + fprintf(stdout, "Using default cache directory %s\n", tmp); + gf_free(tmp); + } gf_cfg_set_key(cfg, "Downloader", "CleanCache", "yes"); gf_cfg_set_key(cfg, "Compositor", "AntiAlias", "All"); @@ -357,11 +365,83 @@ GF_Config *create_default_config(char *file_path, char *file_name) gf_cfg_set_key(cfg, "Network", "UDPTimeout", "10000"); gf_cfg_set_key(cfg, "Network", "BufferLength", "3000"); +#if defined(__DARWIN__) || defined(__APPLE__) + gf_cfg_set_key(cfg, "Video", "DriverName", "SDL Video Output"); +#endif + + if (gui_path[0]) { + FILE *f; + strcat(gui_path, "gui/gui.bt"); + f = fopen(gui_path, "rt"); + if (f) { + fclose(f); + gf_cfg_set_key(cfg, "General", "StartupFile", gui_path); + } + } + + /*store and reload*/ gf_cfg_del(cfg); return gf_cfg_new(file_path, file_name); } +#if (defined(__DARWIN__) || defined(__APPLE__) ) +#include +#endif /* Apple, needs this for _NSGetExecutablePath on Mac OS X */ + +static void check_config_directories(GF_Config *cfg) +{ +#if (defined(__DARWIN__) || defined(__APPLE__) ) + char mod_path[GF_MAX_PATH]; + char gui_path[GF_MAX_PATH]; + char root_path[GF_MAX_PATH]; + char *sep; + const char *opt; + u32 size = GF_MAX_PATH; + if (_NSGetExecutablePath(root_path, &size)!=0) return; + /*installed or symlink on system, do not attempt to modify the path*/ + if (!strnicmp(root_path, "/usr/", 5)) return; + sep = strstr(root_path, ".app/"); + if (sep) { + sep[4] = 0; + } + gui_path[0] = '\0'; + strcpy(mod_path, root_path); + strcat(mod_path, "/Contents/MacOS/modules/"); + { + struct stat buf; + int status_dir; + memset(&buf, 0, sizeof(struct stat)); + status_dir = stat(mod_path, &buf); + if (!status_dir){ + if (!(buf.st_mode & S_IFDIR)){ +#ifdef GPAC_MODULES_PATH + strcpy(mod_path, GPAC_MODULES_PATH); + gui_path[0] = '\0'; +#endif + } else { + /* OK, it means we are in an .app directory ! */ + strcpy(gui_path, root_path); + strcat(gui_path, "/Contents/MacOS/gui/gui.bt"); + } + } else { +#ifdef GPAC_MODULES_PATH + strcpy(mod_path, GPAC_MODULES_PATH); + gui_path[0] = '\0'; +#endif + } + } + opt = gf_cfg_get_key(cfg, "General", "ModulesDirectory"); + /*modules directory has changed, forced to new location*/ + if (!opt || strcmp(opt, mod_path)) { + gf_cfg_set_key(cfg, "General", "ModulesDirectory", mod_path); + if (gui_path[0]) + gf_cfg_set_key(cfg, "General", "StartupFile", gui_path); + } +#endif +} + + static void PrintTime(u64 time) { u32 ms, h, m, s; @@ -387,18 +467,23 @@ static void UpdateRTInfo(const char *legend) return; if (display_rti) { + char szMsg[1024]; 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 (rti.total_cpu_usage) { + 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) ); + } else { + sprintf(szMsg, "FPS %02.2f - CPU %02d - Mem %d kB", + gf_term_get_framerate(term, 0), rti.process_cpu_usage, (u32) (rti.gpac_memory / 1024) ); + } + 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) ); + fprintf(stdout, "%s\r", szMsg); } 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); @@ -473,10 +558,71 @@ void switch_bench() gf_term_set_speed(term, bench_mode ? bench_speed : FIX_ONE); } } +#ifndef WIN32 +#include +int getch() { + struct termios old; + struct termios new; + int rc; + if (tcgetattr(0, &old) == -1) { + return -1; + } + new = old; + new.c_lflag &= ~(ICANON | ECHO); + new.c_cc[VMIN] = 1; + new.c_cc[VTIME] = 0; + if (tcsetattr(0, TCSANOW, &new) == -1) { + return -1; + } + rc = getchar(); + (void) tcsetattr(0, TCSANOW, &old); + return rc; +} +#else +int getch(){ + return getchar(); +} +#endif + +/** + * Reads a line of input from stdin + * @param line the buffer to fill + * @param + */ +static const char * read_line_input(char * line, int maxSize, Bool showContent){ + char read; + int i = 0; + if (fflush( stdout )) + perror("Failed to flush buffer %s"); + do { + line[i] = '\0'; + if (i >= maxSize - 1) + return line; + read = getch(); + if (read == 8 || read == 127){ + if (i > 0){ + fprintf(stdout, "\b \b"); + i--; + } + } else if (read > 32){ + fputc(showContent ? read : '*', stdout); + line[i++] = read; + } + fflush(stdout); + } while (read != '\n'); + if (!read) + return 0; + return line; +} Bool GPAC_EventProc(void *ptr, GF_Event *evt) { if (!term) return 0; + + if (gui_mode) { + if (evt->type==GF_EVENT_QUIT) Run = 0; + return 0; + } switch (evt->type) { case GF_EVENT_DURATION: @@ -494,6 +640,8 @@ Bool GPAC_EventProc(void *ptr, GF_Event *evt) } else { servName = evt->message.service; } + servName = ""; + 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)); @@ -644,7 +792,7 @@ Bool GPAC_EventProc(void *ptr, GF_Event *evt) ResetCaption(); break; case GF_EVENT_EOS: - restart = 1; + if (loop_at_end) restart = 1; break; case GF_EVENT_SIZE: if (user.init_flags & GF_TERM_WINDOWLESS) { @@ -697,54 +845,27 @@ Bool GPAC_EventProc(void *ptr, GF_Event *evt) gf_term_user_event(term, evt); break; case GF_EVENT_AUTHORIZATION: - if (!strlen(evt->auth.user)) { - fprintf(stdout, "Authorization required for site %s\n", evt->auth.site_url); - fprintf(stdout, "login: "); - scanf("%s", evt->auth.user); - } else { - fprintf(stdout, "Authorization required for %s@%s\n", evt->auth.user, evt->auth.site_url); + { + int maxTries = 1; + assert( evt->type == GF_EVENT_AUTHORIZATION); + assert( evt->auth.user); + assert( evt->auth.password); + assert( evt->auth.site_url); + while ((!strlen(evt->auth.user) || !strlen(evt->auth.password)) && (maxTries--) >= 0){ + fprintf(stdout, "**** Authorization required for site %s ****\n", evt->auth.site_url); + fprintf(stdout, "login : "); + read_line_input(evt->auth.user, 50, 1); + fprintf(stdout, "\npassword: "); + read_line_input(evt->auth.password, 50, 0); + printf("*********\n"); + } + if (maxTries < 0){ + printf("**** No User or password has been filled, aborting ***\n"); + return 0; } - fprintf(stdout, "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; } @@ -809,6 +930,7 @@ GF_Config *loadconfigfile(char *filepath) } success: fprintf(stdout, "Using config file in %s directory\n", szPath); + check_config_directories(cfg); return cfg; } @@ -826,23 +948,23 @@ void list_modules(GF_ModuleManager *modules) void set_navigation() { GF_Err e; - char navstr[20], nav; + char nav; u32 type = gf_term_get_option(term, GF_OPT_NAVIGATION_TYPE); e = GF_OK; + fflush(stdin); + if (!type) { fprintf(stdout, "Content/compositor doesn't allow user-selectable navigation\n"); } else if (type==1) { fprintf(stdout, "Select Navigation (\'N\'one, \'E\'xamine, \'S\'lide): "); - scanf("%s", navstr); - nav = navstr[0]; + nav = getch(); if (nav=='N') e = gf_term_set_option(term, GF_OPT_NAVIGATION, GF_NAVIGATE_NONE); else if (nav=='E') e = gf_term_set_option(term, GF_OPT_NAVIGATION, GF_NAVIGATE_EXAMINE); else if (nav=='S') e = gf_term_set_option(term, GF_OPT_NAVIGATION, GF_NAVIGATE_SLIDE); else fprintf(stdout, "Unknown selector \'%c\' - only \'N\',\'E\',\'S\' allowed\n", nav); } else if (type==2) { fprintf(stdout, "Select Navigation (\'N\'one, \'W\'alk, \'F\'ly, \'E\'xamine, \'P\'an, \'S\'lide, \'G\'ame, \'V\'R, \'O\'rbit): "); - scanf("%s", navstr); - nav = navstr[0]; + nav = getch(); if (nav=='N') e = gf_term_set_option(term, GF_OPT_NAVIGATION, GF_NAVIGATE_NONE); else if (nav=='W') e = gf_term_set_option(term, GF_OPT_NAVIGATION, GF_NAVIGATE_WALK); else if (nav=='F') e = gf_term_set_option(term, GF_OPT_NAVIGATION, GF_NAVIGATE_FLY); @@ -868,7 +990,7 @@ static Bool get_time_list(char *arg, u32 *times, u32 *nb_times) /*SMPTE time code*/ if (strchr(arg, ':') && strchr(arg, ';') && strchr(arg, '/')) { - if (sscanf(arg, "%02d:%02d:%02d;%02d/%02d", &h, &m, &s, &f, &fps)==5) { + if (sscanf(arg, "%02ud:%02ud:%02ud;%02ud/%02ud", &h, &m, &s, &f, &fps)==5) { sec = 0; if (fps) sec = ((Double)f) / fps; sec += 3600*h + 60*m + s; @@ -881,7 +1003,7 @@ static Bool get_time_list(char *arg, u32 *times, u32 *nb_times) str = strchr(arg, '-'); if (str) str[0] = 0; /*HH:MM:SS:MS time code*/ - if (strchr(arg, ':') && (sscanf(arg, "%02d:%02d:%02d:%02d", &h, &m, &s, &ms)==4)) { + if (strchr(arg, ':') && (sscanf(arg, "%02ud:%02ud:%02ud:%02ud", &h, &m, &s, &ms)==4)) { sec = ms; sec /= 1000; sec += 3600*h + 60*m + s; @@ -937,6 +1059,10 @@ static void init_rti_logs(char *rti_file, char *url, Bool use_rtix) } } +#ifdef WIN32 +#include +#endif + int main (int argc, char **argv) { char c; @@ -959,6 +1085,9 @@ int main (int argc, char **argv) FILE *playlist = NULL; FILE *logfile = NULL; Float scale = 1; +#ifndef WIN32 + dlopen(NULL, RTLD_NOW|RTLD_GLOBAL); +#endif /*by default use current dir*/ strcpy(the_url, "."); @@ -979,6 +1108,10 @@ int main (int argc, char **argv) i++; } else if (!strcmp(arg, "-mem-track")) enable_mem_tracker = 1; + else if (!strcmp(arg, "-h") || !strcmp(arg, "-help")) { + PrintUsage(); + return 1; + } } gf_sys_init(enable_mem_tracker); @@ -1009,6 +1142,10 @@ int main (int argc, char **argv) use_rtix = 1; } else if (!strcmp(arg, "-fill")) { fill_ar = 1; + } else if (!strcmp(arg, "-gui")) { + gui_mode = 1; + } else if (!strcmp(arg, "-guid")) { + gui_mode = 2; } else if (!strcmp(arg, "-show")) { visible = 1; } else if (!strcmp(arg, "-avi")) { @@ -1034,11 +1171,15 @@ int main (int argc, char **argv) if(depth_dump) dump_mode=7; /*grayscale depth .bmp dump*/ else dump_mode=2; if ((url_arg || (i+2<(u32)argc)) && get_time_list(argv[i+1], times, &nb_times)) i++; + } else if (!strcmp(arg, "-png")) { + dump_mode=11; + if ((url_arg || (i+2<(u32)argc)) && get_time_list(argv[i+1], times, &nb_times)) i++; } else if (!strcmp(arg, "-raw")) { dump_mode = 3; if ((url_arg || (i+2<(u32)argc)) && get_time_list(argv[i+1], times, &nb_times)) i++; } else if (!stricmp(arg, "-size")) { + /*usage of %ud breaks sscanf on MSVC*/ if (sscanf(argv[i+1], "%dx%d", &forced_width, &forced_height) != 2) { forced_width = forced_height = 0; } @@ -1074,10 +1215,11 @@ int main (int argc, char **argv) i++; } else if (!strcmp(arg, "-no-wnd")) user.init_flags |= GF_TERM_WINDOWLESS; + else if (!strcmp(arg, "-no-back")) user.init_flags |= GF_TERM_WINDOW_TRANSPARENT; #if defined(__DARWIN__) || defined(__APPLE__) - else if (!strcmp(arg, "-thread")) not_threaded = 0; + else if (!strcmp(arg, "-thread")) threading_flags = 0; #else - else if (!strcmp(arg, "-no-thread")) not_threaded = 1; + else if (!strcmp(arg, "-no-thread")) threading_flags = GF_TERM_NO_DECODER_THREAD | GF_TERM_NO_COMPOSITOR_THREAD | GF_TERM_WINDOW_NO_THREAD; #endif else if (!strcmp(arg, "-no-audio")) no_audio = 1; else if (!strcmp(arg, "-no-regulation")) no_regulation = 1; @@ -1085,6 +1227,7 @@ int main (int argc, char **argv) else if (!strcmp(arg, "-pause")) pause_at_first = 1; else if (!strcmp(arg, "-exit")) auto_exit = 1; else if (!strcmp(arg, "-mem-track")) enable_mem_tracker = 1; + else if (!strcmp(arg, "-loop")) loop_at_end = 1; else if (!strcmp(arg, "-opt")) { char *sep, *sep2, szSec[1024], szKey[1024], szVal[1024]; sep = strchr(argv[i+1], ':'); @@ -1105,9 +1248,11 @@ int main (int argc, char **argv) i++; } else if (!strncmp(arg, "-run-for=", 9)) simulation_time = atoi(arg+9); - else { + else if (!stricmp(arg, "-help")) { PrintUsage(); return 1; + } else { + fprintf(stdout, "Unrecognized option %s - skipping\n", arg); } } if (dump_mode && !url_arg) { @@ -1116,6 +1261,22 @@ int main (int argc, char **argv) if (logfile) fclose(logfile); return 1; } + + + if (!gui_mode) { + str = gf_cfg_get_key(cfg_file, "General", "ForceGUI"); + if (str && !strcmp(str, "yes")) gui_mode = 1; + } + + if (gui_mode) { + threading_flags = GF_TERM_NO_DECODER_THREAD | GF_TERM_NO_COMPOSITOR_THREAD; + if (gui_mode==1) { + hide_shell(1); + user.init_flags |= GF_TERM_WINDOW_NO_DECORATION; + } + } + + if (dump_mode) rti_file = NULL; if (!logs_set) { @@ -1129,7 +1290,7 @@ int main (int argc, char **argv) /*setup dumping options*/ if (dump_mode) { - user.init_flags |= GF_TERM_NO_AUDIO | GF_TERM_NO_THREAD | GF_TERM_NO_REGULATION /*| GF_TERM_INIT_HIDE*/; + user.init_flags |= GF_TERM_NO_AUDIO | GF_TERM_NO_DECODER_THREAD | GF_TERM_NO_COMPOSITOR_THREAD | GF_TERM_NO_REGULATION /*| GF_TERM_INIT_HIDE*/; if (visible || dump_mode==8) user.init_flags |= GF_TERM_INIT_HIDE; } else { init_w = forced_width; @@ -1138,6 +1299,7 @@ int main (int argc, char **argv) fprintf(stdout, "Loading modules\n"); str = gf_cfg_get_key(cfg_file, "General", "ModulesDirectory"); + assert( str ); user.modules = gf_modules_new((const unsigned char *) str, cfg_file); if (user.modules) i = gf_modules_get_count(user.modules); @@ -1155,10 +1317,12 @@ int main (int argc, char **argv) user.EventProc = GPAC_EventProc; /*dummy in this case (global vars) but MUST be non-NULL*/ user.opaque = user.modules; - if (not_threaded) user.init_flags |= GF_TERM_NO_THREAD; + if (threading_flags) user.init_flags |= threading_flags; if (no_audio) user.init_flags |= GF_TERM_NO_AUDIO; if (no_regulation) user.init_flags |= GF_TERM_NO_REGULATION; + if (threading_flags & (GF_TERM_NO_DECODER_THREAD|GF_TERM_NO_COMPOSITOR_THREAD) ) term_step = 1; + fprintf(stdout, "Loading GPAC Terminal\n"); term = gf_term_new(&user); if (!term) { @@ -1172,7 +1336,6 @@ int main (int argc, char **argv) } fprintf(stdout, "Terminal Loaded\n"); - if (dump_mode) { // gf_term_set_option(term, GF_OPT_VISIBLE, 0); if (fill_ar) gf_term_set_option(term, GF_OPT_ASPECT_RATIO, GF_ASPECT_RATIO_FILL_SCREEN); @@ -1217,18 +1380,22 @@ int main (int argc, char **argv) } else /*connect if requested*/ - if (url_arg) { + if (!gui_mode && url_arg) { char *ext; + strcpy(the_url, url_arg); ext = strrchr(the_url, '.'); - if (ext && (!stricmp(ext, ".m3u") || !stricmp(ext, ".pls"))) { + if (ext && strncmp("http:", the_url, 5) && (!stricmp(ext, ".m3u") || !stricmp(ext, ".pls"))) { fprintf(stdout, "Opening Playlist %s\n", the_url); playlist = gf_f64_open(the_url, "rt"); if (playlist) { strcpy(pl_path, the_url); - fscanf(playlist, "%s", the_url); - fprintf(stdout, "Opening URL %s\n", the_url); - gf_term_connect_with_path(term, the_url, pl_path); + if (1 > fscanf(playlist, "%s", the_url)) + fprintf(stderr, "Cannot read any URL from playlist\n"); + else { + fprintf(stdout, "Opening URL %s\n", the_url); + gf_term_connect_with_path(term, the_url, pl_path); + } } else { fprintf(stdout, "Hit 'h' for help\n\n"); } @@ -1245,12 +1412,17 @@ int main (int argc, char **argv) gf_term_connect(term, str); startup_file = 1; } + if (url_arg) { + gf_cfg_set_key(cfg_file, "Temp", "GUIStartupFile", url_arg); + } } + if (gui_mode==2) gui_mode=0; + if (start_fs) gf_term_set_option(term, GF_OPT_FULLSCREEN, 1); while (Run) { /*we don't want getchar to block*/ - if (!gf_prompt_has_input()) { + if (gui_mode || !gf_prompt_has_input()) { if (restart) { restart = 0; gf_term_play_from_time(term, 0, 0); @@ -1261,7 +1433,7 @@ int main (int argc, char **argv) goto force_input; } if (!use_rtix || display_rti) UpdateRTInfo(NULL); - if (not_threaded) { + if (term_step) { gf_term_process_step(term); if (auto_exit && gf_term_get_option(term, GF_OPT_IS_OVER)) { Run = 0; @@ -1291,17 +1463,27 @@ force_input: startup_file = 0; gf_term_disconnect(term); fprintf(stdout, "Enter the absolute URL\n"); - scanf("%s", the_url); + if (1 > scanf("%s", the_url)){ + fprintf(stderr, "Cannot read absolute URL, aborting\n"); + break; + } 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); + if (1 > scanf("%s", the_url)){ + fprintf(stderr, "Cannot read the absolute URL, aborting.\n"); + break; + } playlist = gf_f64_open(the_url, "rt"); if (playlist) { - fscanf(playlist, "%s", the_url); + if (1 > fscanf(playlist, "%s", the_url)){ + fprintf(stderr, "Cannot read any URL from playlist, aborting.\n"); + fclose( playlist); + break; + } fprintf(stdout, "Opening URL %s\n", the_url); gf_term_connect(term, the_url); } @@ -1324,9 +1506,15 @@ force_input: if (playlist) { u32 count; gf_term_disconnect(term); - scanf("%d", &count); + if (1 > scanf("%u", &count)){ + fprintf(stderr, "Cannot read number, aborting.\n"); + break; + } while (count) { - fscanf(playlist, "%s", the_url); + if (fscanf(playlist, "%s", the_url)){ + fprintf(stderr, "Failed to read line, aborting\n"); + break; + } count--; } fprintf(stdout, "Opening URL %s\n", the_url); @@ -1399,18 +1587,20 @@ force_input: case 'i': if (is_connected) { u32 ID; - fprintf(stdout, "Enter OD ID (0 for main OD): "); - fflush(stdout); - scanf("%d", &ID); + do { + fprintf(stdout, "Enter OD ID (0 for main OD): "); + fflush(stdout); + } while( 1 > scanf("%ud", &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); + do { + fprintf(stdout, "Enter OD number (0 for main OD): "); + fflush(stdout); + } while( 1 > scanf("%ud", &num)); ViewOD(term, (u32)-1, num); } break; @@ -1440,10 +1630,11 @@ force_input: 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); + do { + fprintf(stdout, "Enter Inline OD ID if any or 0 : "); + fflush(stdout); + } while( 1 > scanf("%ud", &odid)); if (odid) { GF_ObjectManager *root_odm = gf_term_get_root_object(term); if (!root_odm) break; @@ -1457,9 +1648,10 @@ force_input: odm = NULL; } } - fprintf(stdout, "Enter file radical name (+\'.x\' for XML dumping) - \"std\" for stdout: "); - fflush(stdout); - scanf("%s", radname); + do{ + fprintf(stdout, "Enter file radical name (+\'.x\' for XML dumping) - \"std\" for stdout: "); + fflush(stdout); + } while( 1 > scanf("%s", radname)); sExt = strrchr(radname, '.'); xml_dump = 0; if (sExt) { @@ -1534,7 +1726,10 @@ force_input: fprintf(stdout, "Enter command to send:\n"); fflush(stdin); szCom[0] = 0; - scanf("%[^\t\n]", szCom); + if (1 > scanf("%[^\t\n]", szCom)){ + fprintf(stderr, "Cannot read command to send, aborting.\n"); + break; + } e = gf_term_scene_update(term, NULL, szCom); if (e) fprintf(stdout, "Processing command failed: %s\n", gf_error_to_string(e)); } @@ -1544,7 +1739,10 @@ force_input: { char szLog[1024]; fprintf(stdout, "Enter new log level:\n"); - scanf("%s", szLog); + if (1 > scanf("%s", szLog)){ + fprintf(stderr, "Cannot read new log level, aborting.\n"); + break; + } gf_log_set_level(gf_log_parse_level(szLog)); } break; @@ -1552,7 +1750,10 @@ force_input: { char szLog[1024]; fprintf(stdout, "Enter new log tools:\n"); - scanf("%s", szLog); + if (1 > scanf("%s", szLog)){ + fprintf(stderr, "Cannot read new log tools, aborting.\n"); + break; + } gf_log_set_tools(gf_log_parse_tools(szLog)); } break; @@ -1566,8 +1767,9 @@ force_input: 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); + do { + fprintf(stdout, "Enter new video cache memory in kBytes (current %ud):\n", gf_term_get_option(term, GF_OPT_VIDEO_CACHE_SIZE)); + } while (1 > scanf("%ud", &size)); gf_term_set_option(term, GF_OPT_VIDEO_CACHE_SIZE, size); } break; @@ -1590,7 +1792,7 @@ force_input: 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); + char *dst = (char*)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) { @@ -1605,7 +1807,7 @@ force_input: fprintf(stdout, "Writing file dump.png\n"); } } - if (dst) free(dst); + if (dst) gf_free(dst); gf_term_release_screen_buffer(term, &fb); } } @@ -1644,7 +1846,10 @@ force_input: gf_sys_close(); if (rti_logs) fclose(rti_logs); if (logfile) fclose(logfile); - fprintf(stdout, "Bye\n"); + + if (gui_mode) { + hide_shell(2); + } return 0; } @@ -2119,7 +2324,10 @@ void PrintGPACConfig() char *secName = NULL; fprintf(stdout, "Enter section name (\"*\" for complete dump):\n"); - scanf("%s", szName); + if (1 > scanf("%s", szName)){ + fprintf(stderr, "No section name, aborting.\n"); + return; + } if (strcmp(szName, "*")) secName = szName; fprintf(stdout, "\n\n*** GPAC Configuration ***\n\n"); diff --git a/applications/mp4client/mp4client.rc b/applications/mp4client/mp4client.rc new file mode 100644 index 0000000..f390733 --- /dev/null +++ b/applications/mp4client/mp4client.rc @@ -0,0 +1,72 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// Français (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 +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +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_ICON3 ICON "..\\..\\doc\\osmo4.ico" +#endif // Français (France) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/applications/mp4client/resource.h b/applications/mp4client/resource.h new file mode 100644 index 0000000..2b88c52 --- /dev/null +++ b/applications/mp4client/resource.h @@ -0,0 +1,18 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by mp4client.rc +// +#define IDI_ICON1 102 +#define IDI_ICON2 103 +#define IDI_ICON3 105 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 106 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/applications/osmo4_android/.classpath b/applications/osmo4_android/.classpath new file mode 100644 index 0000000..2ae0f23 --- /dev/null +++ b/applications/osmo4_android/.classpath @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/applications/osmo4_android/.project b/applications/osmo4_android/.project new file mode 100644 index 0000000..2bb783b --- /dev/null +++ b/applications/osmo4_android/.project @@ -0,0 +1,33 @@ + + + Osmo4 + + + + + + com.android.ide.eclipse.adt.ResourceManagerBuilder + + + + + com.android.ide.eclipse.adt.PreCompilerBuilder + + + + + org.eclipse.jdt.core.javabuilder + + + + + com.android.ide.eclipse.adt.ApkBuilder + + + + + + com.android.ide.eclipse.adt.AndroidNature + org.eclipse.jdt.core.javanature + + diff --git a/applications/osmo4_android/.settings/de.loskutov.anyedit.AnyEditTools.prefs b/applications/osmo4_android/.settings/de.loskutov.anyedit.AnyEditTools.prefs new file mode 100644 index 0000000..c2330bf --- /dev/null +++ b/applications/osmo4_android/.settings/de.loskutov.anyedit.AnyEditTools.prefs @@ -0,0 +1,16 @@ +#Fri Oct 08 13:52:16 CEST 2010 +activeContentFilterList=*.makefile,makefile,*.Makefile,Makefile,Makefile.*,*.mk,MANIFEST.MF +addNewLine=true +convertActionOnSaave=AnyEdit.CnvrtTabToSpaces +eclipse.preferences.version=1 +inActiveContentFilterList= +javaTabWidthForJava=true +org.eclipse.jdt.ui.editor.tab.width=2 +projectPropsEnabled=false +removeTrailingSpaces=true +replaceAllSpaces=false +replaceAllTabs=false +saveAndAddLine=false +saveAndConvert=false +saveAndTrim=true +useModulo4Tabs=false diff --git a/applications/osmo4_android/AndroidManifest.xml b/applications/osmo4_android/AndroidManifest.xml new file mode 100644 index 0000000..b0d3ccf --- /dev/null +++ b/applications/osmo4_android/AndroidManifest.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/applications/osmo4_android/build.xml b/applications/osmo4_android/build.xml new file mode 100644 index 0000000..e65e2d8 --- /dev/null +++ b/applications/osmo4_android/build.xml @@ -0,0 +1,104 @@ + + + + + + + + + + + + + + + + + + + + + + + + Android Ant Build. Available targets: + help: Displays this help. + clean: Removes output files created by other targets. + compile: Compiles project's .java files into .class files. + debug: Builds the application and signs it with a debug key. + release: Builds the application. The generated apk file must be + signed before it is published. + install: Installs/reinstalls the debug package onto a running + emulator or device. + If the application was previously installed, the + signatures must match. + uninstall: Uninstalls the application from a running emulator or + device. + + + + + + + + + + + + diff --git a/applications/osmo4_android/default.properties b/applications/osmo4_android/default.properties new file mode 100644 index 0000000..9d79b12 --- /dev/null +++ b/applications/osmo4_android/default.properties @@ -0,0 +1,11 @@ +# This file is automatically generated by Android Tools. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must be checked in Version Control Systems. +# +# To customize properties used by the Ant build system use, +# "build.properties", and override values to adapt the script to your +# project structure. + +# Project target. +target=android-4 diff --git a/applications/osmo4_android/jni/README.txt b/applications/osmo4_android/jni/README.txt new file mode 100644 index 0000000..05c508f --- /dev/null +++ b/applications/osmo4_android/jni/README.txt @@ -0,0 +1,2 @@ +To build Android version, Please execute the script : +../../../build/android/jni/gpac_build_android diff --git a/applications/osmo4_android/jni/wrapper-bitmap-20-09-2010.cpp b/applications/osmo4_android/jni/wrapper-bitmap-20-09-2010.cpp new file mode 100644 index 0000000..b6fda33 --- /dev/null +++ b/applications/osmo4_android/jni/wrapper-bitmap-20-09-2010.cpp @@ -0,0 +1,960 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) ENST 2009- + * Authors: Jean Le Feuvre + * All rights reserved + * + * Created by NGO Van Luyen, Ivica ARSOV / ARTEMIS / Telecom SudParis /Institut TELECOM on Oct, 2010 + * + * This file is part of GPAC / Wrapper + * + * 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 "wrapper.h" + +#include + +#undef PI +#define PI 3.1415926535897932f + +#include +#ifndef ANDROID_NDK +//#include +#endif /* !ANDROID_NDK */ +//--------------------------------------------------------------------------------------------------- +//--------------------------------------------------------------------------------------------------- +//CPeriodic +//------------------------------- +CPeriodic::CPeriodic(){} +//------------------------------- +CPeriodic::~CPeriodic(){} +//------------------------------- +void CPeriodic::Start(){ +} +//------------------------------- +void CPeriodic::Cancel(){} +//--------------------------------------------------------------------------------------------------- + +void initGL() +{ + /* Enable smooth shading */ + glShadeModel(GL_SMOOTH); + + /* Set the background black */ + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + + /* Depth buffer setup */ + glClearDepthf(1.0f); + + /* Enables Depth Testing */ + glEnable(GL_DEPTH_TEST); + + /* The Type Of Depth Test To Do */ + glDepthFunc(GL_LEQUAL); + + /* Really Nice Perspective Calculations */ + glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); +} + +void gluPerspective(GLfloat fovy, GLfloat aspect, + GLfloat zNear, GLfloat zFar) +{ + GLfloat xmin, xmax, ymin, ymax; + + ymax = zNear * (GLfloat)tan(fovy * PI / 360); + ymin = -ymax; + xmin = ymin * aspect; + xmax = ymax * aspect; + + glFrustumx((GLfixed)(xmin * 65536), (GLfixed)(xmax * 65536), + (GLfixed)(ymin * 65536), (GLfixed)(ymax * 65536), + (GLfixed)(zNear * 65536), (GLfixed)(zFar * 65536)); +} + +void resizeWindow(int width, int height) +{ + /* Height / width ration */ + GLfloat ratio; + + /* Protect against a divide by zero */ + if (height==0) + { + height=1; + } + + ratio=(GLfloat)width/(GLfloat)height; + + /* Setup our viewport. */ + glViewport(0, 0, (GLsizei)width, (GLsizei)height); + + /* change to the projection matrix and set our viewing volume. */ + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + + /* Set our perspective */ + gluPerspective(45.0f, ratio, 0.1f, 100.0f); + + /* Make sure we're chaning the model view and not the projection */ + glMatrixMode(GL_MODELVIEW); + + /* Reset The View */ + glLoadIdentity(); +} + +void drawGLScene() +{ + GLfloat vertices[4][3]; + + /* Clear The Screen And The Depth Buffer */ + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + /* Enable VERTEX array */ + glEnableClientState(GL_VERTEX_ARRAY); + + /* Setup pointer to VERTEX array */ + glVertexPointer(3, GL_FLOAT, 0, vertices); + + /* Move Left 1.5 Units And Into The Screen 6.0 */ + glLoadIdentity(); + glTranslatef(-1.5f, 0.0f, -6.0f); + + /* Top Of Triangle */ + vertices[0][0]=0.0f; vertices[0][1]=1.0f; vertices[0][2]=0.0f; + /* Left Of Triangle */ + vertices[1][0]=-1.0f; vertices[1][1]=-1.0f; vertices[1][2]=0.0f; + /* Right Of Triangle */ + vertices[2][0]=1.0f; vertices[2][1]=-1.0f; vertices[2][2]=0.0f; + + /* Drawing Using Triangles, draw triangles using 3 vertices */ + glDrawArrays(GL_TRIANGLES, 0, 3); + + /* Move Right 3 Units */ + glLoadIdentity(); + glTranslatef(1.5f, 0.0f, -6.0f); + + /* Top Right Of The Quad */ + vertices[0][0]=1.0f; vertices[0][1]=1.0f; vertices[0][2]=0.0f; + /* Top Left Of The Quad */ + vertices[1][0]=-1.0f; vertices[1][1]=1.0f; vertices[1][2]=0.0f; + /* Bottom Left Of The Quad */ + vertices[2][0]=1.0f; vertices[2][1]=-1.0f; vertices[2][2]=0.0f; + /* Bottom Right Of The Quad */ + vertices[3][0]=-1.0f; vertices[3][1]=-1.0f; vertices[3][2]=0.0f; + + /* Drawing using triangle strips, draw triangles using 4 vertices */ + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + /* Disable vertex array */ + glDisableClientState(GL_VERTEX_ARRAY); + + /* Flush all drawings */ + glFinish(); +} + +//--------------------------------------------------------------------------------------------------- +//CNativeWrapper +//------------------------------- +CNativeWrapper::CNativeWrapper(){ + do_log = 1; + logfile = NULL; + + m_pTimer = NULL; +#ifndef GPAC_GUI_ONLY + memset(&m_user, 0, sizeof(GF_User)); + m_term = NULL; + m_mx = NULL; + memset(&m_rti, 0, sizeof(GF_SystemRTInfo)); +#endif + +#ifdef DEBUG_MODE + debug_f = fopen(DEBUG_FILE, "w"); +#endif +} +//------------------------------- +CNativeWrapper::~CNativeWrapper(){ + Shutdown(); + + if (logfile) fclose(logfile); + +#ifdef DEBUG_MODE + if (debug_f){ + debug_log("~CNativeWrapper()\n"); + fclose(debug_f); + } + +#endif +} +//------------------------------- +void CNativeWrapper::debug_log(const char* msg){ +#ifdef DEBUG_MODE + fprintf(debug_f, "%s\n", msg); + fflush(debug_f); +#endif +} +//------------------------------- +void CNativeWrapper::Shutdown() +{ + + if (m_mx) gf_mx_del(m_mx); + + MessageBox("Osmo4 shutdown request", ""); + if (m_pTimer) { + m_pTimer->Cancel(); + delete m_pTimer; + m_pTimer = NULL; + } +#ifndef GPAC_GUI_ONLY + if (m_term) { + GF_Terminal *t = m_term; + m_term = NULL; + gf_term_del(t); + } + if (m_user.config) { + gf_cfg_del(m_user.config); + m_user.config = NULL; + } + if (m_user.modules) { + gf_modules_del(m_user.modules); + m_user.modules = NULL; + } +#endif + MessageBox("Osmo4 shutdown OK", ""); +} +//------------------------------- +int CNativeWrapper::MessageBox(const char* msg, const char* title){ + //call java function to display a message box + + debug_log(msg); + + return 1; +} +//------------------------------- +int CNativeWrapper::OnTick(){ +#ifndef GPAC_GUI_ONLY + if (m_term) gf_term_process_step(m_term); + + /*check RTI display*/ + //if (show_rti && gf_sys_get_rti(500, &m_rti, 0)) DisplayRTI(); +#endif + /*never stop...*/ + return 1; +} +//------------------------------- +void CNativeWrapper::DisplayRTI(){ +#ifndef GPAC_GUI_ONLY + // display some system informations ? +#endif +} +//------------------------------- +int CNativeWrapper::Quit(int code){ + + Shutdown(); + // send shutdown request to java + return code; +} +//------------------------------- +void CNativeWrapper::on_gpac_log(void *cbk, u32 ll, u32 lm, const char *fmt, va_list list){ + // do log here + FILE *logs = (FILE*)cbk; + + /*if (rti_logs && (lm & GF_LOG_RTI)) { + char szMsg[2048]; + vsprintf(szMsg, fmt, list); + UpdateRTInfo(szMsg + 6 ); + } else { + if (log_time_start) fprintf(logs, "[At %d]", gf_sys_clock() - log_time_start); + vfprintf(logs, fmt, list); + fflush(logs); + }*/ + + vfprintf(logs, fmt, list); + fflush(logs); + +} +//------------------------------- +Bool CNativeWrapper::GPAC_EventProc(void *ptr, GF_Event *evt){ + //event + return true; +} +//------------------------------- +void CNativeWrapper::Osmo4_progress_cbk(void *usr, char *title, u32 done, u32 total){ + +} +//------------------------------- +void CNativeWrapper::SetupLogs(){ + const char *opt; + debug_log("SetupLogs()"); + +//#ifndef GPAC_GUI_ONLY + gf_mx_p(m_mx); + if (do_log) { + gf_log_set_level(0); + do_log = 0; + } + /*setup GPAC logs: log all errors*/ + gf_log_set_level(GF_LOG_DEBUG); + gf_log_set_tools(0xFFFFFFFF); + opt = gf_cfg_get_key(m_user.config, "General", "LogLevel"); + if ((opt && !stricmp(opt, "debug")) /*|| 1*/) { + FILE *logs = fopen(GPAC_LOG_FILE, "wt"); + if (!logs) { + MessageBox("Cannot open log file - disabling logs", "Warning !"); + } else { + MessageBox("Debug log enabled in \\data\\gpac_logs.txt", "Info"); + fclose(logs); + do_log = 1; + gf_log_set_level(GF_LOG_DEBUG); + gf_log_set_tools(0xFFFFFFFF); + } + } + if (!do_log) { + gf_log_set_level(GF_LOG_ERROR); + gf_log_set_tools(0xFFFFFFFF); + } + + + gf_log_set_level(GF_LOG_DEBUG); + gf_log_set_tools(0xFFFFFFFF); + + logfile = fopen(GPAC_LOG_FILE, "wt"); + + gf_log_set_callback(logfile, on_gpac_log); + gf_mx_v(m_mx); + + GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("Osmo4 logs initialized\n")); +//#endif +} +//------------------------------- +int CNativeWrapper::init(JNIEnv * env, jobject * bitmap){ + + debug_log("int CNativeWrapper::init()"); + + int m_Width = 100, m_Height = 100; + + int first_launch = 0; + const char *opt; + + m_window = env; + m_session = bitmap; + + m_mx = gf_mx_new("Osmo4"); + + //load config file + m_user.config = gf_cfg_new(GPAC_CFG_DIR, "GPAC.cfg"); + if (!m_user.config) { + first_launch = 1; + FILE *ft = fopen(GPAC_CFG_DIR"GPAC.cfg", "wt"); + if (!ft) { + MessageBox("Cannot create GPAC Config file", "Fatal Error"); + return Quit(KErrGeneral); + } else { + fclose(ft); + } + m_user.config = gf_cfg_new(GPAC_CFG_DIR, "GPAC.cfg"); + if (!m_user.config) { + MessageBox("GPAC Configuration file not found", "Fatal Error"); + return Quit(KErrGeneral); + } + } + + SetupLogs(); + gf_set_progress_callback(this, Osmo4_progress_cbk); + + opt = gf_cfg_get_key(m_user.config, "General", "ModulesDirectory"); + if (!opt) first_launch = 2; + + if (first_launch) { + /*hardcode module directory*/ + gf_cfg_set_key(m_user.config, "General", "ModulesDirectory", GPAC_MODULES_DIR); + /*hardcode cache directory*/ + gf_cfg_set_key(m_user.config, "General", "CacheDirectory", GPAC_CACHE_DIR); + gf_cfg_set_key(m_user.config, "Downloader", "CleanCache", "yes"); + /*startup file*/ + //gf_cfg_set_key(m_user.config, "General", "StartupFile", GPAC_CFG_DIR"gpac.mp4"); + /*setup UDP traffic autodetect*/ + gf_cfg_set_key(m_user.config, "Network", "AutoReconfigUDP", "yes"); + gf_cfg_set_key(m_user.config, "Network", "UDPTimeout", "10000"); + gf_cfg_set_key(m_user.config, "Network", "BufferLength", "3000"); + + gf_cfg_set_key(m_user.config, "Compositor", "TextureTextMode", "Default"); + //gf_cfg_set_key(m_user.config, "Compositor", "FrameRate", "30"); + + gf_cfg_set_key(m_user.config, "Video", "DriverName", "Android Video Output"); + + gf_cfg_set_key(m_user.config, "FontEngine", "FontReader", "ft_font"); + + gf_cfg_set_key(m_user.config, "FontEngine", "FontDirectory", GPAC_FONT_DIR); + + + /*save cfg and reload*/ + gf_cfg_del(m_user.config); + m_user.config = gf_cfg_new(GPAC_CFG_DIR, "GPAC.cfg"); + if (!m_user.config) { + MessageBox("Cannot save initial GPAC Config file", "Fatal Error"); + return Quit(KErrGeneral); + } + + + MessageBox("Osmo4", "Thank you for Installing"); + } + + /*load modules*/ + opt = gf_cfg_get_key(m_user.config, "General", "ModulesDirectory"); + m_user.modules = gf_modules_new(opt, m_user.config); + if (!m_user.modules || !gf_modules_get_count(m_user.modules)) { + MessageBox(m_user.modules ? "No modules available" : "Cannot create module manager", "Fatal Error"); + if (m_user.modules) gf_modules_del(m_user.modules); + gf_cfg_del(m_user.config); + return Quit(KErrGeneral); + } + + if (first_launch) { + /*first launch, register all files ext*/ + for (u32 i=0; iCanHandleURL(ifce, "test.test"); + gf_modules_close_interface((GF_BaseInterface *)ifce); + } + } + } + + /*we don't thread the terminal, ie appart from the audio renderer, media decoding and visual rendering is + handled by the app process*/ + m_user.init_flags = GF_TERM_NO_THREAD | GF_TERM_NO_REGULATION; + m_user.init_flags |= GF_TERM_NO_AUDIO; + m_user.EventProc = GPAC_EventProc; + m_user.opaque = this; + m_user.os_window_handler = m_window; + m_user.os_display = m_session; + + m_term = gf_term_new(&m_user); + if (!m_term) { + MessageBox("Cannot load GPAC terminal", "Fatal Error"); + gf_modules_del(m_user.modules); + gf_cfg_del(m_user.config); + return Quit(KErrGeneral); + } + MessageBox("GPAC terminal loaded", "Success !"); + + gf_term_set_size(m_term, m_Width, m_Height); + + m_pTimer = new CPeriodic(); + m_pTimer->Start(); + + opt = gf_cfg_get_key(m_user.config, "General", "StartupFile"); + //if (opt) gf_term_connect(m_term, opt); + + //initGL(); +} +//------------------------------- +int CNativeWrapper::connect(const char *url){ + /*debug_log("Starting to connect ..."); + + gf_term_connect_from_time(m_term, url, 0, false); + //gf_term_connect(m_term, url); + + debug_log("connected ...");*/ + + char pl_path[GF_MAX_PATH]; + + strcpy(pl_path, url); + + debug_log("Connecting to ..."); + debug_log(pl_path); + + //gf_term_connect_with_path(m_term, url, pl_path); + gf_term_connect(m_term, url); + debug_log("connected ..."); +} +//----------------------------------------------------- +void CNativeWrapper::disconnect(){ + + gf_term_disconnect(m_term); + debug_log("disconnected ..."); +} +//----------------------------------------------------- +void CNativeWrapper::step(JNIEnv * env, jobject * bitmap){ + m_window = env; + m_session = bitmap; + debug_log("Step ..."); + if (!m_term) + debug_log("m_term ..."); + else + if (!m_term->compositor) + debug_log("compositor ..."); + else + if (!m_term->compositor->video_out) + debug_log("video_out ..."); + else + if (!m_term->compositor->video_out->Setup) + debug_log("Setup ..."); + else + { + debug_log("Video Setup ..."); + m_term->compositor->video_out->Setup(m_term->compositor->video_out, m_window, m_session, -1); + } + gf_term_process_step(m_term); + //drawGLScene(); +} +//----------------------------------------------------- +void CNativeWrapper::resize(int w, int h){ + gf_term_set_size(m_term, w, h); + //resizeWindow(w,h); +} +//----------------------------------------------------- +void CNativeWrapper::onMouseDown(float x, float y){ + char msg[100]; + sprintf(msg, "onMousedown x=%f, y=%f", x, y ); + debug_log(msg); + + GF_Event evt; + evt.type = GF_EVENT_MOUSEDOWN; + evt.mouse.button = GF_MOUSE_LEFT; + evt.mouse.x = x; + evt.mouse.y = y; + + int ret = gf_term_user_event(m_term, &evt); +} +//----------------------------------------------------- +void CNativeWrapper::onMouseUp(float x, float y){ + char msg[100]; + sprintf(msg, "onMouseUp x=%f, y=%f", x, y ); + debug_log(msg); + + GF_Event evt; + evt.type = GF_EVENT_MOUSEUP; + evt.mouse.button = GF_MOUSE_LEFT; + evt.mouse.x = x; + evt.mouse.y = y; + + int ret = gf_term_user_event(m_term, &evt); +} +//----------------------------------------------------- +void CNativeWrapper::onKeyPress(int keycode, int rawkeycode, int up, int flag){ + GF_Event evt; + if (up == 0) evt.type = GF_EVENT_KEYUP; + else evt.type = GF_EVENT_KEYDOWN; + + evt.key.flags = 0; + evt.key.hw_code = rawkeycode; + + char msg[100]; + sprintf(msg, "onKeyPress keycode=%d", keycode); + debug_log(msg); + + //translate_key(keycode, &evt.key); + evt.key.key_code = GF_KEY_A; + int ret = gf_term_user_event(m_term, &evt); + /*generate a key up*/ + + sprintf(msg, "onKeyPress gpac keycode=%d, GF_KEY_A=%d, ret=%d", evt.key.key_code, GF_KEY_A, ret); + debug_log(msg); +} +//----------------------------------------------------- +void CNativeWrapper::translate_key(ANDROID_KEYCODE keycode, GF_EventKey *evt){ + evt->flags = 0; + + char msg[100]; + sprintf(msg, "translate_key keycode=%d", keycode); + debug_log(msg); + + switch (keycode) { + case ANDROID_KEYCODE_BACK: evt->key_code = GF_KEY_BACKSPACE; break; + case ANDROID_KEYCODE_TAB: evt->key_code = GF_KEY_TAB; break; + case ANDROID_KEYCODE_CLEAR: evt->key_code = GF_KEY_CLEAR; break; + case ANDROID_KEYCODE_ENTER: evt->key_code = GF_KEY_ENTER; break; + case ANDROID_KEYCODE_SHIFT_LEFT: evt->key_code = GF_KEY_SHIFT; break; + case ANDROID_KEYCODE_SHIFT_RIGHT: evt->key_code = GF_KEY_SHIFT; break; + //case VK_CONTROL: evt->key_code = GF_KEY_CONTROL; break; + case ANDROID_KEYCODE_ALT_LEFT: evt->key_code = GF_KEY_ALT; break; + case ANDROID_KEYCODE_ALT_RIGHT: evt->key_code = GF_KEY_ALT; break; + //case VK_PAUSE: evt->key_code = GF_KEY_PAUSE; break; + //case VK_CAPITAL: evt->key_code = GF_KEY_CAPSLOCK; break; + //case VK_KANA: evt->key_code = GF_KEY_KANAMODE; break; + //case VK_JUNJA: evt->key_code = GF_KEY_JUNJAMODE; break; + //case VK_FINAL: evt->key_code = GF_KEY_FINALMODE; break; + //case VK_KANJI: evt->key_code = GF_KEY_KANJIMODE; break; + //case VK_ESCAPE: evt->key_code = GF_KEY_ESCAPE; break; + //case VK_CONVERT: evt->key_code = GF_KEY_CONVERT; break; + case ANDROID_KEYCODE_SPACE: evt->key_code = GF_KEY_SPACE; break; + //case VK_PRIOR: evt->key_code = GF_KEY_PAGEUP; break; + //case VK_NEXT: evt->key_code = GF_KEY_PAGEDOWN; break; + //case VK_END: evt->key_code = GF_KEY_END; break; + case ANDROID_KEYCODE_HOME: evt->key_code = GF_KEY_HOME; break; + case ANDROID_KEYCODE_DPAD_LEFT: evt->key_code = GF_KEY_LEFT; break; + case ANDROID_KEYCODE_DPAD_UP: evt->key_code = GF_KEY_UP; break; + case ANDROID_KEYCODE_DPAD_RIGHT: evt->key_code = GF_KEY_RIGHT; break; + case ANDROID_KEYCODE_DPAD_DOWN: evt->key_code = GF_KEY_DOWN; break; + //case VK_SELECT: evt->key_code = GF_KEY_SELECT; break; + //case VK_PRINT: + //case VK_SNAPSHOT: + // evt->key_code = GF_KEY_PRINTSCREEN; break; + //case VK_EXECUTE: evt->key_code = GF_KEY_EXECUTE; break; + //case VK_INSERT: evt->key_code = GF_KEY_INSERT; break; + case ANDROID_KEYCODE_DEL: evt->key_code = GF_KEY_DEL; break; + //case VK_HELP: evt->key_code = GF_KEY_HELP; break; + + +/* case '!': evt->key_code = GF_KEY_EXCLAMATION; break; + case '"': evt->key_code = GF_KEY_QUOTATION; break; + case '#': evt->key_code = GF_KEY_NUMBER; break; + case '$': evt->key_code = GF_KEY_DOLLAR; break; + case '&': evt->key_code = GF_KEY_AMPERSAND; break; + case '\'': evt->key_code = GF_KEY_APOSTROPHE; break; + case '(': evt->key_code = GF_KEY_LEFTPARENTHESIS; break; + case ')': evt->key_code = GF_KEY_RIGHTPARENTHESIS; break; + case ',': evt->key_code = GF_KEY_COMMA; break; + case ':': evt->key_code = GF_KEY_COLON; break; + case ';': evt->key_code = GF_KEY_SEMICOLON; break; + case '<': evt->key_code = GF_KEY_LESSTHAN; break; + case '>': evt->key_code = GF_KEY_GREATERTHAN; break; + case '?': evt->key_code = GF_KEY_QUESTION; break; + case '@': evt->key_code = GF_KEY_AT; break; + case '[': evt->key_code = GF_KEY_LEFTSQUAREBRACKET; break; + case ']': evt->key_code = GF_KEY_RIGHTSQUAREBRACKET; break; + case '\\': evt->key_code = GF_KEY_BACKSLASH; break; + case '_': evt->key_code = GF_KEY_UNDERSCORE; break; + case '`': evt->key_code = GF_KEY_GRAVEACCENT; break; + case ' ': evt->key_code = GF_KEY_SPACE; break; + case '/': evt->key_code = GF_KEY_SLASH; break; + case '*': evt->key_code = GF_KEY_STAR; break; + case '-': evt->key_code = GF_KEY_HIPHEN; break; + case '+': evt->key_code = GF_KEY_PLUS; break; + case '=': evt->key_code = GF_KEY_EQUALS; break; + case '^': evt->key_code = GF_KEY_CIRCUM; break; + case '{': evt->key_code = GF_KEY_LEFTCURLYBRACKET; break; + case '}': evt->key_code = GF_KEY_RIGHTCURLYBRACKET; break; + case '|': evt->key_code = GF_KEY_PIPE; break; +*/ + + +/* case VK_LWIN: return ; + case VK_RWIN: return ; + case VK_APPS: return ; +*/ + case ANDROID_KEYCODE_0: + evt->key_code = GF_KEY_0; + evt->flags = GF_KEY_EXT_NUMPAD; + break; + case ANDROID_KEYCODE_1: + evt->key_code = GF_KEY_1; + evt->flags = GF_KEY_EXT_NUMPAD; + break; + case ANDROID_KEYCODE_2: + evt->key_code = GF_KEY_2; + evt->flags = GF_KEY_EXT_NUMPAD; + break; + case ANDROID_KEYCODE_3: + evt->key_code = GF_KEY_3; + evt->flags = GF_KEY_EXT_NUMPAD; + break; + case ANDROID_KEYCODE_4: + evt->key_code = GF_KEY_4; + evt->flags = GF_KEY_EXT_NUMPAD; + break; + case ANDROID_KEYCODE_5: + evt->key_code = GF_KEY_5; + evt->flags = GF_KEY_EXT_NUMPAD; + break; + case ANDROID_KEYCODE_6: + evt->key_code = GF_KEY_6; + evt->flags = GF_KEY_EXT_NUMPAD; + break; + case ANDROID_KEYCODE_7: + evt->key_code = GF_KEY_7; + evt->flags = GF_KEY_EXT_NUMPAD; + break; + case ANDROID_KEYCODE_8: + evt->key_code = GF_KEY_8; + evt->flags = GF_KEY_EXT_NUMPAD; + break; + case ANDROID_KEYCODE_9: + evt->key_code = GF_KEY_9; + evt->flags = GF_KEY_EXT_NUMPAD; + break; + case ANDROID_KEYCODE_A: + evt->key_code = GF_KEY_9; + evt->flags = GF_KEY_EXT_NUMPAD; + break; + /* + case VK_MULTIPLY: + evt->key_code = GF_KEY_STAR; + evt->flags = GF_KEY_EXT_NUMPAD; + break; + case VK_ADD: + evt->key_code = GF_KEY_PLUS; + evt->flags = GF_KEY_EXT_NUMPAD; + break; + case VK_SEPARATOR: + evt->key_code = GF_KEY_FULLSTOP; + evt->flags = GF_KEY_EXT_NUMPAD; + break; + case VK_SUBTRACT: + evt->key_code = GF_KEY_HYPHEN; + evt->flags = GF_KEY_EXT_NUMPAD; + break; + case VK_DECIMAL: + evt->key_code = GF_KEY_COMMA; + evt->flags = GF_KEY_EXT_NUMPAD; + break; + case VK_DIVIDE: + evt->key_code = GF_KEY_SLASH; + evt->flags = GF_KEY_EXT_NUMPAD; + break; + + case VK_F1: evt->key_code = GF_KEY_F1; break; + case VK_F2: evt->key_code = GF_KEY_F2; break; + case VK_F3: evt->key_code = GF_KEY_F3; break; + case VK_F4: evt->key_code = GF_KEY_F4; break; + case VK_F5: evt->key_code = GF_KEY_F5; break; + case VK_F6: evt->key_code = GF_KEY_F6; break; + case VK_F7: evt->key_code = GF_KEY_F7; break; + case VK_F8: evt->key_code = GF_KEY_F8; break; + case VK_F9: evt->key_code = GF_KEY_F9; break; + case VK_F10: evt->key_code = GF_KEY_F10; break; + case VK_F11: evt->key_code = GF_KEY_F11; break; + case VK_F12: evt->key_code = GF_KEY_F12; break; + case VK_F13: evt->key_code = GF_KEY_F13; break; + case VK_F14: evt->key_code = GF_KEY_F14; break; + case VK_F15: evt->key_code = GF_KEY_F15; break; + case VK_F16: evt->key_code = GF_KEY_F16; break; + case VK_F17: evt->key_code = GF_KEY_F17; break; + case VK_F18: evt->key_code = GF_KEY_F18; break; + case VK_F19: evt->key_code = GF_KEY_F19; break; + case VK_F20: evt->key_code = GF_KEY_F20; break; + case VK_F21: evt->key_code = GF_KEY_F21; break; + case VK_F22: evt->key_code = GF_KEY_F22; break; + case VK_F23: evt->key_code = GF_KEY_F23; break; + case VK_F24: evt->key_code = GF_KEY_F24; break; + + case VK_NUMLOCK: evt->key_code = GF_KEY_NUMLOCK; break; + case VK_SCROLL: evt->key_code = GF_KEY_SCROLL; break; + */ + +/* + * VK_L* & VK_R* - left and right Alt, Ctrl and Shift virtual keys. + * Used only as parameters to GetAsyncKeyState() and GetKeyState(). + * No other API or message will distinguish left and right keys in this way. + */ + /* + case ANDROID_KEYCODE_SHIFT_LEFT: + evt->key_code = GF_KEY_SHIFT; + evt->flags = GF_KEY_EXT_LEFT; + break; + case ANDROID_KEYCODE_SHIFT_RIGHT: + evt->key_code = GF_KEY_SHIFT; + evt->flags = GF_KEY_EXT_RIGHT; + break; + case VK_LCONTROL: + evt->key_code = GF_KEY_CONTROL; + evt->flags = GF_KEY_EXT_LEFT; + break; + case VK_RCONTROL: + evt->key_code = GF_KEY_CONTROL; + evt->flags = GF_KEY_EXT_RIGHT; + break; + case VK_LMENU: + evt->key_code = GF_KEY_ALT; + evt->flags = GF_KEY_EXT_LEFT; + break; + case VK_RMENU: + evt->key_code = GF_KEY_ALT; + evt->flags = GF_KEY_EXT_RIGHT; + break; + */ + + /*thru VK_9 are the same as ASCII '0' thru '9' (0x30 - 0x39) */ + /* VK_A thru VK_Z are the same as ASCII 'A' thru 'Z' (0x41 - 0x5A) */ + default: + if ((keycode>=ANDROID_KEYCODE_A) && (keycode<=ANDROID_KEYCODE_Z)) { evt->key_code = GF_KEY_A + keycode - ANDROID_KEYCODE_A; debug_log("default keycode"); } + else + evt->key_code = GF_KEY_UNIDENTIFIED; + break; + } + + //evt->hw_code = keycode; + evt->hw_code = evt->key_code; +} +//----------------------------------------------------- +GF_Config *CNativeWrapper::create_default_config(char *file_path, char *file_name){ + GF_Config *cfg; + char szPath[GF_MAX_PATH]; + FILE *f; + sprintf(szPath, "%s%c%s", file_path, GF_PATH_SEPARATOR, file_name); + f = fopen(szPath, "wt"); + fprintf(stdout, "create %s: %s\n", szPath, (f==NULL) ? "Error" : "OK"); + if (!f) return NULL; + fclose(f); + + cfg = gf_cfg_new(file_path, file_name); + if (!cfg) return NULL; + +#ifdef GPAC_MODULES_PATH + fprintf(stdout, "Using module directory %s \n", GPAC_MODULES_PATH); + strcpy(szPath, GPAC_MODULES_PATH); +#elif defined(WIN32) + strcpy(szPath, file_path); +#else + fprintf(stdout, "Please enter full path to GPAC modules directory:\n"); + scanf("%s", szPath); +#endif + gf_cfg_set_key(cfg, "General", "ModulesDirectory", szPath); + gf_cfg_set_key(cfg, "Audio", "ForceConfig", "yes"); + gf_cfg_set_key(cfg, "Audio", "NumBuffers", "2"); + gf_cfg_set_key(cfg, "Audio", "TotalDuration", "120"); + gf_cfg_set_key(cfg, "Audio", "DisableNotification", "no"); + gf_cfg_set_key(cfg, "FontEngine", "FontReader", "ft_font"); + + /*these fonts seems installed by default on many systems...*/ + //gf_cfg_set_key(cfg, "FontEngine", "FontSerif", "Bitstream Vera Serif"); + //gf_cfg_set_key(cfg, "FontEngine", "FontSans", "Bitstream Vera Sans"); + //gf_cfg_set_key(cfg, "FontEngine", "FontFixed", "Bitstream Vera Monospace"); + strcpy(szPath, "/system/fonts/"); + + fprintf(stdout, "Using default font directory %s\n", szPath); + gf_cfg_set_key(cfg, "FontEngine", "FontDirectory", szPath); + + fprintf(stdout, "Using /tmp as a cache directory for HTTP downloads:\n"); + gf_cfg_set_key(cfg, "General", "CacheDirectory", "/data/osmo/tmp"); + + gf_cfg_set_key(cfg, "Downloader", "CleanCache", "yes"); + gf_cfg_set_key(cfg, "Compositor", "AntiAlias", "All"); + gf_cfg_set_key(cfg, "Compositor", "FrameRate", "30"); + /*use power-of-2 emulation*/ + gf_cfg_set_key(cfg, "Compositor", "EmulatePOW2", "yes"); + + gf_cfg_set_key(cfg, "Video", "DriverName", "Android Video Output"); + /*x11 only supports scalable zoom*/ + gf_cfg_set_key(cfg, "Compositor", "ScalableZoom", "yes"); + gf_cfg_set_key(cfg, "Audio", "DriverName", "SDL Audio Output"); + + gf_cfg_set_key(cfg, "Video", "SwitchResolution", "no"); + gf_cfg_set_key(cfg, "Network", "AutoReconfigUDP", "yes"); + gf_cfg_set_key(cfg, "Network", "UDPTimeout", "10000"); + gf_cfg_set_key(cfg, "Network", "BufferLength", "3000"); + + /*store and reload*/ + gf_cfg_del(cfg); + return gf_cfg_new(file_path, file_name); +} +//--------------------------------------------------------------------------------------------------- +//--------------------------------------------------------------------------------------------------- +// this function will be called by Java to init gpac + +void gpac_init(JNIEnv * env, jobject * bitmap){ + + gpac_obj = new CNativeWrapper(); + if (gpac_obj) gpac_obj->init(env, bitmap); + //if (gpac_obj) gpac_obj->connect("/data/osmo/tidycity.mp4"); +} + +void gpac_render(JNIEnv * env, jobject * bitmap){ + if (gpac_obj) gpac_obj->step(env, bitmap); +} + +void gpac_connect(const char *url){ + //gpac_obj->connect("/data/osmo/tidycity.mp4"); + gpac_obj->connect(url); +} + +void gpac_resize(int w, int h){ + if (gpac_obj) gpac_obj->resize(w, h); +} + +void gpac_disconnect(){ + if (gpac_obj) gpac_obj->disconnect(); +} + +void gpac_free(){ + if (gpac_obj) delete gpac_obj; +} + +void gpac_onmousedown(float x, float y){ + if (gpac_obj) gpac_obj->onMouseDown(x, y); +} + +void gpac_onmouseup(float x, float y){ + if (gpac_obj) gpac_obj->onMouseUp(x, y); +} +void gpac_onkeypress(int keycode, int rawkeycode, int up, int flag){ + if (gpac_obj) gpac_obj->onKeyPress(keycode, rawkeycode, up, flag); +} +//--------------------------------------------------------------------------------------------------- +//--------------------------------------------------------------------------------------------------- + +JNIEXPORT void JNICALL Java_com_artemis_Osmo4_GpacObject_gpacinit(JNIEnv * env, jobject obj, jobject bitmap, jint width, jint height) +{ + int x = width; + int y = height; + gpac_init(env, &bitmap); + //initGL(); +} +//----------------------------------- +JNIEXPORT void JNICALL Java_com_artemis_Osmo4_GpacObject_gpacconnect(JNIEnv * env, jobject obj, jstring url) +{ + const char *the_url = env->GetStringUTFChars(url, NULL); + //(*env)->ReleaseStringUTFChars(env, prompt, str); + gpac_connect(the_url); +} +//----------------------------------- +JNIEXPORT void JNICALL Java_com_artemis_Osmo4_GpacObject_gpacdisconnect(JNIEnv * env, jobject obj){ + gpac_disconnect(); +} +//----------------------------------- +JNIEXPORT void JNICALL Java_com_artemis_Osmo4_GpacObject_gpacfree(JNIEnv * env, jobject obj) +{ + gpac_free(); +} +//----------------------------------- +JNIEXPORT void JNICALL Java_com_artemis_Osmo4_GpacObject_gpacrender (JNIEnv * env, jobject obj, jobject bitmap) +{ + gpac_render(env, &bitmap); + //drawGLScene(); +} +//----------------------------------- +JNIEXPORT void JNICALL Java_com_artemis_Osmo4_GpacObject_gpacresize (JNIEnv * env, jobject obj, jint width, jint height) +{ + int w = width; + int h = height; + gpac_resize(w,h); + //resizeWindow(w,h); +} +//----------------------------------- +JNIEXPORT void JNICALL Java_com_artemis_Osmo4_GpacObject_gpaceventmousedown(JNIEnv * env, jobject obj, jfloat x, jfloat y){ + gpac_onmousedown(x, y); +} +//----------------------------------- +JNIEXPORT void JNICALL Java_com_artemis_Osmo4_GpacObject_gpaceventmouseup(JNIEnv * env, jobject obj, jfloat x, jfloat y){ + gpac_onmouseup(x, y); +} +//----------------------------------- +JNIEXPORT void JNICALL Java_com_artemis_Osmo4_GpacObject_gpaceventkeypress(JNIEnv * env, jobject obj, jint keycode, jint rawkeycode, jint up, jint flag){ + gpac_onkeypress(keycode, rawkeycode, up, flag); +} +//----------------------------------- diff --git a/applications/osmo4_android/jni/wrapper-bitmap-20-09-2010.h b/applications/osmo4_android/jni/wrapper-bitmap-20-09-2010.h new file mode 100644 index 0000000..8e39ad9 --- /dev/null +++ b/applications/osmo4_android/jni/wrapper-bitmap-20-09-2010.h @@ -0,0 +1,228 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) ENST 2009- + * Authors: Jean Le Feuvre + * All rights reserved + * + * Created by NGO Van Luyen, Ivica ARSOV / ARTEMIS / Telecom SudParis /Institut TELECOM on Oct, 2010 + * + * This file is part of GPAC / Wrapper + * + * 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 + +#define KErrGeneral 1 +#define GPAC_CFG_DIR "/data/osmo/" +#define GPAC_MODULES_DIR "/data/osmo/modules/" +#define GPAC_MODULES_PATH "/data/osmo/modules/" +#define GPAC_CACHE_DIR "/data/osmo/cache/" +#define GPAC_LOG_FILE "/data/osmo/gpac_logs.txt" +#define GPAC_FONT_DIR "/system/fonts/" + +#define DEBUG_MODE 1 +#define DEBUG_FILE "/data/osmo/osmo_debug.txt" + +// keyboard code +#define ANDROID_KEYCODE int +#define ANDROID_KEYCODE_0 7 +#define ANDROID_KEYCODE_1 8 +#define ANDROID_KEYCODE_2 9 +#define ANDROID_KEYCODE_3 10 +#define ANDROID_KEYCODE_4 11 +#define ANDROID_KEYCODE_5 12 +#define ANDROID_KEYCODE_6 13 +#define ANDROID_KEYCODE_7 14 +#define ANDROID_KEYCODE_8 15 +#define ANDROID_KEYCODE_9 16 +#define ANDROID_KEYCODE_A 29 +#define ANDROID_KEYCODE_B 30 +#define ANDROID_KEYCODE_C 31 +#define ANDROID_KEYCODE_D 32 +#define ANDROID_KEYCODE_E 33 +#define ANDROID_KEYCODE_F 34 +#define ANDROID_KEYCODE_G 35 +#define ANDROID_KEYCODE_H 36 +#define ANDROID_KEYCODE_I 37 +#define ANDROID_KEYCODE_J 38 +#define ANDROID_KEYCODE_K 39 +#define ANDROID_KEYCODE_L 40 +#define ANDROID_KEYCODE_M 41 +#define ANDROID_KEYCODE_N 42 +#define ANDROID_KEYCODE_O 43 +#define ANDROID_KEYCODE_P 44 +#define ANDROID_KEYCODE_Q 45 +#define ANDROID_KEYCODE_R 46 +#define ANDROID_KEYCODE_S 47 +#define ANDROID_KEYCODE_T 48 +#define ANDROID_KEYCODE_U 49 +#define ANDROID_KEYCODE_V 50 +#define ANDROID_KEYCODE_W 51 +#define ANDROID_KEYCODE_X 52 +#define ANDROID_KEYCODE_Y 53 +#define ANDROID_KEYCODE_Z 54 +#define ANDROID_KEYCODE_ALT_LEFT 57 +#define ANDROID_KEYCODE_ALT_RIGHT 58 +#define ANDROID_KEYCODE_AT 77 +#define ANDROID_KEYCODE_BACK 4 +#define ANDROID_KEYCODE_BACKSLASH 73 +#define ANDROID_KEYCODE_CALL 5 +#define ANDROID_KEYCODE_CAMERA 27 +#define ANDROID_KEYCODE_CLEAR 28 +#define ANDROID_KEYCODE_COMMA 55 +#define ANDROID_KEYCODE_DEL 67 +#define ANDROID_KEYCODE_DPAD_CENTER 23 +#define ANDROID_KEYCODE_DPAD_DOWN 20 +#define ANDROID_KEYCODE_DPAD_LEFT 21 +#define ANDROID_KEYCODE_DPAD_RIGHT 22 +#define ANDROID_KEYCODE_DPAD_UP 19 +#define ANDROID_KEYCODE_ENDCALL 6 +#define ANDROID_KEYCODE_ENTER 66 +#define ANDROID_KEYCODE_ENVELOPE 65 +#define ANDROID_KEYCODE_EQUALS 70 +#define ANDROID_KEYCODE_EXPLORER 64 +#define ANDROID_KEYCODE_FOCUS 80 +#define ANDROID_KEYCODE_GRAVE 68 +#define ANDROID_KEYCODE_HEADSETHOOK 79 +#define ANDROID_KEYCODE_HOME 3 +#define ANDROID_KEYCODE_LEFT_BRACKET 71 +#define ANDROID_KEYCODE_MEDIA_FAST_FORWARD 90 +#define ANDROID_KEYCODE_MEDIA_NEXT 87 +#define ANDROID_KEYCODE_MEDIA_PLAY_PAUSE 85 +#define ANDROID_KEYCODE_MEDIA_PREVIOUS 88 +#define ANDROID_KEYCODE_MEDIA_REWIND 89 +#define ANDROID_KEYCODE_MEDIA_STOP 86 +#define ANDROID_KEYCODE_MENU 82 +#define ANDROID_KEYCODE_MINUS 69 +#define ANDROID_KEYCODE_MUTE 91 +#define ANDROID_KEYCODE_NUM 78 +#define ANDROID_KEYCODE_PLUS 81 +#define ANDROID_KEYCODE_POWER 26 +#define ANDROID_KEYCODE_RIGHT_BRACKET 72 +#define ANDROID_KEYCODE_SEARCH 84 +#define ANDROID_KEYCODE_SEMICOLON 74 +#define ANDROID_KEYCODE_SHIFT_LEFT 59 +#define ANDROID_KEYCODE_SHIFT_RIGHT 60 +#define ANDROID_KEYCODE_SLASH 76 +#define ANDROID_KEYCODE_SOFT_LEFT 1 +#define ANDROID_KEYCODE_SOFT_RIGHT 2 +#define ANDROID_KEYCODE_SPACE 62 +#define ANDROID_KEYCODE_STAR 17 +#define ANDROID_KEYCODE_SYM 63 +#define ANDROID_KEYCODE_TAB 61 + +#define ANDROID_KEYCODE_UNKWON -1 + +//--------------------------------------------------------------------------------------------------- +//--------------------------------------------------------------------------------------------------- +class CPeriodic{ + public: + CPeriodic(); + ~CPeriodic(); + + void Start(); + void Cancel(); + +}; +//--------------------------------------------------------------------------------------------------- +//--------------------------------------------------------------------------------------------------- +class CNativeWrapper{ + + private: + void* m_window; + void* m_session; + + GF_User *GetUser() { return &m_user; } + GF_Terminal *m_term; + + CPeriodic *m_pTimer; + GF_Mutex *m_mx; + GF_User m_user; + GF_SystemRTInfo m_rti; + + int do_log; + FILE *logfile; + private: + void SetupLogs(); + void Shutdown(); + void DisplayRTI(); + public: + CNativeWrapper(); + ~CNativeWrapper(); + int init(JNIEnv * env, jobject * bitmap); + int OnTick(); + + int connect(const char *url); + void disconnect(); + void step(JNIEnv * env, jobject * bitmap); + void resize(int w, int h); + + void onMouseDown(float x, float y); + void onMouseUp(float x, float y); + void onKeyPress(int keycode, int rawkeycode, int up, int flag); + void translate_key(ANDROID_KEYCODE keycode, GF_EventKey *evt); + public: + int MessageBox(const char* msg, const char* title); + int Quit(int code); + GF_Config *create_default_config(char *file_path, char *file_name); + + static void on_gpac_log(void *cbk, u32 ll, u32 lm, const char *fmt, va_list list); + static Bool GPAC_EventProc(void *ptr, GF_Event *evt); + static void Osmo4_progress_cbk(void *usr, char *title, u32 done, u32 total); + + private: +#ifdef DEBUG_MODE + FILE *debug_f; +#endif + void debug_log(const char* msg); + +}; +//--------------------------------------------------------------------------------------------------- +//--------------------------------------------------------------------------------------------------- +// this function will be called by Java to init gpac +CNativeWrapper* gpac_obj = NULL; + +/* +void gpac_init(); +void gpac_connect(const char *url); +void gpac_disconnect(); +void gpac_render(); +void gpac_free(); +*/ +//--------------------------------------------------------------------------------------------------- +//--------------------------------------------------------------------------------------------------- +extern "C" { + JNIEXPORT void JNICALL Java_com_artemis_Osmo4_GpacObject_gpacinit(JNIEnv * env, jobject obj, jobject bitmap, jint width, jint height); + JNIEXPORT void JNICALL Java_com_artemis_Osmo4_GpacObject_gpacconnect(JNIEnv * env, jobject obj, jstring url); + JNIEXPORT void JNICALL Java_com_artemis_Osmo4_GpacObject_gpacdisconnect(JNIEnv * env, jobject obj); + JNIEXPORT void JNICALL Java_com_artemis_Osmo4_GpacObject_gpacrender(JNIEnv * env, jobject obj, jobject bitmap); + JNIEXPORT void JNICALL Java_com_artemis_Osmo4_GpacObject_gpacresize (JNIEnv * env, jobject obj, jint width, jint height); + JNIEXPORT void JNICALL Java_com_artemis_Osmo4_GpacObject_gpacfree(JNIEnv * env, jobject obj); + + JNIEXPORT void JNICALL Java_com_artemis_Osmo4_GpacObject_gpaceventmousedown(JNIEnv * env, jobject obj, jfloat x, jfloat y); + JNIEXPORT void JNICALL Java_com_artemis_Osmo4_GpacObject_gpaceventmouseup(JNIEnv * env, jobject obj, jfloat x, jfloat y); + JNIEXPORT void JNICALL Java_com_artemis_Osmo4_GpacObject_gpaceventkeypress(JNIEnv * env, jobject obj, jint keycode, jint rawkeycode, jint up, jint flag); +}; + diff --git a/applications/osmo4_android/jni/wrapper.cpp b/applications/osmo4_android/jni/wrapper.cpp new file mode 100644 index 0000000..4855fad --- /dev/null +++ b/applications/osmo4_android/jni/wrapper.cpp @@ -0,0 +1,910 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) ENST 2009- + * Authors: Jean Le Feuvre + * All rights reserved + * + * Created by NGO Van Luyen, Ivica ARSOV / ARTEMIS / Telecom SudParis /Institut TELECOM on Oct, 2010 + * + * This file is part of GPAC / Wrapper + * + * 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 "wrapper.h" +#include "wrapper_jni.c" + +#include +#include + +#define TAG "GPAC_WRAPPER" + +#define LOGV(X, Y) __android_log_print(ANDROID_LOG_VERBOSE, TAG, X, Y) +#define LOGD(X, Y) __android_log_print(ANDROID_LOG_DEBUG, TAG, X, Y) +#define LOGE(X, Y) __android_log_print(ANDROID_LOG_ERROR, TAG, X, Y) +#define LOGW(X, Y) __android_log_print(ANDROID_LOG_WARN, TAG, X, Y) +#define LOGI(X, Y) __android_log_print(ANDROID_LOG_INFO, TAG, X, Y) +#include + +static JavaVM* javaVM = NULL; + +static pthread_key_t jni_thread_env_key = 0; + +/** + * This method is called when a pthread is destroyed, so we can delete the JNI env + */ +static void jni_destroy_env_func(void * env) { + LOGI("Destroying a thread with attached data, env=%p.\n", env); + JavaEnvTh * jniEnv = (JavaEnvTh *) env; + if (jniEnv){ + /*jniEnv->env->DeleteLocalRef(&jniEnv->cbk_displayMessage); + jniEnv->env->DeleteLocalRef(&jniEnv->cbk_onProgress); + jniEnv->env->DeleteLocalRef(&jniEnv->cbk_showKeyboard); + jniEnv->env->DeleteLocalRef(&jniEnv->cbk_setCaption); + jniEnv->env->DeleteLocalRef(&jniEnv->cbk_onLog);*/ + memset(jniEnv, 0, sizeof(JavaEnvTh)); + gf_free(jniEnv); + } + pthread_setspecific(jni_thread_env_key, NULL); + if (javaVM) + javaVM->DetachCurrentThread(); +} + +static int jniRegisterNativeMethods(JNIEnv* env, const char* className, + const JNINativeMethod* gMethods, int numMethods) +{ + jclass clazz; + clazz = env->FindClass(className); + if (clazz == NULL) { + LOGE("Native registration unable to find class '%s'\n", className); + return -1; + } + if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) { + LOGE("RegisterNatives failed for '%s'\n", className); + return -1; + } + return 0; +} + +static JNINativeMethod sMethods[] = { + /* name, signature, funcPtr */ + + {"createInstance", + "(Lcom/artemis/Osmo4/GpacCallback;IILjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)J", + (void*)&Java_com_artemis_Osmo4_GPACInstance_createInstance}, + {"gpacdisconnect", + "()V", + (void*)Java_com_artemis_Osmo4_GPACInstance_gpacdisconnect}, + {"gpacrender", + "()V", + (void*)Java_com_artemis_Osmo4_GPACInstance_gpacrender}, + {"gpacresize", + "(II)V", + (void*)Java_com_artemis_Osmo4_GPACInstance_gpacresize}, + {"gpacfree", + "()V", + (void*)Java_com_artemis_Osmo4_GPACInstance_gpacfree}, + {"gpaceventkeypress", + "(IIIII)V", + (void*)Java_com_artemis_Osmo4_GPACInstance_gpaceventkeypress}, + {"gpaceventmousedown", + "(FF)V", + (void*)Java_com_artemis_Osmo4_GPACInstance_gpaceventmousedown}, + {"gpaceventmouseup", + "(FF)V", + (void*)Java_com_artemis_Osmo4_GPACInstance_gpaceventmouseup}, + {"gpaceventmousemove", + "(FF)V", + (void*)Java_com_artemis_Osmo4_GPACInstance_gpaceventmousemove}, + {"setGpacPreference", + "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V", + (void*)Java_com_artemis_Osmo4_GPACInstance_setGpacPreference}, + NULL +}; + + +jint JNI_OnUnLoad(JavaVM* vm, void* reserved){ + LOGI("Deleting library, vm=%p...\n", vm); + if (pthread_key_delete(jni_thread_env_key)){ + LOGW("Failed to delete key jni_thread_env_key jni_thread_env_key=%p\n", jni_thread_env_key); + } + javaVM = NULL; + jni_thread_env_key = NULL; +} + +//--------------------------------------------------------------------------------------------------- +jint JNI_OnLoad(JavaVM* vm, void* reserved){ + const char * className = "com/artemis/Osmo4/GPACInstance"; + JNIEnv * env; + if (!vm) + return -1; + if (vm->GetEnv((void**)(&env), JNI_VERSION_1_2) != JNI_OK) + return -1; + javaVM = vm; + LOGI("Registering %s natives\n", className); + if (jniRegisterNativeMethods(env, className, sMethods, 9) < 0){ + LOGE("Failed to register native methods for %s !\n", className); + return -1; + } + LOGI("Registering natives DONE, now registering pthread_keys with destructor=%p\n", &jni_destroy_env_func); + int ret = pthread_key_create(&jni_thread_env_key, &jni_destroy_env_func); + if (ret){ + LOGE("Failed to register jni_thread_env_key jni_thread_env_key=%p\n", jni_thread_env_key); + } + return JNI_VERSION_1_2; +} + +//--------------------------------------------------------------------------------------------------- +//CNativeWrapper +//------------------------------- + +CNativeWrapper::CNativeWrapper(){ + do_log = 1; + m_term = NULL; + m_mx = NULL; +#ifndef GPAC_GUI_ONLY + memset(&m_user, 0, sizeof(GF_User)); + memset(&m_rti, 0, sizeof(GF_SystemRTInfo)); +#endif +} +//------------------------------- +CNativeWrapper::~CNativeWrapper(){ + debug_log("~CNativeWrapper()"); + JavaEnvTh * env = getEnv(); + if (env && env->cbk_obj) + env->env->DeleteGlobalRef(env->cbk_obj); + Shutdown(); + debug_log("~CNativeWrapper() : DONE\n"); +} +//------------------------------- +void CNativeWrapper::debug_log(const char* msg){ + LOGV("%s", msg); +} +//------------------------------- +void CNativeWrapper::Shutdown() +{ + debug_log("shutdown"); + if (m_term) + gf_term_disconnect(m_term); + if (m_mx) + gf_mx_del(m_mx); + m_mx = NULL; +#ifndef GPAC_GUI_ONLY + if (m_term) { + GF_Terminal *t = m_term; + m_term = NULL; + gf_term_del(t); + } + if (m_user.config) { + gf_cfg_del(m_user.config); + m_user.config = NULL; + } + if (m_user.modules) { + gf_modules_del(m_user.modules); + m_user.modules = NULL; + } +#endif + m_term = NULL; + debug_log("shutdown end"); +} + +void CNativeWrapper::setJavaEnv(JavaEnvTh * envToSet, JNIEnv *env, jobject callback){ + assert( envToSet ); + jclass localRef = env->GetObjectClass(callback); + envToSet->env = env; + envToSet->javaThreadId = gf_th_id(); + envToSet->cbk_obj = callback; + envToSet->cbk_displayMessage = + env->GetMethodID(localRef, "displayMessage", "(Ljava/lang/String;Ljava/lang/String;I)V"); + envToSet->cbk_onProgress = + env->GetMethodID(localRef, "onProgress", "(Ljava/lang/String;II)V"); + envToSet->cbk_onLog = + env->GetMethodID(localRef, "onLog", "(IILjava/lang/String;)V"); + envToSet->cbk_setCaption = + env->GetMethodID(localRef, "setCaption", "(Ljava/lang/String;)V"); + envToSet->cbk_showKeyboard = + env->GetMethodID(localRef, "showKeyboard", "(Z)V"); + env->DeleteLocalRef(localRef); +} + +static u32 beforeThreadExits(void * param){ + LOGI("Before Thread exist, detach the JavaVM from Thread for thread %p...\n", gf_th_current()); + if (javaVM) + javaVM->DetachCurrentThread(); +} + +JavaEnvTh * CNativeWrapper::getEnv(){ + JNIEnv *env; + JavaEnvTh * javaEnv; + if (!javaVM){ + debug_log("************* No JVM Found ************"); + return NULL; + } + javaEnv = (JavaEnvTh*) pthread_getspecific( jni_thread_env_key ); + if (javaEnv) + return javaEnv; + javaEnv = (JavaEnvTh *) gf_malloc(sizeof(JavaEnvTh)); + if (!javaEnv) + return NULL; + memset(javaEnv, 0, sizeof(JavaEnvTh)); + javaVM->AttachCurrentThread(&env, NULL); + if (!env){ + LOGE("Attaching to thread did faild for thread id=%d", gf_th_id()); + gf_free(javaEnv); + return NULL; + } + LOGI("Rebuilding methods for thread %d", gf_th_id()); + setJavaEnv(javaEnv, env, mainJavaEnv.cbk_obj); + if (pthread_setspecific(jni_thread_env_key, javaEnv)){ + LOGE("Failed to set specific thread data to jni_thread_env_key for thread=%d. No ENV available !", gf_th_id()); + gf_free(javaEnv); + return NULL; + } + GF_Thread * t; + LOGI("Getting current Thread %d...", gf_th_id()); + t = gf_th_current(); + LOGI("Getting current Thread DONE = %p, now registering before exit...", t); + + if (GF_OK != gf_register_before_exit_function(gf_th_current(), &beforeThreadExits)){ + LOGE("Failed to register exit function for thread %p, no javaEnv for current thread.", gf_th_current()); + //javaVM->DetachCurrentThread(); + gf_free(javaEnv); + javaEnv = NULL; + } + LOGI("Registering DONE for %d", gf_th_id()); + return javaEnv; +} + + +//------------------------------- +int CNativeWrapper::MessageBox(const char* msg, const char* title, GF_Err status){ + LOGV("MessageBox start %s", msg); + JavaEnvTh * env = getEnv(); + if (!env || !env->cbk_displayMessage) + return 0; + env->env->PushLocalFrame(2); + jstring tit = env->env->NewStringUTF(title?title:"null"); + jstring mes = env->env->NewStringUTF(msg?msg:"null"); + env->env->CallVoidMethod(env->cbk_obj, env->cbk_displayMessage, mes, tit, status); + env->env->PopLocalFrame(NULL); + LOGV("MessageBox done %s", msg); + return 1; +} +//------------------------------- +void CNativeWrapper::DisplayRTI(){ +#ifndef GPAC_GUI_ONLY + // display some system informations ? +#endif +} +//------------------------------- +int CNativeWrapper::Quit(int code){ + + Shutdown(); + // send shutdown request to java + return code; +} + +#include + +//------------------------------- +void CNativeWrapper::on_gpac_log(void *cbk, u32 ll, u32 lm, const char *fmt, va_list list){ + char szMsg[4096]; + const char * tag; + char unknTag[32]; + int debug; + // We do not want to be flood by mutexes + if (ll == GF_LOG_DEBUG && lm == GF_LOG_MUTEX) + return; + switch (ll){ + case GF_LOG_DEBUG: + debug = ANDROID_LOG_DEBUG; + break; + case GF_LOG_INFO: + debug = ANDROID_LOG_INFO; + break; + case GF_LOG_WARNING: + debug = ANDROID_LOG_WARN; + break; + case GF_LOG_ERROR: + debug = ANDROID_LOG_ERROR; + break; + default: + debug = ANDROID_LOG_INFO; + } + vsnprintf(szMsg, 4096, fmt, list); + CNativeWrapper * self = (CNativeWrapper *) cbk; + if (!self) + goto displayInAndroidlogs; + + { + JavaEnvTh *env = self->getEnv(); + jstring msg; + if (!env || !env->cbk_onLog) + goto displayInAndroidlogs; + env->env->PushLocalFrame(1); + msg = env->env->NewStringUTF(szMsg); + env->env->CallVoidMethod(env->cbk_obj, env->cbk_onLog, debug, lm, msg); + env->env->PopLocalFrame(NULL); + return; + } +displayInAndroidlogs: + { + /* When no callback is properly set, we use direct logging */ + switch( lm){ + case GF_LOG_CORE: + tag="GF_LOG_CORE"; + break; + case GF_LOG_CODING: + tag="GF_LOG_CODING"; + break; + case GF_LOG_CONTAINER: + tag="GF_LOG_CONTAINER"; + break; + case GF_LOG_NETWORK: + tag="GF_LOG_NETWORK"; + break; + case GF_LOG_RTP: + tag="GF_LOG_RTP"; + break; + case GF_LOG_AUTHOR: + tag="GF_LOG_AUTHOR"; + break; + case GF_LOG_SYNC: + tag="GF_LOG_SYNC"; + break; + case GF_LOG_CODEC: + tag="GF_LOG_CODEC"; + break; + case GF_LOG_PARSER: + tag="GF_LOG_PARSER"; + break; + case GF_LOG_MEDIA: + tag="GF_LOG_MEDIA"; + break; + case GF_LOG_SCENE: + tag="GF_LOG_SCENE"; + break; + case GF_LOG_SCRIPT: + tag="GF_LOG_SCRIPT"; + break; + case GF_LOG_INTERACT: + tag="GF_LOG_INTERACT"; + break; + case GF_LOG_COMPOSE: + tag="GF_LOG_COMPOSE"; + break; + case GF_LOG_CACHE: + tag="GF_LOG_CACHE"; + break; + case GF_LOG_MMIO: + tag="GF_LOG_MMIO"; + break; + case GF_LOG_RTI: + tag="GF_LOG_RTI"; + break; + case GF_LOG_SMIL: + tag="GF_LOG_SMIL"; + break; + case GF_LOG_MEMORY: + tag="GF_LOG_MEMORY"; + break; + case GF_LOG_AUDIO: + tag="GF_LOG_AUDIO"; + break; + case GF_LOG_MODULE: + tag="GF_LOG_MODULE"; + break; + case GF_LOG_MUTEX: + tag="GF_LOG_MUTEX"; + break; + default: + snprintf(unknTag, 32, "GPAC_UNKNOWN[%d]", lm); + tag = unknTag; + } + __android_log_print(debug, tag, szMsg); + } +} +//------------------------------- +Bool CNativeWrapper::GPAC_EventProc(void *cbk, GF_Event *evt){ + if (cbk) + { + CNativeWrapper* ptr = (CNativeWrapper*)cbk; + char msg[4096]; + msg[0] = 0; + LOGD("GPAC_EventProc() Message=%d", evt->type); + switch (evt->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_LONGKEYPRESS: + case GF_EVENT_TEXTINPUT: + /* We ignore all these events */ + break; + case GF_EVENT_MEDIA_BEGIN_SESSION_SETUP: + case GF_EVENT_MEDIA_END_SESSION_SETUP: + case GF_EVENT_MEDIA_DATA_REQUEST: + case GF_EVENT_MEDIA_PLAYABLE: + case GF_EVENT_MEDIA_NOT_PLAYABLE: + case GF_EVENT_MEDIA_DATA_PROGRESS: + case GF_EVENT_MEDIA_END_OF_DATA: + case GF_EVENT_MEDIA_STOP: + case GF_EVENT_MEDIA_ERROR: + LOGD("GPAC_EventProc() Media Event detected = [index=%d]", evt->type - GF_EVENT_MEDIA_BEGIN_SESSION_SETUP); + break; + case GF_EVENT_MESSAGE: + { + ptr->debug_log("GPAC_EventProc start"); + if ( evt->message.message ) + { + strcat(msg, evt->message.message); + strcat(msg, ": "); + } + strcat(msg, gf_error_to_string(evt->message.error)); + + ptr->debug_log(msg); + ptr->MessageBox(msg, evt->message.service ? evt->message.service : "GF_EVENT_MESSAGE", evt->message.error); + ptr->debug_log("GPAC_EventProc end"); + }; + break; + case GF_EVENT_CONNECT: + if (evt->connect.is_connected) + ptr->MessageBox("Connected", "Connected to scene", GF_OK); + else + ptr->MessageBox("Disconnected", "Disconnected from scene.", GF_OK); + break; + case GF_EVENT_PROGRESS: + { + const char * szTitle;; + if (evt->progress.progress_type==0) + szTitle = "Buffering"; + else if (evt->progress.progress_type==1) + szTitle = "Downloading..."; + else if (evt->progress.progress_type==2) + szTitle = "Import "; + else + szTitle = "Unknown Progress Event"; + ptr->Osmo4_progress_cbk(ptr, szTitle, evt->progress.done, evt->progress.total); + gf_set_progress(szTitle, evt->progress.done, evt->progress.total); + } + break; + case GF_EVENT_TEXT_EDITING_START: + case GF_EVENT_TEXT_EDITING_END: + { + JavaEnvTh * env = ptr->getEnv(); + if (!env || !env->cbk_showKeyboard) + return 0; + LOGI("Needs to display/hide the Virtual Keyboard (%d)", evt->type); + env->env->CallVoidMethod(env->cbk_obj, env->cbk_showKeyboard, GF_EVENT_TEXT_EDITING_START == evt->type); + LOGV("Done showing virtual keyboard (%d)", evt->type); + } + break; + case GF_EVENT_EOS: + LOGI("EOS Reached (%d)", evt->type); + break; + case GF_EVENT_DISCONNECT: + /* FIXME : not sure about this behaviour */ + if (ptr) + ptr->disconnect(); + break; + default: + LOGI("Unknown Message %d", evt->type); + } + } + return 0; +} + + +void CNativeWrapper::progress_cbk(const char *title, u64 done, u64 total){ + JavaEnvTh *env = getEnv(); + if (!env || !env->cbk_onProgress) + return; + debug_log("Osmo4_progress_cbk start"); + env->env->PushLocalFrame(1); + jstring js = env->env->NewStringUTF(title); + env->env->CallVoidMethod(env->cbk_obj, env->cbk_onProgress, js, done, total); + env->env->PopLocalFrame(NULL); + debug_log("Osmo4_progress_cbk end"); +} + + +//------------------------------- +void CNativeWrapper::Osmo4_progress_cbk(const void *usr, const char *title, u64 done, u64 total){ + if (!usr) + return; + CNativeWrapper * self = (CNativeWrapper *) usr; + self->progress_cbk(title, done, total); +} +//------------------------------- +void CNativeWrapper::SetupLogs(){ + const char *opt; + debug_log("SetupLogs()"); + + gf_mx_p(m_mx); + + /*setup GPAC logs: log all errors*/ + gf_log_set_level(GF_LOG_DEBUG); + gf_log_set_tools(0xFFFFFFFF); + + opt = gf_cfg_get_key(m_user.config, "General", "LogLevel"); + /* FIXME : set the loglevel according to config file */ + + opt = gf_cfg_get_key(m_user.config, "General", "LogTools"); + if (opt) gf_log_set_tools(gf_log_parse_tools(opt)); + + gf_log_set_callback(this, on_gpac_log); + gf_mx_v(m_mx); + + GF_LOG(GF_LOG_INFO, GF_LOG_CORE, ("Osmo4 logs initialized\n")); + /* Test for JNI invocations, should work properly + int k; + for (k = 0 ; k < 512; k++){ + GF_LOG(GF_LOG_INFO, GF_LOG_CORE, ("Message %d\n", k)); + }*/ +} +//------------------------------- +// dir should end with / +int CNativeWrapper::init(JNIEnv * env, void * bitmap, jobject * callback, int width, int height, const char * cfg_dir, const char * modules_dir, const char * cache_dir, const char * font_dir, const char * urlToLoad){ + LOGI("Initializing GPAC with URL=%s...", urlToLoad); + strcpy(m_cfg_dir, cfg_dir); + strcpy(m_modules_dir, modules_dir); + strcpy(m_cache_dir, cache_dir); + strcpy(m_font_dir, font_dir); + + char m_cfg_filename[GF_MAX_PATH]; + strcpy(m_cfg_filename, m_cfg_dir); + strcat(m_cfg_filename, "GPAC.cfg"); + + int m_Width = width; + int m_Height = height; + + int first_launch = 0; + const char *opt; + + m_window = env; + m_session = bitmap; + setJavaEnv(&mainJavaEnv, env, env->NewGlobalRef(*callback)); + if (pthread_setspecific( jni_thread_env_key, &mainJavaEnv)){ + LOGE("Failed to set specific thread data to jni_thread_env_key=%p for main thread !", jni_thread_env_key); + } + + m_mx = gf_mx_new("Osmo4"); + + //load config file + LOGI("Loading User Config %s...", "GPAC.cfg"); + m_user.config = gf_cfg_force_new(cfg_dir, "GPAC.cfg"); + SetupLogs(); + gf_set_progress_callback(this, Osmo4_progress_cbk); + + opt = gf_cfg_get_key(m_user.config, "General", "ModulesDirectory"); + if (!opt) { + FILE * fstart; + char msg[256]; + LOGI("First launch, initializing new Config %s...", "GPAC.cfg"); + /*hardcode module directory*/ + gf_cfg_set_key(m_user.config, "Downloader", "CleanCache", "yes"); + /*startup file*/ + snprintf(msg, 256, "%sgui/gui.bt", cfg_dir); + fstart = fopen(msg, "r"); + if (fstart){ + fclose(fstart); + gf_cfg_set_key(m_user.config, "General", "StartupFile", msg); + } else { + gf_cfg_set_key(m_user.config, "General", "#StartupFile", msg); + } + gf_cfg_set_key(m_user.config, "GUI", "UnhideControlPlayer", "1"); + /*setup UDP traffic autodetect*/ + gf_cfg_set_key(m_user.config, "Network", "AutoReconfigUDP", "yes"); + gf_cfg_set_key(m_user.config, "Network", "UDPTimeout", "10000"); + gf_cfg_set_key(m_user.config, "Network", "BufferLength", "3000"); + gf_cfg_set_key(m_user.config, "Compositor", "TextureTextMode", "Default"); + //gf_cfg_set_key(m_user.config, "Compositor", "FrameRate", "30"); + gf_cfg_set_key(m_user.config, "Audio", "ForceConfig", "no"); + gf_cfg_set_key(m_user.config, "Audio", "NumBuffers", "1"); + gf_cfg_set_key(m_user.config, "FontEngine", "FontReader", "ft_font"); + } + /* All of this has to be done for every instance */ + gf_cfg_set_key(m_user.config, "General", "ModulesDirectory", modules_dir ? modules_dir : GPAC_MODULES_DIR); + gf_cfg_set_key(m_user.config, "General", "CacheDirectory", cache_dir ? cache_dir : GPAC_CACHE_DIR); + gf_cfg_set_key(m_user.config, "General", "LastWorkingDir", cfg_dir); + gf_cfg_set_key(m_user.config, "FontEngine", "FontDirectory", GPAC_FONT_DIR); + gf_cfg_set_key(m_user.config, "Video", "DriverName", "Android Video Output"); + gf_cfg_set_key(m_user.config, "Audio", "DriverName", "Android Audio Output"); + + opt = gf_cfg_get_key(m_user.config, "General", "ModulesDirectory"); + LOGI("loading modules in directory %s...", opt); + m_user.modules = gf_modules_new(opt, m_user.config); + if (!m_user.modules || !gf_modules_get_count(m_user.modules)) { + LOGE("No modules found in directory %s !", opt); + if (m_user.modules) + gf_modules_del(m_user.modules); + gf_cfg_del(m_user.config); + m_user.config = NULL; + return Quit(KErrGeneral); + } + + /*we don't thread the visual compositor to be able to minimize the app and still have audio running*/ + m_user.init_flags = GF_TERM_NO_COMPOSITOR_THREAD | GF_TERM_NO_REGULATION; + //m_user.init_flags |= GF_TERM_NO_AUDIO; + m_user.opaque = this; + + m_user.os_window_handler = m_window; + m_user.os_display = m_session; + m_user.EventProc = GPAC_EventProc; + if (!javaVM){ + LOGE("NO JAVA VM FOUND, m_user=%p !!!!\n", &m_user); + return Quit(KErrGeneral); + } + + LOGD("Loading GPAC terminal, m_user=%p...", &m_user); + m_term = gf_term_new(&m_user); + if (!m_term) { + LOGE("Cannot load GPAC Terminal with m_user=%p", m_user); + MessageBox("Cannot load GPAC terminal", "Fatal Error", GF_SERVICE_ERROR); + gf_modules_del(m_user.modules); + m_user.modules = NULL; + gf_cfg_del(m_user.config); + m_user.config = NULL; + return Quit(KErrGeneral); + } + + //setAudioEnvironment(javaVM); + + LOGD("Setting term size m_user=%p...", &m_user); + gf_term_set_size(m_term, m_Width, m_Height); + + opt = gf_cfg_get_key(m_user.config, "General", "StartupFile"); + LOGD("File loaded at startup=%s.", opt); + + if (!urlToLoad) + urlToLoad = opt; + if (urlToLoad){ + LOGI("Connecting to %s...", urlToLoad); + gf_term_connect(m_term, urlToLoad); + } + debug_log("init end"); + LOGD("Saving config file %s...\n", m_cfg_filename); + gf_cfg_save(m_user.config); + LOGI("Initialization complete, config file saved as %s.\n", m_cfg_filename); + return 0; +} +//------------------------------- +int CNativeWrapper::connect(const char *url){ + if (m_term){ + debug_log("Starting to connect ..."); + gf_term_connect_from_time(m_term, url, 0, false); + debug_log("connected ..."); + } +} + +void CNativeWrapper::setGpacPreference( const char * category, const char * name, const char * value) +{ + if (m_user.config){ + gf_cfg_set_key(m_user.config, category, name, value); + gf_cfg_save(m_user.config); + } +} + +//----------------------------------------------------- +void CNativeWrapper::disconnect(){ + if (m_term){ + debug_log("disconnecting"); + gf_term_disconnect(m_term); + debug_log("disconnected ..."); + } +} +//----------------------------------------------------- +void CNativeWrapper::step(void * env, void * bitmap){ + m_window = env; + m_session = bitmap; + //debug_log("Step ..."); + if (!m_term){ + debug_log("step(): No m_term found."); + return; + } else + if (!m_term->compositor) + debug_log("step(): No compositor found."); + else if (!m_term->compositor->video_out) + debug_log("step(): No video_out found"); + else if (!m_term->compositor->video_out->Setup) + debug_log("step(): No video_out->Setup found"); + else { + //debug_log("step(): gf_term_process_step : start()"); + m_term->compositor->frame_draw_type = GF_SC_DRAW_FRAME; + gf_term_process_step(m_term); + //debug_log("step(): gf_term_process_step : end()"); + } +} + +//----------------------------------------------------- +void CNativeWrapper::setAudioEnvironment(JavaVM* javaVM){ + if (!m_term){ + debug_log("setAudioEnvironment(): no m_term found."); + return; + } + debug_log("setAudioEnvironment start"); + m_term->compositor->audio_renderer->audio_out->Setup(m_term->compositor->audio_renderer->audio_out, javaVM, 0, 0); + debug_log("setAudioEnvironment end"); +} +//----------------------------------------------------- +void CNativeWrapper::resize(int w, int h){ + if (!m_term) + return; + debug_log("resize start"); + gf_term_set_size(m_term, w, h); + debug_log("resize end"); +} +//----------------------------------------------------- +void CNativeWrapper::onMouseDown(float x, float y){ + if (!m_term) + return; + debug_log("onMouseDown start"); + //char msg[100]; + //sprintf(msg, "onMousedown x=%f, y=%f", x, y ); + //debug_log(msg); + + GF_Event evt; + evt.type = GF_EVENT_MOUSEDOWN; + evt.mouse.button = GF_MOUSE_LEFT; + evt.mouse.x = x; + evt.mouse.y = y; + + int ret = gf_term_user_event(m_term, &evt); + debug_log("onMouseDown end"); +} +//----------------------------------------------------- +void CNativeWrapper::onMouseUp(float x, float y){ + if (!m_term) + return; + debug_log("onMouseUp start"); + //char msg[100]; + //sprintf(msg, "onMouseUp x=%f, y=%f", x, y ); + //debug_log(msg); + + GF_Event evt; + evt.type = GF_EVENT_MOUSEUP; + evt.mouse.button = GF_MOUSE_LEFT; + evt.mouse.x = x; + evt.mouse.y = y; + + int ret = gf_term_user_event(m_term, &evt); + debug_log("onMouseUp end"); +} +//----------------------------------------------------- +void CNativeWrapper::onMouseMove(float x, float y){ + if (!m_term) + return; + GF_Event evt; + evt.type = GF_EVENT_MOUSEMOVE; + evt.mouse.button = GF_MOUSE_LEFT; + evt.mouse.x = x; + evt.mouse.y = y; + + int ret = gf_term_user_event(m_term, &evt); +} +//----------------------------------------------------- +void CNativeWrapper::onKeyPress(int keycode, int rawkeycode, int up, int flag, int unicode){ + if (!m_term) + return; + debug_log("onKeyPress start"); + GF_Event evt; + memset(&evt, 0, sizeof(GF_Event)); + if (up == 0) evt.type = GF_EVENT_KEYUP; + else evt.type = GF_EVENT_KEYDOWN; + + evt.key.flags = 0; + evt.key.hw_code = rawkeycode; + + translate_key(keycode, &evt.key); + //evt.key.key_code = GF_KEY_A; + int ret = gf_term_user_event(m_term, &evt); + + if (evt.type == GF_EVENT_KEYUP && unicode){ + memset(&evt, 0, sizeof(GF_Event)); + evt.type = GF_EVENT_TEXTINPUT; + evt.character.unicode_char = unicode; + ret = gf_term_user_event(m_term, &evt); + } +} +//----------------------------------------------------- +void CNativeWrapper::translate_key(ANDROID_KEYCODE keycode, GF_EventKey *evt){ + evt->flags = 0; + switch (keycode) { + case ANDROID_KEYCODE_BACK: evt->key_code = GF_KEY_BACKSPACE; break; + case ANDROID_KEYCODE_TAB: evt->key_code = GF_KEY_TAB; break; + case ANDROID_KEYCODE_CLEAR: evt->key_code = GF_KEY_CLEAR; break; + case ANDROID_KEYCODE_ENTER: evt->key_code = GF_KEY_ENTER; break; + case ANDROID_KEYCODE_SHIFT_LEFT: evt->key_code = GF_KEY_SHIFT; break; + case ANDROID_KEYCODE_SHIFT_RIGHT: evt->key_code = GF_KEY_SHIFT; break; + case ANDROID_KEYCODE_ALT_LEFT: evt->key_code = GF_KEY_ALT; break; + case ANDROID_KEYCODE_ALT_RIGHT: evt->key_code = GF_KEY_ALT; break; + case ANDROID_KEYCODE_SPACE: evt->key_code = GF_KEY_SPACE; break; + case ANDROID_KEYCODE_HOME: evt->key_code = GF_KEY_HOME; break; + case ANDROID_KEYCODE_DPAD_LEFT: evt->key_code = GF_KEY_LEFT; break; + case ANDROID_KEYCODE_DPAD_UP: evt->key_code = GF_KEY_UP; break; + case ANDROID_KEYCODE_DPAD_RIGHT: evt->key_code = GF_KEY_RIGHT; break; + case ANDROID_KEYCODE_DPAD_DOWN: evt->key_code = GF_KEY_DOWN; break; + case ANDROID_KEYCODE_DEL: evt->key_code = GF_KEY_DEL; break; + case ANDROID_KEYCODE_0: + evt->key_code = GF_KEY_0; + evt->flags = GF_KEY_EXT_NUMPAD; + break; + case ANDROID_KEYCODE_1: + evt->key_code = GF_KEY_1; + evt->flags = GF_KEY_EXT_NUMPAD; + break; + case ANDROID_KEYCODE_2: + evt->key_code = GF_KEY_2; + evt->flags = GF_KEY_EXT_NUMPAD; + break; + case ANDROID_KEYCODE_3: + evt->key_code = GF_KEY_3; + evt->flags = GF_KEY_EXT_NUMPAD; + break; + case ANDROID_KEYCODE_4: + evt->key_code = GF_KEY_4; + evt->flags = GF_KEY_EXT_NUMPAD; + break; + case ANDROID_KEYCODE_5: + evt->key_code = GF_KEY_5; + evt->flags = GF_KEY_EXT_NUMPAD; + break; + case ANDROID_KEYCODE_6: + evt->key_code = GF_KEY_6; + evt->flags = GF_KEY_EXT_NUMPAD; + break; + case ANDROID_KEYCODE_7: + evt->key_code = GF_KEY_7; + evt->flags = GF_KEY_EXT_NUMPAD; + break; + case ANDROID_KEYCODE_8: + evt->key_code = GF_KEY_8; + evt->flags = GF_KEY_EXT_NUMPAD; + break; + case ANDROID_KEYCODE_9: + evt->key_code = GF_KEY_9; + evt->flags = GF_KEY_EXT_NUMPAD; + break; + /*thru VK_9 are the same as ASCII '0' thru '9' (0x30 - 0x39) */ + /* VK_A thru VK_Z are the same as ASCII 'A' thru 'Z' (0x41 - 0x5A) */ + default: + if ((keycode>=ANDROID_KEYCODE_A) && (keycode<=ANDROID_KEYCODE_Z)){ + evt->key_code = GF_KEY_A + keycode - ANDROID_KEYCODE_A; + } else { + evt->key_code = GF_KEY_UNIDENTIFIED; + } + break; + } + evt->hw_code = evt->key_code; +} + +//--------------------------------------------------------------------------------------------------- +//--------------------------------------------------------------------------------------------------- diff --git a/applications/osmo4_android/jni/wrapper.h b/applications/osmo4_android/jni/wrapper.h new file mode 100644 index 0000000..e220627 --- /dev/null +++ b/applications/osmo4_android/jni/wrapper.h @@ -0,0 +1,227 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) ENST 2009- + * Authors: Jean Le Feuvre + * All rights reserved + * + * Created by NGO Van Luyen, Ivica ARSOV / ARTEMIS / Telecom SudParis /Institut TELECOM on Oct, 2010 + * + * This file is part of GPAC / Wrapper + * + * 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 + +//#define MAX_PATH 255 + +#define KErrGeneral 1 +//#define GPAC_CFG_DIR "/data/osmo/" +#define GPAC_CFG_DIR m_cfg_dir +//#define GPAC_MODULES_DIR "/data/osmo/modules/" +#define GPAC_MODULES_DIR m_modules_dir +//#define GPAC_MODULES_PATH "/data/osmo/modules/" +#define GPAC_MODULES_PATH m_modules_dir +//#define GPAC_CACHE_DIR "/data/osmo/cache/" +#define GPAC_CACHE_DIR m_cache_dir +//#define GPAC_LOG_FILE "/data/osmo/gpac_logs.txt" +#define GPAC_LOG_FILE m_log_filename +//#define GPAC_FONT_DIR "/system/fonts/" +#define GPAC_FONT_DIR m_font_dir + +#define DEBUG_MODE 1 +//#define DEBUG_FILE "/data/osmo/osmo_debug.txt" +#define DEBUG_FILE m_debug_filename + +// keyboard code +#define ANDROID_KEYCODE int +#define ANDROID_KEYCODE_0 7 +#define ANDROID_KEYCODE_1 8 +#define ANDROID_KEYCODE_2 9 +#define ANDROID_KEYCODE_3 10 +#define ANDROID_KEYCODE_4 11 +#define ANDROID_KEYCODE_5 12 +#define ANDROID_KEYCODE_6 13 +#define ANDROID_KEYCODE_7 14 +#define ANDROID_KEYCODE_8 15 +#define ANDROID_KEYCODE_9 16 +#define ANDROID_KEYCODE_A 29 +#define ANDROID_KEYCODE_B 30 +#define ANDROID_KEYCODE_C 31 +#define ANDROID_KEYCODE_D 32 +#define ANDROID_KEYCODE_E 33 +#define ANDROID_KEYCODE_F 34 +#define ANDROID_KEYCODE_G 35 +#define ANDROID_KEYCODE_H 36 +#define ANDROID_KEYCODE_I 37 +#define ANDROID_KEYCODE_J 38 +#define ANDROID_KEYCODE_K 39 +#define ANDROID_KEYCODE_L 40 +#define ANDROID_KEYCODE_M 41 +#define ANDROID_KEYCODE_N 42 +#define ANDROID_KEYCODE_O 43 +#define ANDROID_KEYCODE_P 44 +#define ANDROID_KEYCODE_Q 45 +#define ANDROID_KEYCODE_R 46 +#define ANDROID_KEYCODE_S 47 +#define ANDROID_KEYCODE_T 48 +#define ANDROID_KEYCODE_U 49 +#define ANDROID_KEYCODE_V 50 +#define ANDROID_KEYCODE_W 51 +#define ANDROID_KEYCODE_X 52 +#define ANDROID_KEYCODE_Y 53 +#define ANDROID_KEYCODE_Z 54 +#define ANDROID_KEYCODE_ALT_LEFT 57 +#define ANDROID_KEYCODE_ALT_RIGHT 58 +#define ANDROID_KEYCODE_AT 77 +#define ANDROID_KEYCODE_BACK 4 +#define ANDROID_KEYCODE_BACKSLASH 73 +#define ANDROID_KEYCODE_CALL 5 +#define ANDROID_KEYCODE_CAMERA 27 +#define ANDROID_KEYCODE_CLEAR 28 +#define ANDROID_KEYCODE_COMMA 55 +#define ANDROID_KEYCODE_DEL 67 +#define ANDROID_KEYCODE_DPAD_CENTER 23 +#define ANDROID_KEYCODE_DPAD_DOWN 20 +#define ANDROID_KEYCODE_DPAD_LEFT 21 +#define ANDROID_KEYCODE_DPAD_RIGHT 22 +#define ANDROID_KEYCODE_DPAD_UP 19 +#define ANDROID_KEYCODE_ENDCALL 6 +#define ANDROID_KEYCODE_ENTER 66 +#define ANDROID_KEYCODE_ENVELOPE 65 +#define ANDROID_KEYCODE_EQUALS 70 +#define ANDROID_KEYCODE_EXPLORER 64 +#define ANDROID_KEYCODE_FOCUS 80 +#define ANDROID_KEYCODE_GRAVE 68 +#define ANDROID_KEYCODE_HEADSETHOOK 79 +#define ANDROID_KEYCODE_HOME 3 +#define ANDROID_KEYCODE_LEFT_BRACKET 71 +#define ANDROID_KEYCODE_MEDIA_FAST_FORWARD 90 +#define ANDROID_KEYCODE_MEDIA_NEXT 87 +#define ANDROID_KEYCODE_MEDIA_PLAY_PAUSE 85 +#define ANDROID_KEYCODE_MEDIA_PREVIOUS 88 +#define ANDROID_KEYCODE_MEDIA_REWIND 89 +#define ANDROID_KEYCODE_MEDIA_STOP 86 +#define ANDROID_KEYCODE_MENU 82 +#define ANDROID_KEYCODE_MINUS 69 +#define ANDROID_KEYCODE_MUTE 91 +#define ANDROID_KEYCODE_NUM 78 +#define ANDROID_KEYCODE_PLUS 81 +#define ANDROID_KEYCODE_POWER 26 +#define ANDROID_KEYCODE_RIGHT_BRACKET 72 +#define ANDROID_KEYCODE_SEARCH 84 +#define ANDROID_KEYCODE_SEMICOLON 74 +#define ANDROID_KEYCODE_SHIFT_LEFT 59 +#define ANDROID_KEYCODE_SHIFT_RIGHT 60 +#define ANDROID_KEYCODE_SLASH 76 +#define ANDROID_KEYCODE_SOFT_LEFT 1 +#define ANDROID_KEYCODE_SOFT_RIGHT 2 +#define ANDROID_KEYCODE_SPACE 62 +#define ANDROID_KEYCODE_STAR 17 +#define ANDROID_KEYCODE_SYM 63 +#define ANDROID_KEYCODE_TAB 61 + +#define ANDROID_KEYCODE_UNKWON -1 + +#include + +typedef struct _JavaEnvTh { + JNIEnv * env; + u32 javaThreadId; + jobject cbk_obj; + jmethodID cbk_displayMessage; + jmethodID cbk_onProgress; + jmethodID cbk_showKeyboard; + jmethodID cbk_setCaption; + jmethodID cbk_onLog; +} JavaEnvTh; + + +//--------------------------------------------------------------------------------------------------- +//--------------------------------------------------------------------------------------------------- +class CNativeWrapper{ + + private: + void* m_window; + void* m_session; + + GF_User *GetUser() { return &m_user; } + GF_Terminal *m_term; + + /* + * Callback management + */ + JavaEnvTh mainJavaEnv; + + GF_Mutex *m_mx; + GF_User m_user; + GF_SystemRTInfo m_rti; + + int do_log; + private: + char m_cfg_dir[GF_MAX_PATH]; + char m_modules_dir[GF_MAX_PATH]; + char m_cache_dir[GF_MAX_PATH]; + char m_font_dir[GF_MAX_PATH]; + void setJavaEnv(JavaEnvTh * envToSet, JNIEnv *env, jobject callback); + private: + void SetupLogs(); + void Shutdown(); + void DisplayRTI(); + protected: + JavaEnvTh * getEnv(); + + public: + CNativeWrapper(); + ~CNativeWrapper(); + int init(JNIEnv * env, void * bitmap, jobject * callback, int width, int height, const char * cfg_dir, const char * modules_dir, const char * cache_dir, const char * font_dir, const char * urlToLoad); + + int connect(const char *url); + void disconnect(); + void step(void * env, void * bitmap); + void resize(int w, int h); + void setAudioEnvironment(JavaVM* javaVM); + + void onMouseDown(float x, float y); + void onMouseUp(float x, float y); + void onMouseMove(float x, float y); + void onKeyPress(int keycode, int rawkeycode, int up, int flag, int unicode); + void translate_key(ANDROID_KEYCODE keycode, GF_EventKey *evt); + void setGpacPreference( const char * category, const char * name, const char * value); + public: + int MessageBox(const char* msg, const char* title, GF_Err status); + int Quit(int code); + GF_Config *create_default_config(char *file_path, char *file_name); + + static void on_gpac_log(void *cbk, u32 ll, u32 lm, const char *fmt, va_list list); + static Bool GPAC_EventProc(void *cbk, GF_Event *evt); + void progress_cbk(const char *title, u64 done, u64 total); + static void Osmo4_progress_cbk(const void *usr, const char *title, u64 done, u64 total); + + private: +#ifdef DEBUG_MODE + FILE *debug_f; +#endif + void debug_log(const char* msg); + +}; + diff --git a/applications/osmo4_android/jni/wrapper_jni.c b/applications/osmo4_android/jni/wrapper_jni.c new file mode 100644 index 0000000..48d9c27 --- /dev/null +++ b/applications/osmo4_android/jni/wrapper_jni.c @@ -0,0 +1,181 @@ +//#include "wrapper_jni.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#define jniTAG "WRAPPER_JNI" + +#define jniLOGV(X) __android_log_print(ANDROID_LOG_VERBOSE, jniTAG, X) +#define jniLOGI(X) __android_log_print(ANDROID_LOG_INFO, jniTAG, X) +#define jniLOGE(X) __android_log_print(ANDROID_LOG_ERROR, jniTAG, X) + +#define CAST_HANDLE(wr) jclass c = env->GetObjectClass(obj);\ + if (!c) return;\ + jfieldID fid = env->GetFieldID(c, "handle", "J");\ + if (!fid){\ + __android_log_print(ANDROID_LOG_ERROR, jniTAG, "No Field ID, ERROR");\ + return;\ + }\ + jlong h = env->GetLongField(obj, fid);\ + CNativeWrapper* wr = (CNativeWrapper*) h; +// __android_log_print(ANDROID_LOG_VERBOSE, jniTAG, "Handle = %p", wr); + +/* + * Class: com_artemis_Osmo4_GPACInstance + * Method: createInstance + * Signature: (Lcom/artemis/Osmo4/GpacCallback;IILjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I + */ +JNIEXPORT jlong JNICALL Java_com_artemis_Osmo4_GPACInstance_createInstance(JNIEnv * env, jclass obj, jobject callback, jint width, jint height, jstring cfg_dir, jstring modules_dir, jstring cache_dir, jstring font_dir, jstring url_to_open) +{ + jboolean isCopy; + const char * s1 = env->GetStringUTFChars(cfg_dir, &isCopy); + const char * s2 = env->GetStringUTFChars(modules_dir, &isCopy); + const char * s3 = env->GetStringUTFChars(cache_dir, &isCopy); + const char * s4 = env->GetStringUTFChars(font_dir, &isCopy); + const char * s5 = NULL; + if (url_to_open) + s5 = env->GetStringUTFChars(url_to_open, &isCopy); + else + s5 = NULL; + CNativeWrapper * gpac_obj = new CNativeWrapper(); + if (gpac_obj){ + int w = width; + int h = height; + jniLOGI("Calling gpac_obj->init()..."); + if (gpac_obj->init(env, NULL, &callback, + w, h, + s1, s2, s3, s4, s5)){ + jniLOGE("FAILED to init(), return code not 0"); + delete gpac_obj; + gpac_obj = NULL; + } + } else { + jniLOGE("FAILED to create new CNativeWrapper() : not enough memory ?"); + } + + env->ReleaseStringUTFChars(cfg_dir, s1); + env->ReleaseStringUTFChars(modules_dir, s2); + env->ReleaseStringUTFChars(cache_dir, s3); + env->ReleaseStringUTFChars(font_dir, s4); + if (s5) + env->ReleaseStringUTFChars(font_dir, s5); + __android_log_print(ANDROID_LOG_VERBOSE, jniTAG, "Returned Handle = %p", gpac_obj); + return (jlong) gpac_obj; +} +//----------------------------------- +JNIEXPORT void JNICALL Java_com_artemis_Osmo4_GPACInstance_gpacconnect(JNIEnv * env, jobject obj, jstring fileName) +{ + CAST_HANDLE(wr); + jniLOGV("connect::start"); + if (!wr){ + jniLOGV("connect::end : aborted"); + return; + } + jboolean isCopy; + const char * cFileName = env->GetStringUTFChars(fileName, &isCopy); + + wr->connect(cFileName); + + env->ReleaseStringUTFChars(fileName, cFileName); + jniLOGV("connect::end"); +} +//----------------------------------- +JNIEXPORT void JNICALL Java_com_artemis_Osmo4_GPACInstance_gpacdisconnect(JNIEnv * env, jobject obj){ + CAST_HANDLE(wr); + jniLOGV("disconnect::start"); + if (wr) + wr->disconnect(); + jniLOGV("disconnect::end"); +} +//----------------------------------- +JNIEXPORT void JNICALL Java_com_artemis_Osmo4_GPACInstance_gpacfree(JNIEnv * env, jobject obj) +{ + CAST_HANDLE(wr); + jniLOGV("free::start"); + if (wr) + delete wr; + jniLOGV("free::end"); +} +//----------------------------------- +JNIEXPORT void JNICALL Java_com_artemis_Osmo4_GPACInstance_gpacrender (JNIEnv * env, jobject obj) +{ + CAST_HANDLE(wr); + //jniLOGV("render::start"); + if (wr) + wr->step(env, NULL); + //jniLOGV("render::end"); +} +//----------------------------------- +JNIEXPORT void JNICALL Java_com_artemis_Osmo4_GPACInstance_gpacresize (JNIEnv * env, jobject obj, jint width, jint height) +{ + CAST_HANDLE(wr); + jniLOGV("resize::start"); + if (wr) + wr->resize(width, height); + jniLOGV("resize::end"); +} +//----------------------------------- +JNIEXPORT void JNICALL Java_com_artemis_Osmo4_GPACInstance_gpaceventmousedown(JNIEnv * env, jobject obj, jfloat x, jfloat y){ + CAST_HANDLE(wr); + jniLOGV("mouseDown::start"); + if (wr) + wr->onMouseDown(x, y); + jniLOGV("mouseDown::end"); +} +//----------------------------------- +JNIEXPORT void JNICALL Java_com_artemis_Osmo4_GPACInstance_gpaceventmouseup(JNIEnv * env, jobject obj, jfloat x, jfloat y){ + CAST_HANDLE(wr); + jniLOGV("mouseUp::start"); + if (wr) + wr->onMouseUp(x, y); + jniLOGV("mouseUp::end"); +} +//----------------------------------- +JNIEXPORT void JNICALL Java_com_artemis_Osmo4_GPACInstance_gpaceventmousemove(JNIEnv * env, jobject obj, jfloat x, jfloat y){ + CAST_HANDLE(wr); + jniLOGV("mouseMouv::start"); + if (wr) + wr->onMouseMove(x, y); + jniLOGV("mouseMouv::end"); +} +//----------------------------------- +JNIEXPORT void JNICALL Java_com_artemis_Osmo4_GPACInstance_gpaceventkeypress(JNIEnv * env, jobject obj, jint keycode, jint rawkeycode, jint up, jint flag, jint unicode){ + CAST_HANDLE(wr); + jniLOGV("keypress::start"); + if (wr) + wr->onKeyPress(keycode, rawkeycode, up, flag, unicode); + jniLOGV("keypress::end"); +} + + +/* + * Class: com_artemis_Osmo4_GPACInstance + * Method: setGpacPreference + * Signature: (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V + */ +JNIEXPORT void JNICALL Java_com_artemis_Osmo4_GPACInstance_setGpacPreference + (JNIEnv * env, jobject obj, jstring category, jstring name, jstring value){ + CAST_HANDLE(wr); + jboolean isCopy; + const char * scat = env->GetStringUTFChars(category, &isCopy); + const char * sname = env->GetStringUTFChars(name, &isCopy); + const char * svalue; + if (value) + svalue = env->GetStringUTFChars(value, &isCopy); + else + svalue = NULL; + if (wr) + wr->setGpacPreference(scat, sname, svalue); + env->ReleaseStringUTFChars(category, scat); + env->ReleaseStringUTFChars(name, sname); + if (value) + env->ReleaseStringUTFChars(value, svalue); + } +//----------------------------------- + +#ifdef __cplusplus +} +#endif diff --git a/applications/osmo4_android/local.properties b/applications/osmo4_android/local.properties new file mode 100644 index 0000000..3ec4ddb --- /dev/null +++ b/applications/osmo4_android/local.properties @@ -0,0 +1,10 @@ +# This file is automatically generated by Android Tools. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must *NOT* be checked in Version Control Systems, +# as it contains information specific to your local configuration. + +# location of the SDK. This is only used by Ant +# For customization when using a Version Control System, please read the +# header note. +sdk.dir=/opt/android-sdk diff --git a/applications/osmo4_android/proguard.cfg b/applications/osmo4_android/proguard.cfg new file mode 100644 index 0000000..8ad7d33 --- /dev/null +++ b/applications/osmo4_android/proguard.cfg @@ -0,0 +1,34 @@ +-optimizationpasses 5 +-dontusemixedcaseclassnames +-dontskipnonpubliclibraryclasses +-dontpreverify +-verbose +-optimizations !code/simplification/arithmetic,!field/*,!class/merging/* + +-keep public class * extends android.app.Activity +-keep public class * extends android.app.Application +-keep public class * extends android.app.Service +-keep public class * extends android.content.BroadcastReceiver +-keep public class * extends android.content.ContentProvider +-keep public class com.android.vending.licensing.ILicensingService + +-keepclasseswithmembernames class * { + native ; +} + +-keepclasseswithmembernames class * { + public (android.content.Context, android.util.AttributeSet); +} + +-keepclasseswithmembernames class * { + public (android.content.Context, android.util.AttributeSet, int); +} + +-keepclassmembers enum * { + public static **[] values(); + public static ** valueOf(java.lang.String); +} + +-keep class * implements android.os.Parcelable { + public static final android.os.Parcelable$Creator *; +} diff --git a/applications/osmo4_android/res/drawable-hdpi/icon.png b/applications/osmo4_android/res/drawable-hdpi/icon.png new file mode 100644 index 0000000..3f988c1 Binary files /dev/null and b/applications/osmo4_android/res/drawable-hdpi/icon.png differ diff --git a/applications/osmo4_android/res/drawable-ldpi/icon.png b/applications/osmo4_android/res/drawable-ldpi/icon.png new file mode 100644 index 0000000..a7bbccd Binary files /dev/null and b/applications/osmo4_android/res/drawable-ldpi/icon.png differ diff --git a/applications/osmo4_android/res/drawable-mdpi/icon.png b/applications/osmo4_android/res/drawable-mdpi/icon.png new file mode 100644 index 0000000..883b9dc Binary files /dev/null and b/applications/osmo4_android/res/drawable-mdpi/icon.png differ diff --git a/applications/osmo4_android/res/layout/about_dialog.xml b/applications/osmo4_android/res/layout/about_dialog.xml new file mode 100644 index 0000000..ad3ff7f --- /dev/null +++ b/applications/osmo4_android/res/layout/about_dialog.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + diff --git a/applications/osmo4_android/res/layout/auth_requested.xml b/applications/osmo4_android/res/layout/auth_requested.xml new file mode 100644 index 0000000..6b12f81 --- /dev/null +++ b/applications/osmo4_android/res/layout/auth_requested.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + +