Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
home:frispete:vdr
xine-lib
v1.2.12-fixes.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File v1.2.12-fixes.patch of Package xine-lib
diff -r 9e075962b659 include/xine.h --- a/include/xine.h Wed Mar 09 14:31:44 2022 +0200 +++ b/include/xine.h Sat Sep 24 13:04:31 2022 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2000-2021 the xine project + * Copyright (C) 2000-2022 the xine project * * This file is part of xine, a free video player. * @@ -1006,6 +1006,17 @@ */ uint32_t xine_get_stream_info (xine_stream_t *stream, int info) XINE_PROTECTED; const char *xine_get_meta_info (xine_stream_t *stream, int info) XINE_PROTECTED; +#define XINE_QUERY_STREAM_INFO 1 +/** @brief query multiple stream props, thread safe and consistently. + * @param stream the xine stream to query. + * @param sbuf the buffer to write string copies to. sbuf[0] will be set to 0. + * @param sblen the byte size of sbuf. + * @param strings input: -1 terminated list of XINE_META_INFO_*. + * output: list of offsets into sbuf or 0 for NULL strings. + * @param ints input: -1 terminated list of XINE_STREAM_INFO_*. + * output: list of values or 0 where not set or found. + * @return the count of bytes used in sbuf. */ +int xine_query_stream_info (xine_stream_t *stream, char *sbuf, size_t sblen, int *strings, int *ints) XINE_PROTECTED; /* xine_get_stream_info */ #define XINE_STREAM_INFO_BITRATE 0 diff -r 9e075962b659 include/xine/configfile.h --- a/include/xine/configfile.h Wed Mar 09 14:31:44 2022 +0200 +++ b/include/xine/configfile.h Sat Sep 24 13:04:31 2022 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2000-2020 the xine project + * Copyright (C) 2000-2022 the xine project * * This file is part of xine, a free video player. * @@ -89,6 +89,10 @@ * registered item, i.e. the default value if it was * not found in the config file or the current value * from the config file otherwise + * NOTE: config entries registered during class init of + * input, demux, decoder, or post plugins will _always_ + * show up in the application - even if that plugin is + * not currently loaded. * * NOTE on callbacks: * - callback shall be safe to run from _any_ thread. @@ -288,4 +292,3 @@ #endif #endif - diff -r 9e075962b659 include/xine/input_plugin.h --- a/include/xine/input_plugin.h Wed Mar 09 14:31:44 2022 +0200 +++ b/include/xine/input_plugin.h Sat Sep 24 13:04:31 2022 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2000-2021 the xine project + * Copyright (C) 2000-2022 the xine project * * This file is part of xine, a free video player. * @@ -401,6 +401,9 @@ /* data is an int * telling how many seconds to go back into a live stream. * yes this _does_ work sometimes, up to 24h ;-) */ #define INPUT_OPTIONAL_DATA_REWIND 18 +/* ask plugin to generate a new preview from the current position, + * eg to skip a large ID3v2 tag. data is ignored. */ +#define INPUT_OPTIONAL_DATA_NEW_PREVIEW 19 #define MAX_MRL_ENTRIES 255 #define MAX_PREVIEW_SIZE 4096 diff -r 9e075962b659 include/xine/os_types.h --- a/include/xine/os_types.h Wed Mar 09 14:31:44 2022 +0200 +++ b/include/xine/os_types.h Sat Sep 24 13:04:31 2022 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2009 the xine project + * Copyright (C) 2004-2022 the xine project * * This file is part of xine, a free video player. * @@ -38,6 +38,8 @@ typedef unsigned __int32 uint32_t; typedef __int64 int64_t; typedef unsigned __int64 uint64_t; + typedef __int32 intptr_t; + typedef unsigned __int32 uintptr_t; #elif defined(__MACOS__) @@ -50,6 +52,8 @@ typedef UInt32 uint32_t; typedef SInt64 int64_t; typedef UInt64 uint64_t; + typedef SInt32 intptr_t; + typedef UInt32 uintptr_t; #elif defined(__MACOSX__) /* MacOS X Framework build */ @@ -69,7 +73,9 @@ typedef int int32_t; typedef unsigned int uint32_t; typedef long long int64_t; - typedef unsigned long long int64_t; + typedef unsigned long long uint64_t; + typedef long intptr_t; + typedef unsigned long uintptr_t; #elif defined (DJGPP) @@ -82,6 +88,8 @@ typedef unsigned int uint32_t; typedef long long int64_t; typedef unsigned long long uint64_t; + typedef long intptr_t; + typedef unsigned long uintptr_t; #elif defined(R5900) @@ -93,7 +101,10 @@ typedef int int32_t; typedef unsigned uint32_t; typedef long int64_t; - typedef unsigned long int64_t; + typedef unsigned long uint64_t; + /* FIXME: 32bit? */ + typedef long intptr_t; + typedef unsigned long uintptr_t; #else diff -r 9e075962b659 include/xine/sorted_array.h --- a/include/xine/sorted_array.h Wed Mar 09 14:31:44 2022 +0200 +++ b/include/xine/sorted_array.h Sat Sep 24 13:04:31 2022 +0200 @@ -58,13 +58,15 @@ #include <xine/attributes.h> /* version */ -#define XINE_SARRAY 2 +#define XINE_SARRAY 3 /* Array type */ typedef struct xine_sarray_s xine_sarray_t; /* Array element comparator */ -typedef int (*xine_sarray_comparator_t)(void*, void*); +typedef int (*xine_sarray_comparator_t) (void *item1, void *item2); +/* Array element hash value */ +typedef unsigned int (*xine_sarray_hash_func_t) (void *item); /* Constructor */ xine_sarray_t *xine_sarray_new(size_t initial_size, xine_sarray_comparator_t comparator) XINE_MALLOC XINE_PROTECTED; @@ -72,6 +74,11 @@ /* Destructor */ void xine_sarray_delete(xine_sarray_t *sarray) XINE_PROTECTED; +/* XINE_SARRAY >= 3: add optional hash function that returns 0 ... (hash_size - 1). + * Items will be sorted by ascending hash value first, then by comparator within the same hash value. + * NOTE: hash_size is currently limited to 4096. */ +void xine_sarray_set_hash (xine_sarray_t *sarray, xine_sarray_hash_func_t hash_func, unsigned int hash_size) XINE_PROTECTED; + /* Set mode */ /* For both add() and binary_search(): if there are multiple matching indices, * select 1 of them randomly. That is, the order of matches does not matter, just be fast. */ diff -r 9e075962b659 include/xine/xine_internal.h --- a/include/xine/xine_internal.h Wed Mar 09 14:31:44 2022 +0200 +++ b/include/xine/xine_internal.h Sat Sep 24 13:04:31 2022 +0200 @@ -263,6 +263,7 @@ int _x_demux_called_from (xine_stream_t *stream) XINE_PROTECTED; int _x_demux_stop_thread (xine_stream_t *stream) XINE_PROTECTED; int _x_demux_read_header (input_plugin_t *input, void *buffer, off_t size) XINE_PROTECTED; +int _x_demux_read_stream_header (xine_stream_t *stream, input_plugin_t *input, void *buffer, size_t size) XINE_PROTECTED; int _x_demux_check_extension (const char *mrl, const char *extensions); off_t _x_read_abort (xine_stream_t *stream, int fd, char *buf, off_t todo) XINE_PROTECTED; @@ -392,3 +393,4 @@ #endif #endif + diff -r 9e075962b659 include/xine/xineutils.h --- a/include/xine/xineutils.h Wed Mar 09 14:31:44 2022 +0200 +++ b/include/xine/xineutils.h Sat Sep 24 13:04:31 2022 +0200 @@ -30,6 +30,7 @@ #include <stddef.h> #include <pthread.h> +#include <time.h> #ifdef WIN32 #else # include <sys/time.h> @@ -206,7 +207,7 @@ static inline void *xine_small_memcpy (void *to, const void *from, size_t len) { void *t2 = to; size_t l2 = len; -# if !defined(__clang__) +# if !defined(__clang__) && !defined(__cplusplus) __asm__ __volatile__ ( "cld\n\trep movsb" : "=S" (from), "=D" (t2), "=c" (l2), "=m" (*(struct {char foo[len];} *)to) @@ -767,6 +768,58 @@ /** free a fast string if it is not application supplied. */ void xine_fast_string_free (char **fast_string) XINE_PROTECTED; +#define XINE_REF_STRING 1 +/** create or reuse a xine reference counted string, whose contents shall remain unchanged. + * use len == -1 if not kown. */ +char *xine_ref_string_ref (const char *s, int len) XINE_PROTECTED; +/** fast if s is a xine reference counted string. */ +size_t xine_ref_string_len (const char *s) XINE_PROTECTED; +/** same as _x_freep if s is _not_ a xine reference counted string. */ +int xine_ref_string_unref (char **s) XINE_PROTECTED; + +/** stream parsers do delay data, but often do not care about the frame pts + * that need to be delayed as well. this may help here. */ +#define XINE_PTS_QUEUE 1 +typedef struct xine_pts_queue_s xine_pts_queue_t; +/** create a new instance. */ +xine_pts_queue_t *xine_pts_queue_new (void) XINE_PROTECTED; +/** reset after a stream seek. */ +void xine_pts_queue_reset (xine_pts_queue_t *queue) XINE_PROTECTED; +/** tell what the parser has consumed. pts == 0 if unknown. */ +void xine_pts_queue_put (xine_pts_queue_t *queue, size_t bytes, int64_t pts) XINE_PROTECTED; +/** tell what the parser has delivered, and get the filtered pts or 0. */ +int64_t xine_pts_queue_get (xine_pts_queue_t *queue, size_t bytes) XINE_PROTECTED; +/** you no longer need the queue. */ +void xine_pts_queue_delete (xine_pts_queue_t **queue) XINE_PROTECTED; + +/** xine timespec magic. */ +#define XINE_TS 1 +/** XINE_TS >= 1: Well. + * libcurl.curl_getdate () fails on plain "01 January 1970 00:00:04 GMT". + * shell.date fails on "2020-08-31T23:55:00.000+02:00". + * here comes yet another "we can do better" attempt: + * input: + * ts: the fallback or relative base time. + * string: the date/time in some halfway common format. + * output: + * ts: what we got. + * return: 0 (OK) or some Exxx error code. */ +int xine_ts_from_string (struct timespec *ts, const char *string) XINE_PROTECTED; +/** XINE_TS >= 1: a += b. */ +void xine_ts_add (struct timespec *a, const struct timespec *b) XINE_PROTECTED; +/** XINE_TS >= 1: a -= b. */ +void xine_ts_sub (struct timespec *a, const struct timespec *b) XINE_PROTECTED; +/** XINE_TS >= 1: ts * timebase. */ +int64_t xine_ts_to_timebase (const struct timespec *ts, uint32_t timebase) XINE_PROTECTED; + +/** xine rational numbers. */ +#define XINE_RATS 1 +typedef struct { + int64_t num, den; +} xine_rats_t; +/** XINE_RATS >= 1: shorten value. */ +void xine_rats_shorten (xine_rats_t *value) XINE_PROTECTED; + /* don't harm following code */ #ifdef extern # undef extern diff -r 9e075962b659 m4/decoders.m4 --- a/m4/decoders.m4 Wed Mar 09 14:31:44 2022 +0200 +++ b/m4/decoders.m4 Sat Sep 24 13:04:31 2022 +0200 @@ -134,13 +134,36 @@ AC_DEFINE([HAVE_FFMPEG], 1, [Define this if you have the ffmpeg library]) dnl Check presence of ffmpeg/avutil.h to see if it's old or new dnl style for headers. The new style would be preferred actually... + dnl Sigh. at least some 09/2022 ffmpeg version does violate the basic + dnl "get directly what you use directly" rule. especially, + dnl libavformat/avformat.h includes libavcodec/packet.h which uses + dnl (but not includes) libavutil/avutil.h. this means that a mere + dnl AC_CHECK_HEADERS([libavformat/avformat.h]) will fail strangely :-/ ac_save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $FFMPEG_CFLAGS" ac_save_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$CFLAGS $FFMPEG_CFLAGS $AVUTIL_CFLAGS" - AC_CHECK_HEADERS([ffmpeg/avutil.h]) - AC_CHECK_HEADERS([libavutil/avutil.h]) - AC_CHECK_HEADERS([libavutil/sha1.h]) - AC_CHECK_HEADERS([libavutil/sha.h]) + AC_CHECK_HEADERS([ffmpeg/avutil.h libavutil/avutil.h libavutil/sha1.h libavutil/mem.h libavutil/sha.h]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ +#include <libavutil/avutil.h> +#include <libavcodec/avcodec.h> + ]],[[]])], [have_avutil_avcodec_h=yes], [have_avutil_avcodec_h=no]) + test x"$have_avutil_avcodec_h" == x"yes" && AC_DEFINE([HAVE_AVUTIL_AVCODEC_H],[1], + [Define this if you have libavutil/avutil.h and libavcodec/avcodec.h.]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ +#include <libavutil/avutil.h> +#include <libavcodec/avcodec.h> +#include <libavformat/avformat.h> + ]],[[]])], [have_avformat_avformat_h=yes], [have_avformat_avformat_h=no]) + test x"$have_avformat_avformat_h" == x"yes" && AC_DEFINE([HAVE_AVFORMAT_AVFORMAT_H],[1], + [Define this if you have libavformat/avformat.h.]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ +#include <libavutil/avutil.h> +#include <libavcodec/avcodec.h> +#include <libavformat/avformat.h> +#include <libavformat/avio.h> + ]],[[]])], [have_avformat_avio_h=yes], [have_avformat_avio_h=no]) + test x"$have_avformat_avio_h" == x"yes" && AC_DEFINE([HAVE_AVFORMAT_AVIO_H],[1], + [Define this if you have libavformat/avformat.h.]) if test "$ac_cv_header_ffmpeg_avutil_h" = "yes" && test "$ac_cv_header_libavutil_avutil_h" = "yes"; then AC_MSG_ERROR([old & new ffmpeg headers found - you need to clean up!]) fi diff -r 9e075962b659 m4/input.m4 --- a/m4/input.m4 Wed Mar 09 14:31:44 2022 +0200 +++ b/m4/input.m4 Sat Sep 24 13:04:31 2022 +0200 @@ -229,6 +229,7 @@ fi if test x"$have_avformat" = x"yes"; then AC_DEFINE([HAVE_AVFORMAT], 1, [Define this if you have libavformat installed]) + AC_CHECK_HEADERS([libavformat/avformat.h]) fi fi AM_CONDITIONAL([ENABLE_AVFORMAT], [test x"$have_avformat" = x"yes"]) diff -r 9e075962b659 po/POTFILES.in --- a/po/POTFILES.in Wed Mar 09 14:31:44 2022 +0200 +++ b/po/POTFILES.in Sat Sep 24 13:04:31 2022 +0200 @@ -190,6 +190,7 @@ src/video_dec/yuv.c src/video_out/color_matrix.c src/video_out/opengl/xine_glx.c +src/video_out/vaapi/xine_hw_frame_vaapi.c src/video_out/video_out_aa.c src/video_out/video_out_caca.c src/video_out/video_out_directfb.c diff -r 9e075962b659 po/de.po --- a/po/de.po Wed Mar 09 14:31:44 2022 +0200 +++ b/po/de.po Sat Sep 24 13:04:31 2022 +0200 @@ -1217,6 +1217,7 @@ msgstr "Versuchen Sie dies, wenn das Abspielen von MPEG 1/2 hängen bleibt." #: src/combined/ffmpeg/ff_video_decoder.c +#: src/video_out/vaapi/xine_hw_frame_vaapi.c msgid "Enable VAAPI" msgstr "VAAPI einschalten" @@ -1584,7 +1585,7 @@ #: src/demuxers/demux_rawdv.c msgid "Raw DV Video stream" -msgstr "" +msgstr "Einfacher DV Video Datenstrom" #: src/demuxers/demux_realaudio.c msgid "RealAudio file demux plugin" @@ -3611,8 +3612,8 @@ "See the help for the title_format for the meanings of these." msgstr "" "Das selbe Format wie für Fenstertitel. Ähnlich dem Unix date-Befehl. " -"Formatangaben starten mit einem Prozentzeichen. Formatfelder sind %A, %C, " -"%c, %F, %I, %L, %N, %P, %p, %S, %T, %V, %v, und %%.\n" +"Formatangaben starten mit einem Prozentzeichen. Formatfelder sind %A, %C, %" +"c, %F, %I, %L, %N, %P, %p, %S, %T, %V, %v, und %%.\n" "Siehe Hilfe für title_format bezüglich deren Bedeutung." #: src/input/vcd/xineplug_inp_vcd.c @@ -4192,7 +4193,7 @@ #: src/post/planar/eq2.c msgid "Software video equalizer" -msgstr "" +msgstr "Software video Equalizer" #: src/post/planar/eq.c msgid "" @@ -4224,7 +4225,7 @@ #: src/post/planar/eq.c msgid "soft video equalizer" -msgstr "" +msgstr "Software video Equalizer" #: src/post/planar/expand.c msgid "" @@ -4878,6 +4879,24 @@ msgid "GL provider (GLX)" msgstr "GL-Anbieter (GLX)" +#: src/video_out/vaapi/xine_hw_frame_vaapi.c +msgid "" +"Enable VAAPI video decoding with any video output driver. When disabled, " +"only vaapi video output driver uses VAAPI accelerated decoding. Currently " +"only opengl2 video output driver supports this." +msgstr "" +"Schalte VAAPI Beschleunigung für alle Videotreiber ein. Andernfalls versucht " +"dies nur der VAAPI Videotreiber. Zur Zeit klappt das jedoch nur mit dem " +"OpenGL2 Treiber." + +#: src/video_out/vaapi/xine_hw_frame_vaapi.c src/video_out/video_out_vaapi.c +msgid "vaapi: set vaapi_guarded_render to 0 ( no ) 1 ( yes )" +msgstr "vaapi: vaapi_guarded_render 0 (aus) 1 (ein)" + +#: src/video_out/vaapi/xine_hw_frame_vaapi.c +msgid "VAAPI frame provider" +msgstr "VAAPI Bildpuffer-Anbieter" + #: src/video_out/video_out_aa.c msgid "xine video output plugin using the ascii-art library" msgstr "xine Bildusgabe über ASCII-Art Bibliothek" @@ -5151,6 +5170,30 @@ "Es benötigt aber eine schnelle Grafikkarte.\n" #: src/video_out/video_out_opengl2.c +msgid "opengl2: video scale mode" +msgstr "opengl2: Video Größenänderungsverfahren" + +#: src/video_out/video_out_opengl2.c +msgid "" +"0: Simple. Very fast, very sharp,\n" +" but also stairsteps, uneven lines, and flickering movement.\n" +"\n" +"1: Linear blending. Fast, very smooth, but also a bit blurry.\n" +"\n" +"2: Catmullrom blending. Very smooth, sharp, but needs fast hardware.\n" +"\n" +"3: Cosinus blending. Smooth, very sharp, but needs fast hardware.\n" +msgstr "" +"0: Einfach. Sehr schnell, sehr scharf,\n" +" aber auch Treppenstufen, unruhige Linien, und flackernde Bewegungen.\n" +"\n" +"1: Linear. Schnell, sehr weich, aber auch etwas verwaschen.\n" +"\n" +"2: Catmullrom. Sehr weich, scharf, braucht aber eine schnelle Grafikkarte.\n" +"\n" +"3: Cosinus. Weich, sehr scharf, braucht aber eine schnelle Grafikkarte..\n" + +#: src/video_out/video_out_opengl2.c msgid "xine video output plugin using opengl 2.0" msgstr "Opengl 2.0 Bildausgabe" @@ -5434,7 +5477,7 @@ #: src/video_out/video_out_vaapi.c msgid "vaapi: opengl rendering tfp" -msgstr "" +msgstr "vaapi: opengl tfp Modus" #: src/video_out/video_out_vaapi.c msgid "vaapi: VDR osd width workaround." @@ -5450,10 +5493,6 @@ "vaapi: Wähle Entflechtmethode: 0 (kein), 1 (verweben), 2 (weichzeichnen)." #: src/video_out/video_out_vaapi.c -msgid "vaapi: set vaapi_guarded_render to 0 ( no ) 1 ( yes )" -msgstr "vaapi: vaapi_guarded_render 0 (aus) 1 (ein)" - -#: src/video_out/video_out_vaapi.c msgid "" "vaapi: set scaling level to : default (default) fast (fast) hq (HQ) nla " "(anamorphic)" diff -r 9e075962b659 src/audio_dec/xine_faad_decoder.c --- a/src/audio_dec/xine_faad_decoder.c Wed Mar 09 14:31:44 2022 +0200 +++ b/src/audio_dec/xine_faad_decoder.c Sat Sep 24 13:04:31 2022 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2000-2020 the xine project + * Copyright (C) 2000-2022 the xine project * * This file is part of xine, a free video player. * @@ -49,15 +49,11 @@ #define FAAD_MIN_STREAMSIZE 768 /* 6144 bits/channel */ -typedef struct faad_class_s { +typedef struct { audio_decoder_class_t decoder_class; xine_t *xine; - /* provide a single configuration for both "faad" and "faad-latm" */ - struct faad_class_s *master; - int refs; - int gain_db; /* fixed point scalers (v * gain?_i) >> 32 */ int32_t gain_i, gain3_i, gain6_i, gain9_i, gain12_i; @@ -67,10 +63,10 @@ int caps; } faad_class_t; -typedef struct faad_decoder_s { +typedef struct { audio_decoder_t audio_decoder; - faad_class_t *class, *master; + faad_class_t *class; xine_stream_t *stream; @@ -97,7 +93,7 @@ unsigned char *dec_config; int dec_config_size; - unsigned long rate; + uint32_t rate; int bits_per_sample; unsigned char num_channels; int sbr; @@ -114,6 +110,16 @@ uint32_t adts_fake; uint8_t adts_lasthead[2]; + struct { + int64_t base_pts; + uint32_t seconds; + uint32_t samples; + enum { + _ADIF_UNKNOWN = 0, + _ADIF_YES, + _ADIF_NO + } mode; + } adif; } faad_decoder_t; @@ -196,7 +202,7 @@ this->out_channels = out_chan[this->out_mode]; this->out_used = out_used[this->out_mode]; xprintf (this->class->xine, XINE_VERBOSITY_DEBUG, - "faad_audio_decoder: channel layout: %s -> %s\n", + LOG_MODULE ": channel layout: %s -> %s\n", input_names[this->in_mode], out_names[this->out_mode]); return 1; } @@ -352,7 +358,7 @@ if (double_samplerates[(bits >> (32 - 5 - 4)) & 15] != ((bits >> (32 - 5 - 4 - 4 - 4)) & 15)) break; conf[0] = (conf[0] & 7) | (AOT_SBR << 3); - xprintf (this->class->xine, XINE_VERBOSITY_DEBUG, "faad_audio_decoder: using AOT_PS -> AOT_SBR hack\n"); + xprintf (this->class->xine, XINE_VERBOSITY_DEBUG, LOG_MODULE ": using AOT_PS -> AOT_SBR hack\n"); res = NeAACDecInit2 (this->faac_dec, conf, len, &rate, &num_channels); conf[0] = save; } while (0); @@ -368,6 +374,7 @@ faad_open_output (this); faad_meta_info_set (this); this->used_last = 0; + this->adif.mode = _ADIF_NO; return res; } /* no, its not working */ @@ -391,6 +398,9 @@ this->rate = rate; this->num_channels = num_channels; faad_close_output (this); + if (this->adif.mode == _ADIF_YES) { + xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, LOG_MODULE ": got new AAC config from ADIF\n"); + } } if (this->output_open <= 0) faad_open_output (this); @@ -480,7 +490,7 @@ this->size--; continue; } - xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "faad_audio_decoder: got new AAC config from ADTS\n"); + xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, LOG_MODULE ": got new AAC config from ADTS\n"); memcpy (this->adts_lasthead, q + 2, 2); /* TJ. A note on that nasty SBR hack below. * SBR (Spectral Band Replication) is a more efficient algorithm for encoding high pitched sound. @@ -511,7 +521,7 @@ this->adts_fake |= AOT_AAC_LC << (32 - 5 - 4 - 4 - 4 - 5); /* + 3 more 0 bits: frameLength, dependsOnCoreCoder, extensionFlag1 = 25 bits = 4 bytes */ this->adts_fake = bebf_ADJ32 (this->adts_fake); - xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "faad_audio_decoder: trying fake AAC config to enable SBR\n"); + xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, LOG_MODULE ": trying fake AAC config to enable SBR\n"); if (faad_apply_conf (this, (uint8_t *)&this->adts_fake, 4) >= 0) break; this->adts_fake = 0; @@ -550,7 +560,7 @@ break; used = l; if (latm_state & BEBF_LATM_GOT_CONF) { - xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "faad_audio_decoder: got new AAC config from LATM\n"); + xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, LOG_MODULE ": got new AAC config from LATM\n"); if (faad_apply_conf (this, this->latm.config, this->latm.conflen) < 0) { inbuf++; this->size--; @@ -634,7 +644,7 @@ * also does that. */ this->used_last = used; xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, - "faad_audio_decoder: empty outbuf, pts %" PRId64 ".\n", pts); + LOG_MODULE ": empty outbuf, pts %" PRId64 ".\n", pts); } else if (this->used_last > 0) { this->used_last = used; } @@ -671,8 +681,6 @@ if ((this->in_channels <= 2) && (this->out_channels > this->in_channels)) memset (audio_buffer->mem, 0, this->out_channels * done * 2); - this->master = this->class->master; - if (this->class->caps & FIXED_POINT_CAP) { /* hint compiler to use 32 to 64 bit multiply instruction where available. */ /* also, that shift optimizes to almost nothing at 32bit system. */ @@ -686,18 +694,18 @@ if (this->in_channels < 6) { if (this->in_channels < 2) { if (this->out_used <= 1) { /* M -> M */ - int32_t g1 = this->master->gain_i; + int32_t g1 = this->class->gain_i; do { int32_t v; GET1 (0, 0); p++; q++; } while (--n); } else { /* M -> M M ... */ - int32_t g1 = this->master->gain_i; + int32_t g1 = this->class->gain_i; do { int32_t v; GET1 (0, 0); q[1] = q[0]; p++; q += this->out_channels; } while (--n); } } else { if (this->out_used < 2) { /* L R -> M */ - int32_t g1 = this->master->gain6_i; + int32_t g1 = this->class->gain6_i; do { int32_t v; GET2 (0, 0); p += 2; q++; } while (--n); } else { /* L R -> L R ... */ - int32_t g1 = this->master->gain_i; + int32_t g1 = this->class->gain_i; do { int32_t v; GET1 (0, 0); GET1 (1, 1); p += 2; q += this->out_channels; } while (--n); } } @@ -707,15 +715,15 @@ case 0: /* C L R SL SR B -> M */ do { int32_t v; - v = ((int64_t)this->master->gain9_i * (int64_t)(p[1] + p[2]) - + (int64_t)this->master->gain6_i * (int64_t)(p[0] + p[5]) - + (int64_t)this->master->gain12_i * (int64_t)(p[3] + p[4])) >> 32; + v = ((int64_t)this->class->gain9_i * (int64_t)(p[1] + p[2]) + + (int64_t)this->class->gain6_i * (int64_t)(p[0] + p[5]) + + (int64_t)this->class->gain12_i * (int64_t)(p[3] + p[4])) >> 32; *q++ = sat16 (v); p += this->in_channels; } while (--n); break; case 1: { /* C L R SL SR B -> L R */ - int32_t g1 = this->master->gain3_i; - int32_t g2 = this->master->gain6_i; + int32_t g1 = this->class->gain3_i; + int32_t g2 = this->class->gain6_i; do { int32_t m = p[0] + p[5]; int32_t v; @@ -724,34 +732,34 @@ } while (--n); } break; case 2: { /* C L R SL SR B -> L R SL SR */ - int32_t g1 = this->master->gain3_i; + int32_t g1 = this->class->gain3_i; do { - int64_t m = (int64_t)this->master->gain6_i * (int64_t)(p[0] + p[5]); + int64_t m = (int64_t)this->class->gain6_i * (int64_t)(p[0] + p[5]); int32_t v; GET1M (1, 0); GET1M (2, 1); GET1 (3, 2); GET1 (4, 3); p += this->in_channels; q += 4; } while (--n); } break; case 3: { /* C L R SL SR B -> L R SL SR 0 B */ - int32_t g1 = this->master->gain3_i; + int32_t g1 = this->class->gain3_i; do { - int64_t m = (int64_t)this->master->gain6_i * (int64_t)p[0]; + int64_t m = (int64_t)this->class->gain6_i * (int64_t)p[0]; int32_t v; GET1M (1, 0); GET1M (2, 1); GET1 (3, 2); GET1 (4, 3); q[4] = 0; GET1 (5, 5); p += this->in_channels; q += 6; } while (--n); } break; case 4: { /* C L R SL SR B -> L R SL SR C 0 */ - int32_t g1 = this->master->gain3_i; + int32_t g1 = this->class->gain3_i; do { - int64_t m = (int64_t)this->master->gain6_i * (int64_t)p[5]; + int64_t m = (int64_t)this->class->gain6_i * (int64_t)p[5]; int32_t v; GET1M (1, 0); GET1M (2, 1); GET1 (3, 2); GET1 (4, 3); GET1 (0, 4); q[5] = 0; p += this->in_channels; q += 6; } while (--n); } break; case 5: { /* C L R SL SR B -> L R SL SR C B */ - int32_t g1 = this->master->gain_i; + int32_t g1 = this->class->gain_i; do { int32_t v; GET1 (1, 0); GET1 (2, 1); GET1 (3, 2); GET1 (4, 3); GET1 (0, 4); GET1 (5, 5); @@ -777,18 +785,18 @@ if (this->in_channels < 6) { if (this->in_channels < 2) { if (this->out_used <= 1) { /* M -> M */ - float g1 = this->master->gain_f; + float g1 = this->class->gain_f; do { int32_t v; GET1 (0, 0); p++; q++; } while (--n); } else { /* M -> M M ... */ - float g1 = this->master->gain_f; + float g1 = this->class->gain_f; do { int32_t v; GET1 (0, 0); q[1] = q[0]; p++; q += this->out_channels; } while (--n); } } else { if (this->out_used < 2) { /* L R -> M */ - float g1 = this->master->gain6_f; + float g1 = this->class->gain6_f; do { int32_t v; GET2 (0, 0); p += 2; q++; } while (--n); } else { /* L R -> L R ... */ - float g1 = this->master->gain_f; + float g1 = this->class->gain_f; do { int32_t v; GET1 (0, 0); GET1 (1, 1); p += 2; q += this->out_channels; } while (--n); } } @@ -798,15 +806,15 @@ case 0: /* C L R SL SR B -> M */ do { int32_t v; - v = this->master->gain9_f * (p[1] + p[2]) - + this->master->gain6_f * (p[0] + p[5]) - + this->master->gain12_f * (p[3] + p[4]); + v = this->class->gain9_f * (p[1] + p[2]) + + this->class->gain6_f * (p[0] + p[5]) + + this->class->gain12_f * (p[3] + p[4]); *q++ = sat16 (v); p += this->in_channels; } while (--n); break; case 1: { /* C L R SL SR B -> L R */ - float g1 = this->master->gain3_f; - float g2 = this->master->gain6_f; + float g1 = this->class->gain3_f; + float g2 = this->class->gain6_f; do { float m = p[0] + p[5]; int32_t v; @@ -815,34 +823,34 @@ } while (--n); } break; case 2: { /* C L R SL SR B -> L R SL SR */ - float g1 = this->master->gain3_f; + float g1 = this->class->gain3_f; do { - float m = this->master->gain6_f * (p[0] + p[5]); + float m = this->class->gain6_f * (p[0] + p[5]); int32_t v; GET1M (1, 0); GET1M (2, 1); GET1 (3, 2); GET1 (4, 3); p += this->in_channels; q += 4; } while (--n); } break; case 3: { /* C L R SL SR B -> L R SL SR 0 B */ - float g1 = this->master->gain3_f; + float g1 = this->class->gain3_f; do { - float m = this->master->gain6_f * p[0]; + float m = this->class->gain6_f * p[0]; int32_t v; GET1M (1, 0); GET1M (2, 1); GET1 (3, 2); GET1 (4, 3); q[4] = 0; GET1 (5, 5); p += this->in_channels; q += 6; } while (--n); } break; case 4: { /* C L R SL SR B -> L R SL SR C 0 */ - float g1 = this->master->gain3_f; + float g1 = this->class->gain3_f; do { - float m = this->master->gain6_f * p[5]; + float m = this->class->gain6_f * p[5]; int32_t v; GET1M (1, 0); GET1M (2, 1); GET1 (3, 2); GET1 (4, 3); GET1 (0, 4); q[5] = 0; p += this->in_channels; q += 6; } while (--n); } break; case 5: { /* C L R SL SR B -> L R SL SR C B */ - float g1 = this->master->gain_f; + float g1 = this->class->gain_f; do { int32_t v; GET1 (1, 0); GET1 (2, 1); GET1 (3, 2); GET1 (4, 3); GET1 (0, 4); GET1 (5, 5); @@ -860,8 +868,15 @@ } xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG + 1, - "faad_audio_decoder: outbuf samples %d, pts %" PRId64 ".\n", + LOG_MODULE ": outbuf samples %d, pts %" PRId64 ".\n", (int)audio_buffer->num_frames, audio_buffer->vpts); + if (this->adif.mode == _ADIF_YES) { + this->adif.samples += audio_buffer->num_frames; + if (this->adif.samples >= this->rate) { + this->adif.samples -= this->rate; + this->adif.seconds += 1; + } + } this->stream->audio_out->put_buffer (this->stream->audio_out, audio_buffer, this->stream); decoded -= done; @@ -909,7 +924,7 @@ this->dec_config = b; this->dec_config_size = len; this->latm_mode = BEBF_LATM_IS_RAW; - xprintf (this->class->xine, XINE_VERBOSITY_DEBUG, "faad_audio_decoder: got new AAC config from demuxer\n"); + xprintf (this->class->xine, XINE_VERBOSITY_DEBUG, LOG_MODULE ": got new AAC config from demuxer\n"); if (!this->faac_dec) return; @@ -951,10 +966,26 @@ if ((int)buf->size <= 0) return; + if (this->adif.mode == _ADIF_UNKNOWN) { + if (buf->size >= 4) { + this->adif.mode = !memcmp (buf->content, "ADIF", 4) ? _ADIF_YES : _ADIF_NO; + } + } + if (this->adif.mode == _ADIF_YES) { + if (buf->pts != this->adif.base_pts) { + this->adif.base_pts = buf->pts; + this->adif.seconds = 0; + this->adif.samples = 0; + } + buf->pts = this->adif.base_pts + + (int64_t)90000 * this->adif.seconds + + 90000u * this->adif.samples / this->rate; + } + /* Queue pts values as frames may overlap buffer boundaries (mpeg-ts). */ faad_pts_add (this, buf->pts, buf->size); xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG + 1, - "faad_audio_decoder: inbuf bytes %d, pts %" PRId64 ".\n", buf->size, buf->pts); + LOG_MODULE ": inbuf bytes %d, pts %" PRId64 ".\n", buf->size, buf->pts); if (this->size + buf->size + 8 > this->max_audio_src_size) { size_t s = this->size + 2 * buf->size + 8; @@ -992,7 +1023,7 @@ if ((buf->type & (BUF_MAJOR_MASK | BUF_DECODER_MASK)) == BUF_AUDIO_AAC_LATM) #endif xprintf (this->class->xine, XINE_VERBOSITY_DEBUG, - "faad_audio_decoder: stream says LATM but is ADTS\n"); + LOG_MODULE ": stream says LATM but is ADTS\n"); } } } @@ -1003,6 +1034,7 @@ static void faad_discontinuity (audio_decoder_t *this_gen) { faad_decoder_t *this = xine_container_of(this_gen, faad_decoder_t, audio_decoder); + this->adif.base_pts = -1; faad_pts_reset (this); } @@ -1052,7 +1084,12 @@ this->dec_config_size = 0; this->rate = 0; this->used_last = 0; + this->adif.base_pts = 0; + this->adif.seconds = 0; + this->adif.samples = 0; + this->adif.mode = _ADIF_UNKNOWN; #endif + this->rate = 1; /* no / 0 please */ faad_pts_reset (this); @@ -1124,63 +1161,38 @@ /* class management */ -static void faad_class_ref (faad_class_t *this) { - if (this) - (this->refs)++; -} +static void faad_class_dispose (audio_decoder_class_t *this_gen) { + faad_class_t *this = (faad_class_t *)this_gen; -static void faad_class_unref (audio_decoder_class_t *this_gen) { - faad_class_t *this = (faad_class_t *)this_gen, *master; - xine_t *xine; - if (!this) - return; - xine = this->xine; - master = this->master; - (this->refs)--; - if (!this->refs && (master != this)) - free (this); - if (--(master->refs)) - return; - xine->config->unregister_callback (xine->config, "audio.processing.faad_gain_dB"); - free (master); + this->xine->config->unregister_callbacks (this->xine->config, + "audio.processing.faad_gain_dB", NULL, this, sizeof (*this)); + free (this); } static void *faad_init_plugin (xine_t *xine, const void *data, const char *id) { - - faad_class_t *this, *master; - struct cfg_entry_s *entry; + faad_class_t *this; (void)data; this = calloc(1, sizeof (faad_class_t)); if (!this) return NULL; - faad_class_ref (this); this->decoder_class.open_plugin = open_plugin; this->decoder_class.identifier = id; this->decoder_class.description = N_("Freeware Advanced Audio Decoder"); - this->decoder_class.dispose = faad_class_unref; + this->decoder_class.dispose = faad_class_dispose; this->xine = xine; - master = NULL; - entry = xine->config->lookup_entry (xine->config, "audio.processing.faad_gain_dB"); - if (entry && (entry->callback == gain_cb)) - master = (faad_class_t *)entry->callback_data; - if (master) { - faad_class_ref (master); - this->master = master; - } else { - faad_class_ref (this); - this->master = this; - this->gain_db = xine->config->register_num (xine->config, - "audio.processing.faad_gain_dB", -3, - _("FAAD audio gain (dB)"), - _("Some AAC tracks are encoded too loud and thus play distorted.\n" + /* we made 2 classes just to allow different xine decoder priorities. + * xine config now supports multiple change callbacks per key. */ + this->gain_db = xine->config->register_num (xine->config, + "audio.processing.faad_gain_dB", -3, + _("FAAD audio gain (dB)"), + _("Some AAC tracks are encoded too loud and thus play distorted.\n" "This cannot be fixed by volume control, but by this setting."), - 10, gain_cb, this); - gain_update (this); - } + 10, gain_cb, this); + gain_update (this); return this; } diff -r 9e075962b659 src/audio_out/audio_directx_out.c --- a/src/audio_out/audio_directx_out.c Wed Mar 09 14:31:44 2022 +0200 +++ b/src/audio_out/audio_directx_out.c Sat Sep 24 13:04:31 2022 +0200 @@ -27,6 +27,10 @@ * - posibility of bad state when buffer overrun */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + typedef unsigned char boolean; #include <windows.h> diff -r 9e075962b659 src/audio_out/audio_oss_out.c --- a/src/audio_out/audio_oss_out.c Wed Mar 09 14:31:44 2022 +0200 +++ b/src/audio_out/audio_oss_out.c Sat Sep 24 13:04:31 2022 +0200 @@ -1002,6 +1002,9 @@ int audio_devs; char *parse; + this->mixer.fd = -1; + mixer_dev[0] = 0; + mixer_num = config->register_num(config, "audio.device.oss_mixer_number", -1, _("OSS audio mixer number, -1 for none"), _("The full mixer device name is created by taking the " @@ -1024,10 +1027,10 @@ snprintf(mixer_dev, sizeof(mixer_dev), "%smixer", mixer_name); else snprintf(mixer_dev, sizeof(mixer_dev), "%smixer%d", mixer_name, mixer_num); + + this->mixer.fd = xine_open_cloexec(mixer_dev, O_RDONLY); } - this->mixer.fd = xine_open_cloexec(mixer_dev, O_RDONLY); - if(this->mixer.fd != -1) { ioctl(this->mixer.fd, SOUND_MIXER_READ_DEVMASK, &audio_devs); diff -r 9e075962b659 src/combined/ffmpeg/demux_avformat.c --- a/src/combined/ffmpeg/demux_avformat.c Wed Mar 09 14:31:44 2022 +0200 +++ b/src/combined/ffmpeg/demux_avformat.c Sat Sep 24 13:04:31 2022 +0200 @@ -29,6 +29,8 @@ #include <pthread.h> #include <errno.h> +#include <libavutil/avutil.h> +#include <libavcodec/avcodec.h> #include <libavformat/avformat.h> #include <libavcodec/version.h> #include <libavformat/avio.h> @@ -922,3 +924,4 @@ (void)data; return (demux_class_t *)&this; } + diff -r 9e075962b659 src/combined/ffmpeg/ff_audio_decoder.c --- a/src/combined/ffmpeg/ff_audio_decoder.c Wed Mar 09 14:31:44 2022 +0200 +++ b/src/combined/ffmpeg/ff_audio_decoder.c Sat Sep 24 13:04:31 2022 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2001-2021 the xine project + * Copyright (C) 2001-2022 the xine project * * This file is part of xine, a free video player. * @@ -31,11 +31,18 @@ #include <pthread.h> #include <math.h> -#ifdef HAVE_FFMPEG_AVUTIL_H -# include <avcodec.h> +#if defined(HAVE_LIBAVUTIL_AVUTIL_H) +# include <libavutil/avutil.h> +#endif + +#if defined(HAVE_LIBAVUTIL_MEM_H) +# include <libavutil/mem.h> +#endif + +#if defined(HAVE_AVUTIL_AVCODEC_H) +# include <libavcodec/avcodec.h> #else -# include <libavcodec/avcodec.h> -# include <libavutil/mem.h> +# include <avcodec.h> #endif #define LOG_MODULE "ffmpeg_audio_dec" @@ -76,10 +83,18 @@ AVCodecContext *context; const AVCodec *codec; + struct { + uint8_t *buf; + size_t len; + } parse, decode, send; + char *decode_buffer; int decoder_ok; + int pkt_sent; AVCodecParserContext *parser_context; + xine_pts_queue_t *pts_queue; + #if XFF_AUDIO > 3 AVFrame *av_frame; #endif @@ -293,7 +308,7 @@ this->context->codec_type = this->codec->type; this->context->codec_tag = _x_stream_info_get(this->stream, XINE_STREAM_INFO_AUDIO_FOURCC); - /* Use parser for EAC3, AAC LATM and MPEG. + /* Use parser for EAC3, AAC LATM, and MPEG. * Fixes: * - DVB streams where multiple AAC LATM frames are packed to single PES * - DVB streams where MPEG audio frames do not follow PES packet boundaries @@ -302,15 +317,16 @@ if (codec_type == BUF_AUDIO_AAC_LATM || codec_type == BUF_AUDIO_EAC3 || codec_type == BUF_AUDIO_A52 || - codec_type == BUF_AUDIO_MPEG) { - - xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, - "ffmpeg_audio_dec: using parser\n"); + codec_type == BUF_AUDIO_MPEG || + codec_type == BUF_AUDIO_COOK) { - this->parser_context = av_parser_init(this->codec->id); - if (!this->parser_context) { + this->parser_context = av_parser_init (this->codec->id); + if (this->parser_context) { + xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, + "ffmpeg_audio_dec: using parser\n"); + } else { xprintf (this->stream->xine, XINE_VERBOSITY_LOG, - "ffmpeg_audio_dec: couldn't init parser\n"); + "ffmpeg_audio_dec: couldn't init parser\n"); } } #endif @@ -341,6 +357,7 @@ this->decoder_ok = 1; + xine_pts_queue_reset (this->pts_queue); return 1; } @@ -644,24 +661,29 @@ } } -#define CLIP_16(v) ((v + 0x8000) & ~0xffff ? (v >> 31) ^ 0x7fff : v) +static int ff_audio_parse (ff_audio_decoder_t *this) { + int offs, parser_consumed; -static int ff_audio_decode (ff_audio_decoder_t *this, - int16_t *decode_buffer, int *decode_buffer_size, uint8_t *buf, int size) { - int parsed = 1, consumed, parser_consumed; - int offs; - - parser_consumed = ff_aac_mode_parse (this, buf, size, &offs); + this->pkt_sent = 0; + /* NOTE #1: our own parser uses this->buf for efficiancy. when it says 0, + * keep remaining bytes, and add more later. */ + parser_consumed = ff_aac_mode_parse (this, this->parse.buf, this->parse.len, &offs); if (parser_consumed < 0) { - *decode_buffer_size = 0; - return offs; + this->decode.buf = NULL; + this->decode.len = 0; + this->parse.buf += offs; + this->parse.len -= offs; + return 0; } else if (parser_consumed > 0) { - buf += offs; - size = parser_consumed - offs; + this->decode.buf = this->parse.buf + offs; + this->decode.len = parser_consumed - offs; + this->parse.buf += parser_consumed; + this->parse.len -= parser_consumed; + return 1; } - + /* parser_consumed == 0 */ #if XFF_PARSE > 1 - else + /* NOTE #2: ffmpeg parser uses its own buf, and thus consumes all. */ if (this->parser_context) { uint8_t *outbuf; int outsize; @@ -674,48 +696,85 @@ * and so on, with no need for warnings. */ do { int ret = av_parser_parse2 (this->parser_context, this->context, - &outbuf, &outsize, buf, size, 0, 0, 0); + &outbuf, &outsize, this->parse.buf, this->parse.len, 0, 0, 0); parser_consumed += ret; - buf += ret; - size -= ret; - } while (size > 0 && outsize <= 0); - + this->parse.buf += ret; + this->parse.len -= ret; + } while (this->parse.len && (outsize <= 0)); /* nothing to decode ? */ if (outsize <= 0) { - *decode_buffer_size = 0; - return parser_consumed; + this->decode.len = 0; + return 0; } - /* decode next packet */ - buf = outbuf; - size = outsize; + this->decode.buf = outbuf; + this->decode.len = outsize; + return 1; } #endif /* XFF_PARSE > 1 */ - else parsed = 0; + this->decode.buf = this->parse.buf; + this->decode.len = this->parse.len; + return 1; +} -#if XFF_AUDIO > 2 - this->avpkt->data = buf; - this->avpkt->size = size; +static void ff_audio_unparse (ff_audio_decoder_t *this) { + if (this->decode.buf && this->parse.buf + && (this->decode.buf >= this->parse.buf) + && (this->decode.buf <= this->parse.buf + this->parse.len)) { + /* parser just mapped through the input buf. post back. */ + this->parse.len = (this->parse.buf + this->parse.len) - this->decode.buf; + this->parse.buf = this->decode.buf; + } +} + +#define CLIP_16(v) ((v + 0x8000) & ~0xffff ? (v >> 31) ^ 0x7fff : v) + +static int ff_audio_decode (ff_audio_decoder_t *this) { + int16_t *decode_buffer = (int16_t *)this->send.buf; + int consumed, got_frame = 0; +#if XFF_AUDIO >= 4 + float gain = this->class->gain; +#endif +#if XFF_AUDIO >= 3 + this->avpkt->data = this->decode.buf; + this->avpkt->size = this->decode.len; this->avpkt->flags = AV_PKT_FLAG_KEY; -# if XFF_AUDIO > 3 - int got_frame; - float gain = this->class->gain; +# if XFF_AUDIO >= 4 if (!this->av_frame) this->av_frame = XFF_ALLOC_FRAME (); -# if XFF_AUDIO == 5 - { +# if XFF_AUDIO == 5 + if (!this->pkt_sent) { int err = avcodec_send_packet (this->context, this->avpkt); /* xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "ff_audio_dec: send (%d) = %d.\n", (int)size, err); */ - /* multiple frames per packet */ - consumed = (err >= 0) ? size : ((err == AVERROR (EAGAIN)) ? 0 : err); + /* NOTE: multiple frames per packet should now be avoided, + * we no longer know the individual frame sizes here. + * kludge: consume 1 symbolic byte. */ + if (err >= 0) { + consumed = 1; + this->pkt_sent = 1; + } else { + consumed = (err == AVERROR (EAGAIN)) ? 0 : err; + } + } else { + consumed = 1; + } + { /* that calls av_frame_unref () first. */ - err = avcodec_receive_frame (this->context, this->av_frame); + int err = avcodec_receive_frame (this->context, this->av_frame); /* xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "ff_audio_dec: recv () = %d.\n", err); */ - got_frame = (err == 0); + if (err != 0) { + /* now, these are all frames from this packet. */ + this->decode.buf += this->decode.len; + this->decode.len = 0; + this->send.len = 0; + this->pkt_sent = 0; + return 0; + } + got_frame = 1; } -# else +# else consumed = avcodec_decode_audio4 (this->context, this->av_frame, &got_frame, this->avpkt); -# endif +# endif if ((consumed >= 0) && got_frame) { /* setup may have altered while decoding */ ff_map_channels (this); @@ -725,10 +784,10 @@ int channels = this->ao_channels; int bytes, i, j, shift = this->downmix_shift; /* limit buffer */ - if (*decode_buffer_size < samples * channels * 2) - samples = *decode_buffer_size / (channels * 2); + if ((int)this->send.len < samples * channels * 2) + samples = this->send.len / (channels * 2); bytes = samples * channels * 2; - *decode_buffer_size = bytes; + this->send.len = bytes; /* TJ. convert to packed int16_t while respecting the user's speaker arrangement. I tried to speed up and not to pull in libswresample. */ for (i = 2; i < channels; i++) if (this->map[i] < 0) { @@ -968,38 +1027,49 @@ v += *p++; *q++ = v >> 1; } - *decode_buffer_size = samples * 2; + this->send.len = samples * 2; } - } else *decode_buffer_size = 0; -# else - consumed = avcodec_decode_audio3 (this->context, decode_buffer, decode_buffer_size, this->avpkt); + } else { /* !((consumed >= 0) && got_frame) */ + this->send.len = 0; + } +# else /* XFF_AUDIO < 4 */ + { + int slen = this->send.len; + consumed = avcodec_decode_audio3 (this->context, this->send.buf, &slen, this->avpkt); + if (slen > 0) + this->send.len = slen; + } + got_frame = consumed >= 0; ff_map_channels (this); # endif -#else - consumed = avcodec_decode_audio2 (this->context, decode_buffer, decode_buffer_size, buf, size); +#else /* #if XFF_AUDIO < 3 */ + { + int slen = this->send.len; + consumed = avcodec_decode_audio2 (this->context, this->send.buf, &slen, this->decode.buf, this->decode.len); + if (slen > 0) + this->send.len = slen; + } + got_frame = consumed >= 0; ff_map_channels (this); #endif - if (consumed < 0) { + if (consumed >= 0) { + this->decode.buf += consumed; + this->decode.len -= consumed; + return got_frame; + } else { + this->decode.buf += this->decode.len; + this->decode.len = 0; + this->send.len = 0; xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, - "ffmpeg_audio_dec: error decompressing audio frame (%d)\n", consumed); - } else if (parser_consumed && consumed != size) { - - xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, - "ffmpeg_audio_dec: decoder didn't consume all data\n"); + "ffmpeg_audio_dec: error decompressing audio frame (%d)\n", consumed); + return 0; } - - return parsed ? parser_consumed : consumed; } static void ff_audio_decode_data (audio_decoder_t *this_gen, buf_element_t *buf) { ff_audio_decoder_t *this = xine_container_of(this_gen, ff_audio_decoder_t, audio_decoder); - int bytes_consumed; - int decode_buffer_size; - int out; - audio_buffer_t *audio_buffer; - int bytes_to_send; unsigned int codec_type = buf->type & (BUF_MAJOR_MASK | BUF_DECODER_MASK); if (buf->decoder_flags & BUF_FLAG_SPECIAL) { @@ -1025,193 +1095,200 @@ ff_audio_ensure_buffer_size(this, this->size + buf->size); xine_fast_memcpy (&this->buf[this->size], buf->content, buf->size); this->size += buf->size; + xine_pts_queue_put (this->pts_queue, buf->size, buf->pts); if (this->parser_context || buf->decoder_flags & BUF_FLAG_FRAME_END) { /* time to decode a frame */ - int offset = 0; - /* pad input data */ - memset(this->buf + this->size, 0, AV_INPUT_BUFFER_PADDING_SIZE); + memset (this->buf + this->size, 0, AV_INPUT_BUFFER_PADDING_SIZE); + this->parse.buf = this->buf; + this->parse.len = this->size; - while (this->size > 0) { - - decode_buffer_size = AVCODEC_MAX_AUDIO_FRAME_SIZE; - - bytes_consumed = ff_audio_decode (this, (int16_t *)this->decode_buffer, &decode_buffer_size, - &this->buf[offset], this->size); - if (bytes_consumed < 0) + while (this->parse.len) { + if (!ff_audio_parse (this)) break; - /* NOTE #1: our own parser uses this->buf for efficiancy. when it says 0, - * keep remaining bytes, and add more later. */ - /* NOTE #2: ffmpeg parser uses its own buf, and thus consumes all. */ - offset += bytes_consumed; - this->size -= bytes_consumed; - if (decode_buffer_size <= 0) { - if (bytes_consumed > 0) - continue; - /* nothing used and nothing sent?? - * a) ADTS probe running (< 16k bytes) - * b) we can play this all day, or drop the undigestible after a while. */ - if (this->size >= (64 << 10)) - this->size = 0; - break; - } + while (this->decode.len) { + audio_buffer_t *audio_buffer; + int bytes_to_send; + int64_t pts; + uint32_t have_len = this->decode.len, out; - if (this->ff_sample_rate != this->context->sample_rate || - this->ao_mode != this->new_mode) { - xprintf(this->stream->xine, XINE_VERBOSITY_LOG, - _("ffmpeg_audio_dec: codec parameters changed\n")); - /* close if it was open, and always trigger 1 new open attempt below */ - ff_audio_output_close(this); - } + this->send.buf = this->decode_buffer; + this->send.len = AVCODEC_MAX_AUDIO_FRAME_SIZE; + if (!ff_audio_decode (this)) { + have_len -= this->decode.len; + xine_pts_queue_get (this->pts_queue, have_len); + if (have_len > 0) + continue; + /* nothing used and nothing sent?? + * a) ADTS probe running (< 16k bytes) + * b) we can play this all day, or drop the undigestible after a while. */ + if (this->size >= (64 << 10)) { + this->decode.buf += this->decode.len; + this->decode.len = 0; + xine_pts_queue_get (this->pts_queue, this->parse.len); + this->parse.buf += this->parse.len; + this->parse.len = 0; + } + break; + } + have_len -= this->decode.len; + pts = xine_pts_queue_get (this->pts_queue, have_len); - if (!this->output_open) { - if (!this->ff_sample_rate || !this->ao_mode) { - this->ff_sample_rate = this->context->sample_rate; - this->ao_mode = this->new_mode; - } - if (!this->ff_sample_rate || !this->new_mode) { - xprintf(this->stream->xine, XINE_VERBOSITY_LOG, - _("ffmpeg_audio_dec: cannot read codec parameters from packet\n")); - /* try to decode next packet. */ - /* there shouldn't be any output yet */ - decode_buffer_size = 0; - /* pts applies only to first audio packet */ - buf->pts = 0; - } else { - this->output_open = (this->stream->audio_out->open) (this->stream->audio_out, - this->stream, 16, this->ff_sample_rate, - this->ao_mode); - if (!this->output_open) { - xprintf(this->stream->xine, XINE_VERBOSITY_LOG, - "ffmpeg_audio_dec: error opening audio output\n"); - this->size = 0; - return; - } - } - } + if ((this->ff_sample_rate != this->context->sample_rate) + || (this->ao_mode != this->new_mode)) { + xprintf (this->stream->xine, XINE_VERBOSITY_LOG, + _("ffmpeg_audio_dec: codec parameters changed\n")); + /* close if it was open, and always trigger 1 new open attempt below */ + ff_audio_output_close (this); + } + + if (!this->output_open) { + if (!this->ff_sample_rate || !this->ao_mode) { + this->ff_sample_rate = this->context->sample_rate; + this->ao_mode = this->new_mode; + } + if (this->ff_sample_rate && this->new_mode) { + this->output_open = this->stream->audio_out->open (this->stream->audio_out, + this->stream, 16, this->ff_sample_rate, this->ao_mode); + if (!this->output_open) { + xprintf (this->stream->xine, XINE_VERBOSITY_LOG, + "ffmpeg_audio_dec: error opening audio output\n"); + this->size = 0; + return; + } + } else { + xprintf (this->stream->xine, XINE_VERBOSITY_LOG, + _("ffmpeg_audio_dec: cannot read codec parameters from packet\n")); + /* try to decode next packet. */ + /* there shouldn't be any output yet */ + this->send.len = 0; + /* pts applies only to first audio packet */ + buf->pts = 0; + } + } #if XFF_AUDIO < 4 - /* Old style postprocessing */ - if (codec_type == BUF_AUDIO_WMAPRO) { - /* the above codecs output float samples, not 16-bit integers */ - int samples = decode_buffer_size / sizeof(float); - float gain = this->class->gain; - float *p = (float *)this->decode_buffer; - int16_t *q = (int16_t *)this->decode_buffer; - int i; - for (i = samples; i; i--) { - int v = *p++ * gain; - *q++ = CLIP_16 (v); - } - decode_buffer_size = samples * 2; - } - - if ((this->ao_channels != this->ff_channels) || (this->ao_channels > 2)) { - /* Channel reordering and/or mixing */ - int samples = decode_buffer_size / (this->ff_channels * 2); - int channels = this->ao_channels; - int ff_channels = this->ff_channels; - int16_t *p = (int16_t *)this->decode_buffer; - int16_t *q = p; - int shift = this->downmix_shift, i, j; - /* downmix mono output to stereo first */ - if ((channels == 1) && (ff_channels > 1)) - channels = 2; - /* move to end of buf for in-place editing */ - p += AVCODEC_MAX_AUDIO_FRAME_SIZE - decode_buffer_size; - if (p >= q + decode_buffer_size) - xine_fast_memcpy (p, q, decode_buffer_size); - else - memmove (p, q, decode_buffer_size); - /* not very optimized but it only hits when playing multichannel audio through - old ffmpeg - and its still better than previous code there */ - if (this->front_mixes < 2) { - /* just reorder and maybe upmix */ + /* Old style postprocessing */ + if (codec_type == BUF_AUDIO_WMAPRO) { + /* the above codecs output float samples, not 16-bit integers */ + int samples = this->send.len / sizeof(float); + float gain = this->class->gain; + float *p = (float *)this->decode_buffer; + int16_t *q = (int16_t *)this->decode_buffer; + int i; for (i = samples; i; i--) { - q[0] = p[0]; - q[1] = p[this->right[0]]; - for (j = 2; j < channels; j++) - q[j] = this->map[j] < 0 ? 0 : p[this->map[j]]; - p += ff_channels; - q += channels; + int v = *p++ * gain; + *q++ = CLIP_16 (v); } - } else { - /* downmix */ - for (i = samples; i; i--) { - int left = p[0]; - int right = p[this->right[0]]; - for (j = 1; j < this->front_mixes; j++) { - left += p[this->left[j]]; - right += p[this->right[j]]; + this->send.len = samples * 2; + } + + if ((this->ao_channels != this->ff_channels) || (this->ao_channels > 2)) { + /* Channel reordering and/or mixing */ + int samples = this->send.len / (this->ff_channels * 2); + int channels = this->ao_channels; + int ff_channels = this->ff_channels; + int16_t *p = (int16_t *)this->decode_buffer; + int16_t *q = p; + int shift = this->downmix_shift, i, j; + /* downmix mono output to stereo first */ + if ((channels == 1) && (ff_channels > 1)) + channels = 2; + /* move to end of buf for in-place editing */ + p += AVCODEC_MAX_AUDIO_FRAME_SIZE - this->send.len; + if (p >= q + this->send.len) + xine_fast_memcpy (p, q, this->send.len); + else + memmove (p, q, this->send.len); + /* not very optimized but it only hits when playing multichannel audio through + old ffmpeg - and its still better than previous code there */ + if (this->front_mixes < 2) { + /* just reorder and maybe upmix */ + for (i = samples; i; i--) { + q[0] = p[0]; + q[1] = p[this->right[0]]; + for (j = 2; j < channels; j++) + q[j] = this->map[j] < 0 ? 0 : p[this->map[j]]; + p += ff_channels; + q += channels; } - left >>= shift; - q[0] = CLIP_16 (left); - right >>= shift; - q[1] = CLIP_16 (right); - for (j = 2; j < channels; j++) - q[j] = this->map[j] < 0 ? 0 : p[this->map[j]] >> shift; - p += ff_channels; - q += channels; + } else { + /* downmix */ + for (i = samples; i; i--) { + int left = p[0]; + int right = p[this->right[0]]; + for (j = 1; j < this->front_mixes; j++) { + left += p[this->left[j]]; + right += p[this->right[j]]; + } + left >>= shift; + q[0] = CLIP_16 (left); + right >>= shift; + q[1] = CLIP_16 (right); + for (j = 2; j < channels; j++) + q[j] = this->map[j] < 0 ? 0 : p[this->map[j]] >> shift; + p += ff_channels; + q += channels; + } } + /* final mono downmix */ + if (channels > this->ao_channels) { + p = (int16_t *)this->decode_buffer; + q = p; + for (i = samples; i; i--) { + int v = *p++; + v += *p++; + *q++ = v >> 1; + } + } + this->send.len = samples * this->ao_channels * 2; } - /* final mono downmix */ - if (channels > this->ao_channels) { - p = (int16_t *)this->decode_buffer; - q = p; - for (i = samples; i; i--) { - int v = *p++; - v += *p++; - *q++ = v >> 1; - } - } - decode_buffer_size = samples * this->ao_channels * 2; - } #endif - /* dispatch the decoded audio */ - out = 0; - while (out < decode_buffer_size) { - int stream_status = xine_get_status(this->stream); + /* dispatch the decoded audio */ + out = 0; + while (out < this->send.len) { + int stream_status = xine_get_status (this->stream); - if (stream_status == XINE_STATUS_QUIT || stream_status == XINE_STATUS_STOP) { - this->size = 0; - return; - } + if ((stream_status == XINE_STATUS_QUIT) || (stream_status == XINE_STATUS_STOP)) { + this->size = 0; + return; + } + + audio_buffer = this->stream->audio_out->get_buffer (this->stream->audio_out); + if (audio_buffer->mem_size == 0) { + xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, + "ffmpeg_audio_dec: Help! Allocated audio buffer with nothing in it!\n"); + return; + } - audio_buffer = - this->stream->audio_out->get_buffer (this->stream->audio_out); - if (audio_buffer->mem_size == 0) { - xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, - "ffmpeg_audio_dec: Help! Allocated audio buffer with nothing in it!\n"); - return; - } + /* fill up this buffer */ + if ((int)(this->send.len - out) > audio_buffer->mem_size) + bytes_to_send = audio_buffer->mem_size; + else + bytes_to_send = this->send.len - out; + + xine_fast_memcpy (audio_buffer->mem, &this->decode_buffer[out], bytes_to_send); + out += bytes_to_send; - /* fill up this buffer */ - if ((decode_buffer_size - out) > audio_buffer->mem_size) - bytes_to_send = audio_buffer->mem_size; + /* byte count / 2 (bytes / sample) / channels */ + audio_buffer->num_frames = bytes_to_send / 2 / this->ao_channels; + audio_buffer->vpts = pts; + pts = 0; /* only first buffer gets the real pts */ + this->stream->audio_out->put_buffer (this->stream->audio_out, audio_buffer, this->stream); + } /* output loop */ + } /* decode loop */ + ff_audio_unparse (this); + } /* parse loop */ + this->size = this->parse.len; + if (this->size > 0) { + int offs = this->parse.buf - this->buf; + if (offs > 0) { + if (offs >= this->size) + memcpy (this->buf, this->buf + offs, this->size); else - bytes_to_send = decode_buffer_size - out; - - xine_fast_memcpy(audio_buffer->mem, &this->decode_buffer[out], bytes_to_send); - out += bytes_to_send; - - /* byte count / 2 (bytes / sample) / channels */ - audio_buffer->num_frames = bytes_to_send / 2 / this->ao_channels; - - audio_buffer->vpts = buf->pts; - - buf->pts = 0; /* only first buffer gets the real pts */ - this->stream->audio_out->put_buffer (this->stream->audio_out, - audio_buffer, this->stream); - } - } - if (this->size > 0) { - if (offset > 0) { - if (offset >= this->size) - memcpy (this->buf, this->buf + offset, this->size); - else - memmove (this->buf, this->buf + offset, this->size); + memmove (this->buf, this->buf + offs, this->size); } } } @@ -1242,6 +1319,7 @@ ff_audio_reset_parser(this); ff_aac_mode_set (this, 1); + xine_pts_queue_reset (this->pts_queue); } static void ff_audio_discontinuity (audio_decoder_t *this_gen) { @@ -1290,6 +1368,8 @@ XFF_PACKET_UNREF (this->avpkt); + xine_pts_queue_delete (&this->pts_queue); + free (this_gen); } @@ -1346,6 +1426,8 @@ return NULL; } while (0); + this->pts_queue = xine_pts_queue_new (); + return &this->audio_decoder; } diff -r 9e075962b659 src/combined/ffmpeg/ff_video_decoder.c --- a/src/combined/ffmpeg/ff_video_decoder.c Wed Mar 09 14:31:44 2022 +0200 +++ b/src/combined/ffmpeg/ff_video_decoder.c Sat Sep 24 13:04:31 2022 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2001-2021 the xine project + * Copyright (C) 2001-2022 the xine project * * This file is part of xine, a free video player. * @@ -32,11 +32,18 @@ #include <math.h> #include <assert.h> -#ifdef HAVE_FFMPEG_AVUTIL_H -# include <avcodec.h> +#if defined(HAVE_LIBAVUTIL_AVUTIL_H) +# include <libavutil/avutil.h> +#endif + +#if defined(HAVE_LIBAVUTIL_MEM_H) +# include <libavutil/mem.h> +#endif + +#if defined(HAVE_AVUTIL_AVCODEC_H) +# include <libavcodec/avcodec.h> #else -# include <libavcodec/avcodec.h> -# include <libavutil/mem.h> +# include <avcodec.h> #endif #define LOG_MODULE "ffmpeg_video_dec" @@ -127,7 +134,6 @@ uint8_t decoder_ok:1; uint8_t decoder_init_mode:1; - uint8_t is_mpeg12:1; #ifdef HAVE_POSTPROC uint8_t pp_available:1; #endif @@ -1699,7 +1705,7 @@ static int ff_check_extradata(ff_video_decoder_t *this, unsigned int codec_type, buf_element_t *buf) { - if (this->context && this->context->extradata) + if (this->context->extradata) return 1; switch (codec_type) { @@ -1713,8 +1719,6 @@ static void ff_init_mpeg12_mode(ff_video_decoder_t *this) { - this->is_mpeg12 = 1; - if (this->decoder_init_mode) { _x_meta_info_set_utf8(this->stream, XINE_META_INFO_VIDEOCODEC, "mpeg-1 (ffmpeg)"); @@ -1725,7 +1729,8 @@ if ( this->mpeg_parser == NULL ) { this->mpeg_parser = calloc(1, sizeof(mpeg_parser_t)); - mpeg_parser_init(this->mpeg_parser, AV_INPUT_BUFFER_PADDING_SIZE); + if (this->mpeg_parser) + mpeg_parser_init(this->mpeg_parser, AV_INPUT_BUFFER_PADDING_SIZE); } } @@ -1739,7 +1744,7 @@ ff_init_mpeg12_mode(this); } - if (this->decoder_init_mode && !this->is_mpeg12) { + else if (this->decoder_init_mode && !this->mpeg_parser) { if (!ff_check_extradata(this, codec_type, buf)) return; @@ -2066,9 +2071,12 @@ lprintf("handle_mpeg12_buffer\n"); - if (!this->is_mpeg12) { + if (!this->mpeg_parser) { /* initialize mpeg parser */ ff_init_mpeg12_mode(this); + if (!this->mpeg_parser) { + return; + } } #ifdef DEBUG_MPEG_PARSER @@ -2653,7 +2661,7 @@ } } else { - if (this->decoder_init_mode && !this->is_mpeg12) + if (this->decoder_init_mode) ff_handle_preview_buffer(this, buf); /* decode */ @@ -2875,7 +2883,7 @@ #endif } - if (this->is_mpeg12) + if (this->mpeg_parser) mpeg_parser_reset(this->mpeg_parser); /* this->pts_tag_pass = 0; */ @@ -2997,7 +3005,6 @@ #ifndef HAVE_ZERO_SAFE_MEM this->size = 0; this->decoder_ok = 0; - this->is_mpeg12 = 0; this->aspect_ratio = 0; this->pts_tag_pass = 0; #ifdef HAVE_POSTPROC diff -r 9e075962b659 src/combined/ffmpeg/ffmpeg_compat.h --- a/src/combined/ffmpeg/ffmpeg_compat.h Wed Mar 09 14:31:44 2022 +0200 +++ b/src/combined/ffmpeg/ffmpeg_compat.h Sat Sep 24 13:04:31 2022 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2000-2021 the xine project + * Copyright (C) 2000-2022 the xine project * * This file is part of xine, a unix video player. * @@ -25,22 +25,24 @@ #define XFF_INT_VERSION(major,minor,micro) ((major<<16)|(minor<<8)|micro) -#ifndef LIBAVCODEC_VERSION_INT -# if defined(LIBAVCODEC_VERSION_MAJOR) && defined(LIBAVCODEC_VERSION_MINOR) -# define LIBAVCODEC_VERSION_INT XFF_INT_VERSION(LIBAVCODEC_VERSION_MAJOR,LIBAVCODEC_VERSION_MINOR,0) -# else -# error ffmpeg headers must be included first ! -# endif +/** NOTE: since 2022-09-01, ffmpeg headers are more detached from each other. + * this goes that far: + * libavformat/avformat.h includes libavcodec/packet.h which uses + * (but not includes) libavutil/avutil.h :-/ */ + +#if !defined(LIBAVUTIL_VERSION_INT) && defined(LIBAVUTIL_VERSION_MAJOR) && defined(LIBAVUTIL_VERSION_MINOR) +# define LIBAVUTIL_VERSION_INT XFF_INT_VERSION(LIBAVUTIL_VERSION_MAJOR,LIBAVUTIL_VERSION_MINOR,0) +#endif +#if !defined(LIBAVUTIL_VERSION_INT) +# error avutil.h must be included first ! #endif -#ifndef LIBAVUTIL_VERSION_INT -# if defined(LIBAVUTIL_VERSION_MAJOR) && defined(LIBAVUTIL_VERSION_MINOR) -# define LIBAVUTIL_VERSION_INT XFF_INT_VERSION(LIBAVUTIL_VERSION_MAJOR,LIBAVUTIL_VERSION_MINOR,0) -# else -# error ffmpeg headers must be included first ! -# endif +#if !defined(LIBAVCODEC_VERSION_INT) && defined(LIBAVCODEC_VERSION_MAJOR) && defined(LIBAVCODEC_VERSION_MINOR) +# define LIBAVCODEC_VERSION_INT XFF_INT_VERSION(LIBAVCODEC_VERSION_MAJOR,LIBAVCODEC_VERSION_MINOR,0) #endif +#if defined(LIBAVCODEC_VERSION_INT) + #if LIBAVCODEC_VERSION_INT >= XFF_INT_VERSION(52,0,0) # define bits_per_sample bits_per_coded_sample #endif @@ -136,38 +138,6 @@ # define XFF_VAAPI 2 /** << libavutil/hwcontext.h, libavutil/hwcontext_vaapi.h */ #endif -#if LIBAVUTIL_VERSION_INT >= XFF_INT_VERSION(52,0,0) -# define PIX_FMT_NONE AV_PIX_FMT_NONE -# define PIX_FMT_YUV420P AV_PIX_FMT_YUV420P -# define PIX_FMT_YUVJ420P AV_PIX_FMT_YUVJ420P -# define PIX_FMT_YUV444P AV_PIX_FMT_YUV444P -# define PIX_FMT_YUVJ444P AV_PIX_FMT_YUVJ444P -# define PIX_FMT_YUV410P AV_PIX_FMT_YUV410P -# define PIX_FMT_YUV411P AV_PIX_FMT_YUV411P -# define PIX_FMT_ARGB AV_PIX_FMT_ARGB -# define PIX_FMT_BGRA AV_PIX_FMT_BGRA -# define PIX_FMT_RGB24 AV_PIX_FMT_RGB24 -# define PIX_FMT_BGR24 AV_PIX_FMT_BGR24 -# define PIX_FMT_RGB555BE AV_PIX_FMT_RGB555BE -# define PIX_FMT_RGB555LE AV_PIX_FMT_RGB555LE -# define PIX_FMT_RGB565BE AV_PIX_FMT_RGB565BE -# define PIX_FMT_RGB565LE AV_PIX_FMT_RGB565LE -# define PIX_FMT_PAL8 AV_PIX_FMT_PAL8 -# define PixelFormat AVPixelFormat -/* video_out/video_out_vaapi */ -# if LIBAVCODEC_VERSION_INT < XFF_INT_VERSION(59,0,100) /** << revise this */ -# define PIX_FMT_VAAPI_VLD AV_PIX_FMT_VAAPI_VLD -# define PIX_FMT_VAAPI_IDCT AV_PIX_FMT_VAAPI_IDCT -# define PIX_FMT_VAAPI_MOCO AV_PIX_FMT_VAAPI_MOCO -# else -# define PIX_FMT_VAAPI_VLD AV_PIX_FMT_VAAPI -# define PIX_FMT_VAAPI_IDCT AV_PIX_FMT_VAAPI -# define PIX_FMT_VAAPI_MOCO AV_PIX_FMT_VAAPI -# endif - -# define CODEC_FLAG_BITEXACT AV_CODEC_FLAG_BITEXACT -#endif - #if LIBAVCODEC_VERSION_INT >= XFF_INT_VERSION(54,25,0) /* dxr3/ffmpeg_encoder */ # define CODEC_ID_MPEG1VIDEO AV_CODEC_ID_MPEG1VIDEO @@ -194,10 +164,6 @@ /* ff_*_decoder mapping is already handled by mkcodeclists.pl */ #endif -#ifndef AVCODEC_MAX_AUDIO_FRAME_SIZE -# define AVCODEC_MAX_AUDIO_FRAME_SIZE 192000 -#endif - #if LIBAVCODEC_VERSION_INT >= XFF_INT_VERSION(55,0,100) # define XFF_AV_BUFFER 1 #endif @@ -290,5 +256,51 @@ # define XFF_AVCODEC_REGISTER_ALL() do {} while(0) #endif +#if LIBAVCODEC_VERSION_INT < XFF_INT_VERSION(59,24,100) +# define XFF_AUDIO_CHANNEL_LAYOUT 1 /* AVCodecContext.channels, .channel_leyout */ +#else +# define XFF_AUDIO_CHANNEL_LAYOUT 2 /* AVCodecContext.ch_layout.nb_channels, .ch_layout.u.mask */ +#endif + +#ifndef AVCODEC_MAX_AUDIO_FRAME_SIZE +# define AVCODEC_MAX_AUDIO_FRAME_SIZE 192000 +#endif + +#if LIBAVUTIL_VERSION_INT >= XFF_INT_VERSION(52,0,0) +# define PIX_FMT_NONE AV_PIX_FMT_NONE +# define PIX_FMT_YUV420P AV_PIX_FMT_YUV420P +# define PIX_FMT_YUVJ420P AV_PIX_FMT_YUVJ420P +# define PIX_FMT_YUV444P AV_PIX_FMT_YUV444P +# define PIX_FMT_YUVJ444P AV_PIX_FMT_YUVJ444P +# define PIX_FMT_YUV410P AV_PIX_FMT_YUV410P +# define PIX_FMT_YUV411P AV_PIX_FMT_YUV411P +# define PIX_FMT_ARGB AV_PIX_FMT_ARGB +# define PIX_FMT_BGRA AV_PIX_FMT_BGRA +# define PIX_FMT_RGB24 AV_PIX_FMT_RGB24 +# define PIX_FMT_BGR24 AV_PIX_FMT_BGR24 +# define PIX_FMT_RGB555BE AV_PIX_FMT_RGB555BE +# define PIX_FMT_RGB555LE AV_PIX_FMT_RGB555LE +# define PIX_FMT_RGB565BE AV_PIX_FMT_RGB565BE +# define PIX_FMT_RGB565LE AV_PIX_FMT_RGB565LE +# define PIX_FMT_PAL8 AV_PIX_FMT_PAL8 +# define PixelFormat AVPixelFormat +/* video_out/video_out_vaapi */ +# if LIBAVCODEC_VERSION_INT < XFF_INT_VERSION(59,0,100) /** << revise this */ +# define PIX_FMT_VAAPI_VLD AV_PIX_FMT_VAAPI_VLD +# define PIX_FMT_VAAPI_IDCT AV_PIX_FMT_VAAPI_IDCT +# define PIX_FMT_VAAPI_MOCO AV_PIX_FMT_VAAPI_MOCO +# else +# define PIX_FMT_VAAPI_VLD AV_PIX_FMT_VAAPI +# define PIX_FMT_VAAPI_IDCT AV_PIX_FMT_VAAPI +# define PIX_FMT_VAAPI_MOCO AV_PIX_FMT_VAAPI +# endif + +# define CODEC_FLAG_BITEXACT AV_CODEC_FLAG_BITEXACT +#endif + +#else /* defined(LIBAVCODEC_VERSION_INT) */ +# error avcodec.h must be included first ! +#endif /* defined(LIBAVCODEC_VERSION_INT) */ + #endif /* XINE_AVCODEC_COMPAT_H */ diff -r 9e075962b659 src/combined/ffmpeg/ffmpeg_decoder.c --- a/src/combined/ffmpeg/ffmpeg_decoder.c Wed Mar 09 14:31:44 2022 +0200 +++ b/src/combined/ffmpeg/ffmpeg_decoder.c Sat Sep 24 13:04:31 2022 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2001-2020 the xine project + * Copyright (C) 2001-2022 the xine project * * This file is part of xine, a free video player. * @@ -26,12 +26,20 @@ #include <pthread.h> -#ifdef HAVE_FFMPEG_AVUTIL_H -# include <avcodec.h> -#else -# include <libavcodec/avcodec.h> +#if defined(HAVE_LIBAVUTIL_AVUTIL_H) +# include <libavutil/avutil.h> +#endif + +#if defined(HAVE_LIBAVUTIL_MEM_H) # include <libavutil/mem.h> #endif + +#if defined(HAVE_AVUTIL_AVCODEC_H) +# include <libavcodec/avcodec.h> +#else +# include <avcodec.h> +#endif + #ifdef HAVE_AVFORMAT # include <libavformat/avformat.h> // av_register_all() #endif @@ -90,7 +98,7 @@ }; static const demuxer_info_t demux_info_avformat = { - .priority = -1, + .priority = -2, }; #endif /* HAVE_AVFORMAT */ diff -r 9e075962b659 src/combined/ffmpeg/input_avio.c --- a/src/combined/ffmpeg/input_avio.c Wed Mar 09 14:31:44 2022 +0200 +++ b/src/combined/ffmpeg/input_avio.c Sat Sep 24 13:04:31 2022 +0200 @@ -28,6 +28,7 @@ #include <string.h> #include <pthread.h> +#include <libavcodec/avcodec.h> #include <libavformat/avio.h> #include <libavutil/avutil.h> diff -r 9e075962b659 src/combined/xine_ogg_demuxer.c --- a/src/combined/xine_ogg_demuxer.c Wed Mar 09 14:31:44 2022 +0200 +++ b/src/combined/xine_ogg_demuxer.c Sat Sep 24 13:04:31 2022 +0200 @@ -583,16 +583,18 @@ if (!this->chapter_info) { this->chapter_info = (chapter_info_t *)calloc(1, sizeof(chapter_info_t)); - this->chapter_info->current_chapter = -1; } - this->chapter_info->max_chapter = chapter_no; - this->chapter_info->entries = realloc( this->chapter_info->entries, chapter_no*sizeof(chapter_entry_t)); - this->chapter_info->entries[chapter_no-1].name = chapter_name; - this->chapter_info->entries[chapter_no-1].start_pts = (msec + (1000.0 * sec) + (60000.0 * min) + (3600000.0 * hour))*90; - - free (chapter_time); + if (this->chapter_info) { + this->chapter_info->current_chapter = -1; + this->chapter_info->max_chapter = chapter_no; + this->chapter_info->entries = realloc( this->chapter_info->entries, chapter_no*sizeof(chapter_entry_t)); + this->chapter_info->entries[chapter_no-1].name = chapter_name; + this->chapter_info->entries[chapter_no-1].start_pts = (msec + (1000.0 * sec) + (60000.0 * min) + (3600000.0 * hour))*90; + } else { + _x_freep (&chapter_name); + } + _x_freep (&chapter_time); chapter_no = 0; - chapter_time = chapter_name = 0; } } free(chapter_name); diff -r 9e075962b659 src/demuxers/Makefile.am --- a/src/demuxers/Makefile.am Wed Mar 09 14:31:44 2022 +0200 +++ b/src/demuxers/Makefile.am Sat Sep 24 13:04:31 2022 +0200 @@ -123,7 +123,6 @@ demux_vox.c \ demux_wav.c \ flacutils.h \ - id3.c \ id3.h xineplug_dmx_audio_la_LIBADD = $(XINE_LIB) $(LTLIBINTL) diff -r 9e075962b659 src/demuxers/asfheader.c --- a/src/demuxers/asfheader.c Wed Mar 09 14:31:44 2022 +0200 +++ b/src/demuxers/asfheader.c Sat Sep 24 13:04:31 2022 +0200 @@ -415,7 +415,7 @@ /* get stream names */ if (asf_stream_extension->stream_name_count) { - asf_stream_extension->stream_names = malloc (asf_stream_extension->stream_name_count * sizeof (void*)); + asf_stream_extension->stream_names = calloc (asf_stream_extension->stream_name_count, sizeof (void*)); for (i = 0; i < asf_stream_extension->stream_name_count; i++) { char *name; uint16_t length; diff -r 9e075962b659 src/demuxers/demux_aac.c --- a/src/demuxers/demux_aac.c Wed Mar 09 14:31:44 2022 +0200 +++ b/src/demuxers/demux_aac.c Sat Sep 24 13:04:31 2022 +0200 @@ -48,136 +48,320 @@ #include "id3.h" +typedef enum { + DEMUX_AAC_ADTS = 0, + DEMUX_AAC_ADIF +} demux_aac_mode_t; + typedef struct { demux_plugin_t demux_plugin; xine_stream_t *stream; fifo_buffer_t *audio_fifo; input_plugin_t *input; + demux_aac_mode_t mode; int status; - - int seek_flag; /* this is set when a seek just occurred */ + int id3v2_tag_size; + int last_read_res; + int pts_offs; + /* this is set when a seek just occurred */ + int seek_flag; + /* time of last config change */ + int64_t base_pts; + /* frames since that */ + uint32_t frame_num; + /* frames in current sync */ + uint32_t frame_count; + /* current config. in sbr mode, these really are to be doubled, but we just want the ratio ;-) */ + uint32_t samples_per_frame; + uint32_t samples_per_second; + /* maximum ADTS frame size is 13 bits (8191). */ + uint32_t bgot, bdelivered; + uint8_t buf[9 << 10]; } demux_aac_t; -static int probe_aac_file(xine_stream_t *stream, input_plugin_t *input) { - int i; - uint8_t peak[MAX_PREVIEW_SIZE]; - uint32_t signature; +static int probe_aac_file (xine_stream_t *stream, input_plugin_t *input, demux_aac_mode_t *mode) { + uint8_t buf[MAX_PREVIEW_SIZE]; uint16_t syncword = 0; - uint32_t id3size = 0; - off_t data_start = 0; - - _x_assert(MAX_PREVIEW_SIZE > 10); - - if (_x_demux_read_header(input, &signature, 4) != 4) - return 0; + int data_start = -1, bsize, i; /* Check if there's an ID3v2 tag at the start */ - if ( id3v2_istag(signature) ) { - if (input->seek(input, 4, SEEK_SET) != 4) - return 0; - - id3v2_parse_tag(input, stream, signature); - } - - if (input->read(input, &signature, 4) != 4) - return 0; + data_start = xine_parse_id3v2_tag (stream, input); + lprintf("Getting a buffer of size %u\n", sizeof (buf)); + bsize = _x_demux_read_stream_header (stream, input, buf, sizeof (buf)); + if (bsize < 10) + return -1; /* Check for an ADIF header - should be at the start of the file */ - if (_x_is_fourcc(&signature, "ADIF")) { + if (_x_is_fourcc (buf, "ADIF")) { lprintf("found ADIF header\n"); - return 1; + *mode = DEMUX_AAC_ADIF; + return data_start; } /* Look for an ADTS header - might not be at the start of the file */ - if (input->get_capabilities(input) & INPUT_CAP_SEEKABLE) { - lprintf("Getting a buffer of size %u\n", MAX_PREVIEW_SIZE); - - if (input->read(input, peak, MAX_PREVIEW_SIZE) != MAX_PREVIEW_SIZE ) - return 0; - if (input->seek(input, 0, SEEK_SET) != 0) - return 0; - - } else if (_x_demux_read_header(input, peak, MAX_PREVIEW_SIZE) != - MAX_PREVIEW_SIZE) - return 0; - - for (i=0; i<MAX_PREVIEW_SIZE; i++) { - if ((syncword & 0xfff6) == 0xfff0) { - data_start = i - 2; - lprintf("found ADTS header at offset %d\n", i-2); + for (i = 0; i < bsize; i++) { + if ((syncword & 0xfff6) == 0xfff0) break; - } - - syncword = (syncword << 8) | peak[i]; + syncword = (syncword << 8) | buf[i]; } /* did we really find the ADTS header? */ - if (i == MAX_PREVIEW_SIZE) - return 0; /* No, we didn't */ + if (i == bsize) + return -1; /* No, we didn't */ + + data_start += i - 2; + *mode = DEMUX_AAC_ADTS; + lprintf ("found ADTS header at offset %d\n", i - 2); /* Look for second ADTS header to confirm it's really aac */ - if (data_start + 5 < MAX_PREVIEW_SIZE) { - int frame_size = ((peak[data_start+3] & 0x03) << 11) | - (peak[data_start+4] << 3) | - ((peak[data_start+5] & 0xe0) >> 5); + if (data_start + 5 < bsize) { + int frame_size = (_X_BE_32 (buf + data_start + 2) >> 5) & 0x1fff; lprintf("first frame size %d\n", frame_size); - if ((frame_size > 0) && - (data_start+frame_size < MAX_PREVIEW_SIZE-1) && - /* first 28 bits must be identical */ - memcmp(&peak[data_start], &peak[data_start+frame_size], 4) == 0 && - (peak[data_start+3]>>4==peak[data_start+frame_size+3]>>4)) - { + if ((frame_size > 0) && (data_start + frame_size + 4 <= bsize) && + /* first 28 bits must be identical */ + !((_X_BE_32 (buf + data_start) ^ _X_BE_32 (buf + data_start + frame_size)) & 0xfffffff0)) { lprintf("found second ADTS header\n"); - - if (input->seek(input, data_start+id3size, SEEK_SET) < 0) - return 0; - return 1; + if (input->seek (input, data_start, SEEK_SET) < 0) + input->seek (input, data_start + frame_size, SEEK_SET); + return data_start; } } - return 0; + return -1; +} + +/* FIXME: both ADTS and ADIF allow the first 4 AAC object types only, + * and have no short frame bit like in a regular config bitstream. + * for now, just assume always 1024 frame samples. */ +static uint32_t demux_aac_samples_per_frame (uint32_t object_type) { + (void)object_type; + return 1024; +} + +static const uint32_t demux_aac_sample_rates[16] = { + 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, + 16000, 12000, 11025, 8000, 0, 0, 0, 0 +}; + +static void demux_aac_apply_adts (demux_aac_t *this, const uint8_t *buf) { + uint32_t samples_per_frame, samples_per_second = 48000, object_type; + uint32_t word = _X_BE_32 (buf); + + object_type = ((word >> 14) & 3) + 1; + samples_per_frame = demux_aac_samples_per_frame (object_type); + samples_per_second = demux_aac_sample_rates[(word >> 10) & 15]; + if (!samples_per_second) + return; + this->frame_count = (buf[6] & 3) + 1; + if ((this->samples_per_frame ^ samples_per_frame) | (this->samples_per_second ^ samples_per_second)) { + if (this->samples_per_second) + this->base_pts += (int64_t)this->frame_num * 90000 * (int64_t)this->samples_per_frame / (int64_t)this->samples_per_second; + this->frame_num = 0; + this->samples_per_frame = samples_per_frame; + this->samples_per_second = samples_per_second; + xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, + LOG_MODULE ": ADTS frame duration %u/%u.\n", + (unsigned int)this->samples_per_frame, (unsigned int)this->samples_per_second); + } +} + +static uint32_t demux_aac_get_bits (const uint8_t *buf, uint32_t bitpos, uint32_t bits) { + uint32_t word = _X_BE_32 (buf + (bitpos >> 3)); + + return (word << (bitpos & 7)) >> (32 - bits); +} + +#undef _FULL_ADIF + +static void demux_aac_apply_adif (demux_aac_t *this, const uint8_t *buf) { + uint32_t samples_per_frame, samples_per_second = 48000; + uint32_t word, bitpos = 0; + uint32_t object_type, sf_index, bitstream_type; +#ifdef _FULL_ADIF + uint32_t num_program_config_elements, u; + uint32_t num_front_channel_elements, num_side_channel_elements; + uint32_t num_back_channel_elements, num_lfe_channel_elements; + uint32_t num_assoc_data_elements, num_valid_cc_elements; +#endif + + bitpos += 32; /* "AIDF" */ + word = demux_aac_get_bits (buf, bitpos, 1); bitpos += 1; /* copyright */ + if (word) + bitpos += 72; /* copyright_string */ + bitpos += 2; /* original_copy:1, home:1 */ + bitstream_type = demux_aac_get_bits (buf, bitpos, 1); bitpos += 1; + bitpos += 23; /* bitrate:23 */ +#ifdef _FULL_ADIF + num_program_config_elements = demux_aac_get_bits (buf, bitpos, 4) + 1; +#endif + bitpos += 4; +#ifdef _FULL_ADIF + for (u = 0; u < num_program_config_elements; u++) +#endif + { + if (!bitstream_type) + bitpos += 20; /* buffer_fullness:20 */ + bitpos += 4; /* element_instance_tag:4 */ + object_type = demux_aac_get_bits (buf, bitpos, 2) + 1; bitpos += 2; + sf_index = demux_aac_get_bits (buf, bitpos, 4); bitpos += 4; +#ifdef _FULL_ADIF + num_front_channel_elements = demux_aac_get_bits (buf, bitpos, 4); bitpos += 4; + num_side_channel_elements = demux_aac_get_bits (buf, bitpos, 4); bitpos += 4; + num_back_channel_elements = demux_aac_get_bits (buf, bitpos, 4); bitpos += 4; + num_lfe_channel_elements = demux_aac_get_bits (buf, bitpos, 2); bitpos += 2; + num_assoc_data_elements = demux_aac_get_bits (buf, bitpos, 3); bitpos += 3; + num_valid_cc_elements = demux_aac_get_bits (buf, bitpos, 4); bitpos += 4; + word = demux_aac_get_bits (buf, bitpos, 1); bitpos += 1; /* mono_mixdown_present:1 */ + if (word) + bitpos += 4; /* mono_mixdown_element_number:4 */ + word = demux_aac_get_bits (buf, bitpos, 1); bitpos += 1; /* stereo_mixdown_present:1 */ + if (word) + bitpos += 4; /* stereo_mixdown_element_number:4 */ + word = demux_aac_get_bits (buf, bitpos, 1); bitpos += 1; /* matrix_mixdown_idx_present:1 */ + if (word) + bitpos += 3; /* matrix_mixdown_idx:2, pseudo_surround_enable:1 */ + bitpos += num_front_channel_elements * 5; /* front_element_is_cpe[i]:1, front_element_tag_select[i]:4 */ + bitpos += num_side_channel_elements * 5; /* side_element_is_cpe[i]:1, side_element_tag_select[i]:4 */ + bitpos += num_back_channel_elements * 5; /* back_element_is_cpe[i]:1, back_element_tag_select[i]:4 */ + bitpos += num_lfe_channel_elements * 4; /* lfe_element_tag_select[i]:4 */ + bitpos += num_assoc_data_elements * 4; /* assoc_data_element_tag_select[i]:4 */ + bitpos += num_valid_cc_elements * 5; /* cc_element_is_ind_sw[i]:1, valid_cc_element_tag_select[i]:4 */ + bitpos = (bitpos + 7) & ~7; /* byte align */ + word = buf[bitpos >> 3]; /* comment_field_bytes */ + bitpos += word << 3; /* comment_field_data[i] */ +#endif + } + + samples_per_frame = demux_aac_samples_per_frame (object_type); + samples_per_second = demux_aac_sample_rates[sf_index]; + if (!samples_per_second) + return; +#ifdef _FULL_ADIF + this->frame_count = num_program_config_elements; +#else + this->frame_count = 1; +#endif + if ((this->samples_per_frame ^ samples_per_frame) | (this->samples_per_second ^ samples_per_second)) { + if (this->samples_per_second) + this->base_pts += (int64_t)this->frame_num * 90000 * (int64_t)this->samples_per_frame / (int64_t)this->samples_per_second; + this->frame_num = 0; + this->samples_per_frame = samples_per_frame; + this->samples_per_second = samples_per_second; + xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, + LOG_MODULE ": AIDF frame duration %u/%u.\n", + (unsigned int)this->samples_per_frame, (unsigned int)this->samples_per_second); + } +} + +static int demux_aac_next (demux_aac_t *this, uint8_t *buf) { + if (this->mode == DEMUX_AAC_ADTS) { + uint32_t u = this->bdelivered + 7, word = 0; + + while (1) { + if (u < this->bgot) { + uint32_t size = (_X_BE_32 (this->buf + this->bdelivered + 2) >> 5) & 0x1fff; + + do { + word = (word << 8) + this->buf[u++]; + if ((word & 0xfff6) == 0xfff0) + break; + } while (u < this->bgot); + if ((word & 0xfff6) == 0xfff0) { + if (this->bdelivered + size <= u - 2) + break; + continue; + } + } + if (u > sizeof (this->buf) - 512) { + uint32_t l = this->bgot - this->bdelivered; + + if (this->bdelivered < 512) /* a single frame of 8 kbyte ?? */ + l = 4; + if (this->bdelivered >= l) + memcpy (this->buf, this->buf + this->bdelivered, l); + else + memmove (this->buf, this->buf + this->bdelivered, l); + u -= this->bdelivered; + this->bdelivered = 0; + this->bgot = l; + } + this->last_read_res = this->input->read (this->input, this->buf + this->bgot, 512); + if (this->last_read_res <= 0) + break; + this->bgot += this->last_read_res; + } + if ((word & 0xfff6) == 0xfff0) { + demux_aac_apply_adts (this, this->buf + this->bdelivered); + u -= 2 + this->bdelivered; + memcpy (buf, this->buf + this->bdelivered, u); + this->bdelivered += u; + return u; + } + } else { /* DEMUX_AAC_ADIF */ + /* SIGH. ADIF is merely a single head plus a sequence of raw frames. + * This can hardly do more than play from the beginning. + * nobody seems to use that anymore, and even ffmpeg doer not support all that. + * lets not repeat half the decoders work here just to count frames, + * and let decoder and engine add time info. */ + int r = this->input->read (this->input, buf, 2048); + + if (r > 0) { + if ((r > 4) && !memcmp (buf, "ADIF", 4)) + demux_aac_apply_adif (this, buf); + } + this->frame_count = 0; + return r; + } + { + uint32_t l = this->bgot - this->bdelivered; + + if (l) + memcpy (buf, this->buf + this->bdelivered, l); + this->bgot = 0; + this->bdelivered = 0; + return l; + } } static int demux_aac_send_chunk(demux_plugin_t *this_gen) { demux_aac_t *this = (demux_aac_t *) this_gen; - int bytes_read; - off_t current_pos, length; - uint32_t bitrate; - - buf_element_t *buf = NULL; + off_t current_pos = this->input->get_current_pos (this->input) + this->bdelivered - this->bgot; + off_t length = this->input->get_length (this->input); + int bitrate = _x_stream_info_get (this->stream, XINE_STREAM_INFO_AUDIO_BITRATE); + buf_element_t *buf = this->audio_fifo->buffer_pool_alloc (this->audio_fifo); - /* just load an entire buffer from wherever the audio happens to be */ - buf = this->audio_fifo->buffer_pool_alloc (this->audio_fifo); - buf->type = BUF_AUDIO_AAC; - buf->pts = 0; - - length = this->input->get_length(this->input); - current_pos = this->input->get_current_pos(this->input); - bitrate = _x_stream_info_get(this->stream, XINE_STREAM_INFO_AUDIO_BITRATE); + if (this->seek_flag) { + this->seek_flag = 0; + _x_demux_control_newpts (this->stream, this->base_pts, BUF_FLAG_SEEK); + } - if (length) - buf->extra_info->input_normpos = (int)((double) current_pos * 65535/length); - - if (bitrate) - buf->extra_info->input_time = (8*current_pos) / (bitrate/1000); - - bytes_read = this->input->read(this->input, buf->content, buf->max_size); - if (bytes_read <= 0) { - buf->free_buffer(buf); + buf->type = BUF_AUDIO_AAC; + buf->decoder_flags |= BUF_FLAG_FRAME_END; + buf->size = demux_aac_next (this, buf->content); + if (buf->size > 0) { + if (!this->frame_count) { + buf->pts = this->base_pts; + buf->extra_info->input_time = -1; + } else if (this->samples_per_second) { + buf->pts = this->base_pts + this->pts_offs + + (int64_t)this->frame_num * 90000 * (int64_t)this->samples_per_frame / (int64_t)this->samples_per_second; + buf->extra_info->input_time = buf->pts / 90; + } else if (bitrate > 0) { + buf->pts = this->base_pts; + buf->extra_info->input_time = 8000 * current_pos / bitrate; + } + if (length > 0) + buf->extra_info->input_normpos = (int)((double)current_pos * 65535 / length); + this->audio_fifo->put (this->audio_fifo, buf); + this->frame_num += this->frame_count; + } else { + buf->free_buffer (buf); this->status = DEMUX_FINISHED; - return this->status; - } else - buf->size = bytes_read; - - /* each buffer stands on its own */ - buf->decoder_flags |= BUF_FLAG_FRAME_END; - - this->audio_fifo->put (this->audio_fifo, buf); - + } return this->status; } @@ -187,6 +371,8 @@ this->audio_fifo = this->stream->audio_fifo; + this->pts_offs = this->input->get_optional_data (this->input, NULL, INPUT_OPTIONAL_DATA_PTSOFFS); + this->status = DEMUX_OK; /* send start buffers */ @@ -207,14 +393,42 @@ off_t start_pos, int start_time, int playing) { demux_aac_t *this = (demux_aac_t *) this_gen; - (void)start_pos; - (void)start_time; - /* if thread is not running, initialize demuxer */ - if( !playing ) { + if (playing) { + int ntime = 0; + off_t pos = -1; + uint32_t input_caps = this->input->get_capabilities (this->input); + + this->pts_offs = this->input->get_optional_data (this->input, NULL, INPUT_OPTIONAL_DATA_PTSOFFS); + + if (input_caps & (INPUT_CAP_SEEKABLE | INPUT_CAP_SLOW_SEEKABLE | INPUT_CAP_TIME_SEEKABLE)) { + /* this container has neither frame num nor pts. maybe input can help? */ + if ((input_caps & INPUT_CAP_TIME_SEEKABLE) && start_time) { + pos = this->input->seek_time (this->input, start_time, SEEK_SET); + if (pos >= 0) + ntime = this->input->get_current_time ? this->input->get_current_time (this->input) : 0; + } else { + off_t len = this->input->get_length (this->input); + int br = _x_stream_info_get (this->stream, XINE_STREAM_INFO_AUDIO_BITRATE); + if (len > 0) { + pos = this->input->seek (this->input, (int64_t)len * start_pos / (int)0xffff, SEEK_SET); + if (pos >= 0) + ntime = br > 0 ? (int64_t)pos * 8000 / br : 0; + } + } + if (pos >= 0) { + this->base_pts = (int64_t)ntime * 90; + this->frame_num = 0; + this->bdelivered = 0; + this->bgot = 0; + this->seek_flag = 1; + this->status = DEMUX_OK; + } + } + } else { + /* if thread is not running, initialize demuxer */ /* send new pts */ - _x_demux_control_newpts(this->stream, 0, 0); - + _x_demux_control_newpts (this->stream, 0, 0); this->status = DEMUX_OK; } @@ -251,12 +465,14 @@ input_plugin_t *input) { demux_aac_t *this; + demux_aac_mode_t mode; + int id3v2_tag_size; switch (stream->content_detection_method) { case METHOD_BY_MRL: case METHOD_BY_CONTENT: case METHOD_EXPLICIT: - if (!probe_aac_file(stream, input)) + if ((id3v2_tag_size = probe_aac_file (stream, input, &mode)) < 0) return NULL; break; default: @@ -266,9 +482,22 @@ this = calloc(1, sizeof(demux_aac_t)); if (!this) return NULL; - +#ifndef HAVE_ZERO_SAFE_MEM + this->seek_flag = 0; + this->pts_offs = 0; + this->base_pts = 0; + this->frame_num = 0; + this->samples_per_frame = 0; + this->samples_per_second = 0; + this->bdelivered = 0; + this->bgot = 0; +#endif + this->frame_count = 1; + this->last_read_res = 1; this->stream = stream; this->input = input; + this->mode = mode; + this->id3v2_tag_size = id3v2_tag_size; this->demux_plugin.send_headers = demux_aac_send_headers; this->demux_plugin.send_chunk = demux_aac_send_chunk; @@ -282,7 +511,7 @@ this->status = DEMUX_FINISHED; - _x_stream_info_set(stream, XINE_STREAM_INFO_HAS_VIDEO, 0); + /* _x_stream_info_set(stream, XINE_STREAM_INFO_HAS_VIDEO, 0); */ _x_stream_info_set(stream, XINE_STREAM_INFO_HAS_AUDIO, 1); return &this->demux_plugin; @@ -304,4 +533,3 @@ return (void*)&demux_aac_class; } - diff -r 9e075962b659 src/demuxers/demux_dts.c --- a/src/demuxers/demux_dts.c Wed Mar 09 14:31:44 2022 +0200 +++ b/src/demuxers/demux_dts.c Sat Sep 24 13:04:31 2022 +0200 @@ -218,10 +218,10 @@ switch (dts_version) { case 0: /* BE16 */ - case 1: /* LE16 */ + //case 1: /* LE16 */ this->frame_size = fsize * 8 / 16 * 2; break; - case 2: /* BE14 */ + //case 2: /* BE14 */ case 3: /* LE14 */ this->frame_size = fsize * 8 / 14 * 2; break; diff -r 9e075962b659 src/demuxers/demux_flac.c --- a/src/demuxers/demux_flac.c Wed Mar 09 14:31:44 2022 +0200 +++ b/src/demuxers/demux_flac.c Sat Sep 24 13:04:31 2022 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2000-2019 the xine project + * Copyright (C) 2000-2022 the xine project * * This file is part of xine, a free video player. * @@ -360,26 +360,20 @@ unsigned int block_length; unsigned char buffer[FLAC_SEEKPOINT_SIZE]; unsigned char *streaminfo = flac->streaminfo + sizeof(xine_waveformatex); - int i; - - /* fetch the file signature, 4 bytes will read both the fLaC - * signature and the */ - if (_x_demux_read_header(flac->input, &signature, 4) != 4) - return 0; - - flac->input->seek(flac->input, 4, SEEK_SET); + int id3v2_tag_size, i; /* Unfortunately some FLAC files have an ID3 flag prefixed on them * before the actual FLAC headers... these are barely legal, but * users use them and want them working, so check and skip the ID3 * tag if present. */ - if ( id3v2_istag(signature) ) { - id3v2_parse_tag(flac->input, flac->stream, signature); + id3v2_tag_size = xine_parse_id3v2_tag (flac->stream, flac->input); + /* fetch the file signature, 4 bytes will read both the fLaC + * signature and the */ + if (_x_demux_read_stream_header (flac->stream, flac->input, &signature, 4) != 4) + return 0; - if ( flac->input->read(flac->input, &signature, 4) != 4 ) - return 0; - } + flac->input->seek (flac->input, id3v2_tag_size + 4, SEEK_SET); /* validate signature */ if ( signature != ME_FOURCC('f', 'L', 'a', 'C') ) diff -r 9e075962b659 src/demuxers/demux_iff.c --- a/src/demuxers/demux_iff.c Wed Mar 09 14:31:44 2022 +0200 +++ b/src/demuxers/demux_iff.c Sat Sep 24 13:04:31 2022 +0200 @@ -333,6 +333,8 @@ case IFF_BMHD_CHUNK: if( this->bmhd == NULL ) this->bmhd = (BitMapHeader *)calloc(1, sizeof(BitMapHeader)); + if( this->bmhd == NULL ) + break; this->bmhd->w = _X_BE_16(&buffer[0]); this->bmhd->h = _X_BE_16(&buffer[2]); this->bmhd->x = _X_BE_16(&buffer[4]); @@ -409,12 +411,16 @@ case IFF_GRAB_CHUNK: if( this->grab == NULL ) this->grab = (Point2D *)calloc(1, sizeof(Point2D)); + if( this->grab == NULL ) + break; this->grab->x = _X_BE_16(&buffer[0]); this->grab->y = _X_BE_16(&buffer[2]); break; case IFF_DEST_CHUNK: if( this->dest == NULL ) this->dest = (DestMerge *)calloc(1, sizeof(DestMerge)); + if( this->dest == NULL ) + break; this->dest->depth = buffer[0]; this->dest->pad1 = buffer[1]; this->dest->plane_pick = _X_BE_16(&buffer[2]); @@ -427,6 +433,8 @@ case IFF_CAMG_CHUNK: if( this->camg == NULL ) this->camg = (CamgChunk *)calloc(1, sizeof(CamgChunk)); + if( this->camg == NULL ) + break; this->camg->view_modes = _X_BE_32(&buffer[0]); this->bih.biCompression = this->camg->view_modes; if( this->camg->view_modes & CAMG_PAL && @@ -446,6 +454,8 @@ case IFF_CCRT_CHUNK: if( this->ccrt == NULL ) this->ccrt = (CcrtChunk *)calloc(1, sizeof(CcrtChunk)); + if( this->ccrt == NULL ) + break; this->ccrt->direction = _X_BE_16(&buffer[0]); this->ccrt->start = buffer[2]; this->ccrt->end = buffer[3]; @@ -456,12 +466,16 @@ case IFF_DPI_CHUNK: if( this->dpi == NULL ) this->dpi = (DPIHeader *)calloc(1, sizeof(DPIHeader)); + if( this->dpi == NULL ) + break; this->dpi->x = _X_BE_16(&buffer[0]); this->dpi->y = _X_BE_16(&buffer[0]); break; case IFF_ANHD_CHUNK: if( this->anhd == NULL ) this->anhd = (AnimHeader *)calloc(1, sizeof(AnimHeader)); + if( this->anhd == NULL ) + break; this->anhd->operation = buffer[0]; this->anhd->mask = buffer[1]; this->anhd->w = _X_BE_16(&buffer[2]); @@ -503,6 +517,8 @@ case IFF_DPAN_CHUNK: if( this->dpan == NULL ) this->dpan = (DPAnimChunk *)calloc(1, sizeof(DPAnimChunk)); + if( this->dpan == NULL ) + break; this->dpan->version = _X_BE_16(&buffer[0]); this->dpan->nframes = _X_BE_16(&buffer[2]); this->dpan->fps = buffer[4]; @@ -1076,8 +1092,10 @@ buf->decoder_flags = BUF_FLAG_HEADER|BUF_FLAG_STDHEADER|BUF_FLAG_FRAMERATE|BUF_FLAG_FRAME_END; buf->decoder_info[0] = this->video_pts_inc; /* initial video_step */ buf->decoder_info[1] = 0; + if( this->bmhd ) { buf->decoder_info[2] = this->bmhd->xaspect; buf->decoder_info[3] = this->bmhd->yaspect; + } memcpy(buf->content, &this->bih, sizeof(this->bih)); this->video_fifo->put(this->video_fifo, buf); diff -r 9e075962b659 src/demuxers/demux_ipmovie.c --- a/src/demuxers/demux_ipmovie.c Wed Mar 09 14:31:44 2022 +0200 +++ b/src/demuxers/demux_ipmovie.c Sat Sep 24 13:04:31 2022 +0200 @@ -616,8 +616,8 @@ /* send init info to the audio decoder */ if ((this->audio_fifo) && (this->audio_type)) { - this->wave.nSamplesPerSec = this->wave.nSamplesPerSec; - this->wave.wBitsPerSample = this->wave.wBitsPerSample; + //this->wave.nSamplesPerSec = this->wave.nSamplesPerSec; + //this->wave.wBitsPerSample = this->wave.wBitsPerSample; this->wave.nBlockAlign = (this->wave.wBitsPerSample / 8) * this->wave.nChannels; this->wave.nAvgBytesPerSec = this->wave.nBlockAlign * this->wave.nSamplesPerSec; diff -r 9e075962b659 src/demuxers/demux_matroska.c --- a/src/demuxers/demux_matroska.c Wed Mar 09 14:31:44 2022 +0200 +++ b/src/demuxers/demux_matroska.c Sat Sep 24 13:04:31 2022 +0200 @@ -1538,6 +1538,8 @@ if (track->video_track) return 1; track->video_track = (matroska_video_track_t *)calloc(1, sizeof(matroska_video_track_t)); + if (!track->video_track) + return 0; if (!ebml_read_master (ebml, &elem)) return 0; if ((elem.len > 0) && !parse_video_track(this, track->video_track)) @@ -1549,6 +1551,8 @@ if (track->audio_track) return 1; track->audio_track = (matroska_audio_track_t *)calloc(1, sizeof(matroska_audio_track_t)); + if (!track->audio_track) + return 0; if (!ebml_read_master (ebml, &elem)) return 0; if ((elem.len > 0) && !parse_audio_track(this, track->audio_track)) @@ -1912,6 +1916,8 @@ /* alloc and initialize a track with 0 */ track = calloc(1, sizeof(matroska_track_t)); + if (!track) + return 0; track->compress_algo = MATROSKA_COMPRESS_NONE; this->tracks[this->num_tracks] = track; diff -r 9e075962b659 src/demuxers/demux_mpc.c --- a/src/demuxers/demux_mpc.c Wed Mar 09 14:31:44 2022 +0200 +++ b/src/demuxers/demux_mpc.c Sat Sep 24 13:04:31 2022 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2018 the xine project + * Copyright (C) 2005-2022 the xine project * * This file is part of xine, a free video player. * @@ -62,6 +62,7 @@ input_plugin_t *input; int status; + int start; unsigned char header[HEADER_SIZE]; unsigned int frames; double samplerate; @@ -80,36 +81,10 @@ unsigned int id3v2_size = 0; /* Read the file header */ + this->start = xine_parse_id3v2_tag (this->stream, this->input); if (_x_demux_read_header(this->input, this->header, HEADER_SIZE) != HEADER_SIZE) return 0; - /* TODO: non-seeking version */ - if (INPUT_IS_SEEKABLE(this->input)) { - /* Check for id3v2 tag */ - if (id3v2_istag(_X_BE_32(this->header))) { - - lprintf("found id3v2 header\n"); - - /* Read tag size */ - - id3v2_size = _X_BE_32_synchsafe(&this->header[6]) + 10; - - /* Add footer size if one is present */ - if (this->header[5] & 0x10) - id3v2_size += 10; - - lprintf("id3v2 size: %u\n", id3v2_size); - - /* Seek past tag */ - if (this->input->seek(this->input, id3v2_size, SEEK_SET) < 0) - return 0; - - /* Read musepack header */ - if (this->input->read(this->input, this->header, HEADER_SIZE) != HEADER_SIZE) - return 0; - } - } - /* Validate signature - We only support SV 7.x at the moment */ if ( memcmp(this->header, "MP+", 3) != 0 || ((this->header[3]&0x0f) != 0x07)) diff -r 9e075962b659 src/demuxers/demux_mpgaudio.c --- a/src/demuxers/demux_mpgaudio.c Wed Mar 09 14:31:44 2022 +0200 +++ b/src/demuxers/demux_mpgaudio.c Sat Sep 24 13:04:31 2022 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2000-2018 the xine project + * Copyright (C) 2000-2022 the xine project * * This file is part of xine, a free video player. * @@ -836,64 +836,30 @@ * mp3 stream detection * return 1 if detected, 0 otherwise */ -static int detect_mpgaudio_file(input_plugin_t *input, - int *version, int *layer) { +static int detect_mpgaudio_file (xine_stream_t *stream, input_plugin_t *input, int *version, int *layer) { uint8_t buf[MAX_PREVIEW_SIZE]; - int preview_len; + int id3v2_tag_size, preview_len; uint32_t head; *version = *layer = 0; - preview_len = _x_demux_read_header(input, buf, sizeof(buf)); + /* check if a mp3 frame follows the tag. id3v2 are not specific to mp3 files, + * flac files can contain id3v2 tags. */ + id3v2_tag_size = xine_parse_id3v2_tag (stream, input); + preview_len = _x_demux_read_stream_header (stream, input, buf, sizeof (buf)); if (preview_len < 4) - return 0; + return -1; head = _X_ME_32(buf); lprintf("got preview %08x\n", head); - if (id3v2_istag(head)) { - /* check if a mp3 frame follows the tag - * id3v2 are not specific to mp3 files, - * flac files can contain id3v2 tags - */ - int tag_size = _X_BE_32_synchsafe(&buf[6]); - lprintf("try to skip id3v2 tag (%d bytes)\n", tag_size); - /* id3v2 tags may be larger than preview size (album artwork JPG or something). - * try to skip by seeking. - */ - if (INPUT_IS_SEEKABLE (input)) { - if (input->seek (input, 10 + tag_size, SEEK_SET) < 0) - return 0; - preview_len = input->read (input, buf, MAX_PREVIEW_SIZE); - if (!sniff_buffer_looks_like_mp3 (buf, preview_len, version, layer)) { - lprintf ("sniff_buffer_looks_like_mp3 failed\n"); - return 0; - } else { - lprintf ("a valid mp3 frame follows the id3v2 tag\n"); - return 1; - } - } - if ((10 + tag_size) >= preview_len) { - lprintf("cannot skip id3v2 tag\n"); - return 0; - } - if ((10 + tag_size + 4) >= preview_len) { - lprintf("cannot read mp3 frame header\n"); - return 0; - } - if (!sniff_buffer_looks_like_mp3(&buf[10 + tag_size], preview_len - 10 - tag_size, version, layer)) { - lprintf ("sniff_buffer_looks_like_mp3 failed\n"); - return 0; - } else { - lprintf ("a valid mp3 frame follows the id3v2 tag\n"); - } - } else if (head == MPEG_MARKER) { - return 0; + if (head == MPEG_MARKER) { + return -1; } else if (!sniff_buffer_looks_like_mp3(buf, preview_len, version, layer)) { lprintf ("sniff_buffer_looks_like_mp3 failed\n"); - return 0; + return -1; } - return 1; + return id3v2_tag_size; } static void demux_mpgaudio_send_headers (demux_plugin_t *this_gen) { @@ -1175,7 +1141,7 @@ switch (stream->content_detection_method) { case METHOD_BY_CONTENT: { - if (!detect_mpgaudio_file(input, &version, &layer)) + if (detect_mpgaudio_file (stream, input, &version, &layer) < 0) return NULL; } break; diff -r 9e075962b659 src/demuxers/demux_playlist.c --- a/src/demuxers/demux_playlist.c Wed Mar 09 14:31:44 2022 +0200 +++ b/src/demuxers/demux_playlist.c Sat Sep 24 13:04:31 2022 +0200 @@ -102,7 +102,7 @@ } static playlist_t detect_by_content (input_plugin_t *input) { - char buf[256], *tmp; + char buf[MAX_PREVIEW_SIZE], *tmp; int len; len = _x_demux_read_header (input, buf, sizeof(buf)-1); diff -r 9e075962b659 src/demuxers/demux_qt.c --- a/src/demuxers/demux_qt.c Wed Mar 09 14:31:44 2022 +0200 +++ b/src/demuxers/demux_qt.c Sat Sep 24 13:04:31 2022 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2001-2021 the xine project + * Copyright (C) 2001-2022 the xine project * * This file is part of xine, a free video player. * @@ -1225,14 +1225,34 @@ /* in mp4 files the audio fourcc is always 'mp4a' - the codec is * specified by the object type id field in the esds atom */ if (p->codec_fourcc == MP4A_FOURCC) { + static const uint8_t atag_index[256] = { + [0x40] = 0, /* AAC, MP4ALS */ + [0x66] = 0, /* MPEG2 AAC Main */ + [0x67] = 0, /* MPEG2 AAC Low */ + [0x68] = 0, /* MPEG2 AAC SSR */ + [0x69] = 1, /* MP3 13818-3, MP2 11172-3 */ + [0x6B] = 1, /* MP3 11172-3 */ + [0xA5] = 2, /* AC3 */ + [0xA6] = 3, /* EAC3 */ + [0xA9] = 4, /* DTS mp4ra.org */ + [0xDD] = 5, /* Vorbis non standard, gpac uses it */ + [0xE1] = 6, /* QCELP */ + }; + static const struct { + uint32_t buftype; + char name[8]; + } atag_info[7] = { + [0] = { BUF_AUDIO_AAC, "aac" }, /** << note: using this as default. */ + [1] = { BUF_AUDIO_MPEG, "mp3" }, + [2] = { BUF_AUDIO_A52, "ac3" }, + [3] = { BUF_AUDIO_EAC3, "eac3" }, + [4] = { BUF_AUDIO_DTS, "dts" }, + [5] = { BUF_AUDIO_VORBIS, "vorbis" }, + [6] = { BUF_AUDIO_QCLP, "qcelp" }, + }; p->s.audio.vbr = 1; - if (p->object_type_id == 221) { - p->codec_buftype = BUF_AUDIO_VORBIS; - memcpy (p->codec_str, "vorbis", 7); - } else if (p->object_type_id == 107) { - p->codec_buftype = BUF_AUDIO_MPEG; - memcpy (p->codec_str, "mp3", 4); - } + p->codec_buftype = atag_info[atag_index[p->object_type_id & 255]].buftype; + strcpy (p->codec_str, atag_info[atag_index[p->object_type_id & 255]].name); } /* if this is MP4 audio, mark the trak as VBR */ else if ((p->codec_fourcc == SAMR_FOURCC) || diff -r 9e075962b659 src/demuxers/demux_rawdv.c --- a/src/demuxers/demux_rawdv.c Wed Mar 09 14:31:44 2022 +0200 +++ b/src/demuxers/demux_rawdv.c Sat Sep 24 13:04:31 2022 +0200 @@ -242,12 +242,12 @@ /* printf("aaux %d: %2.2x %2.2x %2.2x %2.2x %2.2x\n", j, s[0], s[1], s[2], s[3], s[4]); */ - int smp, flag; + int smp/*, flag*/; done = 1; smp = (s[4] >> 3) & 0x07; - flag = s[3] & 0x20; + /*flag = s[3] & 0x20; if (flag == 0) { switch (smp) { @@ -261,7 +261,7 @@ abuf->decoder_info[1] = 32000; break; } - } else { + } else {*/ switch (smp) { case 0: abuf->decoder_info[1] = 48000; @@ -273,7 +273,7 @@ abuf->decoder_info[1] = 32000; break; } - } + /*}*/ } } } diff -r 9e075962b659 src/demuxers/demux_real.c --- a/src/demuxers/demux_real.c Wed Mar 09 14:31:44 2022 +0200 +++ b/src/demuxers/demux_real.c Sat Sep 24 13:04:31 2022 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2000-2018 the xine project + * Copyright (C) 2000-2022 the xine project * * This file is part of xine, a free video player. * @@ -1684,9 +1684,10 @@ this->audio_stream->audio_time = timestamp; else timestamp = this->audio_stream->audio_time; - /* nasty kludge, maybe this is somewhere in mdpr? */ + /* nasty kludge was here due to a ff_audio_dec bug. if (this->audio_stream->buf_type == BUF_AUDIO_COOK) timestamp += 120; + */ pts = (int64_t) timestamp * 90; /* if we have a seekable stream then use the timestamp for the data diff -r 9e075962b659 src/demuxers/demux_smjpeg.c --- a/src/demuxers/demux_smjpeg.c Wed Mar 09 14:31:44 2022 +0200 +++ b/src/demuxers/demux_smjpeg.c Sat Sep 24 13:04:31 2022 +0200 @@ -75,6 +75,7 @@ /* video information */ unsigned int video_type; xine_bmiheader bih; + int64_t last_frame_pts; /* audio information */ unsigned int audio_type; @@ -194,7 +195,6 @@ unsigned int remaining_sample_bytes; unsigned char preamble[SMJPEG_CHUNK_PREAMBLE_SIZE]; off_t current_file_pos; - int64_t last_frame_pts = 0; unsigned int audio_frame_count = 0; /* load the next sample */ @@ -228,9 +228,7 @@ * Therefore, manually compute the pts values for the audio samples. */ if (chunk_tag == sndD_TAG) { - pts = audio_frame_count; - pts *= 90000; - pts /= (this->audio_sample_rate * this->audio_channels); + pts = INT64_C(90000) * audio_frame_count / ((int64_t)this->audio_sample_rate * this->audio_channels); audio_frame_count += ((remaining_sample_bytes - 4) * 2); } else { pts = _X_BE_32(&preamble[4]); @@ -255,12 +253,12 @@ buf->extra_info->input_time = pts / 90; buf->pts = pts; - if (last_frame_pts) { + if (this->last_frame_pts) { buf->decoder_flags |= BUF_FLAG_FRAMERATE; - buf->decoder_info[0] = buf->pts - last_frame_pts; + buf->decoder_info[0] = buf->pts - this->last_frame_pts; } - if ((int)remaining_sample_bytes > buf->max_size) + if (remaining_sample_bytes > (unsigned)buf->max_size) buf->size = buf->max_size; else buf->size = remaining_sample_bytes; @@ -294,7 +292,7 @@ } if (chunk_tag == vidD_TAG) - last_frame_pts = buf->pts; + this->last_frame_pts = pts; return this->status; } @@ -359,6 +357,7 @@ this->status = DEMUX_OK; } + this->last_frame_pts = 0; return this->status; } diff -r 9e075962b659 src/demuxers/demux_wav.c --- a/src/demuxers/demux_wav.c Wed Mar 09 14:31:44 2022 +0200 +++ b/src/demuxers/demux_wav.c Sat Sep 24 13:04:31 2022 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2001-2018 the xine project + * Copyright (C) 2001-2022 the xine project * * This file is part of xine, a free video player. * @@ -64,6 +64,8 @@ off_t data_start; off_t data_size; + int seek_align; + int chunk_size; int send_newpts; int seek_flag; /* this is set when a seek just occurred */ @@ -154,19 +156,22 @@ /* search for the 'data' chunk */ this->data_start = this->data_size = 0; - if (find_chunk_by_tag(this, data_TAG, NULL, &this->data_start)==0) - { + if (find_chunk_by_tag (this, data_TAG, NULL, &this->data_start) == 0) + return 0; + + /* Get the data length from the file itself, instead of the data TAG, for broken files */ + if (this->input->seek (this->input, this->data_start, SEEK_SET) != this->data_start) return 0; - } - else - { - /* Get the data length from the file itself, instead of the data - * TAG, for broken files */ - if (this->input->seek(this->input, this->data_start, SEEK_SET) != this->data_start) - return 0; - this->data_size = this->input->get_length(this->input); - return 1; - } + + /* XXX */ + if (this->wave->nBlockAlign <= 0) + this->wave->nBlockAlign = 4; + this->seek_align = this->wave->nBlockAlign; + this->chunk_size = (this->wave->nBlockAlign < PREFERED_BLOCK_SIZE) + ? PREFERED_BLOCK_SIZE / this->wave->nBlockAlign * this->wave->nBlockAlign + : this->wave->nBlockAlign; + this->data_size = this->input->get_length (this->input); + return 1; } static int demux_wav_send_chunk(demux_plugin_t *this_gen) { @@ -179,14 +184,9 @@ /* just load data chunks from wherever the stream happens to be * pointing; issue a DEMUX_FINISHED status if EOF is reached */ - if (this->wave->nBlockAlign < PREFERED_BLOCK_SIZE) { - remaining_sample_bytes = PREFERED_BLOCK_SIZE / this->wave->nBlockAlign * - this->wave->nBlockAlign; - } else{ - remaining_sample_bytes = this->wave->nBlockAlign; - } - current_file_pos = - this->input->get_current_pos(this->input) - this->data_start; + remaining_sample_bytes = this->chunk_size; + + current_file_pos = this->input->get_current_pos(this->input) - this->data_start; current_pts = current_file_pos; current_pts *= 90000; @@ -204,8 +204,17 @@ } buf = this->audio_fifo->buffer_pool_alloc (this->audio_fifo); - if( this->data_size ) - buf->extra_info->input_normpos = (int)( (double) current_file_pos * 65535 / this->data_size); + do { + if (this->data_size <= 0) + break; + /* file being written right now? */ + if (current_file_pos > this->data_size) { + this->data_size = this->input->get_length (this->input); + if (this->data_size <= 0) + break; + } + buf->extra_info->input_normpos = (int)((double)current_file_pos * 65535 / this->data_size); + } while (0); buf->extra_info->input_time = current_pts / 90; buf->pts = current_pts; @@ -291,20 +300,20 @@ off_t start_pos, int start_time, int playing) { demux_wav_t *this = (demux_wav_t *) this_gen; - start_pos = (off_t) ( (double) start_pos / 65535 * - this->data_size ); + start_pos = this->data_size > 0 ? (off_t)((double)start_pos * this->data_size / 65535) : 0; this->status = DEMUX_OK; + /* if input is non-seekable, do not proceed with the rest of this + * seek function */ + if (!(this->input->get_capabilities (this->input) & (INPUT_CAP_SEEKABLE | INPUT_CAP_SLOW_SEEKABLE))) + return this->status; + this->send_newpts = 1; if (playing) { - this->seek_flag = 1; + this->seek_flag = BUF_FLAG_SEEK; _x_demux_flush_engine (this->stream); } - /* if input is non-seekable, do not proceed with the rest of this - * seek function */ - if (!INPUT_IS_SEEKABLE(this->input)) - return this->status; /* time-based seeking, the start_pos code will then align the blocks * if necessary */ @@ -327,8 +336,8 @@ * the block alignment integer-wise, and multiply the quotient by the * block alignment to get the new aligned offset. Add the data start * offset and seek to the new position. */ - start_pos /= this->wave->nBlockAlign; - start_pos *= this->wave->nBlockAlign; + start_pos /= this->seek_align; + start_pos *= this->seek_align; start_pos += this->data_start; this->input->seek(this->input, start_pos, SEEK_SET); @@ -374,7 +383,6 @@ input_plugin_t *input) { demux_wav_t *this; - uint32_t align; switch (stream->content_detection_method) { case METHOD_BY_MRL: @@ -411,15 +419,6 @@ return NULL; } - /* special block alignment hack so that the demuxer doesn't send - * packets with individual PCM samples */ - if ((this->wave->nAvgBytesPerSec / this->wave->nBlockAlign) == - this->wave->nSamplesPerSec) { - align = PCM_BLOCK_ALIGN / this->wave->nBlockAlign; - align = align * this->wave->nBlockAlign; - this->wave->nBlockAlign = align; - } - return &this->demux_plugin; } diff -r 9e075962b659 src/demuxers/group_audio.c --- a/src/demuxers/group_audio.c Wed Mar 09 14:31:44 2022 +0200 +++ b/src/demuxers/group_audio.c Sat Sep 24 13:04:31 2022 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2000-2018 the xine project + * Copyright (C) 2000-2022 the xine project * * This file is part of xine, a free video player. * @@ -32,88 +32,31 @@ /* * exported plugin catalog entries */ - -static const demuxer_info_t demux_info_aac = { - .priority = -1, -}; - -static const demuxer_info_t demux_info_ac3 = { - .priority = 8, -}; - -static const demuxer_info_t demux_info_aud = { - .priority = -2, -}; - -static const demuxer_info_t demux_info_aiff = { - .priority = 10, -}; - -static const demuxer_info_t demux_info_cdda = { - .priority = 6, -}; - -static const demuxer_info_t demux_info_dts = { - .priority = 8, -}; - -static const demuxer_info_t demux_info_flac = { - .priority = 10, -}; - -static const demuxer_info_t demux_info_mpgaudio = { - .priority = 0, -}; - -static const demuxer_info_t demux_info_mpc = { - .priority = 1, -}; - -static const demuxer_info_t demux_info_realaudio = { - .priority = 10, -}; - -static const demuxer_info_t demux_info_shn = { - .priority = 0, -}; - -static const demuxer_info_t demux_info_snd = { - .priority = 10, -}; - -static const demuxer_info_t demux_info_tta = { - .priority = 10, -}; - -static const demuxer_info_t demux_info_voc = { - .priority = 10, -}; - -static const demuxer_info_t demux_info_vox = { - .priority = 10, -}; - -static const demuxer_info_t demux_info_wav = { - .priority = 6, -}; +static const demuxer_info_t demux_info_minus_3 = { .priority = -3 }; +static const demuxer_info_t demux_info_minus_1 = { .priority = -1 }; +static const demuxer_info_t demux_info_plus__0 = { .priority = 0 }; +static const demuxer_info_t demux_info_plus__1 = { .priority = 1 }; +static const demuxer_info_t demux_info_plus__6 = { .priority = 6 }; +static const demuxer_info_t demux_info_plus__8 = { .priority = 8 }; +static const demuxer_info_t demux_info_plus_10 = { .priority = 10 }; const plugin_info_t xine_plugin_info[] EXPORTED = { /* type, API, "name", version, special_info, init_function */ - { PLUGIN_DEMUX, 27, "aac", XINE_VERSION_CODE, &demux_info_aac, demux_aac_init_plugin }, - { PLUGIN_DEMUX, 27, "ac3", XINE_VERSION_CODE, &demux_info_ac3, demux_ac3_init_plugin }, - { PLUGIN_DEMUX, 27, "aud", XINE_VERSION_CODE, &demux_info_aud, demux_aud_init_plugin }, - { PLUGIN_DEMUX, 27, "aiff", XINE_VERSION_CODE, &demux_info_aiff, demux_aiff_init_plugin }, - { PLUGIN_DEMUX, 27, "cdda", XINE_VERSION_CODE, &demux_info_cdda, demux_cdda_init_plugin }, - { PLUGIN_DEMUX, 27, "dts", XINE_VERSION_CODE, &demux_info_dts, demux_dts_init_plugin }, - { PLUGIN_DEMUX, 27, "flac", XINE_VERSION_CODE, &demux_info_flac, demux_flac_init_plugin }, - { PLUGIN_DEMUX, 27, "mp3", XINE_VERSION_CODE, &demux_info_mpgaudio, demux_mpgaudio_init_class }, - { PLUGIN_DEMUX, 27, "mpc", XINE_VERSION_CODE, &demux_info_mpc, demux_mpc_init_plugin }, - { PLUGIN_DEMUX, 27, "realaudio", XINE_VERSION_CODE, &demux_info_realaudio, demux_realaudio_init_plugin }, - { PLUGIN_DEMUX, 27, "shn", XINE_VERSION_CODE, &demux_info_shn, demux_shn_init_plugin }, - { PLUGIN_DEMUX, 27, "snd", XINE_VERSION_CODE, &demux_info_snd, demux_snd_init_plugin }, - { PLUGIN_DEMUX, 27, "tta", XINE_VERSION_CODE, &demux_info_tta, demux_tta_init_plugin }, - { PLUGIN_DEMUX, 27, "voc", XINE_VERSION_CODE, &demux_info_voc, demux_voc_init_plugin }, - { PLUGIN_DEMUX, 27, "vox", XINE_VERSION_CODE, &demux_info_vox, demux_vox_init_plugin }, - { PLUGIN_DEMUX, 27, "wav", XINE_VERSION_CODE, &demux_info_wav, demux_wav_init_plugin }, + { PLUGIN_DEMUX, 27, "aac", XINE_VERSION_CODE, &demux_info_minus_1, demux_aac_init_plugin }, + { PLUGIN_DEMUX, 27, "ac3", XINE_VERSION_CODE, &demux_info_plus__8, demux_ac3_init_plugin }, + { PLUGIN_DEMUX, 27, "aud", XINE_VERSION_CODE, &demux_info_minus_3, demux_aud_init_plugin }, + { PLUGIN_DEMUX, 27, "aiff", XINE_VERSION_CODE, &demux_info_plus_10, demux_aiff_init_plugin }, + { PLUGIN_DEMUX, 27, "cdda", XINE_VERSION_CODE, &demux_info_plus__6, demux_cdda_init_plugin }, + { PLUGIN_DEMUX, 27, "dts", XINE_VERSION_CODE, &demux_info_plus__8, demux_dts_init_plugin }, + { PLUGIN_DEMUX, 27, "flac", XINE_VERSION_CODE, &demux_info_plus_10, demux_flac_init_plugin }, + { PLUGIN_DEMUX, 27, "mp3", XINE_VERSION_CODE, &demux_info_plus__0, demux_mpgaudio_init_class }, + { PLUGIN_DEMUX, 27, "mpc", XINE_VERSION_CODE, &demux_info_plus__1, demux_mpc_init_plugin }, + { PLUGIN_DEMUX, 27, "realaudio", XINE_VERSION_CODE, &demux_info_plus_10, demux_realaudio_init_plugin }, + { PLUGIN_DEMUX, 27, "shn", XINE_VERSION_CODE, &demux_info_plus__0, demux_shn_init_plugin }, + { PLUGIN_DEMUX, 27, "snd", XINE_VERSION_CODE, &demux_info_plus_10, demux_snd_init_plugin }, + { PLUGIN_DEMUX, 27, "tta", XINE_VERSION_CODE, &demux_info_plus_10, demux_tta_init_plugin }, + { PLUGIN_DEMUX, 27, "voc", XINE_VERSION_CODE, &demux_info_plus_10, demux_voc_init_plugin }, + { PLUGIN_DEMUX, 27, "vox", XINE_VERSION_CODE, &demux_info_plus_10, demux_vox_init_plugin }, + { PLUGIN_DEMUX, 27, "wav", XINE_VERSION_CODE, &demux_info_plus__6, demux_wav_init_plugin }, { PLUGIN_NONE, 0, NULL, 0, NULL, NULL } }; diff -r 9e075962b659 src/demuxers/group_games.c --- a/src/demuxers/group_games.c Wed Mar 09 14:31:44 2022 +0200 +++ b/src/demuxers/group_games.c Sat Sep 24 13:04:31 2022 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2000-2018 the xine project + * Copyright (C) 2000-2022 the xine project * * This file is part of xine, a free video player. * @@ -33,62 +33,20 @@ * exported plugin catalog entries */ -static const demuxer_info_t demux_info_eawve = { - .priority = 10, -}; - -static const demuxer_info_t demux_info_idcin = { - .priority = 10, -}; - -static const demuxer_info_t demux_info_ipmovie = { - .priority = 10, -}; - -static const demuxer_info_t demux_info_vqa = { - .priority = 10, -}; - -static const demuxer_info_t demux_info_wc3movie = { - .priority = 10, -}; - -static const demuxer_info_t demux_info_roq = { - .priority = 10, -}; - -static const demuxer_info_t demux_info_str = { - .priority = 10, -}; - -static const demuxer_info_t demux_info_film = { - .priority = 10, -}; - -static const demuxer_info_t demux_info_smjpeg = { - .priority = 10, -}; - -static const demuxer_info_t demux_info_fourxm = { - .priority = 10, -}; - -static const demuxer_info_t demux_info_vmd = { - .priority = 10, -}; +static const demuxer_info_t demux_info_plus_10 = { .priority = 10 }; const plugin_info_t xine_plugin_info[] EXPORTED = { /* type, API, "name", version, special_info, init_function */ - { PLUGIN_DEMUX, 27, "wve", XINE_VERSION_CODE, &demux_info_eawve, demux_eawve_init_plugin}, - { PLUGIN_DEMUX, 27, "idcin", XINE_VERSION_CODE, &demux_info_idcin, demux_idcin_init_plugin }, - { PLUGIN_DEMUX, 27, "ipmovie", XINE_VERSION_CODE, &demux_info_ipmovie, demux_ipmovie_init_plugin }, - { PLUGIN_DEMUX, 27, "vqa", XINE_VERSION_CODE, &demux_info_vqa, demux_vqa_init_plugin }, - { PLUGIN_DEMUX, 27, "wc3movie", XINE_VERSION_CODE, &demux_info_wc3movie, demux_wc3movie_init_plugin }, - { PLUGIN_DEMUX, 27, "roq", XINE_VERSION_CODE, &demux_info_roq, demux_roq_init_plugin }, - { PLUGIN_DEMUX, 27, "str", XINE_VERSION_CODE, &demux_info_str, demux_str_init_plugin }, - { PLUGIN_DEMUX, 27, "film", XINE_VERSION_CODE, &demux_info_film, demux_film_init_plugin }, - { PLUGIN_DEMUX, 27, "smjpeg", XINE_VERSION_CODE, &demux_info_smjpeg, demux_smjpeg_init_plugin }, - { PLUGIN_DEMUX, 27, "fourxm", XINE_VERSION_CODE, &demux_info_fourxm, demux_fourxm_init_plugin }, - { PLUGIN_DEMUX, 27, "vmd", XINE_VERSION_CODE, &demux_info_vmd, demux_vmd_init_plugin }, + { PLUGIN_DEMUX, 27, "wve", XINE_VERSION_CODE, &demux_info_plus_10, demux_eawve_init_plugin}, + { PLUGIN_DEMUX, 27, "idcin", XINE_VERSION_CODE, &demux_info_plus_10, demux_idcin_init_plugin }, + { PLUGIN_DEMUX, 27, "ipmovie", XINE_VERSION_CODE, &demux_info_plus_10, demux_ipmovie_init_plugin }, + { PLUGIN_DEMUX, 27, "vqa", XINE_VERSION_CODE, &demux_info_plus_10, demux_vqa_init_plugin }, + { PLUGIN_DEMUX, 27, "wc3movie", XINE_VERSION_CODE, &demux_info_plus_10, demux_wc3movie_init_plugin }, + { PLUGIN_DEMUX, 27, "roq", XINE_VERSION_CODE, &demux_info_plus_10, demux_roq_init_plugin }, + { PLUGIN_DEMUX, 27, "str", XINE_VERSION_CODE, &demux_info_plus_10, demux_str_init_plugin }, + { PLUGIN_DEMUX, 27, "film", XINE_VERSION_CODE, &demux_info_plus_10, demux_film_init_plugin }, + { PLUGIN_DEMUX, 27, "smjpeg", XINE_VERSION_CODE, &demux_info_plus_10, demux_smjpeg_init_plugin }, + { PLUGIN_DEMUX, 27, "fourxm", XINE_VERSION_CODE, &demux_info_plus_10, demux_fourxm_init_plugin }, + { PLUGIN_DEMUX, 27, "vmd", XINE_VERSION_CODE, &demux_info_plus_10, demux_vmd_init_plugin }, { PLUGIN_NONE, 0, NULL, 0, NULL, NULL } }; diff -r 9e075962b659 src/demuxers/group_video.c --- a/src/demuxers/group_video.c Wed Mar 09 14:31:44 2022 +0200 +++ b/src/demuxers/group_video.c Sat Sep 24 13:04:31 2022 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2000-2020 the xine project + * Copyright (C) 2000-2022 the xine project * * This file is part of xine, a free video player. * @@ -32,43 +32,32 @@ /* * exported plugin catalog entries */ -static const demuxer_info_t demux_info_avi = { .priority = 10 }; -static const demuxer_info_t demux_info_elem = { .priority = 0 }; -static const demuxer_info_t demux_info_flv = { .priority = 10 }; -static const demuxer_info_t demux_info_iff = { .priority = 10 }; -static const demuxer_info_t demux_info_ivf = { .priority = 1 }; -static const demuxer_info_t demux_info_mpeg = { .priority = 9 }; -static const demuxer_info_t demux_info_mpeg_block = { .priority = 10 }; -static const demuxer_info_t demux_info_mpeg_pes = { .priority = 10 }; -static const demuxer_info_t demux_info_matroska = { .priority = 10 }; -static const demuxer_info_t demux_info_qt = { .priority = 10 }; -static const demuxer_info_t demux_info_raw_dv = { .priority = 1 }; -static const demuxer_info_t demux_info_real = { .priority = 10 }; +static const demuxer_info_t demux_info_plus__0 = { .priority = 0 }; +static const demuxer_info_t demux_info_plus__1 = { .priority = 1 }; +static const demuxer_info_t demux_info_plus__9 = { .priority = 9 }; +static const demuxer_info_t demux_info_plus_10 = { .priority = 10 }; /* probe mpeg-ts first, and detect TV recordings cut at an unhappy byte pos. */ -static const demuxer_info_t demux_info_ts = { .priority = 12 }; -static const demuxer_info_t demux_info_vc1es = { .priority = 0 }; -static const demuxer_info_t demux_info_yuv_frames = { .priority = 0 }; -static const demuxer_info_t demux_info_yuv4mpeg2 = { .priority = 10 }; +static const demuxer_info_t demux_info_plus_12 = { .priority = 12 }; const plugin_info_t xine_plugin_info[] EXPORTED = { /* type, API, "name", version, special_info, init_function */ - { PLUGIN_DEMUX, 27, "avi", XINE_VERSION_CODE, &demux_info_avi, demux_avi_init_class }, - { PLUGIN_DEMUX, 27, "elem", XINE_VERSION_CODE, &demux_info_elem, demux_elem_init_class }, - { PLUGIN_DEMUX, 27, "flashvideo", XINE_VERSION_CODE, &demux_info_flv, demux_flv_init_class }, - { PLUGIN_DEMUX, 27, "iff", XINE_VERSION_CODE, &demux_info_iff, demux_iff_init_class }, - { PLUGIN_DEMUX, 27, "ivf", XINE_VERSION_CODE, &demux_info_ivf, demux_ivf_init_class }, - { PLUGIN_DEMUX, 27, "matroska", XINE_VERSION_CODE, &demux_info_matroska, demux_matroska_init_class }, - { PLUGIN_DEMUX, 27, "mpeg", XINE_VERSION_CODE, &demux_info_mpeg, demux_mpeg_init_class }, - { PLUGIN_DEMUX, 27, "mpeg_block", XINE_VERSION_CODE, &demux_info_mpeg_block, demux_mpeg_block_init_class }, - { PLUGIN_DEMUX, 27, "mpeg-ts", XINE_VERSION_CODE, &demux_info_ts, demux_ts_init_class }, - { PLUGIN_DEMUX, 27, "mpeg_pes", XINE_VERSION_CODE, &demux_info_mpeg_pes, demux_pes_init_class }, - { PLUGIN_DEMUX, 27, "quicktime", XINE_VERSION_CODE, &demux_info_qt, demux_qt_init_class }, - { PLUGIN_DEMUX, 27, "rawdv", XINE_VERSION_CODE, &demux_info_raw_dv, demux_rawdv_init_class }, - { PLUGIN_DEMUX, 27, "real", XINE_VERSION_CODE, &demux_info_real, demux_real_init_class }, - { PLUGIN_DEMUX, 27, "vc1es", XINE_VERSION_CODE, &demux_info_vc1es, demux_vc1es_init_class }, - { PLUGIN_DEMUX, 27, "yuv_frames", XINE_VERSION_CODE, &demux_info_yuv_frames, demux_yuv_frames_init_class }, - { PLUGIN_DEMUX, 27, "yuv4mpeg2", XINE_VERSION_CODE, &demux_info_yuv4mpeg2, demux_yuv4mpeg2_init_class }, + { PLUGIN_DEMUX, 27, "avi", XINE_VERSION_CODE, &demux_info_plus_10, demux_avi_init_class }, + { PLUGIN_DEMUX, 27, "elem", XINE_VERSION_CODE, &demux_info_plus__0, demux_elem_init_class }, + { PLUGIN_DEMUX, 27, "flashvideo", XINE_VERSION_CODE, &demux_info_plus_10, demux_flv_init_class }, + { PLUGIN_DEMUX, 27, "iff", XINE_VERSION_CODE, &demux_info_plus_10, demux_iff_init_class }, + { PLUGIN_DEMUX, 27, "ivf", XINE_VERSION_CODE, &demux_info_plus__1, demux_ivf_init_class }, + { PLUGIN_DEMUX, 27, "matroska", XINE_VERSION_CODE, &demux_info_plus_10, demux_matroska_init_class }, + { PLUGIN_DEMUX, 27, "mpeg", XINE_VERSION_CODE, &demux_info_plus__9, demux_mpeg_init_class }, + { PLUGIN_DEMUX, 27, "mpeg_block", XINE_VERSION_CODE, &demux_info_plus_10, demux_mpeg_block_init_class }, + { PLUGIN_DEMUX, 27, "mpeg-ts", XINE_VERSION_CODE, &demux_info_plus_12, demux_ts_init_class }, + { PLUGIN_DEMUX, 27, "mpeg_pes", XINE_VERSION_CODE, &demux_info_plus_10, demux_pes_init_class }, + { PLUGIN_DEMUX, 27, "quicktime", XINE_VERSION_CODE, &demux_info_plus_10, demux_qt_init_class }, + { PLUGIN_DEMUX, 27, "rawdv", XINE_VERSION_CODE, &demux_info_plus__1, demux_rawdv_init_class }, + { PLUGIN_DEMUX, 27, "real", XINE_VERSION_CODE, &demux_info_plus_10, demux_real_init_class }, + { PLUGIN_DEMUX, 27, "vc1es", XINE_VERSION_CODE, &demux_info_plus__0, demux_vc1es_init_class }, + { PLUGIN_DEMUX, 27, "yuv_frames", XINE_VERSION_CODE, &demux_info_plus__0, demux_yuv_frames_init_class }, + { PLUGIN_DEMUX, 27, "yuv4mpeg2", XINE_VERSION_CODE, &demux_info_plus_10, demux_yuv4mpeg2_init_class }, { PLUGIN_NONE, 0, NULL, 0, NULL, NULL } }; diff -r 9e075962b659 src/demuxers/id3.c --- a/src/demuxers/id3.c Wed Mar 09 14:31:44 2022 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,908 +0,0 @@ -/* - * Copyright (C) 2000-2019 the xine project - * - * This file is part of xine, a free video player. - * - * xine is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * xine is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * - * ID3 tag parser - * - * Supported versions: v1, v1.1, v2.2, v2.3, v2.4 - * TODO: - * v2.2: unsynchronize - * v2.3: unsynchronize - * unzip support - * v2.4: unsynchronize - * unzip support - * - * ID3v2 specs: http://www.id3.org/ - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include <stdlib.h> - -#define LOG_MODULE "id3" -#define LOG_VERBOSE -/* -#define LOG -*/ - -#include <xine/xine_internal.h> -#include <xine/xineutils.h> -#include "bswap.h" -#include "id3.h" - -#define ID3_GENRE_COUNT (sizeof (id3_genre) / sizeof (id3_genre[0])) -static const char id3_genre[][24] = - {"Blues", "Classic Rock", "Country", "Dance", "Disco", - "Funk", "Grunge", "Hip-Hop", "Jazz", "Metal", - "New Age", "Oldies", "Other", "Pop", "R&B", - "Rap", "Reggae", "Rock", "Techno", "Industrial", - "Alternative", "Ska", "Death Metal", "Pranks", "Soundtrack", - "Euro-Techno", "Ambient", "Trip-Hop", "Vocal", "Jazz+Funk", - "Fusion", "Trance", "Classical", "Instrumental", "Acid", - "House", "Game", "Sound Clip", "Gospel", "Noise", - "AlternRock", "Bass", "Soul", "Punk", "Space", - "Meditative", "Instrumental Pop", "Instrumental Rock", "Ethnic", "Gothic", - "Darkwave", "Techno-Industrial", "Electronic", "Pop-Folk", "Eurodance", - "Dream", "Southern Rock", "Comedy", "Cult", "Gangsta", - "Top 40", "Christian Rap", "Pop/Funk", "Jungle", "Native American", - "Cabaret", "New Wave", "Psychedelic", "Rave", "Showtunes", - "Trailer", "Lo-Fi", "Tribal", "Acid Punk", "Acid Jazz", - "Polka", "Retro", "Musical", "Rock & Roll", "Hard Rock", - "Folk", "Folk-Rock", "National Folk", "Swing", "Fast Fusion", - "Bebob", "Latin", "Revival", "Celtic", "Bluegrass", - "Avantgarde", "Gothic Rock", "Progressive Rock", "Psychedelic Rock", "Symphonic Rock", - "Slow Rock", "Big Band", "Chorus", "Easy Listening", "Acoustic", - "Humour", "Speech", "Chanson", "Opera", "Chamber Music", - "Sonata", "Symphony", "Booty Bass", "Primus", "Porn Groove", - "Satire", "Slow Jam", "Club", "Tango", "Samba", - "Folklore", "Ballad", "Power Ballad", "Rhythmic Soul", "Freestyle", - "Duet", "Punk Rock", "Drum Solo", "A capella", "Euro-House", - "Dance Hall", "Goa", "Drum & Bass", "Club-House", "Hardcore", "Terror", - "Indie", "BritPop", "Negerpunk", "Polsk Punk", "Beat", - "Christian Gangsta Rap", "Heavy Metal", "Black Metal", "Crossover", - "Contemporary Christian", "Christian Rock", "Merengue", "Salsa", - "Thrash Metal", "Anime", "JPop", "Synthpop" }; - -#define ID3_ENCODING_COUNT 4 -static const char id3_encoding[][12] = { - "ISO-8859-1", /* 0x00 */ - "UTF-16", /* 0x01 */ - "UTF-16BE", /* 0x02 */ - "UTF-8"}; /* 0x03 */ - -int id3v1_parse_tag (input_plugin_t *input, xine_stream_t *stream) { - - id3v1_tag_t tag; - char track[4]; - - /* id3v1 */ - if (input->read (input, (char *)&tag, 128) != 128) { - return 0; - } - - if ( (tag.tag[0]=='T') && (tag.tag[1]=='A') && (tag.tag[2]=='G') ) { - - lprintf("id3v1 tag found\n"); - _x_meta_info_n_set(stream, XINE_META_INFO_TITLE, tag.title, 30); - _x_meta_info_n_set(stream, XINE_META_INFO_ARTIST, tag.artist, 30); - _x_meta_info_n_set(stream, XINE_META_INFO_ALBUM, tag.album, 30); - _x_meta_info_n_set(stream, XINE_META_INFO_YEAR, tag.year, 4); - _x_meta_info_n_set(stream, XINE_META_INFO_COMMENT, tag.comment, 30); - - /* check for a track number: ID3v1.1, which is a clever hack on ID3v1 */ - if (tag.comment[28] == 0 && tag.comment[29] != 0) { - snprintf(track, 4, "%d", (unsigned char)tag.comment[29]); - _x_meta_info_set(stream, XINE_META_INFO_TRACK_NUMBER, track); - } - - if (tag.genre < ID3_GENRE_COUNT) { - _x_meta_info_set(stream, XINE_META_INFO_GENRE, id3_genre[tag.genre]); - } - } - return 1; -} - -/* id3v2 "genre" parsing code. what a ugly format ! */ -static int id3v2_parse_genre(char* dest, const char *src, size_t len) { - int state = 0; - char *buf = dest; - unsigned int index = 0; - - while (*src) { - lprintf("state=%d\n", state); - if ((buf - dest) >= (ptrdiff_t)len) - return 0; - - switch (state) { - case 0: - /* initial state */ - if (*src == '(') { - state = 1; - index = 0; - src++; - } else { - *buf = *src; - buf++; src++; - } - break; - case 1: - /* ( */ - if (*src == '(') { - *buf = *src; - buf++; src++; - state = 0; - } else if (*src == 'R') { - src++; - state = 2; - } else if (*src == 'C') { - src++; - state = 3; - } else if ((*src >= '0') && (*src <= '9')) { - index = 10 * index + (*src - '0'); - src++; - } else if (*src == ')') { - if (index < ID3_GENRE_COUNT) { - strncpy(buf, id3_genre[index], len - (buf - dest)); - buf += strlen(id3_genre[index]); - } else { - lprintf("invalid index: %d\n", index); - } - src++; - state = 0; - } else { - lprintf("parsing error\n"); - return 0; - } - break; - case 2: - /* (R */ - if (*src == 'X') { - src++; - state = 4; - } else { - lprintf("parsing error\n"); - return 0; - } - break; - case 3: - /* (C */ - if (*src == 'R') { - strncpy(dest, id3_genre[index], len - (buf - dest)); - buf += strlen(id3_genre[index]); - src++; - state = 5; - } else { - lprintf("parsing error\n"); - return 0; - } - break; - case 4: - /* (RX */ - if (*src == ')') { - strncpy(dest, "Remix", len - (buf - dest)); - buf += strlen("Remix"); - src++; - state = 0; - } else { - lprintf("parsing error\n"); - return 0; - } - break; - case 5: - /* (CR */ - if (*src == ')') { - strncpy(dest, "Cover", len - (buf - dest)); - buf += strlen("Cover"); - src++; - state = 0; - } else { - lprintf("parsing error\n"); - return 0; - } - break; - } - } - if ((buf - dest) >= (ptrdiff_t)len) { - return 0; - } else { - *buf = '\0'; - } - return 1; -} - -static int id3v2_parse_header(input_plugin_t *input, uint32_t id3_signature, - id3v2_header_t *tag_header) { - uint8_t buf[6]; - - tag_header->id = be2me_32(id3_signature); - if (input->read (input, buf, 6) != 6) { - return 0; - } - - tag_header->revision = buf[0]; - tag_header->flags = buf[1]; - tag_header->size = _X_BE_32_synchsafe(&buf[2]); - - lprintf("tag: ID3 v2.%d.%d\n", tag_header->id & 0xFF, tag_header->revision); - lprintf("flags: %d\n", tag_header->flags); - lprintf("size: %zu\n", tag_header->size); - return 1; -} - -/* id3 v2.2 */ - -static int id3v22_parse_frame_header(input_plugin_t *input, - id3v22_frame_header_t *frame_header) { - uint8_t buf[ID3V22_FRAME_HEADER_SIZE]; - int len; - - len = input->read (input, buf, ID3V22_FRAME_HEADER_SIZE); - if (len != ID3V22_FRAME_HEADER_SIZE) { - return 0; - } - - frame_header->id = (buf[0] << 16) + (buf[1] << 8) + buf[2]; - - frame_header->size = _X_BE_24(&buf[3]); - - lprintf("frame: %c%c%c: size: %zu\n", buf[0], buf[1], buf[2], - frame_header->size); - - return 1; -} - -static int id3v22_interp_frame(input_plugin_t *input, - xine_stream_t *stream, - id3v22_frame_header_t *frame_header) { - const size_t bufsize = frame_header->size + 2; - char *buf; - int enc; - - if ( bufsize < 3 ) /* frames has to be _at least_ 1 byte */ - return 0; - buf = malloc(bufsize); - if (!buf) - return 0; - - if (input->read (input, buf, frame_header->size) != (int)frame_header->size) { - lprintf("read error\n"); - free(buf); - return 0; - } - - buf[frame_header->size] = 0; - buf[frame_header->size + 1] = 0; - enc = buf[0]; - if( enc >= ID3_ENCODING_COUNT ) - enc = 0; - - switch (frame_header->id) { - case ( BE_FOURCC(0, 'T', 'C', 'O') ): - { - char tmp[1024]; - - if (id3v2_parse_genre(tmp, buf + 1, sizeof(tmp))) { - _x_meta_info_set(stream, XINE_META_INFO_GENRE, tmp); - } - } - break; - - case ( BE_FOURCC(0, 'T', 'T', '2') ): - _x_meta_info_set_generic(stream, XINE_META_INFO_TITLE, buf + 1, id3_encoding[enc]); - break; - - case ( BE_FOURCC(0, 'T', 'P', '1') ): - _x_meta_info_set_generic(stream, XINE_META_INFO_ARTIST, buf + 1, id3_encoding[enc]); - break; - - case ( BE_FOURCC(0, 'T', 'A', 'L') ): - _x_meta_info_set_generic(stream, XINE_META_INFO_ALBUM, buf + 1, id3_encoding[enc]); - break; - - case ( BE_FOURCC(0, 'T', 'Y', 'E') ): - _x_meta_info_set_generic(stream, XINE_META_INFO_YEAR, buf + 1, id3_encoding[enc]); - break; - - case ( BE_FOURCC(0, 'C', 'O', 'M') ): - if (frame_header->size > 4) - _x_meta_info_set_generic(stream, XINE_META_INFO_COMMENT, buf + 1 + 3, id3_encoding[enc]); - break; - - case ( BE_FOURCC(0, 'T', 'R', 'K') ): - _x_meta_info_set(stream, XINE_META_INFO_TRACK_NUMBER, buf + 1); - break; - - default: - lprintf("unhandled frame\n"); - } - - free(buf); - return 1; -} - - -static int id3v22_parse_tag(input_plugin_t *input, - xine_stream_t *stream, - uint32_t id3_signature) { - id3v2_header_t tag_header; - id3v22_frame_header_t tag_frame_header; - unsigned int pos = 0; - - if (!id3v2_parse_header(input, id3_signature, &tag_header)) { - xprintf(stream->xine, XINE_VERBOSITY_DEBUG, LOG_MODULE ": id3v2_parse_header problem\n"); - return 0; - } - - if (tag_header.flags & ID3V22_ZERO_FLAG) { - /* invalid flags */ - xprintf(stream->xine, XINE_VERBOSITY_DEBUG, - LOG_MODULE ": invalid header flags (%02x)\n", tag_header.flags); - input->seek (input, tag_header.size, SEEK_CUR); - return 0; - } - - if (tag_header.flags & ID3V22_COMPRESS_FLAG) { - /* compressed tag: not supported */ - xprintf(stream->xine, XINE_VERBOSITY_DEBUG, - LOG_MODULE ": compressed tags are not supported\n"); - input->seek (input, tag_header.size, SEEK_CUR); - return 0; - } - - if (tag_header.flags & ID3V22_UNSYNCH_FLAG) { - /* unsynchronized tag: not supported */ - xprintf(stream->xine, XINE_VERBOSITY_DEBUG, - LOG_MODULE ": unsynchronized tags are not supported\n"); - input->seek (input, tag_header.size, SEEK_CUR); - return 0; - } - - /* frame parsing */ - while ((pos + ID3V22_FRAME_HEADER_SIZE) <= tag_header.size) { - - if (!id3v22_parse_frame_header(input, &tag_frame_header)) { - xprintf(stream->xine, XINE_VERBOSITY_DEBUG, - LOG_MODULE ": id3v2_parse_frame_header problem\n"); - return 0; - } - pos += ID3V22_FRAME_HEADER_SIZE; - - if (!tag_frame_header.id) { - /* end of frames, the rest is padding */ - input->seek (input, tag_header.size - pos, SEEK_CUR); - return 1; - } - - if ((pos + tag_frame_header.size) > tag_header.size) { - xprintf(stream->xine, XINE_VERBOSITY_DEBUG, - LOG_MODULE ": invalid frame header\n"); - input->seek (input, tag_header.size - pos, SEEK_CUR); - return 1; - } - - if (!id3v22_interp_frame(input, stream, &tag_frame_header)) { - xprintf(stream->xine, XINE_VERBOSITY_DEBUG, - LOG_MODULE ": invalid frame content\n"); - } - pos += tag_frame_header.size; - } - - return 1; -} - -/* id3 v2.3 */ - -static int id3v23_parse_frame_header(input_plugin_t *input, - id3v23_frame_header_t *frame_header) { - uint8_t buf[ID3V23_FRAME_HEADER_SIZE]; - int len; - - len = input->read (input, buf, ID3V23_FRAME_HEADER_SIZE); - if (len != ID3V23_FRAME_HEADER_SIZE) { - return 0; - } - - frame_header->id = _X_BE_32(buf); - frame_header->size = _X_BE_32(&buf[4]); - frame_header->flags = _X_BE_16(buf + 8); - - lprintf("frame: %c%c%c%c, size: %zu, flags: %X\n", buf[0], buf[1], buf[2], buf[3], - frame_header->size, frame_header->flags); - - return 1; -} - -static int id3v23_parse_frame_ext_header(input_plugin_t *input, - id3v23_frame_ext_header_t *frame_ext_header) { - uint8_t buf[10]; - - if (input->read (input, buf, 4) != 4) { - return 0; - } - frame_ext_header->size = _X_BE_32(&buf[0]); - - if (frame_ext_header->size == 6) { - if (input->read (input, buf, 6) != 6) { - return 0; - } - frame_ext_header->flags = _X_BE_16(buf); - frame_ext_header->padding_size = _X_BE_32(buf + 2); - frame_ext_header->crc = 0; - - } else if (frame_ext_header->size == 10) { - if (input->read (input, buf, 10) != 10) { - return 0; - } - frame_ext_header->flags = _X_BE_16(buf); - frame_ext_header->padding_size = _X_BE_32(buf + 2); - frame_ext_header->crc = _X_BE_32(buf + 6); - - } else { - lprintf("invalid ext header size: %zu\n", frame_ext_header->size); - return 0; - } - - lprintf("ext header: size: %zu, flags: %X, padding_size: %d, crc: %d\n", - frame_ext_header->size, frame_ext_header->flags, - frame_ext_header->padding_size, frame_ext_header->crc); - return 1; -} - -static int id3v23_interp_frame(input_plugin_t *input, - xine_stream_t *stream, - id3v23_frame_header_t *frame_header) { - const size_t bufsize = frame_header->size + 2; - char *buf; - int enc; - - if ( bufsize < 3 ) /* frames has to be _at least_ 1 byte */ - return 0; - buf = malloc(bufsize); - if (!buf) - return 0; - - if (input->read (input, buf, frame_header->size) != (int)frame_header->size) { - lprintf("read error\n"); - free(buf); - return 0; - } - - buf[frame_header->size] = 0; - buf[frame_header->size + 1] = 0; - enc = buf[0]; - if( enc >= ID3_ENCODING_COUNT ) - enc = 0; - - switch (frame_header->id) { - case ( BE_FOURCC('T', 'C', 'O', 'N') ): - { - char tmp[1024]; - - if (id3v2_parse_genre(tmp, buf + 1, sizeof(tmp))) { - _x_meta_info_set(stream, XINE_META_INFO_GENRE, tmp); - } - } - break; - - case ( BE_FOURCC('T', 'I', 'T', '2') ): - _x_meta_info_set_generic(stream, XINE_META_INFO_TITLE, buf + 1, id3_encoding[enc]); - break; - - case ( BE_FOURCC('T', 'P', 'E', '1') ): - _x_meta_info_set_generic(stream, XINE_META_INFO_ARTIST, buf + 1, id3_encoding[enc]); - break; - - case ( BE_FOURCC('T', 'A', 'L', 'B') ): - _x_meta_info_set_generic(stream, XINE_META_INFO_ALBUM, buf + 1, id3_encoding[enc]); - break; - - case ( BE_FOURCC('T', 'Y', 'E', 'R') ): - _x_meta_info_set_generic(stream, XINE_META_INFO_YEAR, buf + 1, id3_encoding[enc]); - break; - - case ( BE_FOURCC('C', 'O', 'M', 'M') ): - if (frame_header->size > 4) - _x_meta_info_set_generic(stream, XINE_META_INFO_COMMENT, buf + 1 + 3, id3_encoding[enc]); - break; - - case ( BE_FOURCC('T', 'R', 'C', 'K') ): - _x_meta_info_set(stream, XINE_META_INFO_TRACK_NUMBER, buf + 1); - break; - - case BE_FOURCC('T', 'P', 'O', 'S'): - _x_meta_info_set(stream, XINE_META_INFO_DISCNUMBER, buf + 1); - break; - - default: - lprintf("unhandled frame\n"); - } - - free(buf); - return 1; -} - -static int id3v23_parse_tag(input_plugin_t *input, - xine_stream_t *stream, - uint32_t id3_signature) { - id3v2_header_t tag_header; - id3v23_frame_header_t tag_frame_header; - id3v23_frame_ext_header_t tag_frame_ext_header; - unsigned int pos = 0; - - if (!id3v2_parse_header(input, id3_signature, &tag_header)) { - xprintf(stream->xine, XINE_VERBOSITY_DEBUG, LOG_MODULE ": id3v2_parse_header problem\n"); - return 0; - } - - if (tag_header.flags & ID3V23_ZERO_FLAG) { - /* invalid flags */ - xprintf(stream->xine, XINE_VERBOSITY_DEBUG, - LOG_MODULE ": invalid header flags (%02x)\n", tag_header.flags); - input->seek (input, tag_header.size, SEEK_CUR); - return 0; - } - - if (tag_header.flags & ID3V23_UNSYNCH_FLAG) { - /* unsynchronized tag: not supported */ - xprintf(stream->xine, XINE_VERBOSITY_DEBUG, - LOG_MODULE ": unsynchronized tags are not supported\n"); - input->seek (input, tag_header.size, SEEK_CUR); - return 0; - } - - if (tag_header.flags & ID3V23_EXT_HEADER_FLAG) { - /* extended header */ - if (!id3v23_parse_frame_ext_header(input, &tag_frame_ext_header)) { - return 0; - } - pos += tag_frame_ext_header.size; - } - - /* frame parsing */ - while ((pos + ID3V23_FRAME_HEADER_SIZE) <= tag_header.size) { - - if (!id3v23_parse_frame_header(input, &tag_frame_header)) { - xprintf(stream->xine, XINE_VERBOSITY_DEBUG, - LOG_MODULE ": id3v2_parse_frame_header problem\n"); - return 0; - } - pos += ID3V23_FRAME_HEADER_SIZE; - - if (!tag_frame_header.id) { - /* end of frames, the rest is padding */ - lprintf("skipping padding %zu bytes\n", tag_header.size - pos); - input->seek (input, tag_header.size - pos, SEEK_CUR); - return 1; - } - - if ((pos + tag_frame_header.size) > tag_header.size) { - xprintf(stream->xine, XINE_VERBOSITY_DEBUG, - LOG_MODULE ": invalid frame header\n"); - input->seek (input, tag_header.size - pos, SEEK_CUR); - return 1; - } - - if (!id3v23_interp_frame(input, stream, &tag_frame_header)) { - xprintf(stream->xine, XINE_VERBOSITY_DEBUG, - LOG_MODULE ": invalid frame content\n"); - } - pos += tag_frame_header.size; - } - - return 1; -} - -/* id3 v2.4 */ - -/* id3v2 "genre" parsing code. what a ugly format ! */ -static int id3v24_parse_genre(char* dest, const char *src, size_t len) { - unsigned int index = 0; - - dest[0] = '\0'; - if (sscanf(src, "%u", &index) == 1) { - if (index < ID3_GENRE_COUNT) { - strlcpy(dest, id3_genre[index], len); - } else { - lprintf("invalid index: %u\n", index); - } - } - return 1; -} - -static int id3v24_parse_frame_header(input_plugin_t *input, - id3v24_frame_header_t *frame_header) { - uint8_t buf[ID3V24_FRAME_HEADER_SIZE]; - int len; - - len = input->read (input, buf, ID3V24_FRAME_HEADER_SIZE); - if (len != ID3V24_FRAME_HEADER_SIZE) { - return 0; - } - - frame_header->id = _X_BE_32(buf); - frame_header->size = _X_BE_32(&buf[4]); - frame_header->flags = _X_BE_16(&buf[8]); - - lprintf("frame: %c%c%c%c, size: %zu, flags: %X\n", buf[0], buf[1], buf[2], buf[3], - frame_header->size, frame_header->flags); - - return 1; -} - -static int id3v24_parse_ext_header(input_plugin_t *input, - id3v24_frame_ext_header_t *frame_ext_header) { - uint8_t buf[5]; - uint8_t flags_size, data_length; - - if (input->read (input, buf, 4) != 4) { - return 0; - } - - frame_ext_header->size = _X_BE_32(&buf[0]); - - if (input->read (input, buf, 2) != 2) { - return 0; - } - - flags_size = buf[0]; - if (flags_size != 1) { - lprintf( - "id3: invalid extended header, flag size must be 1\n"); - return 0; - } - - frame_ext_header->flags = buf[1]; - if (frame_ext_header->flags & ID3V24_EXT_ZERO_FLAG) { - lprintf( - "id3: invalid extended header, unknown flags\n"); - return 0; - } - - /* Update flag */ - if (frame_ext_header->flags & ID3V24_EXT_UPDATE_FLAG) { - if (input->read (input, buf, 1) != 1) { - return 0; - } - data_length = buf[0]; - if (data_length != 0) { - lprintf( - "id3: invalid extended header, unexpected update flag data length\n"); - return 0; - } - } - - /* Crc flag */ - if (frame_ext_header->flags & ID3V24_EXT_CRC_FLAG) { - if (input->read (input, buf, 1) != 1) { - return 0; - } - data_length = buf[0]; - if (data_length != 5) { - lprintf( - "id3: invalid extended header, unexpected crc flag data length\n"); - return 0; - } - if (input->read (input, buf, data_length) == data_length) { - /* ignore crc */ - frame_ext_header->crc = BE_35_synchsafe(buf); - } - } - - /* Restriction flag */ - if (frame_ext_header->flags & ID3V24_EXT_RESTRICTIONS_FLAG) { - if (input->read (input, buf, 1) != 1) { - return 0; - } - data_length = buf[0]; - if (data_length != 1) { - lprintf( - "id3: invalid extended header, unexpected restriction flag data length\n"); - return 0; - } - if (input->read (input, buf, data_length) != data_length) { - return 0; - } - /* ignore restrictions */ - frame_ext_header->restrictions = buf[0]; - } - - lprintf("ext header: size: %zu, flags: %X, crc: %d, restrictions: %8X\n", - frame_ext_header->size, frame_ext_header->flags, - (frame_ext_header->flags & ID3V24_EXT_CRC_FLAG) ? frame_ext_header->crc : 0, - (frame_ext_header->flags & ID3V24_EXT_RESTRICTIONS_FLAG) ? frame_ext_header->restrictions : 0); - return 1; -} - -static int id3v24_interp_frame(input_plugin_t *input, - xine_stream_t *stream, - id3v24_frame_header_t *frame_header) { - const size_t bufsize = frame_header->size + 2; - char *buf; - int enc; - - if ( bufsize < 3 ) /* frames has to be _at least_ 1 byte */ - return 0; - - buf = malloc(bufsize); - if (!buf) - return 0; - - if (input->read (input, buf, frame_header->size) != (int)frame_header->size) { - lprintf("read error\n"); - free(buf); - return 0; - } - - buf[frame_header->size] = 0; - buf[frame_header->size + 1] = 0; - enc = buf[0]; - if( enc >= ID3_ENCODING_COUNT ) - enc = 0; - - lprintf("data: %s\n", buf+1); - - switch (frame_header->id) { - case ( BE_FOURCC('T', 'C', 'O', 'N') ): - { - char tmp[1024]; - - if (id3v24_parse_genre(tmp, buf + 1, sizeof(tmp))) { - _x_meta_info_set(stream, XINE_META_INFO_GENRE, tmp); - } - } - break; - - case ( BE_FOURCC('T', 'I', 'T', '2') ): - _x_meta_info_set_generic(stream, XINE_META_INFO_TITLE, buf + 1, id3_encoding[enc]); - break; - - case ( BE_FOURCC('T', 'P', 'E', '1') ): - _x_meta_info_set_generic(stream, XINE_META_INFO_ARTIST, buf + 1, id3_encoding[enc]); - break; - - case ( BE_FOURCC('T', 'A', 'L', 'B') ): - _x_meta_info_set_generic(stream, XINE_META_INFO_ALBUM, buf + 1, id3_encoding[enc]); - break; - - case ( BE_FOURCC('T', 'Y', 'E', 'R') ): - case ( BE_FOURCC('T', 'D', 'R', 'C') ): - _x_meta_info_set_generic(stream, XINE_META_INFO_YEAR, buf + 1, id3_encoding[enc]); - break; - - case ( BE_FOURCC('C', 'O', 'M', 'M') ): - if (frame_header->size > 4) - _x_meta_info_set_generic(stream, XINE_META_INFO_COMMENT, buf + 1 + 3, id3_encoding[enc]); - break; - - case ( BE_FOURCC('T', 'R', 'C', 'K') ): - _x_meta_info_set(stream, XINE_META_INFO_TRACK_NUMBER, buf + 1); - break; - - case BE_FOURCC('T', 'P', 'O', 'S'): - _x_meta_info_set(stream, XINE_META_INFO_DISCNUMBER, buf + 1); - break; - - default: - lprintf("unhandled frame\n"); - } - - free(buf); - return 1; -} - -static int id3v24_parse_tag(input_plugin_t *input, - xine_stream_t *stream, - uint32_t id3_signature) { - id3v2_header_t tag_header; - id3v24_frame_header_t tag_frame_header; - id3v24_frame_ext_header_t tag_frame_ext_header; - unsigned int pos = 0; - - if (!id3v2_parse_header(input, id3_signature, &tag_header)) { - xprintf(stream->xine, XINE_VERBOSITY_DEBUG, LOG_MODULE ": id3v2_parse_header problem\n"); - return 0; - } - - if (tag_header.flags & ID3V24_ZERO_FLAG) { - /* invalid flags */ - xprintf(stream->xine, XINE_VERBOSITY_DEBUG, - LOG_MODULE ": invalid header flags (%02x)\n", tag_header.flags); - input->seek (input, tag_header.size, SEEK_CUR); - return 0; - } - - if (tag_header.flags & ID3V24_UNSYNCH_FLAG) { - /* it just means that all frames must be flagged UNSYNCH */ - } - - if (tag_header.flags & ID3V24_EXPERIMENTAL_FLAG) { - /* it just means that the tag is in a experimental stage */ - } - - if (tag_header.flags & ID3V24_EXT_HEADER_FLAG) { - /* extended header */ - if (!id3v24_parse_ext_header(input, &tag_frame_ext_header)) { - return 0; - } - pos += tag_frame_ext_header.size; - } - - /* frame parsing */ - while ((pos + ID3V24_FRAME_HEADER_SIZE) <= tag_header.size) { - - if (!id3v24_parse_frame_header(input, &tag_frame_header)) { - xprintf(stream->xine, XINE_VERBOSITY_DEBUG, - LOG_MODULE ": id3v2_parse_frame_header problem\n"); - return 0; - } - pos += ID3V24_FRAME_HEADER_SIZE; - - if (!tag_frame_header.id) { - /* end of frames, the rest is padding */ - input->seek (input, tag_header.size - pos, SEEK_CUR); - return 1; - } - - if ((pos + tag_frame_header.size) > tag_header.size) { - xprintf(stream->xine, XINE_VERBOSITY_DEBUG, - LOG_MODULE ": invalid frame header\n"); - input->seek (input, tag_header.size - pos, SEEK_CUR); - return 1; - } - - if (!id3v24_interp_frame(input, stream, &tag_frame_header)) { - xprintf(stream->xine, XINE_VERBOSITY_DEBUG, - LOG_MODULE ": invalid frame content\n"); - } - pos += tag_frame_header.size; - } - - if (tag_header.flags & ID3V24_FOOTER_FLAG) { - /* ignore footer */ - input->seek (input, ID3V24_FOOTER_SIZE, SEEK_CUR); - } - - return 1; -} - -int id3v2_parse_tag(input_plugin_t *input, - xine_stream_t *stream, - uint32_t id3_signature) { - _x_assert((id3_signature & ID3V2X_MASK) == ID3V2X_TAG); - - switch(id3_signature) { - case ID3V22_TAG: - xprintf(stream->xine, XINE_VERBOSITY_LOG, LOG_MODULE ": ID3V2.2 tag\n"); - return id3v22_parse_tag(input, stream, id3_signature); - - case ID3V23_TAG: - xprintf(stream->xine, XINE_VERBOSITY_LOG, LOG_MODULE ": ID3V2.3 tag\n"); - return id3v23_parse_tag(input, stream, id3_signature); - - case ID3V24_TAG: - xprintf(stream->xine, XINE_VERBOSITY_LOG, LOG_MODULE ": ID3V2.4 tag\n"); - return id3v24_parse_tag(input, stream, id3_signature); - - default: - xprintf(stream->xine, XINE_VERBOSITY_LOG, LOG_MODULE ": Unknown ID3v2 signature: 0x%08x.\n", be2me_32(id3_signature)); - } - - return 0; -} diff -r 9e075962b659 src/demuxers/id3.h --- a/src/demuxers/id3.h Wed Mar 09 14:31:44 2022 +0200 +++ b/src/demuxers/id3.h Sat Sep 24 13:04:31 2022 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2000-2008 the xine project + * Copyright (C) 2000-2022 the xine project * * This file is part of xine, a free video player. * @@ -150,7 +150,15 @@ uint8_t genre; } id3v1_tag_t; -int id3v1_parse_tag (input_plugin_t *input, xine_stream_t *stream); +int id3v1_parse_tag (input_plugin_t *input, xine_stream_t *stream) XINE_PROTECTED; + +/** + * @brief Check/parse/skip a possible initial ID3 v2 tag from the (side) stream. + * @param stream The xine stream currently being read. + * @param input The input plugin to use, or NULL for the stream default. + * @note Safe to call during stream type detection. + * @return The byte count skipped or 0. */ +int xine_parse_id3v2_tag (xine_stream_t *stream, input_plugin_t *input) XINE_PROTECTED; /** * @brief Generic function for ID3v2 tags parsing. @@ -164,7 +172,7 @@ */ int id3v2_parse_tag(input_plugin_t *input, xine_stream_t *stream, - uint32_t id3_signature); + uint32_t id3_signature) XINE_PROTECTED; /** * @brief Checks if the given buffer is an ID3 tag preamble diff -r 9e075962b659 src/dxr3/ffmpeg_encoder.c --- a/src/dxr3/ffmpeg_encoder.c Wed Mar 09 14:31:44 2022 +0200 +++ b/src/dxr3/ffmpeg_encoder.c Sat Sep 24 13:04:31 2022 +0200 @@ -123,7 +123,7 @@ static int lavc_on_update_format(dxr3_driver_t *drv, dxr3_frame_t *frame) { lavc_data_t *this = (lavc_data_t *)drv->enc; - AVCodec *codec; + const AVCodec *codec; unsigned char use_quantizer; if (this->context) { @@ -223,7 +223,7 @@ this->context->gop_size = 0; /*intra frames only */ /* TJ. this version is known to have that but maybe its not the first. */ #if LIBAVCODEC_VERSION_INT >= XFF_INT_VERSION(56,56,101) - av_opt_set_int (codec, "motion_est", 0 /* "zero" */, 0); + av_opt_set_int (this->context->priv_data, "motion_est", 0 /* "zero" */, 0); #else this->context->me_method = ME_ZERO; /*motion estimation type*/ #endif diff -r 9e075962b659 src/input/input_file.c --- a/src/input/input_file.c Wed Mar 09 14:31:44 2022 +0200 +++ b/src/input/input_file.c Sat Sep 24 13:04:31 2022 +0200 @@ -449,7 +449,7 @@ #ifdef HAVE_MMAP this->mmap_base = NULL; do { - uint8_t mmap_base; + uint8_t *mmap_base; size_t tmp_size; /* may cause truncation - if it does, DON'T mmap! */ tmp_size = (size_t)sbuf.st_size; diff -r 9e075962b659 src/input/input_hls.c --- a/src/input/input_hls.c Wed Mar 09 14:31:44 2022 +0200 +++ b/src/input/input_hls.c Sat Sep 24 13:04:31 2022 +0200 @@ -28,6 +28,7 @@ #include <unistd.h> #include <string.h> #include <errno.h> +#include <pthread.h> #define LOG_MODULE "input_hls" #define LOG_VERBOSE @@ -46,6 +47,26 @@ #include "group_network.h" #include "multirate_pref.c" +typedef enum { + HLS_A_none = 0, + HLS_A_AUDIO, + HLS_A_AUTOSELECT, + HLS_A_AVERAGE_BANDWIDTH, + HLS_A_BANDWIDTH, + HLS_A_BYTERANGE, + HLS_A_CODECS, + HLS_A_DEFAULT, + HLS_A_FRAME_RATE, + HLS_A_GROUP_ID, + HLS_A_LANGUAGE, + HLS_A_NAME, + HLS_A_RESOLUTION, + HLS_A_TYPE, + HLS_A_URI, + HLS_A_VIDEO_RANGE, + HLS_A_last +} hls_arg_type_t; + typedef struct { input_class_t input_class; xine_t *xine; @@ -53,20 +74,48 @@ } hls_input_class_t; typedef struct { + uint64_t offs; + uint32_t len; +} hls_byterange_t; + +typedef struct hls_input_plugin_s { input_plugin_t input_plugin; xine_stream_t *stream; xine_nbc_t *nbc; input_plugin_t *in1; uint32_t caps1; + int last_err; + + struct hls_input_plugin_s *main_input; + unsigned int side_index; /** << 0 .. 3 */ + unsigned int num_sides; + struct { + pthread_mutex_t mutex; + time_t avail_start, play_start; /** << seconds since 1970 */ + struct timespec play_systime; + int lag; /** pts */ + uint32_t type; + int init; + int refs; + } sync; /** set by main input, used by sides */ + + struct { + /** The intelligent seek manager. */ xine_mfrag_list_t *list; + /** TJ. A mrl may contain multiple fragments. + * I have seen .m3u8 that merely index a single fragment .mp4 file. + * In theory, this may also skip and reorder fragments like a edit list, + * so we need to store the given offsets here + * (cannot assume 0 or or the list generated values). */ uint64_t *input_offs; /** << bytes + 1, or 0 if unset */ uint32_t *mrl_offs; /** << offs into list_buf */ off_t pos; off_t size; int64_t pts; uint32_t num; - uint32_t current; /** << 1..n or 0 (none) */ +#define HLS_NO_FRAGMENT (~0u) + uint32_t current; /** << 1..n or 0 (init fragment if there) */ } frag; off_t pos; char *list_buf; @@ -77,20 +126,29 @@ LIST_LIVE_REGET } list_type; uint32_t list_seq; - uint32_t items_num; uint32_t prev_size1; /** << the actual preview bytes, for INPUT_OPTIONAL_DATA_[SIZED]_PREVIEW. */ uint32_t prev_size2; /** << for read (), 0 after leaving that range. */ struct timespec frag_dur; /** << != 0 if fixed duration live frags */ struct timespec next_stop; /** << live timeline emulation */ int rewind; /** << seconds */ - const char *items_mrl[20]; - multirate_pref_t items[20]; + +#define HLS_MAX_ITEMS 20 + struct { + uint32_t num; + uint32_t mrl[HLS_MAX_ITEMS]; + uint32_t group[HLS_MAX_ITEMS]; + uint32_t ref[HLS_MAX_ITEMS]; + multirate_pref_t pref[HLS_MAX_ITEMS]; + } items; + const char *list_strtype; const char *list_strseq; + hls_byterange_t list_rangeinit; #define HLS_MAX_MRL 4096 char list_mrl[HLS_MAX_MRL]; char item_mrl[HLS_MAX_MRL]; char prev_item_mrl[HLS_MAX_MRL]; + char side_mrl[1][HLS_MAX_MRL]; size_t bump_pos; size_t bump_size; uint32_t bump_seq; @@ -140,19 +198,19 @@ /* seen size */ s2 = this->in1->get_length (this->in1); /* subfragment? */ - if (this->frag.input_offs[this->frag.current - 1]) { + if (this->frag.input_offs[this->frag.current]) { this->frag.size = s1; if (s1 > 0) return s1; - s2 -= this->frag.input_offs[this->frag.current - 1] - 1; + s2 -= this->frag.input_offs[this->frag.current] - 1; } /* update size */ this->frag.size = s2; if (s2 > 0) { if ((s1 > 0) && (s1 != s2)) { xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, - "input_hls: WTF: fragment #%u changed size from %" PRId64 " to %" PRId64 " bytes!!\n", - (unsigned int)this->frag.current, s1, s2); + LOG_MODULE ".%u: WTF: fragment #%u changed size from %" PRId64 " to %" PRId64 " bytes!!\n", + this->side_index, (unsigned int)this->frag.current, s1, s2); } xine_mfrag_set_index_frag (this->frag.list, this->frag.current, -1, s2); return s2; @@ -283,7 +341,8 @@ } static int hls_input_switch_mrl (hls_input_plugin_t *this) { - xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "input_hls: %s.\n", this->item_mrl); + xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, + LOG_MODULE ".%u: %s.\n", this->side_index, this->item_mrl); if (this->in1) { if (this->in1->get_capabilities (this->in1) & INPUT_CAP_NEW_MRL) { if (this->in1->get_optional_data (this->in1, this->item_mrl, @@ -314,11 +373,13 @@ static int hls_input_open_item (hls_input_plugin_t *this, uint32_t n) { /* valid index ? */ - if ((n < 1) || (n > this->frag.num)) + if (n > this->frag.num) + return 0; + if (!n && !this->list_rangeinit.len) return 0; strcpy (this->prev_item_mrl, this->item_mrl); /* get fragment mrl */ - _x_merge_mrl (this->item_mrl, HLS_MAX_MRL, this->list_mrl, this->list_buf + this->frag.mrl_offs[n - 1]); + _x_merge_mrl (this->item_mrl, HLS_MAX_MRL, this->list_mrl, this->list_buf + this->frag.mrl_offs[n]); /* get input */ if (strcmp (this->prev_item_mrl, this->item_mrl)) { this->caps1 = 0; @@ -326,7 +387,8 @@ return 0; } else { xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, - "input_hls: reuse %s for fragment #%u.\n", (const char *)this->item_mrl, (unsigned int)n); + LOG_MODULE ".%u: reuse %s for fragment #%u.\n", + this->side_index, (const char *)this->item_mrl, (unsigned int)n); } this->caps1 = this->in1->get_capabilities (this->in1); /* input offset */ @@ -334,20 +396,20 @@ int64_t old_pos = this->in1->get_current_pos (this->in1), new_pos; if (old_pos < 0) break; - if (!this->frag.input_offs[n - 1]) + if (!this->frag.input_offs[n]) break; - new_pos = this->frag.input_offs[n - 1] - 1; + new_pos = this->frag.input_offs[n] - 1; if (old_pos == new_pos) break; if (this->caps1 & (INPUT_CAP_SEEKABLE | INPUT_CAP_SLOW_SEEKABLE)) { xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, - "input_hls: seek into fragment @ %" PRId64 ".\n", new_pos); + LOG_MODULE ".%u: seek into fragment @ %" PRId64 ".\n", this->side_index, new_pos); old_pos = this->in1->seek (this->in1, new_pos, SEEK_SET); if (old_pos == new_pos) break; } xprintf (this->stream->xine, XINE_VERBOSITY_LOG, - "input_hls: sub input seek failed.\n"); + LOG_MODULE ".%u: sub input seek failed.\n", this->side_index); } while (0); this->frag.current = n; /* update size info */ @@ -383,6 +445,10 @@ [' '] = 1, ['\r'] = 2, ['\n'] = 2, + ['"'] = 4, + ['\''] = 8, + [','] = 16, + ['='] = 32, [0] = 128 }; @@ -407,6 +473,109 @@ *s = (char *)p; } +static void hls_reset_args (char **a) { + uint32_t u; + for (u = 0; u < HLS_A_last; u++) + a[u] = NULL; +} + +static int hls_parse_args (char **a, char **s) { + uint8_t *p = (uint8_t *)*s; + int n = 0; + + while (*p) { + uint8_t *key, *value = NULL; + uint32_t klen; + while (hls_tab_char[*p] & 1) /* spc */ + p++; + key = p; + while (!(hls_tab_char[*p] & (1 | 16 | 32 | 128))) /* spc, ",", "=", end */ + *p |= 0x20, p++; + klen = p - key; + while (hls_tab_char[*p] & 1) /* spc */ + p++; + if (*p != '=') { + if (*p) + p++; + continue; + } + p++; + while (hls_tab_char[*p] & 1) /* spc */ + p++; + if (*p == '"') { + value = ++p; + while (!(hls_tab_char[*p] & (4 | 128))) /* """, end */ + p++; + } else if (*p == '\'') { + value = ++p; + while (!(hls_tab_char[*p] & (8 | 128))) /* "'", end */ + p++; + } else if (*p) { + value = p; + while (!(hls_tab_char[*p] & (16 | 128))) /* ",", end */ + p++; + } + if (*p) + *p++ = 0; + switch (klen) { + case 3: + if (!memcmp (key, "uri", 3)) + a[HLS_A_URI] = value, n++; + break; + case 4: + if (!memcmp (key, "name", 4)) + a[HLS_A_NAME] = value, n++; + else if (!memcmp (key, "type", 4)) + a[HLS_A_TYPE] = value, n++; + break; + case 5: + if (!memcmp (key, "audio", 5)) + a[HLS_A_AUDIO] = value, n++; + break; + case 6: + if (!memcmp (key, "codecs", 6)) + a[HLS_A_CODECS] = value, n++; + break; + case 7: + if (!memcmp (key, "default", 7)) + a[HLS_A_DEFAULT] = value, n++; + break; + case 8: + if (!memcmp (key, "group-id", 8)) + a[HLS_A_GROUP_ID] = value, n++; + else if (!memcmp (key, "language", 8)) + a[HLS_A_LANGUAGE] = value, n++; + break; + case 9: + if (!memcmp (key, "bandwidth", 9)) + a[HLS_A_BANDWIDTH] = value, n++; + else if (!memcmp (key, "byterange", 9)) + a[HLS_A_BYTERANGE] = value, n++; + break; + case 10: + if (!memcmp (key, "autoselct", 10)) + a[HLS_A_AUTOSELECT] = value, n++; + else if (!memcmp (key, "frame-rate", 10)) + a[HLS_A_FRAME_RATE] = value, n++; + else if (!memcmp (key, "resolution", 10)) + a[HLS_A_RESOLUTION] = value, n++; + break; + case 11: + if (!memcmp (key, "video-range", 11)) + a[HLS_A_VIDEO_RANGE] = value, n++; + break; + case 17: + if (!memcmp (key, "average-bandwidth", 17)) + a[HLS_A_AVERAGE_BANDWIDTH] = value, n++; + break; + default: ; + } + } + + *s = (char *)p; + return n; +} + static uint32_t str2uint32 (char **s) { uint8_t *p = (uint8_t *)*s; uint32_t v = 0; @@ -473,16 +642,26 @@ return v; } +static void hls_parse_byterange (hls_byterange_t *r, char **s) { + hls_skip_spc (s); + r->len = str2uint32 (s); + if (**s == '@') { + (*s)++; + r->offs = str2uint64 (s); + } +} + static int hls_input_load_list (hls_input_plugin_t *this) { ssize_t size; char *line, *lend; uint32_t frag_duration, fixed_duration; + char *args[HLS_A_last]; this->frag.mrl_offs = NULL; _x_freep (&this->frag.input_offs); xine_mfrag_list_close (&this->frag.list); this->frag.num = 0; - this->items_num = 0; + this->items.num = 0; { off_t s = this->in1->get_length (this->in1); @@ -542,6 +721,8 @@ frag_duration = 0; lend = this->list_buf + 4; + hls_reset_args (args); + if (strstr (lend, "#EXTINF:")) { uint32_t fragsize = ~0u; /* fragment list */ @@ -558,15 +739,17 @@ } if (n == 0) return 0; - mem = malloc ((n + 2) * (sizeof (*this->frag.input_offs) + sizeof (this->frag.mrl_offs))); + mem = malloc ((n + 3) * (sizeof (*this->frag.input_offs) + sizeof (this->frag.mrl_offs))); if (!mem) return 0; this->frag.input_offs = (uint64_t *)mem; - mem += (n + 2) * sizeof (*this->frag.input_offs); + mem += (n + 3) * sizeof (*this->frag.input_offs); this->frag.mrl_offs = (uint32_t *)mem; } this->frag.mrl_offs[0] = 0; - this->frag.input_offs[0]= 0; + this->frag.mrl_offs[1] = 0; + this->frag.input_offs[0] = 0; + this->frag.input_offs[1] = 0; xine_mfrag_list_open (&this->frag.list); xine_mfrag_set_index_frag (this->frag.list, 0, 1000000, 0); lend = this->list_buf + 4; @@ -598,9 +781,23 @@ if (*line == '@') { line++; hls_skip_spc (&line); - this->frag.input_offs[this->frag.num] = str2uint64 (&line) + 1; + this->frag.input_offs[this->frag.num + 1] = str2uint64 (&line) + 1; } else { - this->frag.input_offs[this->frag.num] = 1; + this->frag.input_offs[this->frag.num + 1] = 1; + } + } else if ((llen > 7) && !strncasecmp (line + 4, "-X-MAP:", 7)) { + /* hls-ng extension: #EXT-X-MAP:URI="foo.mp4",BYTERANGE="854@0" */ + line += 11; + hls_parse_args (args, &line); + if (args[HLS_A_URI]) + this->frag.mrl_offs[0] = args[HLS_A_URI] - this->list_buf; + if (args[HLS_A_BYTERANGE]) + hls_parse_byterange (&this->list_rangeinit, &args[HLS_A_BYTERANGE]); + if (this->frag.mrl_offs[0]) { + this->frag.input_offs[0] = this->list_rangeinit.offs + 1; + xine_mfrag_set_index_frag (this->frag.list, 0, -1, this->list_rangeinit.len); + } else { + this->list_rangeinit.len = 0; } } else if ((llen > 22) && !strncasecmp (line + 4, "-X-MEDIA-SEQUENCE:", 18)) { line += 22; @@ -615,10 +812,10 @@ } } else if ((llen >= 1) && (line[0] != '#')) { /* mrl */ - this->frag.mrl_offs[this->frag.num] = line - this->list_buf; + this->frag.mrl_offs[this->frag.num + 1] = line - this->list_buf; this->frag.num += 1; - this->frag.mrl_offs[this->frag.num] = 0; - this->frag.input_offs[this->frag.num] = 0; + this->frag.mrl_offs[this->frag.num + 1] = 0; + this->frag.input_offs[this->frag.num + 1] = 0; xine_mfrag_set_index_frag (this->frag.list, this->frag.num, frag_duration, fragsize != ~0u ? (int64_t)fragsize : -1); } } @@ -646,47 +843,80 @@ *lend++ = 0; if ((llen >=4) && !strncasecmp (line, "#EXT", 4)) { /* control tag */ - if ((llen > 8) && !strncasecmp (line + 4, "-X-STREAM-INF:", 14)) { - if (n < sizeof (this->items_mrl) / sizeof (this->items_mrl[0])) { + if ((llen > 18) && !strncasecmp (line + 4, "-X-STREAM-INF:", 14)) { + if (n < HLS_MAX_ITEMS) { line += 18; xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, - "input_hls: item #%u: %s.\n", (unsigned int)n, line); - this->items[n].bitrate = 0; - this->items[n].video_width = 0; - this->items[n].video_height = 0; - this->items[n].lang[0] = 0; - while (*line != 0) { - char *tagend; - lend[-1] = ','; - tagend = line; - while (*tagend != ',') - tagend++; - lend[-1] = 0; - if (!strncasecmp (line, "BANDWIDTH=", 10)) { - line += 10; - this->items[n].bitrate = str2uint32 (&line); + LOG_MODULE ".%u: item #%u: %s.\n", this->side_index, (unsigned int)n, line); + this->items.pref[n].bitrate = 0; + this->items.pref[n].video_width = 0; + this->items.pref[n].video_height = 0; + this->items.pref[n].lang[0] = 0; + hls_reset_args (args); + hls_parse_args (args, &line); + this->items.ref[n] = args[HLS_A_AUDIO] ? args[HLS_A_AUDIO] - this->list_buf : 0; + if (args[HLS_A_BANDWIDTH]) + this->items.pref[n].bitrate = str2uint32 (&args[HLS_A_BANDWIDTH]); + if (args[HLS_A_RESOLUTION]) { + line = args[HLS_A_RESOLUTION]; + this->items.pref[n].video_width = str2uint32 (&line); + if ((*line & 0xdf) == 'X') { + line += 1; + this->items.pref[n].video_height = str2uint32 (&line); } - if (!strncasecmp (line, "RESOLUTION=", 11)) { - line += 11; - this->items[n].video_width = str2uint32 (&line); - if ((*line & 0xdf) == 'X') { - line += 1; - this->items[n].video_height = str2uint32 (&line); + } + if (args[HLS_A_LANGUAGE]) { + line = args[HLS_A_LANGUAGE]; + if ((this->items.pref[n].lang[0] = line[0])) { + if ((this->items.pref[n].lang[1] = line[1])) { + if ((this->items.pref[n].lang[2] = line[2])) + this->items.pref[n].lang[3] = 0; } } - line = tagend; - if (*line) - line++; } } + } else if ((llen > 13) && !strncasecmp (line + 4, "-X-MEDIA:", 9)) { + line += 13; + xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, + LOG_MODULE ".%u: item #%u: %s.\n", this->side_index, (unsigned int)n, line); + hls_reset_args (args); + hls_parse_args (args, &line); + if (args[HLS_A_URI] && (n < HLS_MAX_ITEMS)) { + this->items.mrl[n] = args[HLS_A_URI] - this->list_buf; + this->items.group[n] = args[HLS_A_GROUP_ID] ? args[HLS_A_GROUP_ID] - this->list_buf : 0; + this->items.pref[n].bitrate = args[HLS_A_BANDWIDTH] ? str2uint32 (&args[HLS_A_BANDWIDTH]) : 0; + if (args[HLS_A_RESOLUTION]) { + line = args[HLS_A_RESOLUTION]; + this->items.pref[n].video_width = str2uint32 (&line); + if ((*line & 0xdf) == 'X') { + line += 1; + this->items.pref[n].video_height = str2uint32 (&line); + } + } else { + this->items.pref[n].video_width = 0; + this->items.pref[n].video_height = 0; + } + if (args[HLS_A_LANGUAGE]) { + line = args[HLS_A_LANGUAGE]; + if ((this->items.pref[n].lang[0] = line[0])) { + if ((this->items.pref[n].lang[1] = line[1])) { + if ((this->items.pref[n].lang[2] = line[2])) + this->items.pref[n].lang[3] = 0; + } + } + } else { + this->items.pref[n].lang[0] = 0; + } + n++; + } } } else if ((llen >= 1) && (line[0] != '#')) { /* mrl */ - if (n < sizeof (this->items_mrl) / sizeof (this->items_mrl[0])) - this->items_mrl[n++] = line; + if (n < HLS_MAX_ITEMS) + this->items.mrl[n++] = line - this->list_buf; } } - this->items_num = n; + this->items.num = n; return 2; } @@ -789,7 +1019,7 @@ while (left > 0) { int reget = 0; /* read, safe with unknown size. */ - if (this->frag.current == 0) + if (this->frag.current == HLS_NO_FRAGMENT) break; { ssize_t r = 0; @@ -811,20 +1041,20 @@ b += r; fragleft -= r; } - if (fragleft <= 0) - r = 0; left += fragleft; + if (left == 0) + break; if (r <= 0) { - if (r == 0) { - /* EOF */ - hls_frag_end (this); - } else { - return -1; + if (r < 0) { + this->last_err = errno; + if (!this->last_err) + this->last_err = EINVAL; + break; } } } - if (left == 0) - break; + /* EOF */ + hls_frag_end (this); /* bump */ if (this->list_type != LIST_LIVE_BUMP) { uint32_t n = this->frag.current + 1; @@ -844,7 +1074,7 @@ if (!hls_input_open_bump (this)) { this->list_type = LIST_LIVE_REGET; xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, - "input_hls: LIVE bump error, falling back to reget mode.\n"); + LOG_MODULE ".%u: LIVE bump error, falling back to reget mode.\n", this->side_index); reget = 1; } } @@ -859,7 +1089,8 @@ n = this->bump_seq - this->list_seq; if ((n < 0) || (n >= (int32_t)this->frag.num)) { xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, - "input_hls: LIVE seq discontinuity %u -> %u.\n", (unsigned int)this->bump_seq, (unsigned int)this->list_seq); + LOG_MODULE ".%u: LIVE seq discontinuity %u -> %u.\n", + this->side_index, (unsigned int)this->bump_seq, (unsigned int)this->list_seq); this->bump_seq = this->list_seq; n = 0; } @@ -868,7 +1099,16 @@ } } - return b - (uint8_t *)buf; + { + size_t done = b - (uint8_t *)buf; + if (done > 0) + return done; + } + if (!this->last_err) + return 0; + errno = this->last_err; + this->last_err = 0; + return -1; } static buf_element_t *hls_input_read_block (input_plugin_t *this_gen, fifo_buffer_t *fifo, off_t todo) { @@ -885,6 +1125,7 @@ if (!this) return 0; + this->last_err = 0; if (this->list_type != LIST_VOD) return this->pos; if (!this->frag.list) @@ -949,6 +1190,7 @@ if (!this) return 0; + this->last_err = 0; l = hls_get_size (this); switch (origin) { case SEEK_SET: @@ -1011,16 +1253,17 @@ xine_mfrag_get_index_start (this->frag.list, idx + 1, NULL, &p2); } } + new_offs -= this->frag.pos; - new_offs -= this->frag.pos; if (new_offs > 0) { off_t subpos = new_offs; - if (this->frag.input_offs[this->frag.current - 1]) - subpos += this->frag.input_offs[this->frag.current - 1] - 1; - if (this->in1->seek (this->in1, subpos, SEEK_SET) != subpos) { + if (this->frag.input_offs[this->frag.current]) + subpos += this->frag.input_offs[this->frag.current] - 1; + if (this->in1->seek (this->in1, subpos, SEEK_SET) == subpos) { + this->pos = this->frag.pos + new_offs; + } else { this->in1->seek (this->in1, 0, SEEK_SET); - } else { - this->pos += new_offs; + this->pos = this->frag.pos; } } @@ -1052,19 +1295,42 @@ hls_input_plugin_t *this = (hls_input_plugin_t *)this_gen; if (!this) return; + if (this->in1) { _x_free_input_plugin (this->stream, this->in1); this->in1 = NULL; } + if (this->nbc) { xine_nbc_close (this->nbc); this->nbc = NULL; } + xine_mfrag_list_close (&this->frag.list); _x_freep (&this->list_buf); this->frag.mrl_offs = NULL; _x_freep (&this->frag.input_offs); - free (this); + + if (this->side_index) { + hls_input_plugin_t *main_input = this->main_input; + this->sync.refs = 0; + free (this); + this = main_input; + } + + if (this->sync.init) { + pthread_mutex_lock (&this->sync.mutex); + if (--this->sync.refs == 0) { + pthread_mutex_unlock (&this->sync.mutex); + pthread_mutex_destroy (&this->sync.mutex); + free (this); + } else { + pthread_mutex_unlock (&this->sync.mutex); + } + } else { + if (--this->sync.refs == 0) + free (this); + } } static int hls_input_open (input_plugin_t *this_gen) { @@ -1077,6 +1343,13 @@ hls_input_class_t *cls = (hls_input_class_t *)this->input_plugin.input_class; int try; + if (this->side_index > 0) { + strcpy (this->list_mrl, this->side_mrl[this->side_index - 1]); + strcpy (this->item_mrl, this->side_mrl[this->side_index - 1]); + if (!hls_input_switch_mrl (this)) + return 0; + } + for (try = 8; try > 0; try--) { int n; @@ -1086,57 +1359,95 @@ if (n != 2) return 0; - n = multirate_autoselect (&cls->pref, this->items, this->items_num); + n = multirate_autoselect (&cls->pref, this->items.pref, this->items.num); if (n < 0) { xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, - "input_hls: no auto selected item.\n"); + LOG_MODULE ".%u: no auto selected item.\n", this->side_index); return 0; } xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, - "input_hls: auto selected item #%d.\n", n); - _x_merge_mrl (this->item_mrl, HLS_MAX_MRL, this->list_mrl, this->items_mrl[n]); - xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "input_hls: trying %s.\n", this->item_mrl); + LOG_MODULE ".%u: auto selected item #%d.\n", this->side_index, n); + _x_merge_mrl (this->item_mrl, HLS_MAX_MRL, this->list_mrl, this->list_buf + this->items.mrl[n]); + xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, + LOG_MODULE ".%u: trying %s.\n", this->side_index, this->item_mrl); if (!hls_input_switch_mrl (this)) return 0; strcpy (this->list_mrl, this->item_mrl); + + if ((this->side_index == 0) && this->items.ref[n] && (this->num_sides < 2)) { + int all, group; + uint32_t l; + all = multirate_audio_autoselect (&cls->pref, this->items.pref, this->items.num); + /* hide sound of wrong group from multirate_audio_autoselect (). */ + for (l = 0; l < this->items.num; l++) { + if ((this->items.pref[l].video_width | this->items.pref[l].video_height)) + continue; + if (this->items.group[l] && strcmp (this->list_buf + this->items.ref[n], this->list_buf + this->items.group[l])) + this->items.pref[l].video_width = 1; + } + group = multirate_audio_autoselect (&cls->pref, this->items.pref, this->items.num); + if (group < 0) + group = all; + if (group >= 0) { + xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, + LOG_MODULE ".%u: auto selected separate audio #%d.\n", this->side_index, group); + _x_merge_mrl (this->side_mrl[this->num_sides - 1], HLS_MAX_MRL, this->list_mrl, this->list_buf + this->items.mrl[group]); + this->num_sides++; + } + } + } + + if ((this->num_sides > 1) && !this->sync.init) { + pthread_mutex_init (&this->sync.mutex, NULL); + this->sync.init = 1; } if (try <= 0) { xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, - "input_hls: too many redirections, giving up.\n"); + LOG_MODULE ".%u: too many redirections, giving up.\n", this->side_index); return 0; } { unsigned int d = hls_get_duration (this); xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, - "input_hls: got %u fragments for %u.%03u seconds.\n", (unsigned int)this->frag.num, d / 1000u, d % 1000u); + LOG_MODULE ".%u: got %u fragments for %u.%03u seconds.\n", + this->side_index, (unsigned int)this->frag.num, d / 1000u, d % 1000u); } if (!strncasecmp (this->list_strtype, "VOD", 3) || ((this->frag.num >= 8) && (this->list_seq == 1))) { this->list_type = LIST_VOD; } else { if ((this->frag.num > 1) - && hls_bump_guess (this, this->list_buf + this->frag.mrl_offs[0], this->list_buf + this->frag.mrl_offs[1])) { + && hls_bump_guess (this, this->list_buf + this->frag.mrl_offs[1], this->list_buf + this->frag.mrl_offs[2])) { this->list_type = LIST_LIVE_BUMP; } else if ((this->frag.num > 0) - && hls_bump_find (this, this->list_buf + this->frag.mrl_offs[0], this->list_strseq)) { + && hls_bump_find (this, this->list_buf + this->frag.mrl_offs[1], this->list_strseq)) { this->list_type = LIST_LIVE_BUMP; } else { this->list_type = LIST_LIVE_REGET; } } xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, - "input_hls: %s mode @ seq %s.\n", type_names[this->list_type], this->list_strseq); + LOG_MODULE ".%u: %s mode @ seq %s.\n", + this->side_index, type_names[this->list_type], this->list_strseq); - if (this->list_type == LIST_LIVE_BUMP) { - this->frag.current = 1; - if (!hls_input_open_bump (this)) - return 0; - } else { - if (!hls_input_open_item (this, 1)) - return 0; - } + do { + if (this->list_rangeinit.len) { + if (hls_input_open_item (this, 0)) + break; + xprintf (this->stream->xine, XINE_VERBOSITY_LOG, + LOG_MODULE ".%u: WARNING: cannot get init fragment %s.\n", this->side_index, this->item_mrl); + } + if (this->list_type == LIST_LIVE_BUMP) { + this->frag.current = 1; + if (!hls_input_open_bump (this)) + return 0; + } else { + if (!hls_input_open_item (this, 1)) + return 0; + } + } while (0); hls_live_start (this); try = hls_input_read (&this->input_plugin, this->preview, sizeof (this->preview)); @@ -1148,6 +1459,69 @@ return 1; } +static input_plugin_t *hls_get_side (hls_input_plugin_t *this, int side_index) { + hls_input_plugin_t *side_input; + + if (this->side_index) + return NULL; + if ((side_index < 1) || (side_index >= (int)this->num_sides)) + return NULL; + side_input = malloc (sizeof (*side_input)); + if (!side_input) + return NULL; + + /* clone everything */ + *side_input = *this; + + /* sync */ + if (this->sync.init) { + pthread_mutex_lock (&this->sync.mutex); + this->sync.refs++; + pthread_mutex_unlock (&this->sync.mutex); + } else { + this->sync.refs++; + } + memset (&side_input->sync.mutex, 0, sizeof (side_input->sync.mutex)); + side_input->sync.init = 0; + side_input->sync.refs = 1; + + /* detach */ + side_input->side_index = side_index; + side_input->in1 = NULL; + side_input->caps1 = 0; + side_input->list_buf = NULL; + side_input->list_bsize = 0; + side_input->frag.input_offs = NULL; + side_input->frag.mrl_offs = NULL; + side_input->frag.pos = 0; + side_input->frag.size = 0; + side_input->frag.pts = 0; + side_input->frag.num = 0; + side_input->frag.current = 0; + side_input->frag.list = NULL; + xine_mfrag_list_open (&side_input->frag.list); + side_input->pos = 0; + side_input->list_seq = 1; + side_input->prev_size1 = 0; + side_input->prev_size2 = 0; + side_input->items.num = 0; + side_input->list_strtype = NULL; + side_input->list_strseq = NULL; + side_input->list_mrl[0] = 0; + side_input->item_mrl[0] = 0; + side_input->prev_item_mrl[0] = 0; + + side_input->stream = xine_get_side_stream (this->stream, side_index); + if (!side_input->stream) { + free (side_input->list_buf); + free (side_input); + return NULL; + } + side_input->nbc = xine_nbc_init (side_input->stream); + + return &side_input->input_plugin; +} + static int hls_input_get_optional_data (input_plugin_t *this_gen, void *data, int data_type) { hls_input_plugin_t *this = (hls_input_plugin_t *)this_gen; @@ -1202,6 +1576,20 @@ return INPUT_OPTIONAL_SUCCESS; } + case INPUT_OPTIONAL_DATA_SIDE: + if (!data) + return INPUT_OPTIONAL_UNSUPPORTED; + { + int side_index; + input_plugin_t *side_input; + memcpy (&side_index, data, sizeof (side_index)); + side_input = hls_get_side (this, side_index); + if (!side_input) + return INPUT_OPTIONAL_UNSUPPORTED; + memcpy (data, &side_input, sizeof (side_input)); + } + return INPUT_OPTIONAL_SUCCESS; + case INPUT_OPTIONAL_DATA_FRAGLIST: if (!data) return INPUT_OPTIONAL_UNSUPPORTED; @@ -1211,6 +1599,12 @@ } return INPUT_OPTIONAL_SUCCESS; + case INPUT_OPTIONAL_DATA_REWIND: + if (!data) + return INPUT_OPTIONAL_UNSUPPORTED; + memcpy (&this->rewind, data, sizeof (this->rewind)); + return INPUT_OPTIONAL_SUCCESS; + default: return INPUT_OPTIONAL_UNSUPPORTED; } @@ -1250,16 +1644,20 @@ } while (0); #ifndef HAVE_ZERO_SAFE_MEM + this->side_index = 0; + this->sync.lag = 0; + this->sync.type = 0; + this->sync.init = 0; this->caps1 = 0; + this->last_err = 0; this->frag.list = NULL; this->frag.input_offs = NULL; this->frag.size = 0; this->frag.mrl_offs = NULL; this->frag.num = 0; - this->frag.current = 0; this->list_buf = NULL; this->list_bsize = 0; - this->items_num = 0; + this->items.num = 0; this->prev_size1 = 0; this->prev_size2 = 0; this->frag_dur.tv_sec = 0; @@ -1268,16 +1666,20 @@ this->next_stop.tv_nsec = 0; this->rewind = 0; this->prev_item_mrl[0] = 0; + this->side_mrl[0][0] = 0; #endif this->stream = stream; this->in1 = in1; + this->main_input = this; + this->num_sides = 1; + this->frag.current = HLS_NO_FRAGMENT; /* TJ. yes input_http already does this, but i want to test offline * with a file based service. */ this->nbc = xine_nbc_init (this->stream); - xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "input_hls: %s.\n", mrl + n); + xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, LOG_MODULE ".%u: %s.\n", this->side_index, mrl + n); strlcpy (this->list_mrl, mrl + n, HLS_MAX_MRL); @@ -1334,3 +1736,4 @@ return this; } + diff -r 9e075962b659 src/input/input_http.c --- a/src/input/input_http.c Wed Mar 09 14:31:44 2022 +0200 +++ b/src/input/input_http.c Sat Sep 24 13:04:31 2022 +0200 @@ -51,7 +51,6 @@ #include <xine/xineutils.h> #include <xine/input_plugin.h> #include "tls/xine_tls.h" -#include "net_buf_ctrl.h" #include "group_network.h" #include "http_helper.h" #include "input_helper.h" @@ -142,7 +141,7 @@ xine_stream_t *stream; xine_t *xine; - nbc_t *nbc; + xine_nbc_t *nbc; off_t curpos; uint64_t contentlength; @@ -204,8 +203,11 @@ uint8_t sbuf[32 << 10]; uint8_t sbuf_pad[4]; - int32_t preview_size; - uint8_t preview[MAX_PREVIEW_SIZE]; + struct { + off_t start; + int32_t size; + uint8_t buf[MAX_PREVIEW_SIZE]; + } preview; char mrl[4096]; } http_input_plugin_t; @@ -934,6 +936,7 @@ static off_t http_plugin_read (input_plugin_t *this_gen, void *buf_gen, off_t nlen) { http_input_plugin_t *this = (http_input_plugin_t *) this_gen; char *buf = (char *)buf_gen; + off_t diff; size_t want, num_bytes; if (nlen < 0) @@ -944,12 +947,13 @@ return 0; num_bytes = 0; - if (this->curpos < this->preview_size) { - uint32_t have = this->preview_size - this->curpos; + diff = this->preview.start + this->preview.size - this->curpos; + if (diff > 0) { + uint32_t have = diff, start = this->curpos - this->preview.start; if (have > want) have = want; - lprintf ("%u bytes from preview (which has %u bytes)\n", (unsigned int)have, (unsigned int)this->preview_size); - memcpy (buf, this->preview + this->curpos, have); + lprintf ("%u bytes from preview (which has %u bytes)\n", (unsigned int)have, (unsigned int)this->preview.size); + memcpy (buf, this->preview.buf + start, have); num_bytes += have; want -= have; this->curpos += have; @@ -1056,7 +1060,7 @@ off_t abs_offset; abs_offset = _x_input_seek_preview(this_gen, offset, origin, - &this->curpos, this->contentlength, this->preview_size); + &this->curpos, this->contentlength, this->preview.size); if (abs_offset < 0 && (this->mode & MODE_SEEKABLE)) { @@ -1088,7 +1092,7 @@ sbuf_reset (this); if (this->nbc) { - nbc_close (this->nbc); + xine_nbc_close (this->nbc); this->nbc = NULL; } @@ -1513,9 +1517,9 @@ this->mode |= MODE_HAVE_SBUF; /* skip non-document content */ if ((this->status != 200) && (this->status != 206)) while (this->contentlength > 0) { - ssize_t s = sizeof (this->preview); + ssize_t s = sizeof (this->preview.buf); if ((uint64_t)s > this->contentlength) s = this->contentlength; - s = sbuf_get_bytes (this, this->preview, s); + s = sbuf_get_bytes (this, this->preview.buf, s); if (s <= 0) break; this->contentlength -= s; } @@ -1595,18 +1599,18 @@ lprintf ("end of headers\n"); if (mpegurl_redirect) { - ssize_t l = sbuf_get_bytes (this, this->preview, sizeof (this->preview) - 1); + ssize_t l = sbuf_get_bytes (this, this->preview.buf, sizeof (this->preview.buf) - 1); if (l > 0) { - uint8_t *p = this->preview; + uint8_t *p = this->preview.buf; p[l] = 0; while (p[0] & 0xe0) p++; /* If the newline can't be found, either the 4K buffer is too small, or * more likely something is fuzzy. */ - if (p < this->preview + l) { + if (p < this->preview.buf + l) { *p = 0; - lprintf ("mpegurl pointing to %s\n", (char *)this->preview); - _x_merge_mrl (this->mrl, sizeof (this->mrl), this->mrl, (char *)this->preview); + lprintf ("mpegurl pointing to %s\n", (char *)this->preview.buf); + _x_merge_mrl (this->mrl, sizeof (this->mrl), this->mrl, (char *)this->preview.buf); this->mode |= MODE_AGAIN; } } @@ -1693,20 +1697,20 @@ if (this->curpos > 0) { /* restarting after seek */ - this->preview_size = 0; + this->preview.size = 0; return 1; } /* fill preview buffer */ - this->preview_size = http_plugin_read_int (this, this->preview, MAX_PREVIEW_SIZE); + this->preview.size = http_plugin_read_int (this, this->preview.buf, sizeof (this->preview.buf)); if (this->mode & MODE_NSV) { #define V_NSV (('N' << 24) | ('S' << 16) | ('V' << 8)) int32_t max_bytes = 1 << 20; uint32_t v = 0; - uint8_t *p = this->preview, *e = p + this->preview_size; + uint8_t *p = this->preview.buf, *e = p + this->preview.size; lprintf ("resyncing NSV stream\n"); - while (this->preview_size > 2) { - if ((max_bytes -= this->preview_size) <= 0) + while (this->preview.size > 2) { + if ((max_bytes -= this->preview.size) <= 0) break; while (p < e) { v = (v | *p++) << 8; @@ -1715,11 +1719,11 @@ } if (v == V_NSV) break; - this->preview[0] = e[-2]; - this->preview[1] = e[-1]; - this->preview_size = http_plugin_read_int (this, this->preview + 2, MAX_PREVIEW_SIZE - 2); - p = this->preview + 2; - e = p + this->preview_size; + this->preview.buf[0] = e[-2]; + this->preview.buf[1] = e[-1]; + this->preview.size = http_plugin_read_int (this, this->preview.buf + 2, sizeof (this->preview.buf) - 2); + p = this->preview.buf + 2; + e = p + this->preview.size; } if (v != V_NSV) { xprintf (this->xine, XINE_VERBOSITY_DEBUG, "http: cannot resync NSV stream!\n"); @@ -1728,25 +1732,26 @@ this->fh = -1; return -11; } - this->preview_size = e - p + 3; - if (p - 3 > this->preview) - memmove (this->preview, p - 3, this->preview_size); - if (this->preview_size < MAX_PREVIEW_SIZE) { - int32_t r = http_plugin_read_int (this, this->preview + this->preview_size, MAX_PREVIEW_SIZE - this->preview_size); + this->preview.size = e - p + 3; + if (p - 3 > this->preview.buf) + memmove (this->preview.buf, p - 3, this->preview.size); + if (this->preview.size < (int)sizeof (this->preview.buf)) { + int32_t r = http_plugin_read_int (this, + this->preview.buf + this->preview.size, sizeof (this->preview.buf) - this->preview.size); if (r > 0) - this->preview_size += r; + this->preview.size += r; } lprintf ("NSV stream resynced\n"); } - if (this->preview_size < 0) { - this->preview_size = 0; + if (this->preview.size < 0) { + this->preview.size = 0; xine_log (this->xine, XINE_LOG_MSG, _("input_http: read error %d\n"), errno); _x_tls_deinit (&this->tls); _x_io_tcp_close (this->stream, this->fh); this->fh = -1; return -12; } - lprintf ("preview_size=%d\n", this->preview_size); + lprintf ("preview_size=%d\n", this->preview.size); this->curpos = 0; this->ret = 1; @@ -1778,24 +1783,47 @@ switch (data_type) { case INPUT_OPTIONAL_DATA_PREVIEW: - if (!data || (this->preview_size <= 0)) + if (!data || (this->preview.size <= 0)) break; - memcpy (data, this->preview, this->preview_size); - return this->preview_size; + memcpy (data, this->preview.buf, this->preview.size); + return this->preview.size; case INPUT_OPTIONAL_DATA_SIZED_PREVIEW: - if (!data || (this->preview_size <= 0)) + if (!data || (this->preview.size <= 0)) break; { int want; memcpy (&want, data, sizeof (want)); want = want < 0 ? 0 - : want > this->preview_size ? this->preview_size + : want > this->preview.size ? this->preview.size : want; - memcpy (data, this->preview, want); + memcpy (data, this->preview.buf, want); return want; } + case INPUT_OPTIONAL_DATA_NEW_PREVIEW: + { + uint32_t start = 0; + off_t diff = this->curpos - this->preview.start; + + if (diff == 0) + return INPUT_OPTIONAL_SUCCESS; + if (diff > 0) { + start = diff; + diff = (off_t)this->preview.size - diff; + if (diff > 0) { + memmove (this->preview.buf, this->preview.buf + start, (uint32_t)diff); + start = diff; + } else { + start = 0; + } + } + this->preview.start = this->curpos; + this->preview.size = 0; + this->preview.size = start + http_plugin_read_int (this, this->preview.buf + start, sizeof (this->preview.buf) - start); + } + return INPUT_OPTIONAL_SUCCESS; + case INPUT_OPTIONAL_DATA_MIME_TYPE: *ptr = this->mime_type; /* fall through */ @@ -1822,7 +1850,7 @@ this->mode &= ~(MODE_DONE | MODE_SEEKABLE | MODE_NSV | MODE_LASTFM | MODE_SHOUTCAST); this->shoutcast_interval = 0; this->shoutcast_left = 0; - this->preview_size = 0; + this->preview.size = 0; if ((this->num_msgs < 0) || (this->num_msgs > 8)) this->num_msgs = 8; if (!new_mrl[0]) @@ -1866,7 +1894,8 @@ this->shoutcast_interval = 0; this->shoutcast_left = 0; this->shoutcast_songtitle = NULL; - this->preview_size = 0; + this->preview.start = 0; + this->preview.size = 0; this->url.proto = NULL; this->url.host = NULL; this->url.user = NULL; @@ -1893,7 +1922,7 @@ this->num_msgs = -1; this->stream = stream; this->xine = cls->xine; - this->nbc = stream ? nbc_init (stream) : NULL; + this->nbc = stream ? xine_nbc_init (stream) : NULL; sbuf_init (this); if (cls->head_dump_name && cls->head_dump_name[0]) { diff -r 9e075962b659 src/input/input_mpegdash.c --- a/src/input/input_mpegdash.c Wed Mar 09 14:31:44 2022 +0200 +++ b/src/input/input_mpegdash.c Sat Sep 24 13:04:31 2022 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 the xine project + * Copyright (C) 2021-2022 the xine project * * This file is part of xine, a free video player. * @@ -50,7 +50,6 @@ #include "input_helper.h" #include "group_network.h" #include "multirate_pref.c" -#include "net_buf_ctrl.h" typedef struct { input_class_t input_class; @@ -72,6 +71,7 @@ uint32_t frag_start; /** << frag number offset */ uint32_t frag_duration; /** << timebase units */ uint32_t frag_count; /** << 0 in live mode */ + xine_rats_t fd; /** << shortened frag_duration / timebase */ } mpd_stream_info_t; typedef enum { @@ -110,8 +110,7 @@ struct { pthread_mutex_t mutex; - time_t avail_start, play_start; /** << seconds since 1970 */ - struct timespec play_systime; + struct timespec avail_start, play_start; /** << since 1970 */ int lag; /** pts */ uint32_t type; int init; @@ -187,67 +186,6 @@ return v; } -static time_t mpd_str2time (char *s) { - char buf[256], *tz; - struct tm tm; - time_t ret; - /* Sigh. try to parse something like "1969-12-31T23:59:44Z" or "PT5H30M55S". */ - if (!s) - return (time_t)-1; - - if (((s[0] | 0x20) == 'p') && ((s[1] | 0x20) == 't')) { - ret = 0; - s += 2; - while (1) { - uint32_t v = str2uint32 (&s); - uint32_t z = (*s | 0x20); - if (z == 'h') - ret += 3600u * v; - else if (z == 'm') - ret += 60u * v; - else if (z == 's') - ret += v; - else - break; - s++; - } - return ret; - } - - tm.tm_year = (int)str2uint32 (&s) - 1900; - if (*s++ != '-') - return (time_t)-1; - tm.tm_mon = (int)str2uint32 (&s) - 1; - if (*s++ != '-') - return (time_t)-1; - tm.tm_mday= str2uint32 (&s); - if ((*s++ | 0x20) != 't') - return (time_t)-1; - tm.tm_hour = str2uint32 (&s); - if (*s++ != ':') - return (time_t)-1; - tm.tm_min = str2uint32 (&s); - if (*s++ != ':') - return (time_t)-1; - tm.tm_sec = str2uint32 (&s); - tm.tm_wday = 0; - tm.tm_yday = 0; - tm.tm_isdst = 0; - - tz = getenv ("TZ"); - strlcpy (buf, tz ? tz : "", sizeof (buf)); - setenv ("TZ", "", 1); - tzset (); - ret = mktime (&tm); - if (buf[0]) - setenv ("TZ", buf, 1); - else - unsetenv ("TZ"); - tzset (); - - return ret; -} - static char *mpd_strcasestr (const char *haystack, const char *needle) { const char *n; size_t ln; @@ -374,11 +312,11 @@ if (!this->side_index) { /* main */ char buf[256]; - time_t play_start; - struct timespec ts; + struct timespec play_start = {0, 0}; + xine_rats_t fd; int l; - if (this->sync.avail_start == (time_t)-1) + if (this->sync.avail_start.tv_sec == (time_t)-1) return 0; if (!this->info.timebase || !this->info.frag_duration) return 0; @@ -390,30 +328,33 @@ if (l <= 0) return 0; buf[l] = 0; - play_start = mpd_str2time (buf); - if (play_start == (time_t)-1) + if (xine_ts_from_string (&play_start, buf)) return 0; - ts.tv_sec = 0; - ts.tv_nsec = 0; - xine_gettime (&ts); + xine_gettime (&play_start); this->frag_index = 1; /* heavy magic ;-) */ { - int64_t d = play_start - this->sync.avail_start - this->rewind; - d *= this->info.timebase; - this->frag_num = d / this->info.frag_duration + this->info.frag_start; - this->lag = (d % this->info.frag_duration) * 90000 / this->info.timebase; + int64_t diff; + struct timespec ts = play_start; + xine_ts_sub (&ts, &this->sync.avail_start); + ts.tv_sec -= this->rewind; + fd.num = this->info.frag_duration; + fd.den = this->info.timebase; + xine_rats_shorten (&fd); + diff = xine_ts_to_timebase (&ts, fd.den); + this->frag_num = diff / fd.num + this->info.frag_start; + this->lag = (diff % fd.num) * 90000 / fd.den; } if (this->sync.init) { pthread_mutex_lock (&this->sync.mutex); + this->info.fd = fd; this->sync.play_start = play_start; - this->sync.play_systime = ts; this->sync.lag = this->lag; this->sync.type = this->info.type; pthread_mutex_unlock (&this->sync.mutex); } else { + this->info.fd = fd; this->sync.play_start = play_start; - this->sync.play_systime = ts; this->sync.lag = this->lag; this->sync.type = this->info.type; } @@ -426,26 +367,31 @@ pthread_mutex_lock (&this->sync.mutex); this->sync.avail_start = main_input->sync.avail_start; this->sync.play_start = main_input->sync.play_start; - this->sync.play_systime = main_input->sync.play_systime; this->sync.lag = main_input->sync.lag; this->sync.type = main_input->sync.type; pthread_mutex_unlock (&this->sync.mutex); } else { this->sync.avail_start = main_input->sync.avail_start; this->sync.play_start = main_input->sync.play_start; - this->sync.play_systime = main_input->sync.play_systime; this->sync.lag = main_input->sync.lag; this->sync.type = main_input->sync.type; } - if (this->sync.avail_start == (time_t)-1) + if (this->sync.avail_start.tv_sec == (time_t)-1) return 0; this->frag_index = 1; /* heavy magic ;-) */ { - int64_t d = this->sync.play_start - this->sync.avail_start - this->rewind; - d *= this->info.timebase; - this->frag_num = d / this->info.frag_duration + this->info.frag_start; - this->lag = (d % this->info.frag_duration) * 90000 / this->info.timebase; + int64_t diff; + xine_rats_t fd; + struct timespec ts = this->sync.play_start; + xine_ts_sub (&ts, &this->sync.avail_start); + ts.tv_sec -= this->rewind; + fd.num = this->info.frag_duration; + fd.den = this->info.timebase; + xine_rats_shorten (&fd); + diff = xine_ts_to_timebase (&ts, fd.den); + this->frag_num = diff / fd.num + this->info.frag_start; + this->lag = (diff % fd.num) * 90000 / fd.den; } } @@ -471,12 +417,16 @@ if (!wait) return 2; if (d > 0) { + int64_t diff; int32_t ms; - struct timespec ts = {0, 0}; - xine_gettime (&ts); - ms = (ts.tv_sec - this->sync.play_systime.tv_sec) * 1000; - ms += (ts.tv_nsec - this->sync.play_systime.tv_nsec) / 1000000; - ms = (int64_t)(index - 1) * 1000 * this->info.frag_duration / this->info.timebase - ms; + struct timespec now = {0, 0}, next; + xine_gettime (&now); + diff = (int64_t)(index - 1) * this->info.fd.num * 1000 / this->info.fd.den; + next.tv_sec = diff / 1000; + next.tv_nsec = (diff % 1000) * 1000000; + xine_ts_add (&next, &this->sync.play_start); + xine_ts_sub (&next, &now); + ms = xine_ts_to_timebase (&next, 1000); if ((ms > 0) && (ms < 100000)) { /* save server load and hang up before wait. */ if (this->in1) { @@ -721,8 +671,10 @@ this->seg_base_url = this->base_url; this->time_url = mpd_stree_find (this, "UTCTiming.value", tree_mpd); { + struct timespec ts = {0, 0}; char *s = this->list_buf + mpd_stree_find (this, "availabilityStartTime", tree_mpd); - this->sync.avail_start = mpd_str2time (s); + xine_ts_from_string (&ts, s); + this->sync.avail_start = ts; } { @@ -1089,7 +1041,7 @@ return; if (this->nbc) { - nbc_close (this->nbc); + xine_nbc_close (this->nbc); this->nbc = NULL; } if (this->in1) { @@ -1263,7 +1215,7 @@ free (side_input); return NULL; } - side_input->nbc = nbc_init (side_input->stream); + side_input->nbc = xine_nbc_init (side_input->stream); return &side_input->input_plugin; } @@ -1407,8 +1359,8 @@ this->stream = stream; this->in1 = in1; this->num_sides = 0; - this->sync.avail_start = - this->sync.play_start = (time_t)-1; + this->sync.avail_start.tv_sec = + this->sync.play_start.tv_sec = (time_t)-1; this->sync.refs = 1; xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, @@ -1430,7 +1382,7 @@ this->input_plugin.dispose = mpd_input_dispose; this->input_plugin.input_class = &cls->input_class; - this->nbc = stream ? nbc_init (stream) : NULL; + this->nbc = stream ? xine_nbc_init (stream) : NULL; return &this->input_plugin; } @@ -1471,3 +1423,4 @@ return this; } + diff -r 9e075962b659 src/input/input_stdin_fifo.c --- a/src/input/input_stdin_fifo.c Wed Mar 09 14:31:44 2022 +0200 +++ b/src/input/input_stdin_fifo.c Sat Sep 24 13:04:31 2022 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2000-2021 the xine project + * Copyright (C) 2000-2022 the xine project * * This file is part of xine, a free video player. * @@ -39,9 +39,9 @@ #include <xine/xine_internal.h> #include <xine/xineutils.h> #include <xine/input_plugin.h> -#include "net_buf_ctrl.h" #include "input_helper.h" + #define BUFSIZE 1024 #if defined(WIN32) || defined(__CYGWIN__) @@ -55,7 +55,7 @@ xine_t *xine; xine_stream_t *stream; - nbc_t *nbc; + xine_nbc_t *nbc; char *mrl; int fh; @@ -297,7 +297,7 @@ free (this->ring_buf); if (this->nbc) - nbc_close (this->nbc); + xine_nbc_close (this->nbc); if (this->fh >= 0) { if (this->fh != STDIN_FILENO) { @@ -443,7 +443,7 @@ /* * buffering control */ - this->nbc = nbc_init (stream); + this->nbc = xine_nbc_init (stream); if (!this->nbc) { free (this); return NULL; diff -r 9e075962b659 src/input/multirate_pref.c --- a/src/input/multirate_pref.c Wed Mar 09 14:31:44 2022 +0200 +++ b/src/input/multirate_pref.c Sat Sep 24 13:04:31 2022 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019-2020 the xine project + * Copyright (C) 2019-2022 the xine project * * This file is part of xine, a free video player. * @@ -119,3 +119,28 @@ } return best_n; } + +static inline int multirate_audio_autoselect (multirate_pref_t *pref, multirate_pref_t *list, int list_size) { + multirate_pref_t *item; + int n, best_n, best_b; + if (list_size <= 0) + return -1; + if (list_size == 1) + return 0; + best_n = -1; + best_b = 0x7fffffff; + for (item = list, n = 0; n < list_size; item++, n++) { + int b; + if ((item->video_width | item->video_height)) + continue; + b = item->bitrate - pref->bitrate; + b = b < 0 ? -b : b; + if (item->lang[0] && pref->lang[0] && strcasecmp (item->lang, pref->lang)) + b += 0x40000000; + if (b < best_b) { + best_n = n; + best_b = b; + } + } + return best_n; +} diff -r 9e075962b659 src/input/vcd/vcdio.c --- a/src/input/vcd/vcdio.c Wed Mar 09 14:31:44 2022 +0200 +++ b/src/input/vcd/vcdio.c Sat Sep 24 13:04:31 2022 +0200 @@ -149,6 +149,7 @@ p_vcdplayer->track = (vcdplayer_play_item_info_t *) calloc(p_vcdplayer->i_tracks, sizeof(vcdplayer_play_item_info_t)); + if (p_vcdplayer->track) for (i=0; i<p_vcdplayer->i_tracks; i++) { track_t i_track=i+1; p_vcdplayer->track[i].size @@ -163,6 +164,7 @@ p_vcdplayer->entry = (vcdplayer_play_item_info_t *) calloc(p_vcdplayer->i_entries, sizeof(vcdplayer_play_item_info_t)); + if (p_vcdplayer->entry) for (i=0; i<p_vcdplayer->i_entries; i++) { p_vcdplayer->entry[i].size = vcdinfo_get_entry_sect_count(p_vcdinfo, i); @@ -176,6 +178,7 @@ p_vcdplayer->segment = (vcdplayer_play_item_info_t *) calloc(p_vcdplayer->i_segments, sizeof(vcdplayer_play_item_info_t)); + if (p_vcdplayer->segment) for (i=0; i<p_vcdplayer->i_segments; i++) { p_vcdplayer->segment[i].size = vcdinfo_get_seg_sector_count(p_vcdinfo, i); diff -r 9e075962b659 src/post/visualizations/tdaudioanalyzer.c --- a/src/post/visualizations/tdaudioanalyzer.c Wed Mar 09 14:31:44 2022 +0200 +++ b/src/post/visualizations/tdaudioanalyzer.c Sat Sep 24 13:04:31 2022 +0200 @@ -846,7 +846,7 @@ if (pts) { int offs = (this->ring_put - this->ring_get) & RING_MASK; offs -= this->samples_per_frame >> 1; - pts -= 90000 * offs / (int)port->rate; + pts -= INT64_C(90000) * offs / (int)port->rate; } /* buffer incoming audio */ do { diff -r 9e075962b659 src/video_dec/dav1d.c --- a/src/video_dec/dav1d.c Wed Mar 09 14:31:44 2022 +0200 +++ b/src/video_dec/dav1d.c Sat Sep 24 13:04:31 2022 +0200 @@ -544,11 +544,17 @@ /* multithreading */ ncpu = xine_cpu_count(); +#if DAV1D_API_VERSION_MAJOR > 5 + settings.n_threads = ncpu + 1; + xprintf(stream->xine, XINE_VERBOSITY_DEBUG, LOG_MODULE ": " + "Using %d threads\n", settings.n_threads); +#else settings.n_frame_threads = (ncpu > 8) ? 4 : (ncpu < 2) ? 1 : ncpu/2; settings.n_tile_threads = MAX(1, ncpu - settings.n_frame_threads + 1); xprintf(stream->xine, XINE_VERBOSITY_DEBUG, LOG_MODULE ": " "Using %d frame threads, %d tile threads\n", settings.n_frame_threads, settings.n_tile_threads); +#endif /* dri frame allocator */ settings.allocator.cookie = this; diff -r 9e075962b659 src/video_dec/gdkpixbuf.c --- a/src/video_dec/gdkpixbuf.c Wed Mar 09 14:31:44 2022 +0200 +++ b/src/video_dec/gdkpixbuf.c Sat Sep 24 13:04:31 2022 +0200 @@ -299,6 +299,8 @@ #endif this = (image_decoder_t *) calloc(1, sizeof(image_decoder_t)); + if (!this) + return NULL; this->video_decoder.decode_data = image_decode_data; this->video_decoder.flush = image_flush; diff -r 9e075962b659 src/video_dec/libvdpau/alterh264_decode.c --- a/src/video_dec/libvdpau/alterh264_decode.c Wed Mar 09 14:31:44 2022 +0200 +++ b/src/video_dec/libvdpau/alterh264_decode.c Sat Sep 24 13:04:31 2022 +0200 @@ -1,5 +1,5 @@ /* kate: space-indent on; indent-width 2; mixedindent off; indent-mode cstyle; remove-trailing-space on; - * Copyright (C) 2008-2021 the xine project + * Copyright (C) 2008-2022 the xine project * Copyright (C) 2008 Christophe Thommeret <hftom@free.fr> * * This file is part of xine, a free video player. @@ -87,7 +87,7 @@ : level == VDEC_HW_H264_LOGG_INFO ? XINE_VERBOSITY_DEBUG : /* VDEC_HW_H264_LOGG_DEBUG */ XINE_VERBOSITY_DEBUG + 1; - if (l2 >= this->stream->xine->verbosity) { + if (l2 <= this->stream->xine->verbosity) { va_list va; va_start (va, fmt); @@ -118,7 +118,8 @@ img->pts = frame->pts; img->duration = frame->duration; img->progressive_frame = frame->progressive_frame; - img->bad_frame = frame->bad_frame; + /* try to view small errors as well. */ + img->bad_frame = frame->bad_frame > 1; this->used += 1; if (this->used > 19) { xprintf (this->stream->xine, XINE_VERBOSITY_LOG, @@ -281,7 +282,7 @@ data.aspect = frame->ratio; xine_event_send (this->stream, &event); } - return 1; + return st == VDP_STATUS_OK; } static int vdpau_h264_alter_frame_ready (void *user_data, vdec_hw_h264_frame_t *frame) { diff -r 9e075962b659 src/video_dec/libvdpau/vdec_hw_h264.c --- a/src/video_dec/libvdpau/vdec_hw_h264.c Wed Mar 09 14:31:44 2022 +0200 +++ b/src/video_dec/libvdpau/vdec_hw_h264.c Sat Sep 24 13:04:31 2022 +0200 @@ -437,6 +437,7 @@ _vdec_hw_h264_frame_free (vdec, frame, 0); if (!frame->f.user_data && vdec->frame_new) { vdec->frame_new (vdec->user_data, &frame->f); + frame->f.bad_frame = 0; frame->drawn = 0; vdec->user_frames++; if (vdec->user_frames > (int32_t)vdec->ref_frames_max + 1) @@ -1689,6 +1690,11 @@ } if (lps->slice_type != SLICE_TYPE_I) { + if (vdec->ref_frames_used <= 0) { + vdec->logg (vdec->user_data, VDEC_HW_H264_LOGG_INFO, + LOG_MODULE ": first frame after seek is type %s??\n", + (lps->slice_type == SLICE_TYPE_B) ? "B" : "P"); + } if (prefs < (int)(lps->num_ref_idx_l0_active_minus1 + 1)) bad_frame = 1; if (lps->slice_type == SLICE_TYPE_B) { @@ -1699,9 +1705,11 @@ if (bad_frame) { vdec->logg (vdec->user_data, VDEC_HW_H264_LOGG_ERR, - LOG_MODULE ": Missing refframes, dropping. nrf=%d lo=%d prefs=%d l1=%d brefs=%d type=%d (%d fps)\n", - (int)sps->ref_frames_used, (int)lps->num_ref_idx_l0_active_minus1 + 1, (int)prefs, - (int)lps->num_ref_idx_l1_active_minus1 + 1, (int)brefs, (int)lps->slice_type, fps); + LOG_MODULE ": pts %" PRId64 ": Missing ref frames. refs=%d l0=%d prefs=%d l1=%d brefs=%d type=%s (%d fps)\n", + vdec->seq.pic_pts, sps->ref_frames_used, + (int)lps->num_ref_idx_l0_active_minus1 + 1, prefs, + (int)lps->num_ref_idx_l1_active_minus1 + 1, brefs, + (lps->slice_type == SLICE_TYPE_B) ? "B" : "P", fps); } /* else { @@ -1717,7 +1725,7 @@ return bad_frame; } -static void _vdec_hw_h264_render (vdec_hw_h264_t *vdec, int bad_frame) { +static int _vdec_hw_h264_render (vdec_hw_h264_t *vdec, int bad_frame) { int i, j; vdec_hw_h264_frame_int_t *frame; vdec_hw_h264_sps_t *sps; @@ -1743,10 +1751,10 @@ lps = &vdec->seq.lps; pps = vdec->seq.pps[lps->pps_id]; if (!pps) - return; + return 0; sps = vdec->seq.sps[pps->sps_id]; if (!sps) - return; + return 0; frame->f.profile = sps->profile_idc; frame->f.level = sps->level_idc; @@ -1759,6 +1767,20 @@ vdec->seq.reset = 0; } + /* FIXME: I-frames need not be seek points. They can appear in the + * middle of a group of pictures (GOP), with frames after that + * reference frames before. However, many containers nevertheless + * mark _all_ I-frames as seekable. A seek may thus lead to a + * short period of missing ref frames. + * We can skip decoding here, which means extra artifacts. + * We can try to decode anyway, which may crash. + * We can wait for a real seek point, with a large gap. + * Any ideas? */ + /* + if (bad_frame) + return 0; + */ + vdec->seq.info.field_order_cnt[0] = frame->TopFieldOrderCnt; vdec->seq.info.field_order_cnt[1] = frame->BottomFieldOrderCnt; @@ -1834,19 +1856,28 @@ } frame->f.info = &vdec->seq.info; - if (vdec->frame_render) - vdec->frame_render (vdec->user_data, &frame->f); + i = 1; + if (vdec->frame_render) { + i = vdec->frame_render (vdec->user_data, &frame->f); + if (!i) { + frame->f.bad_frame = 2; + vdec->logg (vdec->user_data, VDEC_HW_H264_LOGG_INFO, + LOG_MODULE ": pts %" PRId64 ": render failure.\n", vdec->seq.pic_pts); + } + } frame->f.info = NULL; + return i; } -static void _vdec_hw_h264_decode_picture (vdec_hw_h264_t *vdec) { +static int _vdec_hw_h264_decode_picture (vdec_hw_h264_t *vdec) { vdec_hw_h264_frame_int_t *cur_frame = &vdec->frames[MAX_REF_FRAMES]; vdec_hw_h264_lps_t *lps = &vdec->seq.lps; + int res; if (cur_frame->missing_header || !vdec->seq.startup_frame) { _vdec_hw_h264_frame_free (vdec, cur_frame, 1); lprintf ("MISSING_HEADER or !startup_frame\n\n"); - return; + return 0; } if (cur_frame->completed && cur_frame->field_pic_flag) { @@ -1863,7 +1894,7 @@ _vdec_hw_h264_dpb_reset (vdec); cur_frame->missing_header = 1; vdec->seq.startup_frame = 0; - return; + return 0; } } @@ -1879,7 +1910,7 @@ _vdec_hw_h264_ref_pic_list_reordering (vdec); lprintf ("............................. slices_count = %d\n", vdec->seq.slices_count); - _vdec_hw_h264_render (vdec, _vdec_hw_h264_check_ref_list (vdec)); + res = _vdec_hw_h264_render (vdec, _vdec_hw_h264_check_ref_list (vdec)); /* _vdec_hw_h264_dec_ref_pic_marking */ _vdec_hw_h264_slice_header_post (vdec); @@ -1905,14 +1936,15 @@ _vdec_hw_h264_frame_free (vdec, cur_frame, 1); lprintf ("\n___________________________________________________________________________________________\n\n"); + return res; } static int _vdec_hw_h264_flush_slices (vdec_hw_h264_t *vdec, uint32_t new_type) { if (vdec->seq.slices_count && ((new_type != vdec->seq.slice_mode) || (vdec->seq.slices_count >= MAX_SLICES))) { - _vdec_hw_h264_decode_picture (vdec); + int res = _vdec_hw_h264_decode_picture (vdec); vdec->seq.slices_count = 0; vdec->seq.slice_mode = new_type; - return 1; + return res; } vdec->seq.slice_mode = new_type; return 0; @@ -2406,4 +2438,3 @@ free (vdec); } - diff -r 9e075962b659 src/video_dec/libvdpau/vdec_hw_h264.h --- a/src/video_dec/libvdpau/vdec_hw_h264.h Wed Mar 09 14:31:44 2022 +0200 +++ b/src/video_dec/libvdpau/vdec_hw_h264.h Sat Sep 24 13:04:31 2022 +0200 @@ -1,6 +1,6 @@ /* kate: tab-indent on; indent-width 2; mixedindent off; indent-mode cstyle; remove-trailing-space on; */ /* - * Copyright (C) 2008-2021 the xine project + * Copyright (C) 2008-2022 the xine project * * This file is part of xine, a free video player. * @@ -47,7 +47,7 @@ #define VDEC_HW_H264_FRAME_BOTTOM_FIELD 2 #define VDEC_HW_H264_FRAME_NEW_SEQ 4 int flags; - int bad_frame; /** "here should have been a frame" */ + int bad_frame; /** 0 (OK), 1 (may be distorted by missing ref frames), 2 (decode error). */ int progressive_frame; int top_field_first; int color_matrix; /** << (MPEG matrix # << 1) | fullrange */ diff -r 9e075962b659 src/video_out/opengl/xine_gl.h --- a/src/video_out/opengl/xine_gl.h Wed Mar 09 14:31:44 2022 +0200 +++ b/src/video_out/opengl/xine_gl.h Sat Sep 24 13:04:31 2022 +0200 @@ -93,15 +93,15 @@ static inline void xine_gl_extensions_load (xine_gl_extensions_t *e, const char *list) { size_t llen; - unsigned char *p; + unsigned char *p, *d; e->list = NULL; e->buf = NULL; if (!list) return; - llen = strlen (list) + 1; - e->buf = malloc (llen); + llen = strlen (list); + e->buf = malloc (llen + 2); /* TJ. I got 298 strings here :-) */ e->list = xine_sarray_new (1024, (xine_sarray_comparator_t)strcmp); if (!e->list || !e->buf) { @@ -110,15 +110,54 @@ } p = e->buf; - memcpy (p, list, llen); - while (*p) { - unsigned char *q = p; - while (*p > ' ') + memcpy (p, list, llen + 1); + /* safe end plug */ + d = p + llen; + memcpy (d, " 0", 2); + while (1) { + unsigned char *q; + /* skip spaces (simple, there should be just 1). */ + while (*p <= ' ') p++; - if (*p) - *p++ = 0; - if (*q) - xine_sarray_add (e->list, q); + /* are we done? */ + if (p >= d) + break; + q = p; + /* find next spc (fast). */ + { + const uint32_t left = (uintptr_t)p & 3; + uint32_t *s = (uint32_t *)(p - left); + static const union { + uint8_t b[4]; + uint32_t v; + } mask[4] = { + {{0xff, 0xff, 0xff, 0xff}}, + {{0x00, 0xff, 0xff, 0xff}}, + {{0x00, 0x00, 0xff, 0xff}}, + {{0x00, 0x00, 0x00, 0xff}}, + }; + static const uint8_t rest[32] = { + 0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, /* big wndian */ + 0, 4, 3, 4, 2, 4, 3, 4, 1, 4, 3, 4, 2, 4, 3, 4 /* little endian */ + }; + const union { + uint32_t v; + uint8_t b[4]; + } endian = {16}; + uint32_t w = (~(*s++)) & mask[left].v; + while (1) { + w = w & 0x80808080 & ((w & 0x7f7f7f7f) + 0x21212121); + if (w) + break; + w = ~(*s++); + } + /* bits 31, 23, 15, 7 -> 3, 2, 1, 0 */ + w = (w * 0x00204081) & 0xffffffff; + w >>= 28; + p = (unsigned char *)s - rest[endian.b[0] + w]; + } + *p++ = 0; + xine_sarray_add (e->list, q); } } diff -r 9e075962b659 src/video_out/video_out_caca.c --- a/src/video_out/video_out_caca.c Wed Mar 09 14:31:44 2022 +0200 +++ b/src/video_out/video_out_caca.c Sat Sep 24 13:04:31 2022 +0200 @@ -44,7 +44,7 @@ # define caca_get_canvas_height cucul_get_canvas_height # define caca_free_canvas cucul_free_canvas # define caca_dither_t cucul_dither_t -# define caca_create_dither define cucul_create_dither +# define caca_create_dither cucul_create_dither # define caca_dither_bitmap cucul_dither_bitmap # define caca_free_dither cucul_free_dither #endif diff -r 9e075962b659 src/video_out/video_out_fb.c --- a/src/video_out/video_out_fb.c Wed Mar 09 14:31:44 2022 +0200 +++ b/src/video_out/video_out_fb.c Sat Sep 24 13:04:31 2022 +0200 @@ -997,6 +997,7 @@ return NULL; this->video_mem_base = MAP_FAILED; + this->xine = class->xine; _x_alphablend_init(&this->alphablend_extra_data, class->xine); @@ -1023,8 +1024,6 @@ } } - this->xine = class->xine; - if(this->fb_fix.line_length) this->fb_bytes_per_line = this->fb_fix.line_length; else @@ -1050,22 +1049,29 @@ xprintf(class->xine, XINE_VERBOSITY_DEBUG, "%s: video mode depth is %d (%d bpp),\n" - " red: %d/%d, green: %d/%d, blue: %d/%d\n", + " red: %d/%d, green: %d/%d, blue: %d/%d\n" + " resolution %dx%x\n", LOG_MODULE, this->depth, this->bpp, this->fb_var.red.length, this->fb_var.red.offset, this->fb_var.green.length, this->fb_var.green.offset, - this->fb_var.blue.length, this->fb_var.blue.offset); + this->fb_var.blue.length, this->fb_var.blue.offset, + this->fb_var.xres, this->fb_var.yres); - if(!setup_yuv2rgb(this, config, &this->fb_var, &this->fb_fix)) + if (!setup_yuv2rgb(this, config, &this->fb_var, &this->fb_fix)) { + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE ": yuv2rgb setup failed\n"); goto error; + } /* mmap whole video memory */ this->mem_size = this->fb_fix.smem_len; this->video_mem_base = mmap(0, this->mem_size, PROT_READ | PROT_WRITE, MAP_SHARED, this->fd, 0); - if (this->video_mem_base == MAP_FAILED) + if (this->video_mem_base == MAP_FAILED) { + xprintf(this->xine, XINE_VERBOSITY_LOG, LOG_MODULE ": " + "mapping frame buffer failed: %s\n", strerror(errno)); goto error; + } return &this->vo_driver; diff -r 9e075962b659 src/video_out/video_out_opengl2.c --- a/src/video_out/video_out_opengl2.c Wed Mar 09 14:31:44 2022 +0200 +++ b/src/video_out/video_out_opengl2.c Sat Sep 24 13:04:31 2022 +0200 @@ -125,6 +125,34 @@ float relw, yuy2_mul, yuy2_div; } opengl2_yuvtex_t; +typedef enum { + SPLINE_CATMULLROM = 0, + SPLINE_COS, + SPLINE_LAST +} opengl2_spline_t; + +typedef enum { + SCALE_SIMPLE = 0, + SCALE_LINEAR, + SCALE_CATMULLROM, + SCALE_COS, + SCALE_LAST +} opengl2_scale_t; + +static const char _opengl2_scale_names[SCALE_LAST][16] = { + [SCALE_SIMPLE] = "Simple", + [SCALE_LINEAR] = "Linear", + [SCALE_CATMULLROM] = "Catmullrom", + [SCALE_COS] = "Cosinus" +}; + +static const float _opengl2_lut_y[SCALE_LAST] = { + [SCALE_SIMPLE] = SPLINE_CATMULLROM + 0.5, + [SCALE_LINEAR] = SPLINE_CATMULLROM + 0.5, + [SCALE_CATMULLROM] = SPLINE_CATMULLROM + 0.5, + [SCALE_COS] = SPLINE_COS + 0.5 +}; + typedef struct { vo_driver_t vo_driver; vo_scale_t sc; @@ -174,7 +202,9 @@ GLuint pass1_texture; GLuint fbo; int pass1_tex_w, pass1_tex_h; - int scale; + int mode_changed, mode_changing, mode1; + opengl2_scale_t mode2; + float lut_y; #define OGL2_BC_LUT 1 #define OGL2_BC_PROG_1 2 #define OGL2_BC_PROG_2 4 @@ -294,8 +324,10 @@ (void)lock; } -static const char * const bicubic_pass1_args[] = {"ARB", "tex", "lut", "spline", NULL}; -static const char *bicubic_pass1_frag= +typedef char opengl2_shader_arg_name_t[8]; + +static const opengl2_shader_arg_name_t bicubic_pass1_args[] = {"ARB", "tex", "lut", "spline", ""}; +static const char bicubic_pass1_frag[] = "#extension GL_ARB_texture_rectangle : enable\n" "uniform sampler2DRect tex, lut;\n" "uniform float spline;\n" @@ -310,8 +342,8 @@ " gl_FragColor = sum;\n" "}\n"; -static const char * const bicubic_pass2_args[] = {"ARB", "tex", "lut", "spline", NULL}; -static const char *bicubic_pass2_frag= +static const opengl2_shader_arg_name_t bicubic_pass2_args[] = {"ARB", "tex", "lut", "spline", ""}; +static const char bicubic_pass2_frag[] = "#extension GL_ARB_texture_rectangle : enable\n" "uniform sampler2DRect tex, lut;\n" "uniform float spline;\n" @@ -327,54 +359,54 @@ "}\n"; #define LUTWIDTH 1000 -#define N_SPLINES 2 -#define CATMULLROM_SPLINE 0 -#define COS_SPLINE 1 -static float compute_cos_spline( float x ) -{ - if ( x < 0.0 ) - x = -x; - return 0.5 * cos( M_PI * x / 2.0 ) + 0.5; +/* TJ. This came out while experimenting with test://y_resolution.bmp :-) + * (0.00 +0.25 2.00) = { 1.0000 0,5971 0,3150 0,1199 0.0000 -0,0526 -0,0533 -0,0269 0.0000 } */ +static double _opengl2_cos_spline (double x) { + if (x < 0.0) + x = -x; + return cos (M_PI * 0.25 * x * (x + 1.0)) * pow (2.0, -2.8 * x); } -static float compute_catmullrom_spline( float x ) -{ - if ( x < 0.0 ) - x = -x; - if ( x < 1.0 ) - return ((9.0 * (x * x * x)) - (15.0 * (x * x)) + 6.0) / 6.0; - if ( x <= 2.0 ) - return ((-3.0 * (x * x * x)) + (15.0 * (x * x)) - (24.0 * x) + 12.0) / 6.0; - return 0.0; +/* (0.00 +0.25 2.00) = { 1.0000 0.8672 0.5625 0,2265 0.0000 -0,0703 -0,0625 -0,0234 0.0000 } */ +static double _opengl2_catmullrom_spline (double x) { + if (x < 0.0) + x = -x; + if (x < 1.0) + return 1.5 * x * x * x - 2.5 * x * x + 1.0; + return -0.5 * x * x * x + 2.5 * x * x - 4.0 * x + 2.0; } +static double (* const _opengl2_spline[SPLINE_LAST]) (double x) = { + [SPLINE_CATMULLROM] = _opengl2_catmullrom_spline, + [SPLINE_COS] = _opengl2_cos_spline +}; + static int create_lut_texture( opengl2_driver_t *that ) { - int i = 0; - float *lut = calloc( sizeof(float) * LUTWIDTH * 4 * N_SPLINES, 1 ); + uint32_t i; + float *lut = calloc( sizeof(float) * LUTWIDTH * 4 * SPLINE_LAST, 1 ); if ( !lut ) return 0; - while ( i < LUTWIDTH ) { - float t, v1, v2, v3, v4, coefsum; - t = (float)i / (float)LUTWIDTH; + for (i = 0; i < LUTWIDTH; i++) { + opengl2_spline_t s; + float *p = lut + i * 4; + double t = (double)i / (double)LUTWIDTH; + + for (s = (opengl2_spline_t)0; s < SPLINE_LAST; s++) { + double v1, v2, v3, v4, coefsum; - v1 = compute_catmullrom_spline( t + 1.0 ); coefsum = v1; - v2 = compute_catmullrom_spline( t ); coefsum += v2; - v3 = compute_catmullrom_spline( t - 1.0 ); coefsum += v3; - v4 = compute_catmullrom_spline( t - 2.0 ); coefsum += v4; - lut[i * 4] = v1 / coefsum; - lut[(i * 4) + 1] = v2 / coefsum; - lut[(i * 4) + 2] = v3 / coefsum; - lut[(i * 4) + 3] = v4 / coefsum; - - lut[(i * 4) + (LUTWIDTH * 4)] = compute_cos_spline( t + 1.0 ); - lut[(i * 4) + (LUTWIDTH * 4) + 1] = compute_cos_spline( t ); - lut[(i * 4) + (LUTWIDTH * 4) + 2] = compute_cos_spline( t - 1.0 ); - lut[(i * 4) + (LUTWIDTH * 4) + 3] = compute_cos_spline( t - 2.0 ); - - ++i; + v1 = _opengl2_spline[s] (t + 1.0); coefsum = v1; + v2 = _opengl2_spline[s] (t); coefsum += v2; + v3 = _opengl2_spline[s] (t - 1.0); coefsum += v3; + v4 = _opengl2_spline[s] (t - 2.0); coefsum += v4; + coefsum = 1.0 / coefsum; + p[(uint32_t)s * LUTWIDTH * 4 + 0] = v1 * coefsum; + p[(uint32_t)s * LUTWIDTH * 4 + 1] = v2 * coefsum; + p[(uint32_t)s * LUTWIDTH * 4 + 2] = v3 * coefsum; + p[(uint32_t)s * LUTWIDTH * 4 + 3] = v4 * coefsum; + } } that->bicubic.lut_texture = 0; @@ -390,14 +422,14 @@ glTexParameteri( GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); glTexParameteri( GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); glTexParameteri( GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); - glTexImage2D( GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA32F, LUTWIDTH, N_SPLINES, 0, GL_RGBA, GL_FLOAT, lut ); + glTexImage2D( GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA32F, LUTWIDTH, SPLINE_LAST, 0, GL_RGBA, GL_FLOAT, lut ); free( lut ); glBindTexture( GL_TEXTURE_RECTANGLE_ARB, 0 ); return 1; } -static const char * const blur_sharpen_args[] = {"ARB", "tex", "mid", "side", "corn", NULL}; -static const char *blur_sharpen_frag= +static const opengl2_shader_arg_name_t blur_sharpen_args[] = {"ARB", "tex", "mid", "side", "corn", ""}; +static const char blur_sharpen_frag[] = "#extension GL_ARB_texture_rectangle : enable\n" "uniform sampler2DRect tex;\n" "uniform float mid, side, corn;\n" @@ -416,8 +448,8 @@ " gl_FragColor = c1 ;\n" "}\n"; -static const char * const yuv420_args[] = {"r_coefs", "g_coefs", "b_coefs", "texY", "texU", "texV", NULL}; -static const char *yuv420_frag = +static const opengl2_shader_arg_name_t yuv420_args[] = {"r_coefs", "g_coefs", "b_coefs", "texY", "texU", "texV", ""}; +static const char yuv420_frag[] = "uniform sampler2D texY, texU, texV;\n" "uniform vec4 r_coefs, g_coefs, b_coefs;\n" "void main(void) {\n" @@ -435,8 +467,8 @@ " gl_FragColor = rgb;\n" "}\n"; -static const char * const yuv420j_args[] = {"r_coefs", "g_coefs", "b_coefs", "texY", "tex_U_V", NULL}; -static const char *yuv420j_frag = +static const opengl2_shader_arg_name_t yuv420j_args[] = {"r_coefs", "g_coefs", "b_coefs", "texY", "tex_U_V", ""}; +static const char yuv420j_frag[] = "uniform sampler2D texY, tex_U_V;\n" "uniform vec4 r_coefs, g_coefs, b_coefs;\n" "void main(void) {\n" @@ -455,51 +487,50 @@ " gl_FragColor = rgb;\n" "}\n"; -static const char * const nv12_args[] = {"r_coefs", "g_coefs", "b_coefs", "texY", "texUV", NULL}; -#define nv12_frag \ - "uniform sampler2D texY, texUV;\n" \ - "uniform vec4 r_coefs, g_coefs, b_coefs;\n" \ - "void main (void) {\n" \ - " vec4 rgb;\n" \ - " vec4 yuv;\n" \ - " vec2 coord = gl_TexCoord[0].xy;\n" \ - " yuv.r = texture2D (texY, coord).r;\n" \ - " yuv.g = texture2D (texUV, coord).r;\n" \ - " yuv.b = texture2D (texUV, coord).%s;\n" \ - " yuv.a = 1.0;\n" \ - " rgb.r = dot( yuv, r_coefs );\n" \ - " rgb.g = dot( yuv, g_coefs );\n" \ - " rgb.b = dot( yuv, b_coefs );\n" \ - " rgb.a = 1.0;\n" \ - " gl_FragColor = rgb;\n" \ - "}\n" +static const opengl2_shader_arg_name_t nv12_args[] = {"r_coefs", "g_coefs", "b_coefs", "texY", "texUV", ""}; +static const char nv12_frag[] = +"uniform sampler2D texY, texUV;\n" +"uniform vec4 r_coefs, g_coefs, b_coefs;\n" +"void main (void) {\n" +" vec4 rgb;\n" +" vec4 yuv;\n" +" vec2 coord = gl_TexCoord[0].xy;\n" +" yuv.r = texture2D (texY, coord).r;\n" +" yuv.g = texture2D (texUV, coord).r;\n" +" yuv.b = texture2D (texUV, coord).$;\n" +" yuv.a = 1.0;\n" +" rgb.r = dot( yuv, r_coefs );\n" +" rgb.g = dot( yuv, g_coefs );\n" +" rgb.b = dot( yuv, b_coefs );\n" +" rgb.a = 1.0;\n" +" gl_FragColor = rgb;\n" +"}\n"; -static const char * const yuv422_args[] = {"r_coefs", "g_coefs", "b_coefs", "texYUV", "yuy2vals", NULL}; -#define yuv422_frag \ - "uniform sampler2D texYUV;\n" \ - "uniform vec4 r_coefs, g_coefs, b_coefs;\n" \ - "uniform vec2 yuy2vals;\n" \ - "void main(void) {\n" \ - " vec4 rgba;\n" \ - " vec4 yuv;\n" \ - " vec4 coord = gl_TexCoord[0].xyxx;\n" \ - " float group_x = floor (coord.x * yuy2vals.x);\n" \ - " coord.z = (group_x + 0.25) * yuy2vals.y;\n" \ - " coord.w = (group_x + 0.75) * yuy2vals.y;\n" \ - " yuv.r = texture2D (texYUV, coord.xy).r;\n" \ - " yuv.g = texture2D (texYUV, coord.zy).%s;\n" \ - " yuv.b = texture2D (texYUV, coord.wy).%s;\n" \ - " yuv.a = 1.0;\n" \ - " rgba.r = dot (yuv, r_coefs);\n" \ - " rgba.g = dot (yuv, g_coefs);\n" \ - " rgba.b = dot (yuv, b_coefs);\n" \ - " rgba.a = 1.0;\n" \ - " gl_FragColor = rgba;\n" \ - "}\n" +static const opengl2_shader_arg_name_t yuv422_args[] = {"r_coefs", "g_coefs", "b_coefs", "texYUV", "yuy2v", ""}; +static const char yuv422_frag[] = +"uniform sampler2D texYUV;\n" +"uniform vec4 r_coefs, g_coefs, b_coefs;\n" +"uniform vec2 yuy2v;\n" +"void main(void) {\n" +" vec4 rgba;\n" +" vec4 yuv;\n" +" vec4 coord = gl_TexCoord[0].xyxx;\n" +" float group_x = floor (coord.x * yuy2v.x);\n" +" coord.z = (group_x + 0.25) * yuy2v.y;\n" +" coord.w = (group_x + 0.75) * yuy2v.y;\n" +" yuv.r = texture2D (texYUV, coord.xy).r;\n" +" yuv.g = texture2D (texYUV, coord.zy).$;\n" +" yuv.b = texture2D (texYUV, coord.wy).$;\n" +" yuv.a = 1.0;\n" +" rgba.r = dot (yuv, r_coefs);\n" +" rgba.g = dot (yuv, g_coefs);\n" +" rgba.b = dot (yuv, b_coefs);\n" +" rgba.a = 1.0;\n" +" gl_FragColor = rgba;\n" +"}\n"; static int opengl2_build_program (opengl2_driver_t *this, - opengl2_program_t *prog, const char *source, const char *name, - const char * const *arg_names) { + opengl2_program_t *prog, const char *source, const char *name, const opengl2_shader_arg_name_t *arg_names) { const char *s = source; GLint length; GLchar *log; @@ -564,11 +595,11 @@ { unsigned int u; - if (!strcmp (arg_names[0], "ARB")) { - for (u = 0; arg_names[u + 1]; u++) + if (!memcmp (arg_names[0], "ARB", 4)) { + for (u = 0; arg_names[u + 1][0]; u++) prog->args[u] = glGetUniformLocationARB (prog->program, arg_names[u + 1]); } else { - for (u = 0; arg_names[u]; u++) + for (u = 0; arg_names[u][0]; u++) prog->args[u] = glGetUniformLocation (prog->program, arg_names[u]); } for (; u < sizeof (prog->args) / sizeof (prog->args[0]); u++) @@ -809,7 +840,7 @@ _x_vo_scale_compute_output_size( &this->sc ); return 1; } - return this->update_csc | this->sharp.changed | this->transform.changed; + return this->update_csc | this->sharp.changed | this->transform.changed | this->bicubic.mode_changed; } @@ -1108,7 +1139,7 @@ glUseProgram (that->bicubic.pass1_program.program); glUniform1i (that->bicubic.pass1_program.args[0], 0); glUniform1i (that->bicubic.pass1_program.args[1], 1); - glUniform1f (that->bicubic.pass1_program.args[2], CATMULLROM_SPLINE); + glUniform1f (that->bicubic.pass1_program.args[2], that->bicubic.lut_y); glBegin (GL_QUADS); glTexCoord2f (info->sx1, info->sy1); glVertex3f ( 0, 0, 0); @@ -1136,7 +1167,7 @@ glUseProgram (that->bicubic.pass2_program.program); glUniform1i (that->bicubic.pass2_program.args[0], 0); glUniform1i (that->bicubic.pass2_program.args[1], 1); - glUniform1f (that->bicubic.pass2_program.args[2], CATMULLROM_SPLINE); + glUniform1f (that->bicubic.pass2_program.args[2], that->bicubic.lut_y); glBegin (GL_QUADS); glTexCoord2f ( 0, 0); glVertex3f (info->dx1, info->dy1, 0); @@ -1172,7 +1203,7 @@ glUseProgram (that->bicubic.pass1_program.program); glUniform1i (that->bicubic.pass1_program.args[0], 0); glUniform1i (that->bicubic.pass1_program.args[1], 1); - glUniform1f (that->bicubic.pass1_program.args[2], CATMULLROM_SPLINE); + glUniform1f (that->bicubic.pass1_program.args[2], that->bicubic.lut_y); glBegin (GL_QUADS); glTexCoord2f (info->sx1, info->sy1); glVertex3f (info->dx1, info->dy1, 0); @@ -1208,7 +1239,7 @@ glUseProgram (that->bicubic.pass2_program.program); glUniform1i (that->bicubic.pass2_program.args[0], 0); glUniform1i (that->bicubic.pass2_program.args[1], 1); - glUniform1f (that->bicubic.pass2_program.args[2], CATMULLROM_SPLINE); + glUniform1f (that->bicubic.pass2_program.args[2], that->bicubic.lut_y); glBegin (GL_QUADS); glTexCoord2f (info->sx1, info->sy1); glVertex3f (info->dx1, info->dy1, 0); @@ -1475,21 +1506,29 @@ } that->transform.changed = 0; - if (that->bicubic.scale) { - if (that->sc.displayed_width != that->sc.output_width) { - if (that->sc.displayed_height != that->sc.output_height) - res = opengl2_draw_video_bicubic (that, &info); - else - res = opengl2_draw_video_cubic_x (that, &info); - } else { - if (that->sc.displayed_height != that->sc.output_height) - res = opengl2_draw_video_cubic_y (that, &info); - else - res = opengl2_draw_video_simple (that, &info); - } + switch (that->bicubic.mode2) { + case SCALE_CATMULLROM: + case SCALE_COS: + if (that->sc.displayed_width != that->sc.output_width) { + if (that->sc.displayed_height != that->sc.output_height) + res = opengl2_draw_video_bicubic (that, &info); + else + res = opengl2_draw_video_cubic_x (that, &info); + } else { + if (that->sc.displayed_height != that->sc.output_height) + res = opengl2_draw_video_cubic_y (that, &info); + else + res = opengl2_draw_video_simple (that, &info); + } + break; + case SCALE_SIMPLE: + res = opengl2_draw_video_simple (that, &info); + break; + default: ; } if (!res) opengl2_draw_video_bilinear (that, &info); + that->bicubic.mode_changed = 0; } // draw unscaled overlays @@ -1733,14 +1772,46 @@ VO_CAP_SHARPNESS; } +static void opengl2_set_bicubic (void *this_gen, xine_cfg_entry_t *entry) { + opengl2_driver_t *this = (opengl2_driver_t *)this_gen; + int mode1 = !!entry->num_value; + if ((this->bicubic.mode1 == mode1) || this->bicubic.mode_changing) + return; + + this->bicubic.mode_changed = 1; + this->bicubic.mode_changing = 1; + this->bicubic.mode1 = mode1; + this->bicubic.mode2 = mode1 ? SCALE_CATMULLROM : SCALE_LINEAR; + this->bicubic.lut_y = _opengl2_lut_y[this->bicubic.mode2]; + this->xine->config->update_num (this->xine->config, "video.output.opengl2_scale_mode", this->bicubic.mode2); + this->bicubic.mode_changing = 0; + + xprintf (this->xine, XINE_VERBOSITY_DEBUG, + LOG_MODULE ": scale mode %s.\n", _opengl2_scale_names[this->bicubic.mode2]); +} -static void opengl2_set_bicubic( void *this_gen, xine_cfg_entry_t *entry ) -{ - opengl2_driver_t *this = (opengl2_driver_t *) this_gen; +static void opengl2_set_scale_mode (void *this_gen, xine_cfg_entry_t *entry) { + opengl2_driver_t *this = (opengl2_driver_t *)this_gen; + opengl2_scale_t mode2 = entry->num_value; + int mode1; + + if ((this->bicubic.mode2 == mode2) || this->bicubic.mode_changing) + return; - this->bicubic.scale = entry->num_value; - xprintf( this->xine, XINE_VERBOSITY_DEBUG, LOG_MODULE ": scale_bicubic=%d\n", this->bicubic.scale); + this->bicubic.mode_changed = 1; + this->bicubic.mode_changing = 1; + this->bicubic.mode2 = mode2; + this->bicubic.lut_y = _opengl2_lut_y[this->bicubic.mode2]; + mode1 = mode2 <= SCALE_LINEAR ? 0 : 1; + if (mode1 != this->bicubic.mode1) { + this->bicubic.mode1 = mode1; + this->xine->config->update_num (this->xine->config, "video.output.opengl2_bicubic_scaling", mode1); + } + this->bicubic.mode_changing = 0; + + xprintf (this->xine, XINE_VERBOSITY_DEBUG, + LOG_MODULE ": scale mode %s.\n", _opengl2_scale_names[this->bicubic.mode2]); } static void opengl2_dispose (vo_driver_t *this_gen) { @@ -1845,6 +1916,8 @@ this->bicubic.pass1_tex_w = 0; this->bicubic.pass1_tex_h = 0; this->bicubic.fbo = 0; + this->bicubic.mode_changed = 0; + this->bicubic.mode_changing = 0; this->ovl_changed = 0; this->num_ovls = 0; this->yuvtex.tex[OGL2_TEX_VIDEO_0] = 0; @@ -1968,17 +2041,26 @@ #define INITHEIGHT 576 do { char buf[1024]; - const char *p2_swizzle = (this->fmt_2p == GL_RG) ? "g" : "a"; + const char p2_swizzle = (this->fmt_2p == GL_RG) ? 'g' : 'a'; + uint32_t u; + if (!opengl2_check_textures_size (this, INITWIDTH, INITHEIGHT, 1)) break; if (!opengl2_build_program (this, &this->csc_shaders[OGL2_cscs_yuv420], yuv420_frag, "yuv420_frag", yuv420_args)) break; if (!opengl2_build_program (this, &this->csc_shaders[OGL2_cscs_yuv420j], yuv420j_frag, "yuv420j_frag", yuv420j_args)) break; - snprintf (buf, sizeof (buf), nv12_frag, p2_swizzle); + memcpy (buf, nv12_frag, sizeof (nv12_frag)); + /* gcc sees these "u" really are compile time constants :-)) */ + u = strchr (nv12_frag, '$') - nv12_frag; + buf[u] = p2_swizzle; if (!opengl2_build_program (this, &this->csc_shaders[OGL2_cscs_nv12], buf, "nv12_frag", nv12_args)) break; - snprintf (buf, sizeof (buf), yuv422_frag, p2_swizzle, p2_swizzle); + memcpy (buf, yuv422_frag, sizeof (yuv422_frag)); + u = strchr (yuv422_frag, '$') - yuv422_frag; + buf[u] = p2_swizzle; + u = strchr (yuv422_frag + u + 1, '$') - yuv422_frag; + buf[u] = p2_swizzle; if (!opengl2_build_program (this, &this->csc_shaders[OGL2_cscs_yuv422], buf, "yuv422_frag", yuv422_args)) break; this->gl->release_current (this->gl); @@ -1989,14 +2071,49 @@ this->contrast = 128; cm_init (this); - if (this->texture_float) { - this->bicubic.scale = config->register_bool (config, - "video.output.opengl2_bicubic_scaling", 0, - _("opengl2: use a bicubic algo to scale the video"), - _("Set to true if you want bicubic scaling.\n\n"), - 10, opengl2_set_bicubic, this); - } else { - this->bicubic.scale = 0; + { + opengl2_scale_t scale_max; + + if (this->texture_float) { + scale_max = SCALE_LAST - 1; + this->bicubic.mode1 = config->register_bool (config, + "video.output.opengl2_bicubic_scaling", 0, + _("opengl2: use a bicubic algo to scale the video"), + _("Set to true if you want bicubic scaling.\n\n"), + 10, opengl2_set_bicubic, this); + } else { + scale_max = SCALE_LINEAR; + this->bicubic.mode1 = 0; + } + this->bicubic.mode2 = config->register_range (config, + "video.output.opengl2_scale_mode", SCALE_LINEAR, 0, scale_max, + _("opengl2: video scale mode"), + _("0: Simple. Very fast, very sharp,\n" + " but also stairsteps, uneven lines, and flickering movement.\n\n" + "1: Linear blending. Fast, very smooth, but also a bit blurry.\n\n" + "2: Catmullrom blending. Very smooth, sharp, but needs fast hardware.\n\n" + "3: Cosinus blending. Smooth, very sharp, but needs fast hardware.\n"), + 10, opengl2_set_scale_mode, this); + if (this->bicubic.mode2 == SCALE_LINEAR) { + if (this->bicubic.mode1) { + this->bicubic.mode_changing = 1; + this->bicubic.mode2 = SCALE_CATMULLROM; + config->update_num (config, "video.output.opengl2_scale_mode", this->bicubic.mode2); + this->bicubic.mode_changing = 0; + } + } else { + int mode1 = this->bicubic.mode2 <= SCALE_LINEAR ? 0 : 1; + + if (this->bicubic.mode1 != mode1) { + this->bicubic.mode_changing = 1; + this->bicubic.mode1 = mode1; + config->update_num (config, "video.output.opengl2_bicubic_scaling", mode1); + this->bicubic.mode_changing = 0; + } + } + this->bicubic.lut_y = _opengl2_lut_y[this->bicubic.mode2]; + xprintf (this->xine, XINE_VERBOSITY_DEBUG, + LOG_MODULE ": scale mode %s.\n", _opengl2_scale_names[this->bicubic.mode2]); } this->hw = _x_hwdec_new(this->xine, &this->vo_driver, class->visual_type, visual_gen, 0); @@ -2032,7 +2149,7 @@ if (gl->make_current (gl)) { xine_gl_extensions_t extensions; - const char *names = glGetString (GL_EXTENSIONS); + const char *names = (const char *)glGetString (GL_EXTENSIONS); xine_gl_extensions_load (&extensions, names); result = xine_gl_extensions_test (&extensions, "GL_ARB_texture_float") ? 2 : 0; @@ -2100,10 +2217,11 @@ /* * exported plugin catalog entry */ - const plugin_info_t xine_plugin_info[] EXPORTED = { /* type, API, "name", version, special_info, init_function */ { PLUGIN_VIDEO_OUT, 22, "opengl2", XINE_VERSION_CODE, &vo_info_opengl2, opengl2_init_class_x11 }, { PLUGIN_VIDEO_OUT, 22, "opengl2", XINE_VERSION_CODE, &vo_info_opengl2_wl, opengl2_init_class_wl }, { PLUGIN_NONE, 0, NULL, 0, NULL, NULL } }; + + diff -r 9e075962b659 src/xine-engine/Makefile.am --- a/src/xine-engine/Makefile.am Wed Mar 09 14:31:44 2022 +0200 +++ b/src/xine-engine/Makefile.am Sat Sep 24 13:04:31 2022 +0200 @@ -33,7 +33,7 @@ video_overlay.c osd.c spu.c scratch.c demux.c vo_scale.c \ xine_interface.c post.c broadcaster.c io_helper.c \ input_rip.c input_cache.c info_helper.c refcounter.c \ - alphablend.c net_buf_ctrl.c builtins.c \ + id3.c alphablend.c net_buf_ctrl.c builtins.c \ xine_private.h libxine_la_DEPENDENCIES = $(XINEUTILS_LIB) $(XDG_BASEDIR_DEPS) \ diff -r 9e075962b659 src/xine-engine/audio_out.c --- a/src/xine-engine/audio_out.c Wed Mar 09 14:31:44 2022 +0200 +++ b/src/xine-engine/audio_out.c Sat Sep 24 13:04:31 2022 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2000-2021 the xine project + * Copyright (C) 2000-2022 the xine project * * This file is part of xine, a free video player. * @@ -2511,6 +2511,11 @@ buf->format.mode = s->stream_info[XINE_STREAM_INFO_AUDIO_MODE]; xine_rwlock_unlock (&s->info_lock); _x_extra_info_merge (buf->extra_info, s->audio_decoder_extra_info); + /* containers like AAC-ADIF cannot provide correct input time + * without doing half the decoders work again. + * if decoder did set pts, use that here. */ + if ((buf->extra_info->input_time == -1) && pts) + buf->extra_info->input_time = pts / 90; buf->vpts = s->s.metronom->got_audio_samples (s->s.metronom, pts, buf->num_frames); if ((s->first_frame.flag >= 2) && !s->video_decoder_plugin) { pthread_mutex_lock (&s->first_frame.lock); diff -r 9e075962b659 src/xine-engine/configfile.c --- a/src/xine-engine/configfile.c Wed Mar 09 14:31:44 2022 +0200 +++ b/src/xine-engine/configfile.c Sat Sep 24 13:04:31 2022 +0200 @@ -67,6 +67,9 @@ cfg_entry_t entry; int *magic; char *internal_key; /** << xine_fast_string_t * */ +#define STRING_BACKLOG_LD 2 + char *string_backlog[(1 << STRING_BACKLOG_LD) + 1]; + uint32_t sb_index; } fat_cfg_entry_t; static void _config_set_fat_entry (fat_cfg_entry_t *entry) { @@ -548,7 +551,7 @@ return 0; } -static size_t config_make_sort_key (char *dest, const char *key, int exp_level) { +static size_t config_make_sort_key (char *dest, const char *key, int *klen, int exp_level) { uint8_t *q = (uint8_t *)dest, *e = q + MAX_SORT_KEY - 7; const uint8_t *p = (const uint8_t *)key, *b; int n; @@ -570,7 +573,7 @@ } else { if (l > (uint32_t)(e - q)) l = e - q; - memcpy (q, b, l); + xine_small_memcpy (q, b, l); q += l; } if (*p) @@ -589,7 +592,7 @@ l = p - b; if (l > (uint32_t)(e - q)) l = e - q; - memcpy (q, b, l); + xine_small_memcpy (q, b, l); q += l; if (*p) p++; @@ -619,10 +622,11 @@ /* entry name */ b = p; - l = strlen ((const char *)b); + l = xine_find_byte ((const char *)b, 0); + *klen = (b - (const uint8_t *)key) + l; if (l > (uint32_t)(e - q)) l = e - q; - memcpy (q, b, l); + xine_small_memcpy (q, b, l); q += l; *q = 0; return q - (uint8_t *)dest; @@ -689,16 +693,20 @@ } #define FIND_ONLY 0x7fffffff -static cfg_entry_t *config_insert (config_values_t *this_gen, const char *key, int exp_level) { +static fat_cfg_entry_t *config_insert (config_values_t *this_gen, const char *key, int exp_level) { fat_config_values_t *this = (fat_config_values_t *)this_gen; very_fat_cfg_entry_t dummy_entry; size_t internal_key_len; fat_cfg_entry_t *entry; - int index, num_entries; + int index, num_entries, klen; + if (!key) + return NULL; + if (!key[0]) + return NULL; _config_set_fat_entry (&dummy_entry.entry); dummy_entry.entry.internal_key = xine_fast_string_init (dummy_entry.buf, sizeof (dummy_entry.buf)); - internal_key_len = config_make_sort_key (dummy_entry.entry.internal_key, key, exp_level); + internal_key_len = config_make_sort_key (dummy_entry.entry.internal_key, key, &klen, exp_level); xine_fast_string_set (dummy_entry.entry.internal_key, NULL, internal_key_len); num_entries = xine_sarray_size (this->key_index); @@ -716,14 +724,19 @@ index = xine_sarray_add (this->key_index, &dummy_entry); if (index >= 0) { cfg_entry_t *e1, *e2; - char *buf; - /* new */ + char *buf, *_key = malloc (klen + 1); + /* new. key should never be NULL or empty. */ + if (!_key) + return NULL; entry = malloc (sizeof (*entry) + internal_key_len + 32); xine_sarray_move_location (this->key_index, entry, index); - if (!entry) + if (!entry) { + free (_key); return NULL; + } + xine_small_memcpy (_key, key, klen + 1); #ifdef HAVE_ZERO_SAFE_MEM - memset (&entry->entry, 0, sizeof (entry->entry)); + memset (entry, 0, sizeof (*entry)); #else entry->entry.num_value = 0; entry->entry.num_default = 0; @@ -738,9 +751,16 @@ entry->entry.description = NULL; entry->entry.callback = NULL; entry->entry.callback_data = NULL; + { + uint32_t u; + + for (u = 0; u < (1 << STRING_BACKLOG_LD) + 1; u++) + entry->string_backlog[u] = NULL; + } #endif + entry->sb_index = 1 << STRING_BACKLOG_LD; entry->entry.config = &this->config; - entry->entry.key = strdup (key); + entry->entry.key = _key; entry->entry.type = XINE_CONFIG_TYPE_UNKNOWN; entry->entry.exp_level = exp_level; _config_set_fat_entry (entry); @@ -775,7 +795,7 @@ entry = xine_sarray_get (this->key_index, index); } } - return &entry->entry; + return entry; } static const char *config_xlate_internal (const char *key, const xine_config_entry_translation_t *trans) @@ -798,7 +818,7 @@ if (!strncmp (key, "decoder.", 8)) { if ((klen > 8 + 9) && !memcmp (key + klen - 9, "_priority", 9) && (klen < _MAX_CFG_KEY + 8 + 9 - 26 - 1)) { memcpy (tmp, "engine.decoder_priorities.", 26); - memcpy (tmp + 26, key + 8, klen - 8 - 9); + xine_small_memcpy (tmp + 26, key + 8, klen - 8 - 9); tmp[26 + klen - 8 - 9] = 0; return tmp; } @@ -817,22 +837,22 @@ char tmp[_MAX_CFG_KEY]; /* try twice at most (second time with translation from old key name) */ - entry = config_insert (this, key, FIND_ONLY); + entry = &(config_insert (this, key, FIND_ONLY))->entry; if (entry) return entry; /* we did not find a match, maybe this is an old config entry name * trying to translate */ - key = config_translate_key (key, tmp, strlen (key)); + key = config_translate_key (key, tmp, xine_find_byte (key, 0)); if (!key) return NULL; - entry = config_insert (this, key, FIND_ONLY); + entry = &(config_insert (this, key, FIND_ONLY))->entry; return entry; } -static char **str_array_dup (const char **from, uint32_t *n) { +static char **str_array_dup (const char *const *from, uint32_t *n) { #ifdef SINGLE_CHUNK_ENUMS uint32_t sizes[257], *sitem, all; - const char **fitem; + const char * const *fitem; char **to, **titem, *q; *n = 0; @@ -843,7 +863,7 @@ sitem = sizes; all = sizeof (char *); while (*fitem && (sitem < sizes + 256)) - all += (*sitem++ = strlen (*fitem++) + 1) + sizeof (char *); + all += (*sitem++ = xine_find_byte (*fitem++, 0) + 1) + sizeof (char *); *sitem = 0; to = malloc (all); if (!to) @@ -862,7 +882,7 @@ *titem = NULL; return to; #else - const char **fitem; + const char * const *fitem; char **to, **titem; *n = 0; @@ -884,6 +904,32 @@ #endif } +static char **str_array_ldup (const char * const *from, const uint32_t *len, uint32_t n) { +#ifdef SINGLE_CHUNK_ENUMS + char **ret, *m; + uint32_t need, u; + need = (n + 1) * (sizeof (char *) + 1); + for (u = 0; u < n; u++) + need += len[u]; + m = malloc (need); + if (!m) + return NULL; + ret = (char **)m; + m += (n + 1) * sizeof (char *); + for (u = 0; u < n; u++) { + ret[u] = m; + xine_small_memcpy (m, from[u], len[u]); m += len[u]; + *m++ = 0; + } + ret[n] = NULL; + return ret; +#else + uint32_t _n; + (void)len; + return str_array_dup (from, &_n); +#endif +} + static void str_array_free (char **a) { char **i; if (!a) @@ -979,16 +1025,27 @@ return value; } -static void config_reset_value(cfg_entry_t *entry) { +static void config_reset_value (fat_cfg_entry_t *entry) { /* NULL is a frequent case. */ - if (entry->str_value) {free (entry->str_value); entry->str_value = NULL;} - if (entry->str_default) {free (entry->str_default); entry->str_default = NULL;} - if (entry->description) {free (entry->description); entry->description = NULL;} - if (entry->help) {free (entry->help); entry->help = NULL;} + if (entry->entry.str_value) {free (entry->entry.str_value); entry->entry.str_value = NULL;} + if (entry->entry.str_default) {free (entry->entry.str_default); entry->entry.str_default = NULL;} + if (entry->entry.description) {free (entry->entry.description); entry->entry.description = NULL;} + if (entry->entry.help) xine_ref_string_unref (&entry->entry.help); + + str_array_free (entry->entry.enum_values); + entry->entry.enum_values = NULL; + entry->entry.num_value = 0; - str_array_free (entry->enum_values); - entry->enum_values = NULL; - entry->num_value = 0; + if (_config_is_fat_entry (entry)) { + uint32_t u; + + for (u = 0; u < (1 << STRING_BACKLOG_LD) + 1; u++) { + if (entry->string_backlog[u]) { + free (entry->string_backlog[u]); + entry->string_backlog[u] = NULL; + } + } + } } static void config_shallow_copy (xine_cfg_entry_t *dest, const cfg_entry_t *src) { @@ -1009,10 +1066,23 @@ dest->callback_data = src->callback_data; } +static void config_new_entry (config_values_t *this, cfg_entry_t *entry) { + /* we created a new entry, call the callback */ + if (this->new_entry_cb) { + xine_cfg_entry_t cb_entry; + /* thread safe extension, private to _x_scan_plugins () + * (.cur is otherwise unused). */ + this->cur = entry; + config_shallow_copy (&cb_entry, entry); + this->new_entry_cb (this->new_entry_cbdata, &cb_entry); + this->cur = NULL; + } +} + static cfg_entry_t *config_register_key (config_values_t *this, const char *key, int exp_level, xine_config_cb_t changed_cb, void *cb_data, const char *description, const char *help) { - cfg_entry_t *entry; + fat_cfg_entry_t *entry; lprintf ("registering %s\n", key); @@ -1024,66 +1094,20 @@ } /* new entry */ - entry->exp_level = exp_level != FIND_ONLY ? exp_level : 0; + entry->entry.exp_level = exp_level != FIND_ONLY ? exp_level : 0; /* add callback */ - _cfg_cb_add (entry, changed_cb, cb_data); + _cfg_cb_add (&entry->entry, changed_cb, cb_data); - /* we created a new entry, call the callback */ - if (this->new_entry_cb) { - xine_cfg_entry_t cb_entry; - /* thread safe extension, private to _x_scan_plugins () - * (.cur is otherwise unused). */ - this->cur = entry; - config_shallow_copy(&cb_entry, entry); - this->new_entry_cb(this->new_entry_cbdata, &cb_entry); - this->cur = NULL; - } - - if (entry->type != XINE_CONFIG_TYPE_UNKNOWN) { + if (entry->entry.type != XINE_CONFIG_TYPE_UNKNOWN) { lprintf ("config entry already registered: %s\n", key); } else { config_reset_value (entry); - entry->description = description ? strdup (description) : NULL; - entry->help = help ? strdup (help) : NULL; + entry->entry.description = description ? strdup (description) : NULL; + entry->entry.help = help ? xine_ref_string_ref (help, -1) : NULL; } - return entry; -} - -static char *config_register_string (config_values_t *this, - const char *key, const char *def_value, const char *description, const char *help, - int exp_level, xine_config_cb_t changed_cb, void *cb_data) { - - cfg_entry_t *entry; - - if (!key || !def_value) { - if (this->xine) - xprintf (this->xine, XINE_VERBOSITY_DEBUG, - "config_register_string: error: config=%p, key=%s, def_value=%s.\n", - (void *)this, key ? key : "NULL", def_value ? def_value : "NULL"); - return NULL; - } - - entry = config_register_key (this, key, exp_level, changed_cb, cb_data, description, help); - if (!entry) - return NULL; - - if (entry->type == XINE_CONFIG_TYPE_UNKNOWN) { - /* set string */ - entry->type = XINE_CONFIG_TYPE_STRING; - entry->num_value = 0; - entry->str_default = strdup (def_value); - if (entry->unknown_value) { - entry->str_value = entry->unknown_value; - entry->unknown_value = NULL; - } else { - entry->str_value = strdup (def_value); - } - } - - pthread_mutex_unlock (&this->config_lock); - return entry->str_value; + return &entry->entry; } static char *config_register_filename (config_values_t *this, @@ -1117,19 +1141,19 @@ entry->str_value = strdup (def_value); } } + config_new_entry (this, entry); pthread_mutex_unlock (&this->config_lock); return entry->str_value; } -static int config_register_num (config_values_t *this, - const char *key, - int def_value, - const char *description, - const char *help, - int exp_level, - xine_config_cb_t changed_cb, - void *cb_data) { +static char *config_register_string (config_values_t *this, const char *key, const char *def_value, + const char *description, const char *help, int exp_level, xine_config_cb_t changed_cb, void *cb_data) { + return config_register_filename (this, key, def_value, 0, description, help, exp_level, changed_cb, cb_data); +} + +static int config_register_num (config_values_t *this, const char *key, int def_value, + const char *description, const char *help, int exp_level, xine_config_cb_t changed_cb, void *cb_data) { cfg_entry_t *entry; @@ -1146,25 +1170,26 @@ return 0; if (entry->type == XINE_CONFIG_TYPE_UNKNOWN) { - const char *val; entry->type = XINE_CONFIG_TYPE_NUM; entry->num_default = def_value; - val = entry->unknown_value; - entry->num_value = val ? xine_str2int32 (&val) : def_value; + entry->range_min = -2147483647 - 1; + entry->range_max = 2147483647; + if (entry->unknown_value) { + const char *val = entry->unknown_value; + entry->num_value = xine_str2int32 (&val); + _x_freep (&entry->unknown_value); + } else { + entry->num_value = def_value; + } } + config_new_entry (this, entry); pthread_mutex_unlock (&this->config_lock); return entry->num_value; } -static int config_register_bool (config_values_t *this, - const char *key, - int def_value, - const char *description, - const char *help, - int exp_level, - xine_config_cb_t changed_cb, - void *cb_data) { +static int config_register_bool (config_values_t *this, const char *key, int def_value, + const char *description, const char *help, int exp_level, xine_config_cb_t changed_cb, void *cb_data) { cfg_entry_t *entry; @@ -1181,26 +1206,28 @@ return 0; if (entry->type == XINE_CONFIG_TYPE_UNKNOWN) { - const char *val; entry->type = XINE_CONFIG_TYPE_BOOL; entry->num_default = def_value; - val = entry->unknown_value; - entry->num_value = val ? xine_str2int32 (&val) : def_value; + entry->range_min = 0; + entry->range_max = 1; + if (entry->unknown_value) { + const char *val = entry->unknown_value; + entry->num_value = xine_str2int32 (&val); + _x_freep (&entry->unknown_value); + } else { + entry->num_value = def_value; + } + entry->num_value = entry->num_value ? 1 : 0; } + config_new_entry (this, entry); pthread_mutex_unlock (&this->config_lock); return entry->num_value; } -static int config_register_range (config_values_t *this, - const char *key, - int def_value, - int min, int max, - const char *description, - const char *help, - int exp_level, - xine_config_cb_t changed_cb, - void *cb_data) { +static int config_register_range (config_values_t *this, const char *key, int def_value, + int min, int max, const char *description, const char *help, int exp_level, + xine_config_cb_t changed_cb, void *cb_data) { cfg_entry_t *entry; @@ -1217,30 +1244,29 @@ return 0; if (entry->type == XINE_CONFIG_TYPE_UNKNOWN) { - const char *val; entry->type = XINE_CONFIG_TYPE_RANGE; entry->num_default = def_value; + if (max < min) + max = min; entry->range_min = min; entry->range_max = max; - val = entry->unknown_value; - entry->num_value = val ? xine_str2int32 (&val) : def_value; - /* validate value (config files can be edited ...) */ - /* Allow default to be out of range. xine-ui uses this for brightness etc. */ - if (entry->num_value != def_value) { - if (entry->num_value > max) { - if (this->xine) - xprintf (this->xine, XINE_VERBOSITY_DEBUG, - "configfile: WARNING: value %d for %s is larger than max (%d)\n", entry->num_value, key, max); - entry->num_value = max; + if (entry->unknown_value) { + const char *val = entry->unknown_value; + entry->num_value = xine_str2int32 (&val); + /* validate value (config files can be edited ...) */ + /* Allow default to be out of range. xine-ui uses this for brightness etc. */ + if (entry->num_value != def_value) { + if (entry->num_value > max) + entry->num_value = max; + else if (entry->num_value < min) + entry->num_value = min; } - if (entry->num_value < min) { - if (this->xine) - xprintf (this->xine, XINE_VERBOSITY_DEBUG, - "configfile: WARNING: value %d for %s is smaller than min (%d)\n", entry->num_value, key, min); - entry->num_value = min; - } + _x_freep (&entry->unknown_value); + } else { + entry->num_value = def_value; } } + config_new_entry (this, entry); pthread_mutex_unlock (&this->config_lock); return entry->num_value; @@ -1271,15 +1297,9 @@ return 0; } -static int config_register_enum (config_values_t *this, - const char *key, - int def_value, - char **values, - const char *description, - const char *help, - int exp_level, - xine_config_cb_t changed_cb, - void *cb_data) { +static int config_register_enum (config_values_t *this, const char *key, + int def_value, char **values, const char *description, const char *help, + int exp_level, xine_config_cb_t changed_cb, void *cb_data) { cfg_entry_t *entry; @@ -1299,16 +1319,18 @@ uint32_t value_count; entry->type = XINE_CONFIG_TYPE_ENUM; entry->num_default = def_value; - if (entry->unknown_value) + if (entry->unknown_value) { entry->num_value = config_parse_enum (entry->unknown_value, (const char **)values); - else + if (entry->num_value < 0) + entry->num_value = def_value; + _x_freep (&entry->unknown_value); + } else { entry->num_value = def_value; + } /* allocate and copy the enum values */ - entry->enum_values = str_array_dup ((const char **)values, &value_count); + entry->enum_values = str_array_dup ((const char * const *)values, &value_count); entry->range_min = 0; entry->range_max = value_count; - if (entry->num_value < 0) - entry->num_value = 0; if (entry->num_value >= (int)value_count) entry->num_value = value_count; } else if (entry->type == XINE_CONFIG_TYPE_ENUM) { @@ -1321,7 +1343,7 @@ } if (*old || *new) { uint32_t value_count; - char **nv = str_array_dup ((const char **)values, &value_count); + char **nv = str_array_dup ((const char * const *)values, &value_count); if (nv) { int found = -1; if ((entry->num_value >=0) && (entry->num_value < entry->range_max)) { @@ -1342,6 +1364,7 @@ } } } + config_new_entry (this, entry); pthread_mutex_unlock (&this->config_lock); return entry->num_value; @@ -1446,20 +1469,31 @@ * as sort of a safe switch protocol there. However, to be really safe, client * needs to lock the config object during any use of the string as well. * Even worse: no callback... + * Doig a private copy inside callback merely moves the thread issue to client side. + * config_lookup_string () is quite safe, but also inefficient as value changes are rare. * Idea #2: keep a full backlog of outdated strings. * Might be misused for flooding the heap. * Idea #3: with callback, park previous string at entry.unknown_value. - * without, park initial string there. */ + * without, park initial string there. + * if we have our nice fat_cfg_entry_t, do both :-) */ if (value != entry->str_value) { - if (entry->str_value) { - if (entry->callback || !entry->unknown_value) { - str_free = entry->unknown_value; - entry->unknown_value = entry->str_value; + fat_cfg_entry_t *e = (fat_cfg_entry_t *)entry; + + if (e->entry.str_value) { + if (_config_is_fat_entry (e)) { + str_free = e->string_backlog[e->sb_index]; + e->string_backlog[e->sb_index] = e->entry.str_value; + e->sb_index = (e->sb_index + 1) & ((1 << STRING_BACKLOG_LD) - 1); } else { - str_free = entry->str_value; + if (e->entry.callback || !e->entry.unknown_value) { + str_free = e->entry.unknown_value; + e->entry.unknown_value = e->entry.str_value; + } else { + str_free = e->entry.str_value; + } } } - entry->str_value = strdup (value); + e->entry.str_value = strdup (value); } break; default: @@ -1530,6 +1564,8 @@ if (!line) break; + if (!line[0]) + continue; /* skip comments */ if (line[0] == '#') @@ -1549,9 +1585,11 @@ continue; } } - - value = strchr (line, ':'); - if (value) { + + line[lsize] = ':'; + value = line + xine_find_byte (line, ':'); + line[lsize] = 0; + if (*value == ':') { cfg_entry_t *entry; size_t klen = value - line; @@ -1561,16 +1599,16 @@ if (version < CONFIG_FILE_VERSION) { /* old config file -> let's see if we have to rename this one */ - entry = config_insert (this, line, FIND_ONLY); + entry = &(config_insert (this, line, FIND_ONLY))->entry; if (!entry) { char tmp[_MAX_CFG_KEY]; const char *key = config_translate_key (line, tmp, klen); if (!key) key = line; /* no translation? fall back on untranslated key */ - entry = config_insert (this, key, 50); + entry = &(config_insert (this, key, 50))->entry; } } else { - entry = config_insert (this, line, 50); + entry = &(config_insert (this, line, 50))->entry; } if (entry) @@ -1596,7 +1634,7 @@ size_t used; int try; - if (!filename || !buf || !bsize) + if (!buf || !bsize) return 0; if (!filename) { buf[0] = 0; @@ -1623,7 +1661,7 @@ if (tbuf[0] == PSEP) { /* absolute link */ used = (size_t)r < bsize ? (size_t)r : bsize - 1; - memcpy (buf, tbuf, used); + xine_small_memcpy (buf, tbuf, used); buf[used] = 0; } else { size_t left; @@ -1632,7 +1670,7 @@ for (p = buf + used; (p > buf) && (p[-1] != PSEP); p--) ; left = bsize - (p - buf); used = (size_t)r < left ? (size_t)r : left - 1; - memcpy (p, tbuf, used); + xine_small_memcpy (p, tbuf, used); p[used] = 0; used += p - buf; } @@ -1749,7 +1787,8 @@ if (q >= e) q = e - 1; *q++ = ':'; - q += strlcpy (q, entry->unknown_value, e - q); + if (entry->unknown_value) + q += strlcpy (q, entry->unknown_value, e - q); if (q >= e) q = e - 1; memcpy (q, "\n\n", 2); q += 2; @@ -1779,7 +1818,8 @@ case XINE_CONFIG_TYPE_STRING: memcpy (q, "# string, default: ", 20); q += 19; - q += strlcpy (q, entry->str_default, e - q); + if (entry->str_default) + q += strlcpy (q, entry->str_default, e - q); if (q >= e) q = e - 1; *q++ = '\n'; @@ -1789,7 +1829,8 @@ if (q >= e) q = e - 1; *q++ = ':'; - q += strlcpy (q, entry->str_value, e - q); + if (entry->str_value) + q += strlcpy (q, entry->str_value, e - q); if (q >= e) q = e - 1; memcpy (q, "\n\n", 2); q += 2; @@ -1879,25 +1920,25 @@ static void config_dispose (config_values_t *this_gen) { fat_config_values_t *this = (fat_config_values_t *)this_gen; - cfg_entry_t *entry, *last; + fat_cfg_entry_t *entry, *last; int n; pthread_mutex_lock (&this->config.config_lock); - entry = this->config.first; + entry = (fat_cfg_entry_t *)this->config.first; lprintf ("dispose\n"); n = 0; while (entry) { last = entry; - entry = entry->next; + entry = (fat_cfg_entry_t *)entry->entry.next; - last->next = NULL; - n += _cfg_cb_clear_report (this->config.xine, last); - _x_freep (&last->key); - _x_freep (&last->unknown_value); + last->entry.next = NULL; + n += _cfg_cb_clear_report (this->config.xine, &last->entry); + _x_freep (&last->entry.key); + _x_freep (&last->entry.unknown_value); - config_reset_value(last); + config_reset_value (last); free (last); } @@ -2052,10 +2093,10 @@ uint8_t *buf1, *buf2, *q; uint32_t total_len, buf_len; - uint32_t key_len = entry->key ? strlen (entry->key) : 0; - uint32_t str_default_len = entry->str_default ? strlen (entry->str_default) : 0; - uint32_t description_len = entry->description ? strlen (entry->description) : 0; - uint32_t help_len = entry->help ? strlen (entry->help) : 0; + uint32_t key_len = entry->key ? xine_find_byte (entry->key, 0) : 0; + uint32_t str_default_len = entry->str_default ? xine_find_byte (entry->str_default, 0) : 0; + uint32_t description_len = entry->description ? xine_find_byte (entry->description, 0) : 0; + uint32_t help_len = entry->help ? xine_ref_string_len (entry->help) : 0; uint32_t value_len[256]; uint32_t value_count, i; char **cur_value; @@ -2069,7 +2110,7 @@ cur_value = entry->enum_values; if (cur_value) { while (*cur_value && (value_count < (sizeof (value_len) / sizeof (value_len[0])))) { - value_len[value_count] = strlen (*cur_value); + value_len[value_count] = xine_find_byte (*cur_value, 0); total_len += 4 + value_len[value_count]; value_count++; cur_value++; @@ -2134,15 +2175,20 @@ char **enum_values; */ - uint8_t *output; + uint8_t buf[2048], *output; if (!value) return NULL; - output = malloc ((strlen (value) * 3 + 3) / 4 + 1); + { + size_t need = (strlen (value) * 3 + 3) / 4 + 1; + output = (need <= sizeof (buf)) ? buf : malloc (need); + } if (output) do { char *enum_values[257]; + uint32_t enum_len[257]; int type, range_min, range_max, exp_level, num_default, num_value; + uint32_t klen, sdlen, dlen, hlen, ylen; char *key, *str_default, *description, *help; uint8_t *p; uint32_t left, value_count, i; @@ -2161,18 +2207,18 @@ num_default = (int32_t)_X_LE_32 (p); p += 4; num_value = (int32_t)_X_LE_32 (p); p += 4; -#define get_string(s) { \ - uint32_t len = _X_LE_32 (p); p[0] = 0; p += 4; \ +#define get_string(s,len) { \ + len = _X_LE_32 (p); p[0] = 0; p += 4; \ if (len > left) \ break; \ left -= len; \ s = (char *)p; p += len; \ } - get_string (key); - get_string (str_default); - get_string (description); - get_string (help); + get_string (key, klen); + get_string (str_default, sdlen); + get_string (description, dlen); + get_string (help, hlen); value_count = _X_LE_32 (p); p[0] = 0; p += 4; if (value_count > 256) @@ -2181,7 +2227,7 @@ break; left -= value_count * 4; for (i = 0; i < value_count; i++) { - get_string (enum_values[i]); + get_string (enum_values[i], enum_len[i]); } if (i < value_count) break; @@ -2210,60 +2256,210 @@ printf (" enum[%2d]: %s\n", i, enum_values[i]); printf ("\n"); #endif - + /* paranoia: user wrappers?? */ + ylen = 0; switch (type) { case XINE_CONFIG_TYPE_STRING: switch (num_value) { case 0: + if (this->register_string == config_register_string) + break; this->register_string (this, key, str_default, description, help, exp_level, NULL, NULL); + ylen = 1; break; default: + if (this->register_filename == config_register_filename) + break; this->register_filename (this, key, str_default, num_value, description, help, exp_level, NULL, NULL); + ylen = 1; break; } break; case XINE_CONFIG_TYPE_RANGE: + if (this->register_range == config_register_range) + break; this->register_range (this, key, num_default, range_min, range_max, description, help, exp_level, NULL, NULL); + ylen = 1; break; case XINE_CONFIG_TYPE_ENUM: + if (this->register_enum == config_register_enum) + break; this->register_enum (this, key, num_default, enum_values, description, help, exp_level, NULL, NULL); + ylen = 1; break; case XINE_CONFIG_TYPE_NUM: + if (this->register_num == config_register_num) + break; this->register_num (this, key, num_default, description, help, exp_level, NULL, NULL); + ylen = 1; break; case XINE_CONFIG_TYPE_BOOL: + if (this->register_bool == config_register_bool) + break; this->register_bool (this, key, num_default, description, help, exp_level, NULL, NULL); + ylen = 1; break; - default: ; + default: + ylen = 1; + } + if (!ylen) { + /* no, lets shortcut all this (especially the double strlen () for those long strings). */ + if (klen) { + fat_cfg_entry_t *entry; + + pthread_mutex_lock (&this->config_lock); + entry = config_insert (this, key, exp_level); + if (entry && (entry->entry.type == XINE_CONFIG_TYPE_UNKNOWN)) { + char *s; + /* new entry */ + entry->entry.type = type; + entry->entry.exp_level = exp_level; + entry->entry.range_min = range_min; + entry->entry.range_max = range_max; + entry->entry.num_default = num_default; + switch (type) { + case XINE_CONFIG_TYPE_STRING: + entry->entry.num_value = num_value; + /* NOTE: some plugins went these to never be NULL. */ + if (entry->entry.unknown_value) { + entry->entry.str_value = entry->entry.unknown_value; + entry->entry.unknown_value = NULL; + } else if ((s = malloc (sdlen + 1)) != NULL) { + entry->entry.str_value = s; + memcpy (s, str_default, sdlen + 1); + } + if ((s = malloc (sdlen + 1)) != NULL) { + entry->entry.str_default = s; + memcpy (s, str_default, sdlen + 1); + } + break; + case XINE_CONFIG_TYPE_ENUM: + if (entry->entry.unknown_value) { + entry->entry.num_value = config_parse_enum (entry->entry.unknown_value, (const char **)enum_values); + if (entry->entry.num_value < 0) + entry->entry.num_value = num_default; + _x_freep (&entry->entry.unknown_value); + } else { + entry->entry.num_value = num_default; + } + /* allocate and copy the enum values */ + entry->entry.enum_values = str_array_ldup ((const char * const *)enum_values, enum_len, value_count); + entry->entry.range_min = 0; + entry->entry.range_max = value_count; + break; + case XINE_CONFIG_TYPE_RANGE: + if (entry->entry.unknown_value) { + const char *val = entry->entry.unknown_value; + entry->entry.num_value = xine_str2int32 (&val); + if (entry->entry.num_value != entry->entry.num_default) { + if (entry->entry.num_value > entry->entry.range_max) + entry->entry.num_value = entry->entry.range_max; + else if (entry->entry.num_value < entry->entry.range_min) + entry->entry.num_value = entry->entry.range_min; + } + _x_freep (&entry->entry.unknown_value); + } else { + entry->entry.num_value = num_default; + } + break; + case XINE_CONFIG_TYPE_NUM: + if (entry->entry.unknown_value) { + const char *val = entry->entry.unknown_value; + entry->entry.num_value = xine_str2int32 (&val); + _x_freep (&entry->entry.unknown_value); + } else { + entry->entry.num_value = num_default; + } + break; + case XINE_CONFIG_TYPE_BOOL: + if (entry->entry.unknown_value) { + const char *val = entry->entry.unknown_value; + entry->entry.num_value = xine_str2int32 (&val); + _x_freep (&entry->entry.unknown_value); + } else { + entry->entry.num_value = num_default; + } + entry->entry.num_value = entry->entry.num_value ? 1 : 0; + break; + default: ; + } + if (dlen && ((s = malloc (dlen + 1)))) { + entry->entry.description = s; + xine_small_memcpy (s, description, dlen + 1); + } + if (hlen) + entry->entry.help = xine_ref_string_ref (help, hlen); + } + /* To make xine plugin cache updates work correctly, + * notify even if this entry is not entirely new. */ + if (entry) + config_new_entry (this, &entry->entry); + pthread_mutex_unlock (&this->config_lock); + } else { + if (this->xine) + xprintf (this->xine, XINE_VERBOSITY_DEBUG, + "config_register_serialized_entry: error: config=%p, key=%s.\n", + (void *)this, key ? key : "NULL"); + } } - key = strdup (key); - free (output); - return key; + str_default = malloc (klen + 1); + if (str_default) + xine_small_memcpy (str_default, key, klen + 1); + if (output != buf) + free (output); + return str_default; } while (0); /* cleanup */ - free (output); + if (output != buf) + free (output); return NULL; } +static unsigned int _config_fat_entry_hash (void *a) { + fat_cfg_entry_t *d = (fat_cfg_entry_t *)a; + const uint8_t *p; + unsigned int hash; + + if (_config_is_fat_entry (d)) { + p = (const uint8_t *)d->internal_key; + hash = *p; + return hash < 15 ? hash : 15; + } + /* should not happen (user manually added entry) */ + /* section name */ + p = (const uint8_t *)d->entry.key; + while (1) { + while (*p > '.') + p++; + if (!*p || (*p == '.')) + break; + p++; + } + hash = config_section_enum ((const uint8_t *)d->entry.key, p - (const uint8_t *)d->entry.key); + return hash ? hash : 15; +} + static int _config_fat_entry_cmp (void *a, void *b) { char buf1[MAX_SORT_KEY + 32], buf2[MAX_SORT_KEY + 32], *fs1, *fs2; fat_cfg_entry_t *d = (fat_cfg_entry_t *)a; fat_cfg_entry_t *e = (fat_cfg_entry_t *)b; + int klen; if (_config_is_fat_entry (d)) { fs1 = d->internal_key; } else { fs1 = xine_fast_string_init (buf1, sizeof (buf1)); - xine_fast_string_set (fs1, NULL, config_make_sort_key (fs1, d->entry.key, d->entry.exp_level)); + xine_fast_string_set (fs1, NULL, config_make_sort_key (fs1, d->entry.key, &klen, d->entry.exp_level)); } if (_config_is_fat_entry (e)) { fs2 = e->internal_key; } else { fs2 = xine_fast_string_init (buf2, sizeof (buf2)); - xine_fast_string_set (fs2, NULL, config_make_sort_key (fs2, e->entry.key, e->entry.exp_level)); + xine_fast_string_set (fs2, NULL, config_make_sort_key (fs2, e->entry.key, &klen, e->entry.exp_level)); } + (void)klen; return xine_fast_string_cmp (fs1, fs2); } @@ -2319,6 +2515,8 @@ this->key_index = xine_sarray_new (1024, _config_fat_entry_cmp); xine_sarray_set_mode (this->key_index, XINE_SARRAY_MODE_UNIQUE); + /* just an optimization attempt :-) */ + xine_sarray_set_hash (this->key_index, _config_fat_entry_hash, 16); return &this->config; } @@ -2379,3 +2577,4 @@ free(key); return handled; } + diff -r 9e075962b659 src/xine-engine/demux.c --- a/src/xine-engine/demux.c Wed Mar 09 14:31:44 2022 +0200 +++ b/src/xine-engine/demux.c Sat Sep 24 13:04:31 2022 +0200 @@ -647,7 +647,8 @@ return 0; } -int _x_demux_read_header (input_plugin_t *input, void *buffer, off_t size) { +int _x_demux_read_stream_header (xine_stream_t *stream, input_plugin_t *input, void *buffer, size_t size) { + xine_stream_private_t *s = (xine_stream_private_t *)stream; int want_size = size; uint32_t caps; @@ -662,11 +663,17 @@ } if (caps & INPUT_CAP_SEEKABLE) { - if (input->seek (input, 0, SEEK_SET) != 0) + int start = 0; + + if (s && (s->id3v2_tag_size >= 0)) + start = s->id3v2_tag_size; + if (input->seek (input, start, SEEK_SET) != start) return 0; want_size = input->read (input, buffer, want_size); - if (input->seek (input, 0, SEEK_SET) != 0) + if (input->seek (input, start, SEEK_SET) != start) return 0; /* no point to continue any further */ + if (want_size <= 0) + return 0; return want_size; } @@ -693,6 +700,10 @@ return 0; } +int _x_demux_read_header (input_plugin_t *input, void *buffer, off_t size) { + return _x_demux_read_stream_header (NULL, input, buffer, size); +} + int _x_demux_check_extension (const char *mrl, const char *extensions){ char *last_dot, *e, *ext_copy, *ext_work; int found = 0; diff -r 9e075962b659 src/xine-engine/id3.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xine-engine/id3.c Sat Sep 24 13:04:31 2022 +0200 @@ -0,0 +1,954 @@ +/* + * Copyright (C) 2000-2022 the xine project + * + * This file is part of xine, a free video player. + * + * xine is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * xine is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * ID3 tag parser + * + * Supported versions: v1, v1.1, v2.2, v2.3, v2.4 + * TODO: + * v2.2: unsynchronize + * v2.3: unsynchronize + * unzip support + * v2.4: unsynchronize + * unzip support + * + * ID3v2 specs: http://www.id3.org/ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> + +#define LOG_MODULE "id3" +#define LOG_VERBOSE +/* +#define LOG +*/ + +#include <xine/xine_internal.h> +#include <xine/xineutils.h> +#include "bswap.h" +#include "../demuxers/id3.h" +#include "xine_private.h" + +#define ID3_GENRE_COUNT (sizeof (id3_genre) / sizeof (id3_genre[0])) +static const char id3_genre[][24] = + {"Blues", "Classic Rock", "Country", "Dance", "Disco", + "Funk", "Grunge", "Hip-Hop", "Jazz", "Metal", + "New Age", "Oldies", "Other", "Pop", "R&B", + "Rap", "Reggae", "Rock", "Techno", "Industrial", + "Alternative", "Ska", "Death Metal", "Pranks", "Soundtrack", + "Euro-Techno", "Ambient", "Trip-Hop", "Vocal", "Jazz+Funk", + "Fusion", "Trance", "Classical", "Instrumental", "Acid", + "House", "Game", "Sound Clip", "Gospel", "Noise", + "AlternRock", "Bass", "Soul", "Punk", "Space", + "Meditative", "Instrumental Pop", "Instrumental Rock", "Ethnic", "Gothic", + "Darkwave", "Techno-Industrial", "Electronic", "Pop-Folk", "Eurodance", + "Dream", "Southern Rock", "Comedy", "Cult", "Gangsta", + "Top 40", "Christian Rap", "Pop/Funk", "Jungle", "Native American", + "Cabaret", "New Wave", "Psychedelic", "Rave", "Showtunes", + "Trailer", "Lo-Fi", "Tribal", "Acid Punk", "Acid Jazz", + "Polka", "Retro", "Musical", "Rock & Roll", "Hard Rock", + "Folk", "Folk-Rock", "National Folk", "Swing", "Fast Fusion", + "Bebob", "Latin", "Revival", "Celtic", "Bluegrass", + "Avantgarde", "Gothic Rock", "Progressive Rock", "Psychedelic Rock", "Symphonic Rock", + "Slow Rock", "Big Band", "Chorus", "Easy Listening", "Acoustic", + "Humour", "Speech", "Chanson", "Opera", "Chamber Music", + "Sonata", "Symphony", "Booty Bass", "Primus", "Porn Groove", + "Satire", "Slow Jam", "Club", "Tango", "Samba", + "Folklore", "Ballad", "Power Ballad", "Rhythmic Soul", "Freestyle", + "Duet", "Punk Rock", "Drum Solo", "A capella", "Euro-House", + "Dance Hall", "Goa", "Drum & Bass", "Club-House", "Hardcore", "Terror", + "Indie", "BritPop", "Negerpunk", "Polsk Punk", "Beat", + "Christian Gangsta Rap", "Heavy Metal", "Black Metal", "Crossover", + "Contemporary Christian", "Christian Rock", "Merengue", "Salsa", + "Thrash Metal", "Anime", "JPop", "Synthpop" }; + +#define ID3_ENCODING_COUNT 4 +static const char id3_encoding[][12] = { + "ISO-8859-1", /* 0x00 */ + "UTF-16", /* 0x01 */ + "UTF-16BE", /* 0x02 */ + "UTF-8"}; /* 0x03 */ + +int id3v1_parse_tag (input_plugin_t *input, xine_stream_t *stream) { + + id3v1_tag_t tag; + char track[4]; + + /* id3v1 */ + if (input->read (input, (char *)&tag, 128) != 128) { + return 0; + } + + if ( (tag.tag[0]=='T') && (tag.tag[1]=='A') && (tag.tag[2]=='G') ) { + + lprintf("id3v1 tag found\n"); + _x_meta_info_n_set(stream, XINE_META_INFO_TITLE, tag.title, 30); + _x_meta_info_n_set(stream, XINE_META_INFO_ARTIST, tag.artist, 30); + _x_meta_info_n_set(stream, XINE_META_INFO_ALBUM, tag.album, 30); + _x_meta_info_n_set(stream, XINE_META_INFO_YEAR, tag.year, 4); + _x_meta_info_n_set(stream, XINE_META_INFO_COMMENT, tag.comment, 30); + + /* check for a track number: ID3v1.1, which is a clever hack on ID3v1 */ + if (tag.comment[28] == 0 && tag.comment[29] != 0) { + snprintf(track, 4, "%d", (unsigned char)tag.comment[29]); + _x_meta_info_set(stream, XINE_META_INFO_TRACK_NUMBER, track); + } + + if (tag.genre < ID3_GENRE_COUNT) { + _x_meta_info_set(stream, XINE_META_INFO_GENRE, id3_genre[tag.genre]); + } + } + return 1; +} + +/* id3v2 "genre" parsing code. what a ugly format ! */ +static int id3v2_parse_genre(char* dest, const char *src, size_t len) { + int state = 0; + char *buf = dest; + unsigned int index = 0; + + while (*src) { + lprintf("state=%d\n", state); + if ((buf - dest) >= (ptrdiff_t)len) + return 0; + + switch (state) { + case 0: + /* initial state */ + if (*src == '(') { + state = 1; + index = 0; + src++; + } else { + *buf = *src; + buf++; src++; + } + break; + case 1: + /* ( */ + if (*src == '(') { + *buf = *src; + buf++; src++; + state = 0; + } else if (*src == 'R') { + src++; + state = 2; + } else if (*src == 'C') { + src++; + state = 3; + } else if ((*src >= '0') && (*src <= '9')) { + index = 10 * index + (*src - '0'); + src++; + } else if (*src == ')') { + if (index < ID3_GENRE_COUNT) { + strncpy(buf, id3_genre[index], len - (buf - dest)); + buf += strlen(id3_genre[index]); + } else { + lprintf("invalid index: %d\n", index); + } + src++; + state = 0; + } else { + lprintf("parsing error\n"); + return 0; + } + break; + case 2: + /* (R */ + if (*src == 'X') { + src++; + state = 4; + } else { + lprintf("parsing error\n"); + return 0; + } + break; + case 3: + /* (C */ + if (*src == 'R') { + strncpy(dest, id3_genre[index], len - (buf - dest)); + buf += strlen(id3_genre[index]); + src++; + state = 5; + } else { + lprintf("parsing error\n"); + return 0; + } + break; + case 4: + /* (RX */ + if (*src == ')') { + strncpy(dest, "Remix", len - (buf - dest)); + buf += strlen("Remix"); + src++; + state = 0; + } else { + lprintf("parsing error\n"); + return 0; + } + break; + case 5: + /* (CR */ + if (*src == ')') { + strncpy(dest, "Cover", len - (buf - dest)); + buf += strlen("Cover"); + src++; + state = 0; + } else { + lprintf("parsing error\n"); + return 0; + } + break; + } + } + if ((buf - dest) >= (ptrdiff_t)len) { + return 0; + } else { + *buf = '\0'; + } + return 1; +} + +static int id3v2_parse_header(input_plugin_t *input, uint32_t id3_signature, + id3v2_header_t *tag_header) { + uint8_t buf[6]; + + tag_header->id = be2me_32(id3_signature); + if (input->read (input, buf, 6) != 6) { + return 0; + } + + tag_header->revision = buf[0]; + tag_header->flags = buf[1]; + tag_header->size = _X_BE_32_synchsafe(&buf[2]); + + lprintf("tag: ID3 v2.%d.%d\n", tag_header->id & 0xFF, tag_header->revision); + lprintf("flags: %d\n", tag_header->flags); + lprintf("size: %zu\n", tag_header->size); + return 1; +} + +/* id3 v2.2 */ + +static int id3v22_parse_frame_header(input_plugin_t *input, + id3v22_frame_header_t *frame_header) { + uint8_t buf[ID3V22_FRAME_HEADER_SIZE]; + int len; + + len = input->read (input, buf, ID3V22_FRAME_HEADER_SIZE); + if (len != ID3V22_FRAME_HEADER_SIZE) { + return 0; + } + + frame_header->id = (buf[0] << 16) + (buf[1] << 8) + buf[2]; + + frame_header->size = _X_BE_24(&buf[3]); + + lprintf("frame: %c%c%c: size: %zu\n", buf[0], buf[1], buf[2], + frame_header->size); + + return 1; +} + +static int id3v22_interp_frame(input_plugin_t *input, + xine_stream_t *stream, + id3v22_frame_header_t *frame_header) { + const size_t bufsize = frame_header->size + 2; + char *buf; + int enc; + + if ( bufsize < 3 ) /* frames has to be _at least_ 1 byte */ + return 0; + buf = malloc(bufsize); + if (!buf) + return 0; + + if (input->read (input, buf, frame_header->size) != (int)frame_header->size) { + lprintf("read error\n"); + free(buf); + return 0; + } + + buf[frame_header->size] = 0; + buf[frame_header->size + 1] = 0; + enc = buf[0]; + if( enc >= ID3_ENCODING_COUNT ) + enc = 0; + + switch (frame_header->id) { + case ( BE_FOURCC(0, 'T', 'C', 'O') ): + { + char tmp[1024]; + + if (id3v2_parse_genre(tmp, buf + 1, sizeof(tmp))) { + _x_meta_info_set(stream, XINE_META_INFO_GENRE, tmp); + } + } + break; + + case ( BE_FOURCC(0, 'T', 'T', '2') ): + _x_meta_info_set_generic(stream, XINE_META_INFO_TITLE, buf + 1, id3_encoding[enc]); + break; + + case ( BE_FOURCC(0, 'T', 'P', '1') ): + _x_meta_info_set_generic(stream, XINE_META_INFO_ARTIST, buf + 1, id3_encoding[enc]); + break; + + case ( BE_FOURCC(0, 'T', 'A', 'L') ): + _x_meta_info_set_generic(stream, XINE_META_INFO_ALBUM, buf + 1, id3_encoding[enc]); + break; + + case ( BE_FOURCC(0, 'T', 'Y', 'E') ): + _x_meta_info_set_generic(stream, XINE_META_INFO_YEAR, buf + 1, id3_encoding[enc]); + break; + + case ( BE_FOURCC(0, 'C', 'O', 'M') ): + if (frame_header->size > 4) + _x_meta_info_set_generic(stream, XINE_META_INFO_COMMENT, buf + 1 + 3, id3_encoding[enc]); + break; + + case ( BE_FOURCC(0, 'T', 'R', 'K') ): + _x_meta_info_set(stream, XINE_META_INFO_TRACK_NUMBER, buf + 1); + break; + + default: + lprintf("unhandled frame\n"); + } + + free(buf); + return 1; +} + + +static int id3v22_parse_tag(input_plugin_t *input, + xine_stream_t *stream, + uint32_t id3_signature) { + id3v2_header_t tag_header; + id3v22_frame_header_t tag_frame_header; + unsigned int pos = 0; + + if (!id3v2_parse_header(input, id3_signature, &tag_header)) { + xprintf(stream->xine, XINE_VERBOSITY_DEBUG, LOG_MODULE ": id3v2_parse_header problem\n"); + return 0; + } + + if (tag_header.flags & ID3V22_ZERO_FLAG) { + /* invalid flags */ + xprintf(stream->xine, XINE_VERBOSITY_DEBUG, + LOG_MODULE ": invalid header flags (%02x)\n", tag_header.flags); + input->seek (input, tag_header.size, SEEK_CUR); + return 0; + } + + if (tag_header.flags & ID3V22_COMPRESS_FLAG) { + /* compressed tag: not supported */ + xprintf(stream->xine, XINE_VERBOSITY_DEBUG, + LOG_MODULE ": compressed tags are not supported\n"); + input->seek (input, tag_header.size, SEEK_CUR); + return 0; + } + + if (tag_header.flags & ID3V22_UNSYNCH_FLAG) { + /* unsynchronized tag: not supported */ + xprintf(stream->xine, XINE_VERBOSITY_DEBUG, + LOG_MODULE ": unsynchronized tags are not supported\n"); + input->seek (input, tag_header.size, SEEK_CUR); + return 0; + } + + /* frame parsing */ + while ((pos + ID3V22_FRAME_HEADER_SIZE) <= tag_header.size) { + + if (!id3v22_parse_frame_header(input, &tag_frame_header)) { + xprintf(stream->xine, XINE_VERBOSITY_DEBUG, + LOG_MODULE ": id3v2_parse_frame_header problem\n"); + return 0; + } + pos += ID3V22_FRAME_HEADER_SIZE; + + if (!tag_frame_header.id) { + /* end of frames, the rest is padding */ + input->seek (input, tag_header.size - pos, SEEK_CUR); + return 1; + } + + if ((pos + tag_frame_header.size) > tag_header.size) { + xprintf(stream->xine, XINE_VERBOSITY_DEBUG, + LOG_MODULE ": invalid frame header\n"); + input->seek (input, tag_header.size - pos, SEEK_CUR); + return 1; + } + + if (!id3v22_interp_frame(input, stream, &tag_frame_header)) { + xprintf(stream->xine, XINE_VERBOSITY_DEBUG, + LOG_MODULE ": invalid frame content\n"); + } + pos += tag_frame_header.size; + } + + return 1; +} + +/* id3 v2.3 */ + +static int id3v23_parse_frame_header(input_plugin_t *input, + id3v23_frame_header_t *frame_header) { + uint8_t buf[ID3V23_FRAME_HEADER_SIZE]; + int len; + + len = input->read (input, buf, ID3V23_FRAME_HEADER_SIZE); + if (len != ID3V23_FRAME_HEADER_SIZE) { + return 0; + } + + frame_header->id = _X_BE_32(buf); + frame_header->size = _X_BE_32(&buf[4]); + frame_header->flags = _X_BE_16(buf + 8); + + lprintf("frame: %c%c%c%c, size: %zu, flags: %X\n", buf[0], buf[1], buf[2], buf[3], + frame_header->size, frame_header->flags); + + return 1; +} + +static int id3v23_parse_frame_ext_header(input_plugin_t *input, + id3v23_frame_ext_header_t *frame_ext_header) { + uint8_t buf[10]; + + if (input->read (input, buf, 4) != 4) { + return 0; + } + frame_ext_header->size = _X_BE_32(&buf[0]); + + if (frame_ext_header->size == 6) { + if (input->read (input, buf, 6) != 6) { + return 0; + } + frame_ext_header->flags = _X_BE_16(buf); + frame_ext_header->padding_size = _X_BE_32(buf + 2); + frame_ext_header->crc = 0; + + } else if (frame_ext_header->size == 10) { + if (input->read (input, buf, 10) != 10) { + return 0; + } + frame_ext_header->flags = _X_BE_16(buf); + frame_ext_header->padding_size = _X_BE_32(buf + 2); + frame_ext_header->crc = _X_BE_32(buf + 6); + + } else { + lprintf("invalid ext header size: %zu\n", frame_ext_header->size); + return 0; + } + + lprintf("ext header: size: %zu, flags: %X, padding_size: %d, crc: %d\n", + frame_ext_header->size, frame_ext_header->flags, + frame_ext_header->padding_size, frame_ext_header->crc); + return 1; +} + +static int id3v23_interp_frame(input_plugin_t *input, + xine_stream_t *stream, + id3v23_frame_header_t *frame_header) { + const size_t bufsize = frame_header->size + 2; + char *buf; + int enc; + + if ( bufsize < 3 ) /* frames has to be _at least_ 1 byte */ + return 0; + buf = malloc(bufsize); + if (!buf) + return 0; + + if (input->read (input, buf, frame_header->size) != (int)frame_header->size) { + lprintf("read error\n"); + free(buf); + return 0; + } + + buf[frame_header->size] = 0; + buf[frame_header->size + 1] = 0; + enc = buf[0]; + if( enc >= ID3_ENCODING_COUNT ) + enc = 0; + + switch (frame_header->id) { + case ( BE_FOURCC('T', 'C', 'O', 'N') ): + { + char tmp[1024]; + + if (id3v2_parse_genre(tmp, buf + 1, sizeof(tmp))) { + _x_meta_info_set(stream, XINE_META_INFO_GENRE, tmp); + } + } + break; + + case ( BE_FOURCC('T', 'I', 'T', '2') ): + _x_meta_info_set_generic(stream, XINE_META_INFO_TITLE, buf + 1, id3_encoding[enc]); + break; + + case ( BE_FOURCC('T', 'P', 'E', '1') ): + _x_meta_info_set_generic(stream, XINE_META_INFO_ARTIST, buf + 1, id3_encoding[enc]); + break; + + case ( BE_FOURCC('T', 'A', 'L', 'B') ): + _x_meta_info_set_generic(stream, XINE_META_INFO_ALBUM, buf + 1, id3_encoding[enc]); + break; + + case ( BE_FOURCC('T', 'Y', 'E', 'R') ): + _x_meta_info_set_generic(stream, XINE_META_INFO_YEAR, buf + 1, id3_encoding[enc]); + break; + + case ( BE_FOURCC('C', 'O', 'M', 'M') ): + if (frame_header->size > 4) + _x_meta_info_set_generic(stream, XINE_META_INFO_COMMENT, buf + 1 + 3, id3_encoding[enc]); + break; + + case ( BE_FOURCC('T', 'R', 'C', 'K') ): + _x_meta_info_set(stream, XINE_META_INFO_TRACK_NUMBER, buf + 1); + break; + + case BE_FOURCC('T', 'P', 'O', 'S'): + _x_meta_info_set(stream, XINE_META_INFO_DISCNUMBER, buf + 1); + break; + + default: + lprintf("unhandled frame\n"); + } + + free(buf); + return 1; +} + +static int id3v23_parse_tag(input_plugin_t *input, + xine_stream_t *stream, + uint32_t id3_signature) { + id3v2_header_t tag_header; + id3v23_frame_header_t tag_frame_header; + id3v23_frame_ext_header_t tag_frame_ext_header; + unsigned int pos = 0; + + if (!id3v2_parse_header(input, id3_signature, &tag_header)) { + xprintf(stream->xine, XINE_VERBOSITY_DEBUG, LOG_MODULE ": id3v2_parse_header problem\n"); + return 0; + } + + if (tag_header.flags & ID3V23_ZERO_FLAG) { + /* invalid flags */ + xprintf(stream->xine, XINE_VERBOSITY_DEBUG, + LOG_MODULE ": invalid header flags (%02x)\n", tag_header.flags); + input->seek (input, tag_header.size, SEEK_CUR); + return 0; + } + + if (tag_header.flags & ID3V23_UNSYNCH_FLAG) { + /* unsynchronized tag: not supported */ + xprintf(stream->xine, XINE_VERBOSITY_DEBUG, + LOG_MODULE ": unsynchronized tags are not supported\n"); + input->seek (input, tag_header.size, SEEK_CUR); + return 0; + } + + if (tag_header.flags & ID3V23_EXT_HEADER_FLAG) { + /* extended header */ + if (!id3v23_parse_frame_ext_header(input, &tag_frame_ext_header)) { + return 0; + } + pos += tag_frame_ext_header.size; + } + + /* frame parsing */ + while ((pos + ID3V23_FRAME_HEADER_SIZE) <= tag_header.size) { + + if (!id3v23_parse_frame_header(input, &tag_frame_header)) { + xprintf(stream->xine, XINE_VERBOSITY_DEBUG, + LOG_MODULE ": id3v2_parse_frame_header problem\n"); + return 0; + } + pos += ID3V23_FRAME_HEADER_SIZE; + + if (!tag_frame_header.id) { + /* end of frames, the rest is padding */ + lprintf("skipping padding %zu bytes\n", tag_header.size - pos); + input->seek (input, tag_header.size - pos, SEEK_CUR); + return 1; + } + + if ((pos + tag_frame_header.size) > tag_header.size) { + xprintf(stream->xine, XINE_VERBOSITY_DEBUG, + LOG_MODULE ": invalid frame header\n"); + input->seek (input, tag_header.size - pos, SEEK_CUR); + return 1; + } + + if (!id3v23_interp_frame(input, stream, &tag_frame_header)) { + xprintf(stream->xine, XINE_VERBOSITY_DEBUG, + LOG_MODULE ": invalid frame content\n"); + } + pos += tag_frame_header.size; + } + + return 1; +} + +/* id3 v2.4 */ + +/* id3v2 "genre" parsing code. what a ugly format ! */ +static int id3v24_parse_genre(char* dest, const char *src, size_t len) { + unsigned int index = 0; + + dest[0] = '\0'; + if (sscanf(src, "%u", &index) == 1) { + if (index < ID3_GENRE_COUNT) { + strlcpy(dest, id3_genre[index], len); + } else { + lprintf("invalid index: %u\n", index); + } + } + return 1; +} + +static int id3v24_parse_frame_header(input_plugin_t *input, + id3v24_frame_header_t *frame_header) { + uint8_t buf[ID3V24_FRAME_HEADER_SIZE]; + int len; + + len = input->read (input, buf, ID3V24_FRAME_HEADER_SIZE); + if (len != ID3V24_FRAME_HEADER_SIZE) { + return 0; + } + + frame_header->id = _X_BE_32(buf); + frame_header->size = _X_BE_32(&buf[4]); + frame_header->flags = _X_BE_16(&buf[8]); + + lprintf("frame: %c%c%c%c, size: %zu, flags: %X\n", buf[0], buf[1], buf[2], buf[3], + frame_header->size, frame_header->flags); + + return 1; +} + +static int id3v24_parse_ext_header(input_plugin_t *input, + id3v24_frame_ext_header_t *frame_ext_header) { + uint8_t buf[5]; + uint8_t flags_size, data_length; + + if (input->read (input, buf, 4) != 4) { + return 0; + } + + frame_ext_header->size = _X_BE_32(&buf[0]); + + if (input->read (input, buf, 2) != 2) { + return 0; + } + + flags_size = buf[0]; + if (flags_size != 1) { + lprintf( + "id3: invalid extended header, flag size must be 1\n"); + return 0; + } + + frame_ext_header->flags = buf[1]; + if (frame_ext_header->flags & ID3V24_EXT_ZERO_FLAG) { + lprintf( + "id3: invalid extended header, unknown flags\n"); + return 0; + } + + /* Update flag */ + if (frame_ext_header->flags & ID3V24_EXT_UPDATE_FLAG) { + if (input->read (input, buf, 1) != 1) { + return 0; + } + data_length = buf[0]; + if (data_length != 0) { + lprintf( + "id3: invalid extended header, unexpected update flag data length\n"); + return 0; + } + } + + /* Crc flag */ + if (frame_ext_header->flags & ID3V24_EXT_CRC_FLAG) { + if (input->read (input, buf, 1) != 1) { + return 0; + } + data_length = buf[0]; + if (data_length != 5) { + lprintf( + "id3: invalid extended header, unexpected crc flag data length\n"); + return 0; + } + if (input->read (input, buf, data_length) == data_length) { + /* ignore crc */ + frame_ext_header->crc = BE_35_synchsafe(buf); + } + } + + /* Restriction flag */ + if (frame_ext_header->flags & ID3V24_EXT_RESTRICTIONS_FLAG) { + if (input->read (input, buf, 1) != 1) { + return 0; + } + data_length = buf[0]; + if (data_length != 1) { + lprintf( + "id3: invalid extended header, unexpected restriction flag data length\n"); + return 0; + } + if (input->read (input, buf, data_length) != data_length) { + return 0; + } + /* ignore restrictions */ + frame_ext_header->restrictions = buf[0]; + } + + lprintf("ext header: size: %zu, flags: %X, crc: %d, restrictions: %8X\n", + frame_ext_header->size, frame_ext_header->flags, + (frame_ext_header->flags & ID3V24_EXT_CRC_FLAG) ? frame_ext_header->crc : 0, + (frame_ext_header->flags & ID3V24_EXT_RESTRICTIONS_FLAG) ? frame_ext_header->restrictions : 0); + return 1; +} + +static int id3v24_interp_frame(input_plugin_t *input, + xine_stream_t *stream, + id3v24_frame_header_t *frame_header) { + const size_t bufsize = frame_header->size + 2; + char *buf; + int enc; + + if ( bufsize < 3 ) /* frames has to be _at least_ 1 byte */ + return 0; + + buf = malloc(bufsize); + if (!buf) + return 0; + + if (input->read (input, buf, frame_header->size) != (int)frame_header->size) { + lprintf("read error\n"); + free(buf); + return 0; + } + + buf[frame_header->size] = 0; + buf[frame_header->size + 1] = 0; + enc = buf[0]; + if( enc >= ID3_ENCODING_COUNT ) + enc = 0; + + lprintf("data: %s\n", buf+1); + + switch (frame_header->id) { + case ( BE_FOURCC('T', 'C', 'O', 'N') ): + { + char tmp[1024]; + + if (id3v24_parse_genre(tmp, buf + 1, sizeof(tmp))) { + _x_meta_info_set(stream, XINE_META_INFO_GENRE, tmp); + } + } + break; + + case ( BE_FOURCC('T', 'I', 'T', '2') ): + _x_meta_info_set_generic(stream, XINE_META_INFO_TITLE, buf + 1, id3_encoding[enc]); + break; + + case ( BE_FOURCC('T', 'P', 'E', '1') ): + _x_meta_info_set_generic(stream, XINE_META_INFO_ARTIST, buf + 1, id3_encoding[enc]); + break; + + case ( BE_FOURCC('T', 'A', 'L', 'B') ): + _x_meta_info_set_generic(stream, XINE_META_INFO_ALBUM, buf + 1, id3_encoding[enc]); + break; + + case ( BE_FOURCC('T', 'Y', 'E', 'R') ): + case ( BE_FOURCC('T', 'D', 'R', 'C') ): + _x_meta_info_set_generic(stream, XINE_META_INFO_YEAR, buf + 1, id3_encoding[enc]); + break; + + case ( BE_FOURCC('C', 'O', 'M', 'M') ): + if (frame_header->size > 4) + _x_meta_info_set_generic(stream, XINE_META_INFO_COMMENT, buf + 1 + 3, id3_encoding[enc]); + break; + + case ( BE_FOURCC('T', 'R', 'C', 'K') ): + _x_meta_info_set(stream, XINE_META_INFO_TRACK_NUMBER, buf + 1); + break; + + case BE_FOURCC('T', 'P', 'O', 'S'): + _x_meta_info_set(stream, XINE_META_INFO_DISCNUMBER, buf + 1); + break; + + default: + lprintf("unhandled frame\n"); + } + + free(buf); + return 1; +} + +static int id3v24_parse_tag(input_plugin_t *input, + xine_stream_t *stream, + uint32_t id3_signature) { + id3v2_header_t tag_header; + id3v24_frame_header_t tag_frame_header; + id3v24_frame_ext_header_t tag_frame_ext_header; + unsigned int pos = 0; + + if (!id3v2_parse_header(input, id3_signature, &tag_header)) { + xprintf(stream->xine, XINE_VERBOSITY_DEBUG, LOG_MODULE ": id3v2_parse_header problem\n"); + return 0; + } + + if (tag_header.flags & ID3V24_ZERO_FLAG) { + /* invalid flags */ + xprintf(stream->xine, XINE_VERBOSITY_DEBUG, + LOG_MODULE ": invalid header flags (%02x)\n", tag_header.flags); + input->seek (input, tag_header.size, SEEK_CUR); + return 0; + } + + if (tag_header.flags & ID3V24_UNSYNCH_FLAG) { + /* it just means that all frames must be flagged UNSYNCH */ + } + + if (tag_header.flags & ID3V24_EXPERIMENTAL_FLAG) { + /* it just means that the tag is in a experimental stage */ + } + + if (tag_header.flags & ID3V24_EXT_HEADER_FLAG) { + /* extended header */ + if (!id3v24_parse_ext_header(input, &tag_frame_ext_header)) { + return 0; + } + pos += tag_frame_ext_header.size; + } + + /* frame parsing */ + while ((pos + ID3V24_FRAME_HEADER_SIZE) <= tag_header.size) { + + if (!id3v24_parse_frame_header(input, &tag_frame_header)) { + xprintf(stream->xine, XINE_VERBOSITY_DEBUG, + LOG_MODULE ": id3v2_parse_frame_header problem\n"); + return 0; + } + pos += ID3V24_FRAME_HEADER_SIZE; + + if (!tag_frame_header.id) { + /* end of frames, the rest is padding */ + input->seek (input, tag_header.size - pos, SEEK_CUR); + return 1; + } + + if ((pos + tag_frame_header.size) > tag_header.size) { + xprintf(stream->xine, XINE_VERBOSITY_DEBUG, + LOG_MODULE ": invalid frame header\n"); + input->seek (input, tag_header.size - pos, SEEK_CUR); + return 1; + } + + if (!id3v24_interp_frame(input, stream, &tag_frame_header)) { + xprintf(stream->xine, XINE_VERBOSITY_DEBUG, + LOG_MODULE ": invalid frame content\n"); + } + pos += tag_frame_header.size; + } + + if (tag_header.flags & ID3V24_FOOTER_FLAG) { + /* ignore footer */ + input->seek (input, ID3V24_FOOTER_SIZE, SEEK_CUR); + } + + return 1; +} + +int id3v2_parse_tag(input_plugin_t *input, + xine_stream_t *stream, + uint32_t id3_signature) { + _x_assert((id3_signature & ID3V2X_MASK) == ID3V2X_TAG); + + switch(id3_signature) { + case ID3V22_TAG: + xprintf(stream->xine, XINE_VERBOSITY_LOG, LOG_MODULE ": ID3V2.2 tag\n"); + return id3v22_parse_tag(input, stream, id3_signature); + + case ID3V23_TAG: + xprintf(stream->xine, XINE_VERBOSITY_LOG, LOG_MODULE ": ID3V2.3 tag\n"); + return id3v23_parse_tag(input, stream, id3_signature); + + case ID3V24_TAG: + xprintf(stream->xine, XINE_VERBOSITY_LOG, LOG_MODULE ": ID3V2.4 tag\n"); + return id3v24_parse_tag(input, stream, id3_signature); + + default: + xprintf(stream->xine, XINE_VERBOSITY_LOG, LOG_MODULE ": Unknown ID3v2 signature: 0x%08x.\n", be2me_32(id3_signature)); + } + + return 0; +} + +/* NOTE: leading id3v2 tags may be large (album artwork image and such). + * make sure to read it just once, especially with non seekable input. + * also, redirect file type detection (demux probing) behind the tag, + * and dont rely on MAX_PREVIEW_SIZE being big enough. */ +int xine_parse_id3v2_tag (xine_stream_t *stream, input_plugin_t *input) { + xine_stream_private_t *s = (xine_stream_private_t *)stream; + uint32_t signature; + int r; + + if (!s) + return 0; + + if (!input) { + input = s->s.input_plugin; + if (!input) + return 0; + } + + if (s->id3v2_tag_size >= 0) { + input->seek (input, s->id3v2_tag_size, SEEK_SET); + return s->id3v2_tag_size; + } + + if (_x_demux_read_header (input, &signature, 4) != 4) + return 0; + if (!id3v2_istag (signature)) { + s->id3v2_tag_size = 0; + return 0; + } + + if (input->seek (input, 4, SEEK_SET) != 4) + return 0; + id3v2_parse_tag (input, &s->s, signature); + s->id3v2_tag_size = input->get_current_pos (input); + r = INPUT_OPTIONAL_UNSUPPORTED; + if (input->get_capabilities (input) & (INPUT_CAP_PREVIEW | INPUT_CAP_SIZED_PREVIEW)) + r = input->get_optional_data (input, NULL, INPUT_OPTIONAL_DATA_NEW_PREVIEW); + + xprintf (stream->xine, XINE_VERBOSITY_DEBUG, + LOG_MODULE ": initial ID3v2 tag (%p, %d bytes)%s.\n", + (void *)stream, s->id3v2_tag_size, + (r == INPUT_OPTIONAL_SUCCESS) ? ", new preview generated" : ""); + return s->id3v2_tag_size; +} diff -r 9e075962b659 src/xine-engine/input_rip.c --- a/src/xine-engine/input_rip.c Wed Mar 09 14:31:44 2022 +0200 +++ b/src/xine-engine/input_rip.c Sat Sep 24 13:04:31 2022 +0200 @@ -231,7 +231,7 @@ lprintf("reading %"PRId64" bytes (curpos = %"PRId64", savepos = %"PRId64")\n", len, this->curpos, this->savepos); /* bail out on bogus args */ - if (!this || !buf || (len < 0)) + if (!buf || (len < 0)) return -1; if (len == 0) return 0; @@ -410,7 +410,7 @@ lprintf("reading %"PRId64" bytes (curpos = %"PRId64", savepos = %"PRId64") (block)\n", len, this->curpos, this->savepos); /* bail out on bogus args */ - if (!this || !fifo || (len <= 0)) + if (!fifo || (len <= 0)) return NULL; left = len; diff -r 9e075962b659 src/xine-engine/load_plugins.c --- a/src/xine-engine/load_plugins.c Wed Mar 09 14:31:44 2022 +0200 +++ b/src/xine-engine/load_plugins.c Sat Sep 24 13:04:31 2022 +0200 @@ -106,8 +106,10 @@ #endif #endif /* 0 */ -#define CACHE_CATALOG_VERSION 4 -#define CACHE_CATALOG_VERSION_STR "4" +#define CACHE_CATALOG_VERSION 5 +#define CACHE_CATALOG_VERSION_STR "5" + +#define MAX_DUPL_CFG_ENTRIES 256 #define __Max(a,b) ((a) > (b) ? (a) : (b)) static const uint8_t plugin_iface_versions[__Max(PLUGIN_TYPE_MAX, PLUGIN_XINE_MODULE) + 1] = { @@ -137,6 +139,8 @@ plugin_info_t info[2]; all_info_t ainfo; plugin_file_t file; +#define FAT_NODE_FLAG_PROBE_CLASS 1 + uint32_t flags; struct fat_node_st *nextplugin, *lastplugin; xine_t *xine; uint32_t supported_types[1]; @@ -168,6 +172,7 @@ node->info[1].type = 0; node->ainfo.decoder_info.supported_types = NULL; node->ainfo.decoder_info.priority = 0; + node->flags = 0; node->file.filename = NULL; node->file.filesize = 0; node->file.filemtime = 0; @@ -289,12 +294,10 @@ if (type == PLUGIN_VIDEO_OUT) { for (list_id = 0, i = 0; list_id < list_size; list_id++) { plugin_node_t *node = xine_sarray_get (list, list_id); - if (node->info) { - const vo_info_t *info = (const vo_info_t *)node->info->special_info; - if (info) { - if (!(mask & ((uint64_t)1 << info->visual_type))) - continue; - } + const vo_info_t *info = (const vo_info_t *)node->info->special_info; + if (info) { + if (!(mask & ((uint64_t)1 << info->visual_type))) + continue; } /* add only unique ids to the list */ if (xine_sarray_add (a, (void *)node->info->id) >= 0) @@ -491,8 +494,7 @@ return entry; } -static int _insert_node (xine_t *this, plugin_file_t *file, fat_node_t *node_cache, const plugin_info_t *info) { - +static int _insert_node (xine_t *this, plugin_file_t *file, fat_node_t *node_cache, const plugin_info_t *info, uint32_t flags) { fat_node_t *entry; const all_info_t *ainfo; unsigned int num_supported_types = 0; @@ -571,7 +573,7 @@ if (node_cache) { entry = node_cache; } else { - size_t idlen = strlen (info->id) + 1; + size_t idlen = xine_find_byte (info->id, 0) + 1; char *q; entry = malloc (sizeof (*entry) + num_supported_types * sizeof (uint32_t) + idlen); if (!entry) @@ -579,6 +581,7 @@ _fat_node_init (entry); entry->node.info = &entry->info[0]; entry->info[0] = *info; + entry->flags = flags; q = (char *)entry + sizeof (*entry) + num_supported_types * sizeof (uint32_t); entry->info[0].id = q; xine_small_memcpy (q, info->id, idlen); @@ -593,10 +596,12 @@ case PLUGIN_VIDEO_OUT: entry->node.priority = entry->ainfo.vo_info.priority = ainfo->vo_info.priority; entry->ainfo.vo_info.visual_type = ainfo->vo_info.visual_type; + entry->flags &= ~FAT_NODE_FLAG_PROBE_CLASS; break; case PLUGIN_AUDIO_OUT: entry->node.priority = entry->ainfo.ao_info.priority = ainfo->ao_info.priority; + entry->flags &= ~FAT_NODE_FLAG_PROBE_CLASS; break; case PLUGIN_AUDIO_DECODER: @@ -609,15 +614,14 @@ { /* cnfig does dup all strings. no need to keep them here. */ + xine_private_t *_xine = (xine_private_t *)this; char key[128], desc[256]; int user_prio; - snprintf (key, sizeof (key) - 1, "engine.decoder_priorities.%s", info->id); + memcpy (key, "engine.decoder_priorities.", 26); + strlcpy (key + 26, info->id, sizeof (key) - 26); snprintf (desc, sizeof (desc) - 1, _("priority for %s decoder"), info->id); user_prio = this->config->register_num (this->config, key, 0, desc, - _("The priority provides a ranking in case some media " - "can be handled by more than one decoder.\n" - "A priority of 0 enables the decoder's default priority."), 20, - _decoder_priority_cb, entry); + _xine->strings.decoder_pri_help, 20, _decoder_priority_cb, entry); /* reset priority on old config files */ if (this->config->current_version < 1) { this->config->update_num (this->config, key, 0); @@ -657,9 +661,11 @@ entry->node.priority = entry->ainfo.input_info.priority = 0; } break; + case PLUGIN_XINE_MODULE: entry->node.priority = ainfo->module_info.priority; entry->ainfo.module_info = ainfo->module_info; + entry->flags &= ~FAT_NODE_FLAG_PROBE_CLASS; break; } entry->info[0].special_info = &entry->ainfo; @@ -737,7 +743,7 @@ } static void _register_plugins_internal (xine_t *this, plugin_file_t *file, - fat_node_t *node_cache, const plugin_info_t *info) { + fat_node_t *node_cache, const plugin_info_t *info, uint32_t flags) { /* user supplied xine_register_plugins () */ static const char * const st_names[10] = { "user/none", @@ -784,9 +790,9 @@ fn = names[n]; } xine_log (this, XINE_LOG_PLUGIN, _("load_plugins: plugin %s:%s found\n"), fn, info->id); - status = _insert_node (this, file, node_cache, info); + status = _insert_node (this, file, node_cache, info, flags); /* get next info */ - if( file && !file->lib_handle ) { + if (file && !file->lib_handle) { lprintf("get cached info\n"); if (status) free (node_cache); @@ -800,7 +806,7 @@ void xine_register_plugins(xine_t *self, const plugin_info_t *info) { if (self) - _register_plugins_internal (self, NULL, NULL, info); + _register_plugins_internal (self, NULL, NULL, info, 0); } /* @@ -930,7 +936,7 @@ file = _insert_file (this->plugin_catalog->file_list, path, &statbuf, lib, q - path); if (file) { - _register_plugins_internal (this, file, fatn_found, info); + _register_plugins_internal (this, file, fatn_found, info, fatn_found ? 0 : FAT_NODE_FLAG_PROBE_CLASS); } else { if (lib != NULL) dlclose(lib); @@ -998,6 +1004,7 @@ typedef struct { config_values_t *v; plugin_node_t *node; + cfg_entry_t *cfg_entry; } new_entry_data_t; #else static void _attach_entry_to_node (plugin_node_t *node, char *key) { @@ -1020,7 +1027,7 @@ if (!d->node->config_entry_list) d->node->config_entry_list = xine_list_new (); if (d->node->config_entry_list && d->v->cur && !xine_list_find (d->node->config_entry_list, d->v->cur)) - xine_list_push_back (d->node->config_entry_list, d->v->cur); + d->cfg_entry = d->v->cur, xine_list_push_back (d->node->config_entry_list, d->v->cur); (void)entry; #else plugin_node_t *node = (plugin_node_t *)user_data; @@ -1164,28 +1171,35 @@ list_size = xine_sarray_size(list); while (list_id < list_size) { - plugin_node_t *node = xine_sarray_get(list, list_id); + fat_node_t *node = xine_sarray_get (list, list_id); + uint32_t flags = IS_FAT_NODE (node) ? node->flags : 0; /* * preload plugins if not cached */ do { - if (!(node->info->type & PLUGIN_MUST_PRELOAD)) /* no preload needed */ + if (!((node->node.info->type & PLUGIN_MUST_PRELOAD) | (flags & FAT_NODE_FLAG_PROBE_CLASS))) /* no preload needed */ break; - if (node->plugin_class) /* is already loaded */ + if (node->node.plugin_class) /* is already loaded */ break; - if (node->file && !node->file->lib_handle) /* lib unavailable */ + if (node->node.file && !node->node.file->lib_handle) /* lib unavailable */ break; - lprintf ("preload plugin %s from %s\n", node->info->id, node->file ? node->file->filename : "libxine/builtins"); - - if (! _load_plugin_class (this, node, NULL)) { + lprintf ("preload plugin %s from %s\n", node->node.info->id, node->node.file ? node->node.file->filename : "libxine/builtins"); + + if (!_load_plugin_class (this, &node->node, NULL)) { /* in case of failure remove from list */ xine_sarray_remove(list, list_id); list_size = xine_sarray_size(list); list_id--; + break; } + + if (node->node.info->type & PLUGIN_MUST_PRELOAD) + break; + + _dispose_plugin_class (&node->node); } while (0); list_id++; } @@ -1199,16 +1213,33 @@ } } +static unsigned int _cfg_entry_hash (void *a) { + char **d = (char **)a; + return ((uintptr_t)(*d) >> 4) & 127; +} + +static int _cfg_entry_cmp (void *a, void *b) { + char **d = (char **)a, **e = (char **)b; + return *d < *e ? -1 : *d > *e ? 1 : 0; +} + /* * save plugin list information to file (cached catalog) */ -static void save_plugin_list(xine_t *this, FILE *fp, xine_sarray_t *list) { - +typedef struct { + xine_sarray_t *list; + cfg_entry_t *entries[MAX_DUPL_CFG_ENTRIES]; + uint32_t have; +} _load_plugins_dupl_cfg_t; + +static void save_plugin_list (xine_t *this, FILE *fp, xine_sarray_t *list, _load_plugins_dupl_cfg_t *cfg) { int list_id = 0; int list_size; #define SAVE_PLUGIN_BUF_SIZE 4096 char b[SAVE_PLUGIN_BUF_SIZE]; char *e = b + SAVE_PLUGIN_BUF_SIZE - 78 - 2 * XINE_MAX_INT64_STR - 5 * XINE_MAX_INT32_STR; + const plugin_file_t *last_file = (const plugin_file_t *)1; + uint32_t last_type = ~0u, last_api = ~0u, last_vers = ~0u; list_size = xine_sarray_size (list); while (list_id < list_size) { @@ -1216,30 +1247,47 @@ char *q = b; const plugin_node_t *node = xine_sarray_get (list, list_id); const plugin_file_t *file = node->file; - if (file) { - *q++ = '['; - q += strlcpy (q, file->filename, e - q); - if (q >= e) - q = e - 1; - memcpy (q, "]\nsize=", 7); q += 7; - xine_uint2str (&q, file->filesize); - memcpy (q, "\nmtime=", 7); q += 7; - xine_uint2str (&q, file->filemtime); + if (file != last_file) { + last_file = file; + last_type = last_api = last_vers = ~0u; + if (file) { + *q++ = '['; + q += strlcpy (q, file->filename, e - q); + if (q >= e) + q = e - 1; + memcpy (q, "]\nsize=", 7); q += 7; + xine_uint2str (&q, file->filesize); + memcpy (q, "\nmtime=", 7); q += 7; + xine_uint2str (&q, file->filemtime); + *q++ = '\n'; + } else { + /* dump builtins for debugging */ + memcpy (q, "[libxine/builtins]\n", 20); q += 19; + } + } + if (node->info->type != last_type) { + last_type = node->info->type; + last_api = last_vers = ~0u; + memcpy (q, "type=", 5); q += 5; + xine_uint32_2str (&q, node->info->type); *q++ = '\n'; - } else { - /* dump builtins for debugging */ - memcpy (q, "[libxine/builtins]\n", 20); q += 19; + } + if (node->info->API != last_api) { + last_api = node->info->API; + memcpy (q, "api=", 4); q += 4; + xine_uint32_2str (&q, node->info->API); + *q++ = '\n'; } - memcpy (q, "type=", 5); q += 5; - xine_uint32_2str (&q, node->info->type); - memcpy (q, "\napi=", 5); q += 5; - xine_uint32_2str (&q, node->info->API); - memcpy (q, "\nid=", 4); q += 4; + if (node->info->version != last_vers) { + last_vers = node->info->version; + memcpy (q, "version=", 8); q += 8; + xine_uint32_2str (&q, node->info->version); + *q++ = '\n'; + } + memcpy (q, "id=", 3); q += 3; q += strlcpy (q, node->info->id, e - q); if (q >= e) q = e - 1; - memcpy (q, "\nversion=", 9); q += 9; - xine_uint32_2str (&q, node->info->version); *q++ = '\n'; switch (node->info->type & PLUGIN_TYPE_MASK){ @@ -1299,7 +1347,7 @@ } case PLUGIN_XINE_MODULE: { const xine_module_info_t *module_info = node->info->special_info; - size_t type_len = strlen(module_info->type); + size_t type_len = xine_find_byte (module_info->type, 0); memcpy (q, "module_type=", 12); q += 12; memcpy (q, module_info->type, type_len); q += type_len; memcpy (q, "\nmodule_sub_type=", 17); q += 17; @@ -1325,6 +1373,20 @@ while ((entry = xine_list_next_value (node->config_entry_list, &ite))) { char *key_value; #ifdef FAST_SCAN_PLUGINS + if (cfg->have < MAX_DUPL_CFG_ENTRIES) { + int cfg_index; + cfg->entries[cfg->have] = entry; + cfg_index = xine_sarray_add (cfg->list, cfg->entries + cfg->have); + if (cfg_index < 0) { + char b2[80], *q = b2; + memcpy (q, "config_key=[", 12); q += 12; + xine_uint32_2str (&q, (cfg_entry_t **)xine_sarray_get (cfg->list, ~cfg_index) - cfg->entries); + memcpy (q, "]\n", 2); q += 2; + fwrite (b2, 1, q - b2, fp); + continue; + } + cfg->have++; + } pthread_mutex_lock (&this->config->config_lock); this->config->cur = entry; key_value = this->config->get_serialized_entry (this->config, NULL); @@ -1334,7 +1396,7 @@ key_value = this->config->get_serialized_entry (this->config, entry); #endif if (key_value) { - size_t slen = strlen (key_value); + size_t slen = xine_find_byte (key_value, 0); #ifdef FAST_SCAN_PLUGINS lprintf (" config key: %s, serialization: %zu bytes\n", entry->key, slen); #else @@ -1356,13 +1418,124 @@ /* * load plugin list information from file (cached catalog) */ +typedef enum { + _K_NONE = 0, + _K_cache_catalog_version, + _K_flush, + _K_filename, + _K_size, + _K_mtime, + _K_type, + _K_api, + _K_id, + _K_version, + _K_visual_type, + _K_supported_types, + _K_vo_priority, + _K_ao_priority, + _K_decoder_priority, + _K_demuxer_priority, + _K_input_priority, + _K_post_type, + _K_config_key, + _K_module_priority, + _K_module_sub_type, + _K_module_type, + _K_LAST +} _k_t; + +static _k_t _key_2_index (const char *key, size_t klen) { + switch (klen) { + int d; + case 0: + return _K_flush; + case 2: + if (!memcmp (key, "id", 2)) + return _K_id; + break; + case 3: + if (!memcmp (key, "api", 3)) + return _K_api; + break; + case 4: + if (!memcmp (key, "size", 4)) + return _K_size; + if (!memcmp (key, "type", 4)) + return _K_type; + break; + case 5: + if (!memcmp (key, "mtime", 5)) + return _K_mtime; + break; + case 7: + if (!memcmp (key, "version", 7)) + return _K_version; + break; + case 9: + if (!memcmp (key, "post_type", 9)) + return _K_post_type; + break; + case 10: + if (!memcmp (key, "config_key", 10)) + return _K_config_key; + break; + case 11: + d = memcmp (key, "module_type", 11); + if (d == 0) { + return _K_module_type; + } else if (d < 0) { + if (!memcmp (key, "ao_priority", 11)) + return _K_ao_priority; + } else { + if (!memcmp (key, "visual_type", 11)) + return _K_visual_type; + if (!memcmp (key, "vo_priority", 11)) + return _K_vo_priority; + } + break; + case 14: + if (!memcmp (key, "input_priority", 14)) + return _K_input_priority; + break; + case 15: + d = memcmp (key, "module_sub_type", 15); + if (d == 0) { + return _K_module_sub_type; + } else if (d < 0) { + if (!memcmp (key, "module_priority", 15)) + return _K_module_priority; + } else { + if (!memcmp (key, "supported_types", 15)) + return _K_supported_types; + } + break; + case 16: + if (!memcmp (key, "decoder_priority", 16)) + return _K_decoder_priority; + if (!memcmp (key, "demuxer_priority", 16)) + return _K_demuxer_priority; + break; + case 21: + if (!memcmp (key, "cache_catalog_version", 21)) + return _K_cache_catalog_version; + break; + default: ; + } + return _K_NONE; +} + static void load_plugin_list (xine_t *this, const char *filename, xine_sarray_t *plugins) { - - fat_node_t node; /** << node.file.filename is not a xine_fast_string_t, never passed there. */ +#ifdef FAST_SCAN_PLUGINS + struct { + cfg_entry_t *entries[MAX_DUPL_CFG_ENTRIES]; + uint32_t have; + } cfg; +#endif + fat_node_t *first_in_file, node; /** << node.file.filename is not a xine_fast_string_t, never passed there. */ size_t stlen, fnlen, idlen; /* We dont have that many types yet ;-) */ uint32_t supported_types[256]; - char *cfgentries[256], dummy_file[3] = "[]"; + char *cfgentries[256], dummy_line[1] = ""; int numcfgs; xine_fast_text_t *xft; @@ -1375,318 +1548,259 @@ return; _fat_node_init (&node); + fnlen = 0; stlen = 0; idlen = 0; fnlen = 0; numcfgs = 0; + first_in_file = NULL; +#ifdef FAST_SCAN_PLUGINS + cfg.have = 0; +#endif while (again) { + _k_t index; size_t lsize; - char *value, *line = xine_fast_text_line (xft, &lsize); + char *line = xine_fast_text_line (xft, &lsize); if (line) { /* skip comments */ if (line[0] == '#') continue; - /* skip (almost) empty lines */ - if (lsize < 3) - continue; } else { /* make sure to flush the last one */ again = 0; - line = dummy_file; - lsize = 2; + line = dummy_line; + lsize = 0; } - /* file name */ - if (line[0] == '[' && version_ok) { - - if (node.file.filename) { - fat_node_t *n; - char *q; - /* get mem for new node */ - n = malloc (sizeof (node) + stlen + idlen + fnlen + 32); - if (!n) - break; - /* fill in */ - *n = node; - n->node.info = &n->info[0]; - q = (char *)n + sizeof (*n); - if (stlen) { - memcpy (&n->supported_types[0], &supported_types[0], stlen); - q += stlen; - n->ainfo.decoder_info.supported_types = &n->supported_types[0]; - } - if (node.info[0].id) { - xine_small_memcpy (q, node.info[0].id, idlen); - n->info[0].id = q; - q += idlen; - } - n->file.filename = xine_fast_string_init (q, fnlen + 32); - xine_fast_string_set (n->file.filename, node.file.filename, fnlen); - /* q += fn_need; */ - n->node.file = &n->file; - n->info[0].special_info = &n->ainfo; - /* register */ - { - int index = xine_sarray_add (plugins, n); - if (index >= 0) { /* new file */ - n->lastplugin = n; - } else { - fat_node_t *first_in_file = xine_sarray_get (plugins, ~index); - first_in_file->lastplugin->nextplugin = n; - first_in_file->lastplugin = n; - } + if ((line[0] == '[') && version_ok) { + /* file name */ + if (line[--lsize] != ']') + continue; + line[lsize] = 0; + line++; + lsize--; + index = _K_filename; + } else { + /* key=value */ + size_t l; + line[lsize] = '='; + l = xine_find_byte (line, '='); + line[lsize] = 0; + index = _key_2_index (line, l); + line += l; + lsize -= l; + if (*line) + *line++ = 0, lsize--; + } + + if (skip && (index != _K_filename)) + continue; + + if (!version_ok) { + if (index == _K_cache_catalog_version) { + const char *val = line; + uint32_t vers = xine_str2uint32 (&val); + /* we are slightly backward compatible */ + if ((vers == CACHE_CATALOG_VERSION) || (vers == CACHE_CATALOG_VERSION - 1)) { + version_ok = 1; + } else { + xine_fast_text_unload (&xft); + return; } - if (numcfgs) { - char **cfgentry; -#ifdef FAST_SCAN_PLUGINS - new_entry_data_t ned; - ned.v = this->config; - ned.node = &n->node; - this->config->set_new_entry_callback (this->config, _new_entry_cb, &ned); -#endif - cfgentries[numcfgs] = NULL; - for (cfgentry = cfgentries; *cfgentry; cfgentry++) { - char *cfg_key = this->config->register_serialized_entry (this->config, *cfgentry); - if (cfg_key) { - /* this node is a cached node */ -#ifdef FAST_SCAN_PLUGINS - free (cfg_key); -#else - _attach_entry_to_node (&n->node, cfg_key); -#endif - } else { - lprintf("failed to deserialize config entry key\n"); - } - } -#ifdef FAST_SCAN_PLUGINS - this->config->unset_new_entry_callback (this->config); -#endif - } - /* reset */ - _fat_node_init (&node); - stlen = 0; - numcfgs = 0; } - - *line++ = ']'; /* skip '[' */ - for (value = line + lsize - 2; *value != ']'; value--) ; - if (value < line) - value = line + lsize - 1; - *value = 0; - fnlen = value - line; - - if ((fnlen == 16) && !memcmp (line, "libxine/builtins", 16)) { - skip = 1; - continue; - } - skip = 0; - - node.file.filename = line; continue; } - if (skip) - continue; - - if ((value = strchr (line, '='))) { - typedef enum { - _K_NONE = 0, - _K_cache_catalog_version, - _K_size, - _K_mtime, - _K_type, - _K_api, - _K_id, - _K_version, - _K_visual_type, - _K_supported_types, - _K_vo_priority, - _K_ao_priority, - _K_decoder_priority, - _K_demuxer_priority, - _K_input_priority, - _K_post_type, - _K_config_key, - _K_module_priority, - _K_module_sub_type, - _K_module_type, - _K_LAST - } _k_t; - _k_t index = _K_NONE; - unsigned int klen = value - line; - const char *val; - *value++ = 0; - val = value; - /* get known key */ - switch (klen) { - int d; - case 2: - if (!memcmp (line, "id", 2)) - index = _K_id; - break; - case 3: - if (!memcmp (line, "api", 3)) - index = _K_api; - break; - case 4: - if (!memcmp (line, "size", 4)) - index = _K_size; - else if (!memcmp (line, "type", 4)) - index = _K_type; - break; - case 5: - if (!memcmp (line, "mtime", 5)) - index = _K_mtime; - break; - case 7: - if (!memcmp (line, "version", 7)) - index = _K_version; - break; - case 9: - if (!memcmp (line, "post_type", 9)) - index = _K_post_type; - break; - case 10: - if (!memcmp (line, "config_key", 10)) - index = _K_config_key; - break; - case 11: - d = memcmp (line, "module_type", 11); - if (d == 0) { - index = _K_module_type; - } else if (d < 0) { - if (!memcmp (line, "ao_priority", 11)) - index = _K_ao_priority; - } else { - if (!memcmp (line, "visual_type", 11)) - index = _K_visual_type; - else if (!memcmp (line, "vo_priority", 11)) - index = _K_vo_priority; - } - break; - case 14: - if (!memcmp (line, "input_priority", 14)) - index = _K_input_priority; - break; - case 15: - d = memcmp (line, "module_sub_type", 15); - if (d == 0) { - index = _K_module_sub_type; - } else if (d < 0) { - if (!memcmp (line, "module_priority", 15)) - index = _K_module_priority; - } else { - if (!memcmp (line, "supported_types", 15)) - index = _K_supported_types; + { + const char *val = line; + union { + uint64_t llu; + unsigned int u; + int i; + } v = {0}; + unsigned int mask = 1u << index; + const uint32_t + set_int32 = (1 << _K_type) | (1 << _K_api) | (1 << _K_visual_type) | (1 << _K_vo_priority) + | (1 << _K_ao_priority) | (1 << _K_decoder_priority) | (1 << _K_demuxer_priority) + | (1 << _K_input_priority) | (1 << _K_module_priority), + set_uint32 = (1 << _K_version) | (1 << _K_post_type) | (1 << _K_module_sub_type), + set_uint64 = (1 << _K_size) | (1 << _K_mtime); + if (set_int32 & mask) + v.i = xine_str2int32 (&val); + else if (set_uint32 & mask) + v.u = xine_str2uint32 (&val); + else if (set_uint64 & mask) + v.llu = xine_str2uint64 (&val); + switch (index) { + case _K_flush: + if (idlen) { + /* flush this entry */ + fat_node_t *n; + char *q; + /* get mem for new node */ + n = malloc (sizeof (node) + stlen + idlen + 1 + fnlen + 1 + 32); + if (!n) + break; + /* fill in */ + *n = node; + n->node.info = &n->info[0]; + q = (char *)n + sizeof (*n); + if (stlen) { + memcpy (&n->supported_types[0], &supported_types[0], stlen); + q += stlen; + n->ainfo.decoder_info.supported_types = &n->supported_types[0]; + } + if (node.info[0].id) { + xine_small_memcpy (q, node.info[0].id, idlen + 1); + n->info[0].id = q; + q += idlen + 1; + } + n->file.filename = xine_fast_string_init (q, fnlen + 32); + xine_fast_string_set (n->file.filename, node.file.filename, fnlen); + /* q += fn_need; */ + n->node.file = &n->file; + n->info[0].special_info = &n->ainfo; + /* register */ + if (first_in_file) { + first_in_file->lastplugin->nextplugin = n; + first_in_file->lastplugin = n; + } else { + int i = xine_sarray_add (plugins, n); + if (i >= 0) { /* new file */ + n->lastplugin = n; + first_in_file = n; + } else { + first_in_file = xine_sarray_get (plugins, ~i); + first_in_file->lastplugin->nextplugin = n; + first_in_file->lastplugin = n; + } + } + if (numcfgs) { + char **cfgentry; +#ifdef FAST_SCAN_PLUGINS + new_entry_data_t ned; + ned.v = this->config; + ned.node = &n->node; + this->config->set_new_entry_callback (this->config, _new_entry_cb, &ned); +#endif + cfgentries[numcfgs] = NULL; + for (cfgentry = cfgentries; *cfgentry; cfgentry++) { + char *cfg_key; +#ifdef FAST_SCAN_PLUGINS + if (cfgentry[0][0] == '[') { + const char *p = *cfgentry + 1; + uint32_t cfg_index = xine_str2uint32 (&p); + if (cfg_index < cfg.have) { + if (!n->node.config_entry_list) + n->node.config_entry_list = xine_list_new (); + if (n->node.config_entry_list && cfg.entries[cfg_index] && + !xine_list_find (n->node.config_entry_list, cfg.entries[cfg_index])) + xine_list_push_back (n->node.config_entry_list, cfg.entries[cfg_index]); + } + continue; + } + ned.cfg_entry = NULL; + cfg_key = this->config->register_serialized_entry (this->config, *cfgentry); + if (cfg.have < MAX_DUPL_CFG_ENTRIES) + cfg.entries[cfg.have++] = ned.cfg_entry; +#else + cfg_key = this->config->register_serialized_entry (this->config, *cfgentry); +#endif + if (cfg_key) { + /* this node is a cached node */ +#ifdef FAST_SCAN_PLUGINS + free (cfg_key); +#else + _attach_entry_to_node (&n->node, cfg_key); +#endif + } else { + lprintf ("failed to deserialize config entry key\n"); + } + } +#ifdef FAST_SCAN_PLUGINS + this->config->unset_new_entry_callback (this->config); +#endif + } + /* reset */ + node.info[0].id = NULL; + idlen = 0; + supported_types[0] = 0; + stlen = 0; + memset (&node.ainfo, 0, sizeof (node.ainfo)); + numcfgs = 0; } break; - case 16: - if (!memcmp (line, "decoder_priority", 16)) - index = _K_decoder_priority; - else if (!memcmp (line, "demuxer_priority", 16)) - index = _K_demuxer_priority; + case _K_filename: + node.file.filename = line; + fnlen = lsize; + skip = ((lsize == 16) && !memcmp (line, "libxine/builtins", 16)) ? 1 : 0; + first_in_file = NULL; break; - case 21: - if (!memcmp (line, "cache_catalog_version", 21)) - index = _K_cache_catalog_version; + case _K_size: + node.file.filesize = v.llu; + break; + case _K_mtime: + node.file.filemtime = v.llu; + break; + case _K_type: + node.info[0].type = v.i; break; - default: ; - } - if( !version_ok ) { - if (index == _K_cache_catalog_version) { - if (xine_str2uint32 (&val) == CACHE_CATALOG_VERSION) - version_ok = 1; - else { - xine_fast_text_unload (&xft); - return; - } - } - } else if (node.file.filename) { - union { - uint64_t llu; - unsigned int u; + case _K_api: + node.info[0].API = v.i; + break; + case _K_id: + node.info[0].id = line; + idlen = lsize; + break; + case _K_version: + node.info[0].version = v.u; + break; + case _K_visual_type: + node.ainfo.vo_info.visual_type = v.i; + break; + case _K_supported_types: { int i; - } v = {0}; - unsigned int mask = 1u << index; - const uint32_t - set_int32 = (1 << _K_type) | (1 << _K_api) | (1 << _K_visual_type) | (1 << _K_vo_priority) - | (1 << _K_ao_priority) | (1 << _K_decoder_priority) | (1 << _K_demuxer_priority) - | (1 << _K_input_priority) | (1 << _K_module_priority), - set_uint32 = (1 << _K_version) | (1 << _K_post_type) | (1 << _K_module_sub_type), - set_uint64 = (1 << _K_size) | (1 << _K_mtime); - if (set_int32 & mask) - v.i = xine_str2int32 (&val); - else if (set_uint32 & mask) - v.u = xine_str2uint32 (&val); - else if (set_uint64 & mask) - v.llu = xine_str2uint64 (&val); - switch (index) { - case _K_size: - node.file.filesize = v.llu; - break; - case _K_mtime: - node.file.filemtime = v.llu; - break; - case _K_type: - node.info[0].type = v.i; - break; - case _K_api: - node.info[0].API = v.i; - break; - case _K_id: - node.info[0].id = value; - idlen = (line + lsize) - value + 1; - break; - case _K_version: - node.info[0].version = v.u; - break; - case _K_visual_type: - node.ainfo.vo_info.visual_type = v.i; - break; - case _K_supported_types: { - int i; - for (i = 0; i < 255; i++) { - if ((supported_types[i] = xine_str2uint32 (&val)) == 0) - break; - } - supported_types[i++] = 0; - stlen = i * sizeof (*supported_types); - break; + for (i = 0; i < 255; i++) { + if ((supported_types[i] = xine_str2uint32 (&val)) == 0) + break; } - case _K_vo_priority: - node.ainfo.vo_info.priority = v.i; - break; - case _K_ao_priority: - node.ainfo.ao_info.priority = v.i; - break; - case _K_decoder_priority: - node.ainfo.decoder_info.priority = v.i; - break; - case _K_demuxer_priority: - node.ainfo.demuxer_info.priority = v.i; - break; - case _K_input_priority: - node.ainfo.input_info.priority = v.i; - break; - case _K_post_type: - node.ainfo.post_info.type = v.u; - break; - case _K_config_key: - if (numcfgs < 255) - cfgentries[numcfgs++] = value; - break; - case _K_module_priority: - node.ainfo.module_info.priority = v.i; - break; - case _K_module_sub_type: - node.ainfo.module_info.sub_type = v.u; - break; - case _K_module_type: - strlcpy(node.ainfo.module_info.type, value, sizeof(node.ainfo.module_info.type)); - default: ; + supported_types[i++] = 0; + stlen = i * sizeof (*supported_types); + break; } + case _K_vo_priority: + node.ainfo.vo_info.priority = v.i; + break; + case _K_ao_priority: + node.ainfo.ao_info.priority = v.i; + break; + case _K_decoder_priority: + node.ainfo.decoder_info.priority = v.i; + break; + case _K_demuxer_priority: + node.ainfo.demuxer_info.priority = v.i; + break; + case _K_input_priority: + node.ainfo.input_info.priority = v.i; + break; + case _K_post_type: + node.ainfo.post_info.type = v.u; + break; + case _K_config_key: + if (numcfgs < 255) + cfgentries[numcfgs++] = line; + break; + case _K_module_priority: + node.ainfo.module_info.priority = v.i; + break; + case _K_module_sub_type: + node.ainfo.module_info.sub_type = v.u; + break; + case _K_module_type: + strlcpy (node.ainfo.module_info.type, line, sizeof (node.ainfo.module_info.type)); + default: ; } } } @@ -1696,28 +1810,31 @@ /** * @brief Returns the complete filename for the plugins' cache file - * @param this Instance pointer, used for logging and libxdg-basedir. + * @param this xine instance pointer, used for logging and libxdg-basedir. + * @param buf write filename here. + * @param bsize write at most this many bytes. * @param createdir If not zero, create the directory structure in which * the file has to reside. - * @return If createdir was not zero, returns NULL if the directory hasn't - * been created; otherwise always returns a new string with the - * name of the cachefile. + * @return the strlen () of filename, or 0 (eg if a directory could not be created). * @internal * * @see XDG Base Directory specification: * http://standards.freedesktop.org/basedir-spec/latest/index.html */ -static char *catalog_filename(xine_t *this, int createdir) { +static size_t catalog_filename (xine_t *this, char *buf, size_t bsize, int createdir) { const char *const xdg_cache_home = xdgCacheHome(&this->basedir_handle); - char *cachefile; + size_t l1, l2; if (!xdg_cache_home) - return NULL; - - cachefile = malloc( strlen(xdg_cache_home) + sizeof("/"PACKAGE"/plugins.cache") ); - if (!cachefile) - return NULL; - strcpy(cachefile, xdg_cache_home); + return 0; + + l1 = xine_find_byte (xdg_cache_home, 0); + l2 = sizeof (PACKAGE) - 1; + if (bsize < l1 + 1 + l2 + 1 + 14 + 1) + return 0; + + memcpy (buf, xdg_cache_home, l1 + 1); + memcpy (buf + l1, "/" PACKAGE "/plugins.cache", 1 + l2 + 14 + 1); /* If we're going to create the directory structure, we concatenate * piece by piece the path, so that we can try to create all the @@ -1726,29 +1843,26 @@ * whole path at once. */ if ( createdir ) { - int result = 0; - - result = mkdir( cachefile, 0700 ); - if ( result != 0 && errno != EEXIST ) { - xprintf (this, XINE_VERBOSITY_LOG, _("Unable to create %s directory: %s\n"), cachefile, strerror(errno)); - free(cachefile); - return NULL; + int result = 0, e; + + buf[l1] = 0; + result = mkdir (buf, 0700); + if ((result != 0) && ((e = errno) != EEXIST)) { + xprintf (this, XINE_VERBOSITY_LOG, _("Unable to create %s directory: %s\n"), buf, strerror (e)); + return 0; } - - strcat(cachefile, "/"PACKAGE); - result = mkdir( cachefile, 0700 ); - if ( result != 0 && errno != EEXIST ) { - xprintf (this, XINE_VERBOSITY_LOG, _("Unable to create %s directory: %s\n"), cachefile, strerror(errno)); - free(cachefile); - return NULL; + buf[l1] = '/'; + + buf[l1 + 1 + l2] = 0; + result = mkdir (buf, 0700); + if ((result != 0) && ((e = errno) != EEXIST)) { + xprintf (this, XINE_VERBOSITY_LOG, _("Unable to create %s directory: %s\n"), buf, strerror (e)); + return 0; } - - strcat(cachefile, "/plugins.cache"); - - } else - strcat(cachefile, "/"PACKAGE"/plugins.cache"); - - return cachefile; + buf[l1 + 1 + l2] = '/'; + } + + return l1 + 1 + l2 + 1 + 14; } /* @@ -1756,23 +1870,35 @@ */ static void save_catalog (xine_t *this) { FILE *fp; - char *const cachefile = catalog_filename(this, 1); - char *cachefile_new; - - if ( ! cachefile ) return; - - cachefile_new = _x_asprintf("%s.new", cachefile); - - if ((fp = fopen (cachefile_new, "wb")) != NULL) { + char oldname[1024 - 4], newname[1024]; + size_t nlen = catalog_filename (this, oldname, sizeof (oldname), 1); + + if (!nlen) + return; + + memcpy (newname, oldname, nlen); + memcpy (newname + nlen, ".new", 5); + + if ((fp = fopen (newname, "wb")) != NULL) { + _load_plugins_dupl_cfg_t cfg; int i; + cfg.list = xine_sarray_new (MAX_DUPL_CFG_ENTRIES, _cfg_entry_cmp); + /* this is mainly a test :-) */ + xine_sarray_set_hash (cfg.list, _cfg_entry_hash, 128); + xine_sarray_set_mode (cfg.list, XINE_SARRAY_MODE_UNIQUE); + cfg.have = 0; + fwrite ("# this file is automatically created by xine, do not edit.\n\n" "cache_catalog_version=" CACHE_CATALOG_VERSION_STR "\n\n", 1, 85, fp); for (i = 0; i < PLUGIN_TYPE_MAX; i++) { - save_plugin_list (this, fp, this->plugin_catalog->plugin_lists[i]); + save_plugin_list (this, fp, this->plugin_catalog->plugin_lists[i], &cfg); } - save_plugin_list (this, fp, this->plugin_catalog->modules_list); + save_plugin_list (this, fp, this->plugin_catalog->modules_list, &cfg); + + xine_sarray_delete (cfg.list); + if (fclose(fp)) { const char *err = strerror (errno); @@ -1780,13 +1906,13 @@ _("failed to save catalogue cache: %s\n"), err); goto do_unlink; } - else if (rename (cachefile_new, cachefile)) + else if (rename (newname, oldname)) { const char *err = strerror (errno); xine_log (this, XINE_LOG_MSG, _("failed to replace catalogue cache: %s\n"), err); do_unlink: - if (unlink (cachefile_new) && errno != ENOENT) + if (unlink (newname) && errno != ENOENT) { err = strerror (errno); xine_log (this, XINE_LOG_MSG, @@ -1794,19 +1920,16 @@ } } } - free(cachefile); - free(cachefile_new); } /* * load cached catalog from file */ static void load_cached_catalog (xine_t *this) { - char *const cachefile = catalog_filename(this, 0); - /* It can't return NULL without creating directories */ - - load_plugin_list (this, cachefile, this->plugin_catalog->cache_list); - free(cachefile); + char filename[1024]; + + if (catalog_filename (this, filename, sizeof (filename), 0) > 0) + load_plugin_list (this, filename, this->plugin_catalog->cache_list); } @@ -1832,7 +1955,7 @@ homedir = xine_get_homedir (); if (!homedir) return -1; - homelen = strlen (homedir); + homelen = xine_find_byte (homedir, 0); if (homelen > XSP_BUFSIZE - 16) homelen = XSP_BUFSIZE - 16; @@ -1840,11 +1963,15 @@ if (!this->x.plugin_catalog) return -1; + this->strings.decoder_pri_help = xine_ref_string_ref ( + _("The priority provides a ranking in case some media can be handled by more than one decoder.\n" + "A priority of 0 enables the decoder's default priority."), -1); + XINE_PROFILE (load_cached_catalog (&this->x)); #ifdef XINE_MAKE_BUILTINS lprintf ("collect_plugins in libxine\n"); - _register_plugins_internal (&this->x, NULL, NULL , xine_builtin_plugin_info); + _register_plugins_internal (&this->x, NULL, NULL , xine_builtin_plugin_info, FAT_NODE_FLAG_PROBE_CLASS); #endif if ((pluginpath = getenv("XINE_PLUGIN_PATH")) != NULL && *pluginpath) { @@ -1870,7 +1997,7 @@ start = stop + 1; collect_plugins (&this->x, buf, q, bufend); } - len = strlen (start); + len = xine_find_byte (start, 0); if (len > (size_t)(bufend - q)) len = bufend - q; xine_small_memcpy (q, start, len); q += len; @@ -1888,7 +2015,7 @@ collect_plugins (&this->x, buf, buf + homelen + 15, bufend); p = XINE_PLUGINROOT; - len = strlen (p); + len = sizeof (XINE_PLUGINROOT) - 1; xine_small_memcpy (buf, p, len); buf[len++] = '.'; for (i = XINE_LT_AGE; i >= 0; i--) { @@ -1976,6 +2103,7 @@ xine_t *xine; plugin_catalog_t *catalog; input_plugin_t *plugin; + input_class_t *skip_class; uint32_t n; if (!stream || !mrl) @@ -1986,8 +2114,12 @@ catalog = xine->plugin_catalog; plugin = NULL; + /* prevent recursion during input_plugin->open (). */ + skip_class = s->s.input_plugin ? s->s.input_plugin->input_class : NULL; + pthread_mutex_lock (&catalog->lock); + /* prevent recursion during input_class->get_instance (). */ n = !s->query_input_plugins[0] ? 0 : !s->query_input_plugins[1] ? 1 : 2; if (n != 2) { @@ -2002,7 +2134,7 @@ } if (class) { s->query_input_plugins[n] = class; - if (s->query_input_plugins[0] != s->query_input_plugins[1]) { + if ((class != skip_class) && (s->query_input_plugins[0] != s->query_input_plugins[1])) { plugin = class->get_instance (class, stream, mrl); if (plugin) { inc_node_ref (node); @@ -3281,7 +3413,7 @@ const char *s = kind ? cls->extensions : cls->mimetypes; if (s) { slitem->s = s; - size += (slitem->len = strlen (s)); + size += (slitem->len = xine_find_byte (s, 0)); slitem++; } } @@ -3489,5 +3621,11 @@ pthread_mutex_destroy(&this->plugin_catalog->lock); _x_freep (&this->plugin_catalog); + + { + xine_private_t *_xine = (xine_private_t *)this; + + xine_ref_string_unref (&_xine->strings.decoder_pri_help); + } } } diff -r 9e075962b659 src/xine-engine/osd.c --- a/src/xine-engine/osd.c Wed Mar 09 14:31:44 2022 +0200 +++ b/src/xine-engine/osd.c Sat Sep 24 13:04:31 2022 +0200 @@ -903,6 +903,7 @@ if (!font) break; memcpy (font->name, b, sizeof (font->name)); + font->name[sizeof(font->name) - 1] = 0; font->version = i; font->size = _X_LE_16 (b + sizeof (font->name) + 1 * 2); font->num_fontchars = _X_LE_16 (b + sizeof (font->name) + 2 * 2); diff -r 9e075962b659 src/xine-engine/video_out.c --- a/src/xine-engine/video_out.c Wed Mar 09 14:31:44 2022 +0200 +++ b/src/xine-engine/video_out.c Sat Sep 24 13:04:31 2022 +0200 @@ -1857,7 +1857,7 @@ * to avoid deadlocks we don't use vo_free_queue_get () * but vo_*_get_dupl () instead. */ -static vo_frame_t *duplicate_frame (vos_t *this, vo_frame_t *img) { +static vo_frame_t *duplicate_frame (vos_t *this, vo_frame_t *img, int locks) { vo_frame_t *dupl; if (!img) @@ -1874,7 +1874,7 @@ } pthread_mutex_lock (&dupl->mutex); - dupl->lock_counter = 1; + dupl->lock_counter = locks; dupl->width = img->width; dupl->height = img->height; dupl->ratio = img->ratio; @@ -2017,7 +2017,25 @@ img = img->next; } this->rp.last_flushed = keep[1]; - if (this->rp.last_flushed) { + do { + if (!this->rp.last_flushed) + break; + /* if filler frame might be tied to a flushing hwaccel decoder, + * do not stand in the way there. newer vdpau_radeonsi is known to + * crash on this. if duplicating the frame should not be enough, + * we might need to drop the filler entirely. */ + if (this->rp.last_flushed->proc_duplicate_frame_data) { + vo_frame_t *dupl = duplicate_frame (this, this->rp.last_flushed, 2); + if (!vo_frame_dec2_lock_int (this, this->rp.last_flushed)) { + *add = this->rp.last_flushed; + add = &this->rp.last_flushed->next; + a++; + } + this->rp.last_flushed = dupl; + if (!dupl) + break; + vo_reref (this, dupl); + } this->rp.last_flushed->next = NULL; if (!this->grab.last_frame) { /* rare late setting */ vo_frame_inc_lock (this->rp.last_flushed); @@ -2025,7 +2043,7 @@ this->grab.last_frame = this->rp.last_flushed; pthread_mutex_unlock (&this->grab.lock); } - } + } while (0); /* Override with first frame. */ if (keep[0]) { keep[0]->vpts = *vpts; @@ -2038,8 +2056,15 @@ /* Make sure clients dont miss this. */ if (this->rp.need_flush_signal) { pthread_mutex_lock (&this->display_queue.mutex); + this->num_frames_delivered = 0; + this->num_frames_skipped = 0; + this->num_frames_discarded = 0; pthread_cond_broadcast (&this->display_queue.done_flushing); pthread_mutex_unlock (&this->display_queue.mutex); + } else { + this->num_frames_delivered = 0; + this->num_frames_skipped = 0; + this->num_frames_discarded = 0; } /* Report success. */ if (n) { @@ -2047,6 +2072,7 @@ LOG_MODULE ": flushed out %d frames (now=%"PRId64", discard=%d).\n", n, *vpts, this->display_queue.discard_frames); } + /* done */ this->redraw_needed = 0; *vpts = 0; return keep[0]; @@ -2114,7 +2140,10 @@ img->vpts, diff); } + /* lock to inform decodeer_thread.vo_frame_draw () earlier. */ + pthread_mutex_lock (&this->display_queue.mutex); this->num_frames_discarded++; + pthread_mutex_unlock (&this->display_queue.mutex); img = vo_ready_pop (this); @@ -2298,7 +2327,7 @@ if (!f) { check_redraw_needed (this, vpts); if (this->redraw_needed) { - f = duplicate_frame (this, this->grab.last_frame); + f = duplicate_frame (this, this->grab.last_frame, 1); if (f) { vo_reref (this, f); f->vpts = vpts; @@ -2409,7 +2438,7 @@ if (this->grab.last_frame && (this->redraw_needed == 1)) { lprintf ("generating still frame (vpts = %" PRId64 ") \n", vpts); /* keep playing still frames */ - img = duplicate_frame (this, this->grab.last_frame); + img = duplicate_frame (this, this->grab.last_frame, 1); if (img) { vo_reref (this, img); img->vpts = vpts; @@ -3330,3 +3359,4 @@ return &this->vo; } + diff -r 9e075962b659 src/xine-engine/xine.c --- a/src/xine-engine/xine.c Wed Mar 09 14:31:44 2022 +0200 +++ b/src/xine-engine/xine.c Sat Sep 24 13:04:31 2022 +0200 @@ -906,6 +906,9 @@ } } + /* reset leading id3v2 tag info */ + stream->id3v2_tag_size = -1; + /* * reset / free meta info * XINE_STREAM_INFO_MAX is at least 99 but the info arrays are sparsely used. @@ -1111,6 +1114,8 @@ stream->s.xine = this; stream->status = XINE_STATUS_IDLE; + stream->id3v2_tag_size = -1; + stream->video_source.name = "video source"; stream->video_source.type = XINE_POST_DATA_VIDEO; stream->video_source.data = &stream->s; @@ -1359,6 +1364,8 @@ s->s.xine = m->s.xine; s->status = XINE_STATUS_IDLE; + s->id3v2_tag_size = -1; + s->video_source.name = "video source"; s->video_source.type = XINE_POST_DATA_VIDEO; s->video_source.data = &m->s; @@ -2018,9 +2025,12 @@ } no_cache = no_cache || (stream->s.input_plugin->get_capabilities (stream->s.input_plugin) & INPUT_CAP_NO_CACHE); - if( !no_cache ) + if( !no_cache ) { /* enable buffered input plugin (request optimizer) */ - stream->s.input_plugin = _x_cache_plugin_get_instance (&stream->s); + input_plugin_t *cache_plugin = _x_cache_plugin_get_instance (&stream->s); + if (cache_plugin) + stream->s.input_plugin = cache_plugin; + } /* Let the plugin request a specific demuxer (if the user hasn't). * This overrides find-by-content & find-by-extension. @@ -2739,6 +2749,7 @@ this->x.clock = NULL; this->port_ticket = NULL; this->speed_change_flags = 0; + this->strings.decoder_pri_help = NULL; #endif pthread_mutex_init (&this->speed_change_lock, NULL); diff -r 9e075962b659 src/xine-engine/xine_interface.c --- a/src/xine-engine/xine_interface.c Wed Mar 09 14:31:44 2022 +0200 +++ b/src/xine-engine/xine_interface.c Sat Sep 24 13:04:31 2022 +0200 @@ -822,6 +822,84 @@ return _x_meta_info_get_public(stream, info); } +int xine_query_stream_info (xine_stream_t *stream, char *sbuf, size_t sblen, int *strings, int *ints) { + xine_stream_private_t *_s = (xine_stream_private_t *)stream; + static const uint8_t tab_special[sizeof (_s->stream_info) / sizeof (_s->stream_info[0])] = { + [XINE_STREAM_INFO_SEEKABLE] = 1, + [XINE_STREAM_INFO_HAS_CHAPTERS] = 2, + [XINE_STREAM_INFO_MAX_AUDIO_CHANNEL] = 3, + [XINE_STREAM_INFO_MAX_SPU_CHANNEL] = 4 + }; + int special_index[5] = {-1, -1, -1, -1, -1}; + uint32_t u, p; + + if (!_s) { + if (sbuf && sblen) + sbuf[0] = 0; + if (strings) { + for (u = 0; strings[u] >= 0; u++) + strings[0] = 0; + } + if (ints) { + for (u = 0; ints[u] >= 0; u++) + ints[0] = 0; + } + return 0; + } + + p = 0; + _s = _s->side_streams[0]; + + xine_rwlock_rdlock (&_s->info_lock); + if (strings) { + if (sbuf && sblen) + sbuf[p++] = 0; + for (u = 0; strings[u] >= 0; u++) { + const char *st; + if (((uint32_t)strings[u] < sizeof (_s->meta_info) / sizeof (_s->meta_info[0])) + && ((st = _s->meta_info[strings[u]]))) { + size_t sl = xine_find_byte (st, 0) + 1; + if (p + sl > sblen) + break; + strings[u] = p; + memcpy (sbuf + p, st, sl); + p += sl; + } else { + strings[u] = 0; + } + } + for (; strings[u] >= 0; u++) + strings[u] = 0; + } + if (ints) { + for (u = 0; ints[u] >= 0; u++) { + if ((uint32_t)ints[u] < sizeof (_s->stream_info) / sizeof (_s->stream_info[0])) { + special_index[tab_special[ints[u]]] = u; + ints[u] = _s->stream_info[ints[u]]; + } else { + ints[u] = 0; + } + } + } + xine_rwlock_unlock (&_s->info_lock); + + if (special_index[1] >= 0) + ints[special_index[1]] = + (_s->s.input_plugin && + (_s->s.input_plugin->get_capabilities (_s->s.input_plugin) + & (INPUT_CAP_SEEKABLE | INPUT_CAP_SLOW_SEEKABLE | INPUT_CAP_TIME_SEEKABLE))) ? 1 : 0; + if (special_index[2] >= 0) + ints[special_index[2]] = + (_s->demux.plugin && (_s->demux.plugin->get_capabilities (_s->demux.plugin) & DEMUX_CAP_CHAPTERS)) || + (_s->s.input_plugin && (_s->s.input_plugin->get_capabilities (_s->s.input_plugin) & INPUT_CAP_CHAPTERS)); + if (special_index[3] >= 0) + ints[special_index[3]] = _s->audio_track_map_entries; + if (special_index[4] >= 0) + ints[special_index[4]] = _s->spu_track_map_entries; + + return p; +} + xine_osd_t *xine_osd_new(xine_stream_t *stream, int x, int y, int width, int height) { xine_osd_t *this = (xine_osd_t *)stream->osd_renderer->new_object(stream->osd_renderer, width, height); this->osd.renderer->set_position(&this->osd, x, y); diff -r 9e075962b659 src/xine-engine/xine_private.h --- a/src/xine-engine/xine_private.h Wed Mar 09 14:31:44 2022 +0200 +++ b/src/xine-engine/xine_private.h Sat Sep 24 13:04:31 2022 +0200 @@ -64,6 +64,40 @@ EXTERN_C_START +static inline uint32_t xine_find_byte (const char *s, uint32_t byte) { + const uint32_t eor = ~((byte << 24) | (byte << 16) | (byte << 8) | byte); + const uint32_t left = (uintptr_t)s & 3; + const uint32_t *p = (const uint32_t *)(s - left); + static const union { + uint8_t b[4]; + uint32_t v; + } mask[4] = { + {{0xff, 0xff, 0xff, 0xff}}, + {{0x00, 0xff, 0xff, 0xff}}, + {{0x00, 0x00, 0xff, 0xff}}, + {{0x00, 0x00, 0x00, 0xff}}, + }; + static const uint8_t rest[32] = { + 0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, /* big wndian */ + 0, 4, 3, 4, 2, 4, 3, 4, 1, 4, 3, 4, 2, 4, 3, 4 /* little endian */ + }; + const union { + uint32_t v; + uint8_t b[4]; + } endian = {16}; + uint32_t w = (*p++ ^ eor) & mask[left].v; + while (1) { + w = w & 0x80808080 & ((w & 0x7f7f7f7f) + 0x01010101); + if (w) + break; + w = *p++ ^ eor; + } + /* bits 31, 23, 15, 7 -> 3, 2, 1, 0 */ + w = (w * 0x00204081) & 0xffffffff; + w >>= 28; + return ((const char *)p - s) - rest[endian.b[0] + w]; +} + /* HAVE_ATOMIC_VARS: 0 = none, 1 = stdatomic.h, 2 = __atomic_*, 3 = __sync_* */ #if (HAVE_ATOMIC_VARS > 0) # if (HAVE_ATOMIC_VARS == 1) @@ -485,6 +519,11 @@ /* special values for set_speed_internal (). now defined in xine/xine_internal.h. */ /* # define XINE_LIVE_PAUSE_ON 0x7ffffffd */ /* # define XINE_LIVE_PAUSE_OFF 0x7ffffffc */ + + /* share some often used (localized) text as xine_ref_string_t. */ + struct { + char *decoder_pri_help; + } strings; } xine_private_t; typedef struct xine_stream_private_st { @@ -548,6 +587,9 @@ /* 1 << side_stream_index (1, 2, 4, 8) */ uint32_t id_flag; + /* a id3v2 tag of this many bytes has been parserd, or -1. */ + int id3v2_tag_size; + /* stream meta information */ /* Grab lock, or use helpers (see info_helper.c). */ xine_rwlock_t info_lock; diff -r 9e075962b659 src/xine-utils/sorted_array.c --- a/src/xine-utils/sorted_array.c Wed Mar 09 14:31:44 2022 +0200 +++ b/src/xine-utils/sorted_array.c Sat Sep 24 13:04:31 2022 +0200 @@ -27,6 +27,7 @@ #include <string.h> #include <xine/attributes.h> #include <xine/sorted_array.h> +#include <xine/os_types.h> #define MIN_CHUNK_SIZE 64 @@ -41,13 +42,60 @@ unsigned int last_add[2]; unsigned int first_test; unsigned int same_dir; + struct { + int *table; + xine_sarray_hash_func_t user_get; + void (*get) (xine_sarray_t *sarray, void *item); + unsigned int size; + unsigned int start; + unsigned int stop; + unsigned int last_value; + unsigned int value; + } hash; void *default_chunk[1]; }; +static void _xine_sarray_hash_get (xine_sarray_t *sarray, void *item) { + unsigned int value = sarray->hash.user_get (item); + + if (value > sarray->hash.size - 1) + value = sarray->hash.size - 1; + sarray->hash.value = value; + sarray->hash.start = sarray->hash.table[value]; + sarray->hash.stop = sarray->hash.table[value + 1]; +} + +static void _xine_sarray_hash_none (xine_sarray_t *sarray, void *item) { + (void)item; + sarray->hash.value = 0; + sarray->hash.start = 0; + sarray->hash.stop = sarray->size; +} + +static void _xine_sarray_hash_insert (xine_sarray_t *sarray) { + if (sarray->hash.table) { + unsigned int u; + + for (u = sarray->hash.value + 1; u <= sarray->hash.size; u++) + sarray->hash.table[u] += 1; + sarray->hash.last_value = sarray->hash.value; + } +} + +static void _xine_sarray_hash_remove (xine_sarray_t *sarray, void *item) { + if (sarray->hash.table) { + unsigned int u; + + sarray->hash.get (sarray, item); + for (u = sarray->hash.value + 1; u <= sarray->hash.size; u++) + sarray->hash.table[u] -= 1; + } +} + static int _xine_sarray_find_default (xine_sarray_t *sarray, void *key) { - unsigned int b = 0, e = sarray->size, m = sarray->first_test; + unsigned int b = sarray->hash.start, e = sarray->hash.stop, m = sarray->first_test; - do { + while (b != e) { int d = sarray->comparator (key, sarray->chunk[m]); if (d == 0) return m; /* found */ @@ -56,15 +104,15 @@ else b = m + 1; m = (b + e) >> 1; - } while (b != e); + } return ~m; /* not found */ } static int _xine_sarray_find_first (xine_sarray_t *sarray, void *key) { - unsigned int b = 0, e = sarray->size, m = sarray->first_test; + unsigned int b = sarray->hash.start, e = sarray->hash.stop, m = sarray->first_test; int found = 0; - do { + while (b != e) { int d = sarray->comparator (key, sarray->chunk[m]); if (d == 0) { found = 1; @@ -75,15 +123,15 @@ b = m + 1; } m = (b + e) >> 1; - } while (b != e); + } return found ? m : ~m; } static int _xine_sarray_find_last (xine_sarray_t *sarray, void *key) { - unsigned int b = 0, e = sarray->size, m = sarray->first_test; + unsigned int b = sarray->hash.start, e = sarray->hash.stop, m = sarray->first_test; int found = 0; - do { + while (b != e) { int d = sarray->comparator (key, sarray->chunk[m]); if (d == 0) { found = 1; @@ -94,37 +142,56 @@ b = m + 1; } m = (b + e) >> 1; - } while (b != e); + } return found ? m : ~m; } int xine_sarray_binary_search (xine_sarray_t *sarray, void *key) { if (!sarray) return ~0; /* not found */ - if (sarray->size == 0) - return ~0; /* not found */ - sarray->first_test = sarray->size >> 1; + sarray->hash.get (sarray, key); + sarray->first_test = (sarray->hash.start + sarray->hash.stop) >> 1; return sarray->find (sarray, key); } +static int _xine_sarray_dummy_comp (void *item1, void *item2) { + intptr_t d = (intptr_t)item1, e = (intptr_t)item2; + + return d < e ? -1 : d > e ? 1 : 0; +} + /* Constructor */ xine_sarray_t *xine_sarray_new (size_t initial_size, xine_sarray_comparator_t comparator) { xine_sarray_t *new_sarray; + if (!comparator) + comparator = _xine_sarray_dummy_comp; if (initial_size == 0) initial_size = MIN_CHUNK_SIZE; + new_sarray = malloc (sizeof (*new_sarray) + (initial_size - 1) * sizeof (void *)); if (!new_sarray) return NULL; + + new_sarray->size = 0; + new_sarray->last_add[0] = 0; + new_sarray->last_add[1] = 0; + new_sarray->same_dir = 0; + new_sarray->hash.table = NULL; + new_sarray->hash.user_get = NULL; + new_sarray->hash.start = 0; + new_sarray->hash.stop = 0; + new_sarray->hash.value = 0; + new_sarray->hash.last_value = 0; + new_sarray->chunk_size = initial_size; new_sarray->comparator = comparator; new_sarray->find = _xine_sarray_find_default; new_sarray->chunk = &new_sarray->default_chunk[0]; - new_sarray->size = 0; new_sarray->mode = XINE_SARRAY_MODE_DEFAULT; - new_sarray->last_add[0]= 0; - new_sarray->last_add[1]= 0; - new_sarray->same_dir = 0; + new_sarray->hash.get = _xine_sarray_hash_none; + new_sarray->hash.size = 1; + return new_sarray; } @@ -133,10 +200,31 @@ if (sarray) { if (sarray->chunk != &sarray->default_chunk[0]) free (sarray->chunk); + free (sarray->hash.table); free (sarray); } } +void xine_sarray_set_hash (xine_sarray_t *sarray, xine_sarray_hash_func_t hash_func, unsigned int hash_size) { + if (!sarray) + return; + if (sarray->hash.user_get == hash_func) + return; + + free (sarray->hash.table); + sarray->hash.table = NULL; + sarray->hash.user_get = NULL; + sarray->hash.get = _xine_sarray_hash_none; + sarray->hash.size = 1; + + if (hash_func && (hash_size > 1) && (hash_size <= 4096) && + ((sarray->hash.table = calloc (1, (hash_size + 1) * sizeof (*sarray->hash.table))))) { + sarray->hash.user_get = hash_func; + sarray->hash.get = _xine_sarray_hash_get; + sarray->hash.size = hash_size; + } +} + size_t xine_sarray_size (const xine_sarray_t *sarray) { return sarray ? sarray->size : 0; } @@ -164,6 +252,12 @@ sarray->last_add[0] = 0; sarray->last_add[1] = 0; sarray->same_dir = 0; + if (sarray->hash.table) { + unsigned int u; + + for (u = 0; u <= sarray->hash.size; u++) + sarray->hash.table[u] = 0; + } } } @@ -189,7 +283,7 @@ void xine_sarray_remove (xine_sarray_t *sarray, unsigned int position) { if (sarray) { if (position < sarray->size) { - void **here = sarray->chunk + position; + void **here = sarray->chunk + position, *item = sarray->chunk[position]; unsigned int u = sarray->size - position - 1; while (u--) { here[0] = here[1]; @@ -199,6 +293,7 @@ sarray->last_add[0] = 0; sarray->last_add[1] = 0; sarray->same_dir = 0; + _xine_sarray_hash_remove (sarray, item); } } } @@ -224,6 +319,7 @@ sarray->last_add[0] = 0; sarray->last_add[1] = 0; sarray->same_dir = 0; + _xine_sarray_hash_remove (sarray, ptr); return ret; } return ~0; @@ -273,29 +369,31 @@ sarray->size++; } } + + _xine_sarray_hash_insert (sarray); } int xine_sarray_add (xine_sarray_t *sarray, void *value) { if (sarray) { unsigned int pos2; - - if (sarray->size == 0) { - pos2 = 0; - } else { - int pos1; + int pos1; - sarray->first_test = sarray->same_dir >= 2 ? sarray->last_add[0] : (sarray->size >> 1); - pos1 = sarray->find (sarray, value); - if (pos1 >= 0) { - if (sarray->mode & XINE_SARRAY_MODE_UNIQUE) - return ~pos1; - pos2 = pos1; - } else { - pos2 = ~pos1; - } + sarray->hash.get (sarray, value); + if ((sarray->same_dir >= 2) && (sarray->hash.value == sarray->hash.last_value)) + sarray->first_test = sarray->last_add[0]; + else + sarray->first_test = (sarray->hash.start + sarray->hash.stop) >> 1; + pos1 = sarray->find (sarray, value); + if (pos1 >= 0) { + if (sarray->mode & XINE_SARRAY_MODE_UNIQUE) + return ~pos1; + pos2 = pos1; + } else { + pos2 = ~pos1; } _xine_sarray_insert (sarray, pos2, value); return pos2; } return 0; } + diff -r 9e075962b659 src/xine-utils/utils.c --- a/src/xine-utils/utils.c Wed Mar 09 14:31:44 2022 +0200 +++ b/src/xine-utils/utils.c Sat Sep 24 13:04:31 2022 +0200 @@ -798,7 +798,7 @@ #ifndef WIN32 return fcntl(fd, F_SETFD, FD_CLOEXEC); #else - return SetHandleInformation((HANDLE)_get_osfhandle(fd), HANDLE_FLAG_INHERIT, 0); + return SetHandleInformation((HANDLE)_get_osfhandle(fd), HANDLE_FLAG_INHERIT, 0) ? 0 : -1; #endif } @@ -807,7 +807,7 @@ #ifndef WIN32 return fcntl(s, F_SETFD, FD_CLOEXEC); #else - return SetHandleInformation((HANDLE)(intptr_t)s, HANDLE_FLAG_INHERIT, 0); + return SetHandleInformation((HANDLE)(intptr_t)s, HANDLE_FLAG_INHERIT, 0) ? 0 : -1; #endif } @@ -1299,11 +1299,15 @@ f = fopen (filename, "rb"); if (!f) return NULL; - if (fseek (f, 0, SEEK_END)) + if (fseek (f, 0, SEEK_END)) { + fclose(f); return NULL; + } filesize = ftell (f); - if (fseek (f, 0, SEEK_SET)) + if (fseek (f, 0, SEEK_SET)) { + fclose(f); return NULL; + } if (filesize > max_size) filesize = max_size; @@ -1399,3 +1403,855 @@ *xft = NULL; } } + +typedef struct { + uint32_t refs; + uint32_t len; + uint32_t magic; +} _xine_ref_string_head_t; + +static _xine_ref_string_head_t *_xine_ref_string_head (char *s) { + const union { + uint8_t b[4]; + uint32_t v; + } _magic = {{'x', 'r', 's', 'h'}}; + _xine_ref_string_head_t *h; + + if (((uintptr_t)s & 7) != 4) + return NULL; + h = (_xine_ref_string_head_t *)s - 1; + if (h->magic != _magic.v) + return NULL; + return h; +} + +char *xine_ref_string_ref (const char *s, int len) { + const union { + uint8_t b[4]; + uint32_t v; + } _magic = {{'x', 'r', 's', 'h'}}; + uint32_t _len; + char *_s = (char *)s; + _xine_ref_string_head_t *h = _xine_ref_string_head (_s); + + if (h) { + h->refs += 1; + return _s; + } + if (!s) + return NULL; + _len = len < 0 ? strlen (s) : (size_t)len; + _s = malloc (sizeof (_xine_ref_string_head_t) + _len + 1); + if (!_s) + return NULL; + h = (_xine_ref_string_head_t *)_s; + _s += sizeof (_xine_ref_string_head_t); + h->refs = 1; + h->len = _len; + h->magic = _magic.v; + memcpy (_s, s, _len + 1); + return _s; +} + +size_t xine_ref_string_len (const char *s) { + _xine_ref_string_head_t *h = _xine_ref_string_head ((char *)s); + + return h ? h->len + : s ? strlen (s) + : 0; +} + +int xine_ref_string_unref (char **s) { + _xine_ref_string_head_t *h; + + if (!s) + return 0; + h = _xine_ref_string_head (*s); + if (!h) { + free (*s); + *s = NULL; + return 0; + } + if (h->refs == 1) { + free (h); + *s = NULL; + return 0; + } + return --h->refs; +} + + +#define XPQ_BACKLOG_LD 3 +#define XPQ_BACKLOG_SIZE (1 << XPQ_BACKLOG_LD) +#define XPQ_BACKLOG_MASK (XPQ_BACKLOG_SIZE - 1) + +typedef enum { + XPQ_A_NONE = 0, + XPQ_A_STALL, + XPQ_A_PUT, + XPQ_A_READY, + XPQ_A_GET +} xine_pts_queue_action_t; + +struct xine_pts_queue_s { + struct { + int64_t last_pts; + uint64_t pos; + struct { + int64_t pts; + uint64_t pos; + } backlog[XPQ_BACKLOG_SIZE]; + uint32_t ring_pos; + } put; + struct { + uint64_t pos; + uint32_t bytes; + uint32_t num; + } get; + xine_pts_queue_action_t last_action; +}; + +xine_pts_queue_t *xine_pts_queue_new (void) { + xine_pts_queue_t *q = calloc (1, sizeof (*q)); + return q; +} + +void xine_pts_queue_reset (xine_pts_queue_t *q) { + if (!q) + return; + memset (q, 0, sizeof (*q)); +} + +void xine_pts_queue_put (xine_pts_queue_t *q, size_t bytes, int64_t pts) { + xine_pts_queue_action_t a = bytes ? XPQ_A_PUT : XPQ_A_READY; + if (!q) + return; + if (pts && (pts != q->put.last_pts)) { + uint32_t u = q->put.ring_pos; + q->put.last_pts = pts; + if (q->last_action != XPQ_A_STALL) { + u = (u + 1) & XPQ_BACKLOG_MASK; + } else { + a = XPQ_A_STALL; + q->get.pos = q->put.backlog[u].pos; + } + q->put.ring_pos = u; + if (q->put.backlog[u].pts) { + /* backlog overrun. parser seems be dropping data. */ + q->get.pos = q->put.backlog[u].pos; + memset (q->put.backlog, 0, sizeof (q->put.backlog)); + a = XPQ_A_STALL; + } + q->put.backlog[u].pts = pts; + q->put.backlog[u].pos = q->put.pos; + } + q->put.pos += bytes; + q->last_action = a; +} + +int64_t xine_pts_queue_get (xine_pts_queue_t *q, size_t bytes) { + int64_t pts = 0; + uint32_t u; + if (!q) + return 0; + /* TODO: parser dropped data. */ + /* find suitable pts, or 0. */ + u = q->put.ring_pos; + do { + if (q->put.backlog[u].pos <= q->get.pos) + break; + u = (u + XPQ_BACKLOG_SIZE - 1) & XPQ_BACKLOG_MASK; + } while (u != q->put.ring_pos); + if (q->put.backlog[u].pos <= q->get.pos) { + pts = q->put.backlog[u].pts; + /* bytes == 0: just peek. bytes != 0: consume. */ + if (bytes) { + q->put.backlog[u].pos = 0; + q->put.backlog[u].pts = 0; + } + } + /* advance. */ + q->get.pos += bytes; + /* parser returned more than it has (filler frames, generated heads). */ + if (q->get.pos > q->put.pos) + q->get.pos = q->put.pos; + /* frame size stats. */ + q->get.bytes += bytes; + q->get.num++; + if ((q->get.bytes | q->get.num) & 0x80000000) { + q->get.bytes >>= 1; + q->get.num >>= 1; + } + q->last_action = XPQ_A_GET; + return pts; +} + +void xine_pts_queue_delete (xine_pts_queue_t **q) { + if (q && *q) { + free (*q); + *q = NULL; + } +} + + +/** xine timespec magic, taken from TJtools. */ +int xine_ts_from_string (struct timespec *ts, const char *s) { +# define _DC_DIGIT 1 +# define _DC_SPACE 2 +# define _DC_Tt 4 +# define _DC_Zz 8 +# define _DC_PLUS 16 +# define _DC_MINUS 32 +# define _DC_DOT 64 +# define _DC_END 128 + static const uint8_t tab_char[256] = { + ['0'] = _DC_DIGIT, ['1'] = _DC_DIGIT, ['2'] = _DC_DIGIT, ['3'] = _DC_DIGIT, + ['4'] = _DC_DIGIT, ['5'] = _DC_DIGIT, ['6'] = _DC_DIGIT, ['7'] = _DC_DIGIT, + ['8'] = _DC_DIGIT, ['9'] = _DC_DIGIT, + ['\t'] = _DC_SPACE, ['\r'] = _DC_SPACE, ['\n'] = _DC_SPACE, [' '] = _DC_SPACE, + ['T'] = _DC_Tt, ['t'] = _DC_Tt, + ['Z'] = _DC_Zz, ['z'] = _DC_Zz, + ['+'] = _DC_PLUS, + ['-'] = _DC_MINUS, + ['.'] = _DC_DOT, + [0] = _DC_END + }; + enum { + _DV_YEAR = 0, /* 1900- */ + _DV_MONTH, /* 1-12 */ + _DV_DAY, /* 1-31 */ + _DV_WEEKDAY, /* 0-6 */ + _DV_AM, /* 0 (24h), 1 (12-11 before noon), 2 (12-11 after noon) */ + _DV_HOUR, /* 0-23 */ + _DV_MINUTE, /* 0-59 */ + _DV_SECOND, /* 0-59 */ + _DV_FRAC, /* 0-999999999 */ + _DV_OFFS, /* in seconds */ + _DV_LAST + }; + static const struct { + char s[11]; + uint8_t type; + int value; + } strings [] = { + {"am ", _DV_AM, 1}, + {"april ", _DV_MONTH, 4}, + {"august ", _DV_MONTH, 8}, + {"cdt ", _DV_OFFS, -18000}, + {"cst ", _DV_OFFS, -21600}, + {"december ", _DV_MONTH, 12}, + {"edt ", _DV_OFFS, -14400}, + {"est ", _DV_OFFS, -18000}, + {"february ", _DV_MONTH, 2}, + {"friday ", _DV_WEEKDAY, 5}, + {"gmt ", _DV_OFFS, 0}, + {"january ", _DV_MONTH, 1}, + {"july ", _DV_MONTH, 7}, + {"june ", _DV_MONTH, 6}, + {"march ", _DV_MONTH, 3}, + {"may ", _DV_MONTH, 5}, + {"mdt ", _DV_OFFS, -21600}, + {"monday ", _DV_WEEKDAY, 1}, + {"mst ", _DV_OFFS, -25200}, + {"november ", _DV_MONTH, 11}, + {"october ", _DV_MONTH, 10}, + {"pdt ", _DV_OFFS, -25200}, + {"pm ", _DV_AM, 2}, + {"pst ", _DV_OFFS, -28800}, + {"saturday ", _DV_WEEKDAY, 6}, + {"september ", _DV_MONTH, 9}, + {"sunday ", _DV_WEEKDAY, 0}, + {"thursday ", _DV_WEEKDAY, 4}, + {"tuesday ", _DV_WEEKDAY, 2}, + {"utc ", _DV_OFFS, 0}, + {"wednesday ", _DV_WEEKDAY, 3}, + }; + time_t res; + int value[_DV_LAST] = { + [_DV_YEAR] = 1970, + [_DV_MONTH] = 1, + [_DV_DAY] = 1 + }; +#define _DV_HAVE_DATE 1 +#define _DV_HAVE_TIME 2 +#define _DV_HAVE_ZONE 4 +#define _DV_HAVE_JTIME 16 + static const uint32_t word_have[_DV_LAST] = { + [_DV_OFFS] = _DV_HAVE_ZONE + }; + static const uint32_t frac[] = { + 100000000, 10000000, 1000000, + 100000, 10000, 1000, + 100, 10, 1 + }; + uint64_t jtime = 0; + uint32_t have = 0; + const uint8_t *p; + + if (!ts) + return EINVAL; + if (!s) + return 0; + + p = (const uint8_t *)s; + + if (((p[0] | 0x20) == 'p') && ((p[1] | 0x20) == 't')) { + /* PT5H30M55S */ + uint32_t _sec = 0, _frac = 0; + p += 2; + while (1) { + uint32_t v = 0, f = 0, z; + while ((z = p[0] ^ '0') < 10u) + v = v * 10u + z, p++; + if (p[0] == '.') { + uint32_t u = 0; + p++; + while (((z = p[0] ^ '0') < 10u) && (u < 9)) + f += frac[u++] * z, p++; + while ((z = p[0] ^ '0') < 10u) + p++; + } + z = p[0] | 0x20; + if (z == 'h') + _sec += 3600u * v; + else if (z == 'm') + _sec += 60u * v; + else if (z == 's') + _sec += v, _frac = f; + else + break; + p++; + } + ts->tv_sec = _sec; + ts->tv_nsec = _frac; + return 0; + } + + do { + const uint8_t *b; + uint8_t buf[12], type; + uint32_t len, digits = 0; + /* skip whitespace */ + while (tab_char[*p] & _DC_SPACE) + p++; + /* get word */ + type = tab_char[*p]; + if (type & _DC_Tt) + type = tab_char[*++p]; + b = p; + if (type & _DC_Zz) { + type = tab_char[*++p]; + if (type & (_DC_PLUS + _DC_MINUS)) { + b = p; + type = tab_char[*++p]; + } + } + if (type & (_DC_PLUS + _DC_MINUS + _DC_DOT)) + p++; + while (1) { + while (!((type = tab_char[*p]) & (_DC_SPACE + _DC_Tt + _DC_Zz + _DC_PLUS + _DC_MINUS + _DC_DOT + _DC_END))) + digits += type & _DC_DIGIT, p++; + len = p - b; + if (type & (_DC_SPACE + _DC_PLUS + _DC_DOT + _DC_END)) + break; + if ((type & (_DC_Tt + _DC_Zz)) && digits) + break; + if ((type & _DC_MINUS) && !(((len == 4) && (digits == 4)) || ((len == 7) && (digits == 6)))) + break; + p++; + } + /* evaluate */ + if ((len > 5) && (digits == len)) { + uint64_t v = 0; + uint32_t u; + for (u = 0; u < len; u++) + v = v * 10u + (b[u] ^ '0'); + jtime = v; + have |= _DV_HAVE_JTIME; + } else if ((len > 1) && (digits + 1 == len) && (b[0] == '@')) { + uint64_t v = 0; + uint32_t u; + for (u = 1; u < len; u++) + v = v * 10u + (b[u] ^ '0'); + jtime = v; + have |= _DV_HAVE_JTIME; + } else if ((digits + 1 == len) && (b[0] == '.')) { + uint32_t u; + b++; + len--; + if (len > 9) + len = 9; + value[_DV_FRAC] = 0; + for (u = 0; u < len; u++) + value[_DV_FRAC] += frac[u] * (b[u] ^ '0'); + } else if ((len == 2) && (digits == 2)) { + /* DD */ + value[_DV_DAY] = (b[0] ^ '0') * 10u + (b[1] ^ '0'); + } else if ((len == 4) && (digits >= 3)) { + if ((digits == 3) && (b[1] == ':')) { + /* h:mm */ + value[_DV_HOUR] = b[0] ^ '0'; + value[_DV_MINUTE] = (b[2] ^ '0') * 10u + (b[3] ^ '0'); + value[_DV_SECOND] = 0; + have |= _DV_HAVE_TIME; + } else if (digits == 4) { + /* YYYY */ + value[_DV_YEAR] = (b[0] ^ '0') * 1000u + (b[1] ^ '0') * 100u + (b[2] ^ '0') * 10u + (b[3] ^ '0'); + have |= _DV_HAVE_DATE; + } + } else if ((len == 5) && (digits == 4)) { + if (b[2] == ':') { + /* hh:mm */ + value[_DV_HOUR] = (b[0] ^ '0') * 10u + (b[1] ^ '0'); + value[_DV_MINUTE] = (b[3] ^ '0') * 10u + (b[4] ^ '0'); + value[_DV_SECOND] = 0; + have |= _DV_HAVE_TIME; + } else if (tab_char[b[0]] & (_DC_Zz + _DC_PLUS + _DC_MINUS)) { + /* [Zz+-]ohom */ + value[_DV_OFFS] = (b[1] ^ '0') * 36000u + (b[2] ^ '0') * 3600u + (b[3] ^ '0') * 600u + (b[4] ^ '0') * 60u; + if (b[0] == '-') + value[_DV_OFFS] = -value[_DV_OFFS]; + have |= _DV_HAVE_ZONE; + } + } else if ((len == 7) && (digits == 5) && (b[1] == ':') && (b[4] == ':')) { + /* h:mm:ss */ + value[_DV_HOUR] = b[0] ^ '0'; + value[_DV_MINUTE] = (b[2] ^ '0') * 10u + (b[3] ^ '0'); + value[_DV_SECOND] = (b[5] ^ '0') * 10u + (b[6] ^ '0'); + have |= _DV_HAVE_TIME; + } else if ((len == 6) && (digits == 4) && (tab_char[b[0]] & (_DC_Zz + _DC_PLUS + _DC_MINUS)) && (b[3] == ':')) { + /* [Zz+-]oh:om */ + value[_DV_OFFS] = (b[1] ^ '0') * 36000u + (b[2] ^ '0') * 3600u + (b[4] ^ '0') * 600u + (b[5] ^ '0') * 60u; + if (b[0] == '-') + value[_DV_OFFS] = -value[_DV_OFFS]; + have |= _DV_HAVE_ZONE; + } else if ((len == 8) && (digits == 6)) { + if ((b[2] == ':') && (b[5] == ':')) { + /* hh:mm:ss */ + value[_DV_HOUR] = (b[0] ^ '0') * 10u + (b[1] ^ '0'); + value[_DV_MINUTE] = (b[3] ^ '0') * 10u + (b[4] ^ '0'); + value[_DV_SECOND] = (b[6] ^ '0') * 10u + (b[7] ^ '0'); + have |= _DV_HAVE_TIME; + } else if ((b[2] == '/') && (b[5] == '/')) { + /* MM/DD/YY */ + value[_DV_MONTH] = (b[0] ^ '0') * 10u + (b[1] ^ '0'); + value[_DV_DAY] = (b[3] ^ '0') * 10u + (b[4] ^ '0'); + value[_DV_YEAR] = (b[6] ^ '0') * 10u + (b[7] ^ '0'); + value[_DV_YEAR] += (value[_DV_YEAR] < 70) ? 2000 : 1900; + have |= _DV_HAVE_DATE; + } + } else if ((len == 10) && (digits == 8)) { + if ((b[2] == '/') && (b[5] == '/')) { + /* MM/DD/YYYY */ + value[_DV_MONTH] = (b[0] ^ '0') * 10u + (b[1] ^ '0'); + value[_DV_DAY] = (b[3] ^ '0') * 10u + (b[4] ^ '0'); + value[_DV_YEAR] = (b[6] ^ '0') * 1000u + (b[7] ^ '0') * 100u + (b[8] ^ '0') * 10u + (b[9] ^ '0'); + have |= _DV_HAVE_DATE; + } else if ((b[4] == '-') && (b[7] == '-')) { + /* YYYY-MM-DD */ + value[_DV_YEAR] = (b[0] ^ '0') * 1000u + (b[1] ^ '0') * 100u + (b[2] ^ '0') * 10u + (b[3] ^ '0'); + value[_DV_MONTH] = (b[5] ^ '0') * 10u + (b[6] ^ '0'); + value[_DV_DAY] = (b[8] ^ '0') * 10u + (b[9] ^ '0'); + have |= _DV_HAVE_DATE; + } + } else if ((len > 0) && (len < sizeof (buf))) { + uint32_t _b = 0, _m, _e = sizeof (strings) / sizeof (strings[0]); + int _d; + /* known word */ + memset (buf, ' ', sizeof (buf)); + for (_m = 0; _m < len; _m++) + buf[_m] |= b[_m]; + do { + _m = (_b + _e) >> 1; + _d = memcmp (buf, strings[_m].s, sizeof (buf) - 1); + if (_d < 0) { + _e = _m; + } else if (_d > 0) { + _b = _m + 1; + } else { + break; + } + } while (_b < _e); + if (!_d) { + value[strings[_m].type] = strings[_m].value; + have |= word_have[strings[_m].type]; + } + } + } while (*p); + if (value[_DV_AM]) { + if ((value[_DV_AM] == 1) && (value[_DV_HOUR] >= 12)) + value[_DV_HOUR] -= 12; + else if ((value[_DV_AM] == 2) && (value[_DV_HOUR] < 12)) + value[_DV_HOUR] += 12; + } + /* relative time */ + if ((have & (_DV_HAVE_DATE + _DV_HAVE_TIME)) == 0) { + if (have & _DV_HAVE_JTIME) { + ts->tv_sec = jtime; + ts->tv_nsec = value[_DV_FRAC]; + } + return 0; + } + if ((have & _DV_HAVE_DATE) == 0) { + ts->tv_sec = ts->tv_sec / (24 * 60 * 60) * (24 * 60 * 60) + + value[_DV_HOUR] * 3600 + value[_DV_MINUTE] * 60 + value[_DV_SECOND] + - value[_DV_OFFS]; + ts->tv_nsec = value[_DV_FRAC]; + return 0; + } + /* with date */ + { + struct tm tm = { + .tm_sec = value[_DV_SECOND], + .tm_min = value[_DV_MINUTE], + .tm_hour = value[_DV_HOUR], + .tm_mday = value[_DV_DAY], + .tm_mon = value[_DV_MONTH] - 1, + .tm_year = value[_DV_YEAR] - 1900, + .tm_wday = value[_DV_WEEKDAY], + .tm_isdst = 0 + }; + const char *tz = getenv ("TZ"); + if (tz) { + if (tz[0]) { + char *_tz = strdup (tz); + setenv ("TZ", "", 1); + tzset (); + res = mktime (&tm); + setenv ("TZ", _tz, 1); + free (_tz); + tzset (); + } else { + tzset (); + res = mktime (&tm); + } + } else { + setenv ("TZ", "", 1); + tzset (); + res = mktime (&tm); + unsetenv ("TZ"); + tzset (); + } + } + if (res == -1) + return errno; + res -= value[_DV_OFFS]; + ts->tv_sec = res; + ts->tv_nsec = value[_DV_FRAC]; + return 0; +} + +void xine_ts_add (struct timespec *a, const struct timespec *b) { + if (!a || !b) + return; + a->tv_sec += b->tv_sec; + a->tv_nsec += b->tv_nsec; + if (a->tv_nsec >= 1000000000) { + a->tv_nsec -= 1000000000; + a->tv_sec += 1; + } +} + +void xine_ts_sub (struct timespec *a, const struct timespec *b) { + if (!a || !b) + return; + a->tv_sec -= b->tv_sec; + a->tv_nsec -= b->tv_nsec; + if (a->tv_nsec < 0) { + a->tv_nsec += 1000000000; + a->tv_sec -= 1; + } +} + +int64_t xine_ts_to_timebase (const struct timespec *ts, uint32_t timebase) { + uint32_t fracbase; + int64_t res; + if (!ts || !timebase) + return 0; + fracbase = (1000000000u + (timebase >> 1)) / timebase; + res = (int64_t)ts->tv_sec * (int32_t)timebase; + if (fracbase) + res += ((uint32_t)ts->tv_nsec + (fracbase >> 1)) / fracbase; + return res; +} + +/** xine rational numbers, taken from TJtools. */ +void xine_rats_shorten (xine_rats_t *value) { + static const unsigned char primediffs[] = { + /* just say 'erat -d 75 3 32768' ;-) */ + 3, 2, 2, 4, 2, 4, 2, 4, 6, 2, 6, 4, 2, 4, 6, 6, 2, 6, + 4, 2, 6, 4, 6, 8, 4, 2, 4, 2, 4, 14, 4, 6, 2, 10, 2, 6, + 6, 4, 6, 6, 2, 10, 2, 4, 2, 12, 12, 4, 2, 4, 6, 2, 10, 6, + 6, 6, 2, 6, 4, 2, 10, 14, 4, 2, 4, 14, 6, 10, 2, 4, 6, 8, + 6, 6, 4, 6, 8, 4, 8, 10, 2, 10, 2, 6, 4, 6, 8, 4, 2, 4, + 12, 8, 4, 8, 4, 6, 12, 2, 18, 6, 10, 6, 6, 2, 6, 10, 6, 6, + 2, 6, 6, 4, 2, 12, 10, 2, 4, 6, 6, 2, 12, 4, 6, 8, 10, 8, + 10, 8, 6, 6, 4, 8, 6, 4, 8, 4, 14, 10, 12, 2, 10, 2, 4, 2, + 10, 14, 4, 2, 4, 14, 4, 2, 4, 20, 4, 8, 10, 8, 4, 6, 6, 14, + 4, 6, 6, 8, 6, 12, 4, 6, 2, 10, 2, 6, 10, 2, 10, 2, 6, 18, + 4, 2, 4, 6, 6, 8, 6, 6, 22, 2, 10, 8, 10, 6, 6, 8, 12, 4, + 6, 6, 2, 6, 12, 10, 18, 2, 4, 6, 2, 6, 4, 2, 4, 12, 2, 6, + 34, 6, 6, 8, 18, 10, 14, 4, 2, 4, 6, 8, 4, 2, 6, 12, 10, 2, + 4, 2, 4, 6, 12, 12, 8, 12, 6, 4, 6, 8, 4, 8, 4, 14, 4, 6, + 2, 4, 6, 2, 6, 10, 20, 6, 4, 2, 24, 4, 2, 10, 12, 2, 10, 8, + 6, 6, 6, 18, 6, 4, 2, 12, 10, 12, 8, 16, 14, 6, 4, 2, 4, 2, + 10, 12, 6, 6, 18, 2, 16, 2, 22, 6, 8, 6, 4, 2, 4, 8, 6, 10, + 2, 10, 14, 10, 6, 12, 2, 4, 2, 10, 12, 2, 16, 2, 6, 4, 2, 10, + 8, 18, 24, 4, 6, 8, 16, 2, 4, 8, 16, 2, 4, 8, 6, 6, 4, 12, + 2, 22, 6, 2, 6, 4, 6, 14, 6, 4, 2, 6, 4, 6, 12, 6, 6, 14, + 4, 6, 12, 8, 6, 4, 26, 18, 10, 8, 4, 6, 2, 6, 22, 12, 2, 16, + 8, 4, 12, 14, 10, 2, 4, 8, 6, 6, 4, 2, 4, 6, 8, 4, 2, 6, + 10, 2, 10, 8, 4, 14, 10, 12, 2, 6, 4, 2, 16, 14, 4, 6, 8, 6, + 4, 18, 8, 10, 6, 6, 8, 10, 12, 14, 4, 6, 6, 2, 28, 2, 10, 8, + 4, 14, 4, 8, 12, 6, 12, 4, 6, 20, 10, 2, 16, 26, 4, 2, 12, 6, + 4, 12, 6, 8, 4, 8, 22, 2, 4, 2, 12, 28, 2, 6, 6, 6, 4, 6, + 2, 12, 4, 12, 2, 10, 2, 16, 2, 16, 6, 20, 16, 8, 4, 2, 4, 2, + 22, 8, 12, 6, 10, 2, 4, 6, 2, 6, 10, 2, 12, 10, 2, 10, 14, 6, + 4, 6, 8, 6, 6, 16, 12, 2, 4, 14, 6, 4, 8, 10, 8, 6, 6, 22, + 6, 2, 10, 14, 4, 6, 18, 2, 10, 14, 4, 2, 10, 14, 4, 8, 18, 4, + 6, 2, 4, 6, 2, 12, 4, 20, 22, 12, 2, 4, 6, 6, 2, 6, 22, 2, + 6, 16, 6, 12, 2, 6, 12, 16, 2, 4, 6, 14, 4, 2, 18, 24, 10, 6, + 2, 10, 2, 10, 2, 10, 6, 2, 10, 2, 10, 6, 8, 30, 10, 2, 10, 8, + 6, 10, 18, 6, 12, 12, 2, 18, 6, 4, 6, 6, 18, 2, 10, 14, 6, 4, + 2, 4, 24, 2, 12, 6, 16, 8, 6, 6, 18, 16, 2, 4, 6, 2, 6, 6, + 10, 6, 12, 12, 18, 2, 6, 4, 18, 8, 24, 4, 2, 4, 6, 2, 12, 4, + 14, 30, 10, 6, 12, 14, 6, 10, 12, 2, 4, 6, 8, 6, 10, 2, 4, 14, + 6, 6, 4, 6, 2, 10, 2, 16, 12, 8, 18, 4, 6, 12, 2, 6, 6, 6, + 28, 6, 14, 4, 8, 10, 8, 12, 18, 4, 2, 4, 24, 12, 6, 2, 16, 6, + 6, 14, 10, 14, 4, 30, 6, 6, 6, 8, 6, 4, 2, 12, 6, 4, 2, 6, + 22, 6, 2, 4, 18, 2, 4, 12, 2, 6, 4, 26, 6, 6, 4, 8, 10, 32, + 16, 2, 6, 4, 2, 4, 2, 10, 14, 6, 4, 8, 10, 6, 20, 4, 2, 6, + 30, 4, 8, 10, 6, 6, 8, 6, 12, 4, 6, 2, 6, 4, 6, 2, 10, 2, + 16, 6, 20, 4, 12, 14, 28, 6, 20, 4, 18, 8, 6, 4, 6, 14, 6, 6, + 10, 2, 10, 12, 8, 10, 2, 10, 8, 12, 10, 24, 2, 4, 8, 6, 4, 8, + 18, 10, 6, 6, 2, 6, 10, 12, 2, 10, 6, 6, 6, 8, 6, 10, 6, 2, + 6, 6, 6, 10, 8, 24, 6, 22, 2, 18, 4, 8, 10, 30, 8, 18, 4, 2, + 10, 6, 2, 6, 4, 18, 8, 12, 18, 16, 6, 2, 12, 6, 10, 2, 10, 2, + 6, 10, 14, 4, 24, 2, 16, 2, 10, 2, 10, 20, 4, 2, 4, 8, 16, 6, + 6, 2, 12, 16, 8, 4, 6, 30, 2, 10, 2, 6, 4, 6, 6, 8, 6, 4, + 12, 6, 8, 12, 4, 14, 12, 10, 24, 6, 12, 6, 2, 22, 8, 18, 10, 6, + 14, 4, 2, 6, 10, 8, 6, 4, 6, 30, 14, 10, 2, 12, 10, 2, 16, 2, + 18, 24, 18, 6, 16, 18, 6, 2, 18, 4, 6, 2, 10, 8, 10, 6, 6, 8, + 4, 6, 2, 10, 2, 12, 4, 6, 6, 2, 12, 4, 14, 18, 4, 6, 20, 4, + 8, 6, 4, 8, 4, 14, 6, 4, 14, 12, 4, 2, 30, 4, 24, 6, 6, 12, + 12, 14, 6, 4, 2, 4, 18, 6, 12, 8, 6, 4, 12, 2, 12, 30, 16, 2, + 6, 22, 14, 6, 10, 12, 6, 2, 4, 8, 10, 6, 6, 24, 14, 6, 4, 8, + 12, 18, 10, 2, 10, 2, 4, 6, 20, 6, 4, 14, 4, 2, 4, 14, 6, 12, + 24, 10, 6, 8, 10, 2, 30, 4, 6, 2, 12, 4, 14, 6, 34, 12, 8, 6, + 10, 2, 4, 20, 10, 8, 16, 2, 10, 14, 4, 2, 12, 6, 16, 6, 8, 4, + 8, 4, 6, 8, 6, 6, 12, 6, 4, 6, 6, 8, 18, 4, 20, 4, 12, 2, + 10, 6, 2, 10, 12, 2, 4, 20, 6, 30, 6, 4, 8, 10, 12, 6, 2, 28, + 2, 6, 4, 2, 16, 12, 2, 6, 10, 8, 24, 12, 6, 18, 6, 4, 14, 6, + 4, 12, 8, 6, 12, 4, 6, 12, 6, 12, 2, 16, 20, 4, 2, 10, 18, 8, + 4, 14, 4, 2, 6, 22, 6, 14, 6, 6, 10, 6, 2, 10, 2, 4, 2, 22, + 2, 4, 6, 6, 12, 6, 14, 10, 12, 6, 8, 4, 36, 14, 12, 6, 4, 6, + 2, 12, 6, 12, 16, 2, 10, 8, 22, 2, 12, 6, 4, 6, 18, 2, 12, 6, + 4, 12, 8, 6, 12, 4, 6, 12, 6, 2, 12, 12, 4, 14, 6, 16, 6, 2, + 10, 8, 18, 6, 34, 2, 28, 2, 22, 6, 2, 10, 12, 2, 6, 4, 8, 22, + 6, 2, 10, 8, 4, 6, 8, 4, 12, 18, 12, 20, 4, 6, 6, 8, 4, 2, + 16, 12, 2, 10, 8, 10, 2, 4, 6, 14, 12, 22, 8, 28, 2, 4, 20, 4, + 2, 4, 14, 10, 12, 2, 12, 16, 2, 28, 8, 22, 8, 4, 6, 6, 14, 4, + 8, 12, 6, 6, 4, 20, 4, 18, 2, 12, 6, 4, 6, 14, 18, 10, 8, 10, + 32, 6, 10, 6, 6, 2, 6, 16, 6, 2, 12, 6, 28, 2, 10, 8, 16, 6, + 8, 6, 10, 24, 20, 10, 2, 10, 2, 12, 4, 6, 20, 4, 2, 12, 18, 10, + 2, 10, 2, 4, 20, 16, 26, 4, 8, 6, 4, 12, 6, 8, 12, 12, 6, 4, + 8, 22, 2, 16, 14, 10, 6, 12, 12, 14, 6, 4, 20, 4, 12, 6, 2, 6, + 6, 16, 8, 22, 2, 28, 8, 6, 4, 20, 4, 12, 24, 20, 4, 8, 10, 2, + 16, 2, 12, 12, 34, 2, 4, 6, 12, 6, 6, 8, 6, 4, 2, 6, 24, 4, + 20, 10, 6, 6, 14, 4, 6, 6, 2, 12, 6, 10, 2, 10, 6, 20, 4, 26, + 4, 2, 6, 22, 2, 24, 4, 6, 2, 4, 6, 24, 6, 8, 4, 2, 34, 6, + 8, 16, 12, 2, 10, 2, 10, 6, 8, 4, 8, 12, 22, 6, 14, 4, 26, 4, + 2, 12, 10, 8, 4, 8, 12, 4, 14, 6, 16, 6, 8, 4, 6, 6, 8, 6, + 10, 12, 2, 6, 6, 16, 8, 6, 6, 12, 10, 2, 6, 18, 4, 6, 6, 6, + 12, 18, 8, 6, 10, 8, 18, 4, 14, 6, 18, 10, 8, 10, 12, 2, 6, 12, + 12, 36, 4, 6, 8, 4, 6, 2, 4, 18, 12, 6, 8, 6, 6, 4, 18, 2, + 4, 2, 24, 4, 6, 6, 14, 30, 6, 4, 6, 12, 6, 20, 4, 8, 4, 8, + 6, 6, 4, 30, 2, 10, 12, 8, 10, 8, 24, 6, 12, 4, 14, 4, 6, 2, + 28, 14, 16, 2, 12, 6, 4, 20, 10, 6, 6, 6, 8, 10, 12, 14, 10, 14, + 16, 14, 10, 14, 6, 16, 6, 8, 6, 16, 20, 10, 2, 6, 4, 2, 4, 12, + 2, 10, 2, 6, 22, 6, 2, 4, 18, 8, 10, 8, 22, 2, 10, 18, 14, 4, + 2, 4, 18, 2, 4, 6, 8, 10, 2, 30, 4, 30, 2, 10, 2, 18, 4, 18, + 6, 14, 10, 2, 4, 20, 36, 6, 4, 6, 14, 4, 20, 10, 14, 22, 6, 2, + 30, 12, 10, 18, 2, 4, 14, 6, 22, 18, 2, 12, 6, 4, 8, 4, 8, 6, + 10, 2, 12, 18, 10, 14, 16, 14, 4, 6, 6, 2, 6, 4, 2, 28, 2, 28, + 6, 2, 4, 6, 14, 4, 12, 14, 16, 14, 4, 6, 8, 6, 4, 6, 6, 6, + 8, 4, 8, 4, 14, 16, 8, 6, 4, 12, 8, 16, 2, 10, 8, 4, 6, 26, + 6, 10, 8, 4, 6, 12, 14, 30, 4, 14, 22, 8, 12, 4, 6, 8, 10, 6, + 14, 10, 6, 2, 10, 12, 12, 14, 6, 6, 18, 10, 6, 8, 18, 4, 6, 2, + 6, 10, 2, 10, 8, 6, 6, 10, 2, 18, 10, 2, 12, 4, 6, 8, 10, 12, + 14, 12, 4, 8, 10, 6, 6, 20, 4, 14, 16, 14, 10, 8, 10, 12, 2, 18, + 6, 12, 10, 12, 2, 4, 2, 12, 6, 4, 8, 4, 44, 4, 2, 4, 2, 10, + 12, 6, 6, 14, 4, 6, 6, 6, 8, 6, 36, 18, 4, 6, 2, 12, 6, 6, + 6, 4, 14, 22, 12, 2, 18, 10, 6, 26, 24, 4, 2, 4, 2, 4, 14, 4, + 6, 6, 8, 16, 12, 2, 42, 4, 2, 4, 24, 6, 6, 2, 18, 4, 14, 6, + 28, 18, 14, 6, 10, 12, 2, 6, 12, 30, 6, 4, 6, 6, 14, 4, 2, 24, + 4, 6, 6, 26, 10, 18, 6, 8, 6, 6, 30, 4, 12, 12, 2, 16, 2, 6, + 4, 12, 18, 2, 6, 4, 26, 12, 6, 12, 4, 24, 24, 12, 6, 2, 12, 28, + 8, 4, 6, 12, 2, 18, 6, 4, 6, 6, 20, 16, 2, 6, 6, 18, 10, 6, + 2, 4, 8, 6, 6, 24, 16, 6, 8, 10, 6, 14, 22, 8, 16, 6, 2, 12, + 4, 2, 22, 8, 18, 34, 2, 6, 18, 4, 6, 6, 8, 10, 8, 18, 6, 4, + 2, 4, 8, 16, 2, 12, 12, 6, 18, 4, 6, 6, 6, 2, 6, 12, 10, 20, + 12, 18, 4, 6, 2, 16, 2, 10, 14, 4, 30, 2, 10, 12, 2, 24, 6, 16, + 8, 10, 2, 12, 22, 6, 2, 16, 20, 10, 2, 12, 12, 18, 10, 12, 6, 2, + 10, 2, 6, 10, 18, 2, 12, 6, 4, 6, 2, 24, 28, 2, 4, 2, 10, 2, + 16, 12, 8, 22, 2, 6, 4, 2, 10, 6, 20, 12, 10, 8, 12, 6, 6, 6, + 4, 18, 2, 4, 12, 18, 2, 12, 6, 4, 2, 16, 12, 12, 14, 4, 8, 18, + 4, 12, 14, 6, 6, 4, 8, 6, 4, 20, 12, 10, 14, 4, 2, 16, 2, 12, + 30, 4, 6, 24, 20, 24, 10, 8, 12, 10, 12, 6, 12, 12, 6, 8, 16, 14, + 6, 4, 6, 36, 20, 10, 30, 12, 2, 4, 2, 28, 12, 14, 6, 22, 8, 4, + 18, 6, 14, 18, 4, 6, 2, 6, 34, 18, 2, 16, 6, 18, 2, 24, 4, 2, + 6, 12, 6, 12, 10, 8, 6, 16, 12, 8, 10, 14, 40, 6, 2, 6, 4, 12, + 14, 4, 2, 4, 2, 4, 8, 6, 10, 6, 6, 2, 6, 6, 6, 12, 6, 24, + 10, 2, 10, 6, 12, 6, 6, 14, 6, 6, 52, 20, 6, 10, 2, 10, 8, 10, + 12, 12, 2, 6, 4, 14, 16, 8, 12, 6, 22, 2, 10, 8, 6, 22, 2, 22, + 6, 8, 10, 12, 12, 2, 10, 6, 12, 2, 4, 14, 10, 2, 6, 18, 4, 12, + 8, 18, 12, 6, 6, 4, 6, 6, 14, 4, 2, 12, 12, 4, 6, 18, 18, 12, + 2, 16, 12, 8, 18, 10, 26, 4, 6, 8, 6, 6, 4, 2, 10, 20, 4, 6, + 8, 4, 20, 10, 2, 34, 2, 4, 24, 2, 12, 12, 10, 6, 2, 12, 30, 6, + 12, 16, 12, 2, 22, 18, 12, 14, 10, 2, 12, 12, 4, 2, 4, 6, 12, 2, + 16, 18, 2, 40, 8, 16, 6, 8, 10, 2, 4, 18, 8, 10, 8, 12, 4, 18, + 2, 18, 10, 2, 4, 2, 4, 8, 28, 2, 6, 22, 12, 6, 14, 18, 4, 6, + 8, 6, 6, 10, 8, 4, 2, 18, 10, 6, 20, 22, 8, 6, 30, 4, 2, 4, + 18, 6, 30, 2, 4, 8, 6, 4, 6, 12, 14, 34, 14, 6, 4, 2, 6, 4, + 14, 4, 2, 6, 28, 2, 4, 6, 8, 10, 2, 10, 2, 10, 2, 4, 30, 2, + 12, 12, 10, 18, 12, 14, 10, 2, 12, 6, 10, 6, 14, 12, 4, 14, 4, 18, + 2, 10, 8, 4, 8, 10, 12, 18, 18, 8, 6, 18, 16, 14, 6, 6, 10, 14, + 4, 6, 2, 12, 12, 4, 6, 6, 12, 2, 16, 2, 12, 6, 4, 14, 6, 4, + 2, 12, 18, 4, 36, 18, 12, 12, 2, 4, 2, 4, 8, 12, 4, 36, 6, 18, + 2, 12, 10, 6, 12, 24, 8, 6, 6, 16, 12, 2, 18, 10, 20, 10, 2, 6, + 18, 4, 2, 40, 6, 2, 16, 2, 4, 8, 18, 10, 12, 6, 2, 10, 8, 4, + 6, 12, 2, 10, 18, 8, 6, 4, 20, 4, 6, 36, 6, 2, 10, 6, 24, 6, + 14, 16, 6, 18, 2, 10, 20, 10, 8, 6, 4, 6, 2, 10, 2, 12, 4, 2, + 4, 8, 10, 6, 12, 18, 14, 12, 16, 8, 6, 16, 8, 4, 2, 6, 18, 24, + 18, 10, 12, 2, 4, 14, 10, 6, 6, 6, 18, 12, 2, 28, 18, 14, 16, 12, + 14, 24, 12, 22, 6, 2, 10, 8, 4, 2, 4, 14, 12, 6, 4, 6, 14, 4, + 2, 4, 30, 6, 2, 6, 10, 2, 30, 22, 2, 4, 6, 8, 6, 6, 16, 12, + 12, 6, 8, 4, 2, 24, 12, 4, 6, 8, 6, 6, 10, 2, 6, 12, 28, 14, + 6, 4, 12, 8, 6, 12, 4, 6, 14, 6, 12, 10, 6, 6, 8, 6, 6, 4, + 2, 4, 8, 12, 4, 14, 18, 10, 2, 16, 6, 20, 6, 10, 8, 4, 30, 36, + 12, 8, 22, 12, 2, 6, 12, 16, 6, 6, 2, 18, 4, 26, 4, 8, 18, 10, + 8, 10, 6, 14, 4, 20, 22, 18, 12, 8, 28, 12, 6, 6, 8, 6, 12, 24, + 16, 14, 4, 14, 12, 6, 10, 12, 20, 6, 4, 8, 18, 12, 18, 10, 2, 4, + 20, 10, 14, 4, 6, 2, 10, 24, 18, 2, 4, 20, 16, 14, 10, 14, 6, 4, + 6, 20, 6, 10, 6, 2, 12, 6, 30, 10, 8, 6, 4, 6, 8, 40, 2, 4, + 2, 12, 18, 4, 6, 8, 10, 6, 18, 18, 2, 12, 16, 8, 6, 4, 6, 6, + 2, 52, 14, 4, 20, 16, 2, 4, 6, 12, 2, 6, 12, 12, 6, 4, 14, 10, + 6, 6, 14, 10, 14, 16, 8, 6, 12, 4, 8, 22, 6, 2, 18, 22, 6, 2, + 18, 6, 16, 14, 10, 6, 12, 2, 6, 4, 8, 18, 12, 16, 2, 4, 14, 4, + 8, 12, 12, 30, 16, 8, 4, 2, 6, 22, 12, 8, 10, 6, 6, 6, 14, 6, + 18, 10, 12, 2, 10, 2, 4, 26, 4, 12, 8, 4, 18, 8, 10, 14, 16, 6, + 6, 8, 10, 6, 8, 6, 12, 10, 20, 10, 8, 4, 12, 26, 18, 4, 12, 18, + 6, 30, 6, 8, 6, 22, 12, 2, 4, 6, 6, 2, 10, 2, 4, 6, 6, 2, + 6, 22, 18, 6, 18, 12, 8, 12, 6, 10, 12, 2, 16, 2, 10, 2, 10, 18, + 6, 20, 4, 2, 6, 22, 6, 6, 18, 6, 14, 12, 16, 2, 6, 6, 4, 14, + 12, 4, 2, 18, 16, 36, 12, 6, 14, 28, 2, 12, 6, 12, 6, 4, 2, 16, + 30, 8, 24, 6, 30, 10, 2, 18, 4, 6, 12, 8, 22, 2, 6, 22, 18, 2, + 10, 2, 10, 30, 2, 28, 6, 14, 16, 6, 20, 16, 2, 6, 4, 32, 4, 2, + 4, 6, 2, 12, 4, 6, 6, 12, 2, 6, 4, 6, 8, 6, 4, 20, 4, 32, + 10, 8, 16, 2, 22, 2, 4, 6, 8, 6, 16, 14, 4, 18, 8, 4, 20, 6, + 12, 12, 6, 10, 2, 10, 2, 12, 28, 12, 18, 2, 18, 10, 8, 10, 48, 2, + 4, 6, 8, 10, 2, 10, 30, 2, 36, 6, 10, 6, 2, 18, 4, 6, 8, 16, + 14, 16, 6, 14, 4, 20, 4, 6, 2, 10, 12, 2, 6, 12, 6, 6, 4, 12, + 2, 6, 4, 12, 6, 8, 4, 2, 6, 18, 10, 6, 8, 12, 6, 22, 2, 6, + 12, 18, 4, 14, 6, 4, 20, 6, 16, 8, 4, 8, 22, 8, 12, 6, 6, 16, + 12, 18, 30, 8, 4, 2, 4, 6, 26, 4, 14, 24, 22, 6, 2, 6, 10, 6, + 14, 6, 6, 12, 10, 6, 2, 12, 10, 12, 8, 18, 18, 10, 6, 8, 16, 6, + 6, 8, 16, 20, 4, 2, 10, 2, 10, 12, 6, 8, 6, 10, 20, 10, 18, 26, + 4, 6, 30, 2, 4, 8, 6, 12, 12, 18, 4, 8, 22, 6, 2, 12, 34, 6, + 18, 12, 6, 2, 28, 14, 16, 14, 4, 14, 12, 4, 6, 6, 2, 36, 4, 6, + 20, 12, 24, 6, 22, 2, 16, 18, 12, 12, 18, 2, 6, 6, 6, 4, 6, 14, + 4, 2, 22, 8, 12, 6, 10, 6, 8, 12, 18, 12, 6, 10, 2, 22, 14, 6, + 6, 4, 18, 6, 20, 22, 2, 12, 24, 4, 18, 18, 2, 22, 2, 4, 12, 8, + 12, 10, 14, 4, 2, 18, 16, 38, 6, 6, 6, 12, 10, 6, 12, 8, 6, 4, + 6, 14, 30, 6, 10, 8, 22, 6, 8, 12, 10, 2, 10, 2, 6, 10, 2, 10, + 12, 18, 20, 6, 4, 8, 22, 6, 6, 30, 6, 14, 6, 12, 12, 6, 10, 2, + 10, 30, 2, 16, 8, 4, 2, 6, 18, 4, 2, 6, 4, 26, 4, 8, 6, 10, + 2, 4, 6, 8, 4, 6, 30, 12, 2, 6, 6, 4, 20, 22, 8, 4, 2, 4, + 72, 8, 4, 8, 22, 2, 4, 14, 10, 2, 4, 20, 6, 10, 18, 6, 20, 16, + 6, 8, 6, 4, 20, 12, 22, 2, 4, 2, 12, 10, 18, 2, 22, 6, 18, 30, + 2, 10, 14, 10, 8, 16, 50, 6, 10, 8, 10, 12, 6, 18, 2, 22, 6, 2, + 4, 6, 8, 6, 6, 10, 18, 2, 22, 2, 16, 14, 10, 6, 2, 12, 10, 20, + 4, 14, 6, 4, 36, 2, 4, 6, 12, 2, 4, 14, 12, 6, 4, 6, 2, 6, + 4, 20, 10, 2, 10, 6, 12, 2, 24, 12, 12, 6, 6, 4, 24, 2, 4, 24, + 2, 6, 4, 6, 8, 16, 6, 2, 10, 12, 14, 6, 34, 6, 14, 6, 4, 2, + 30, 0 + }; + int64_t num, den, left, min, max; + + if (!value) + return; + if ((value->num == 0) || (value->den == 0)) { + value->den = 1; + return; + } + + if (value->den < 0) + value->num = -value->num, value->den = -value->den; + num = value->num; + den = value->den; + min = num < 0 ? -num : num; + if (min < den) { + max = den; + } else { + max = min; + min = den; + } + left = 1; + + /* prime 2, simple and fast */ + while (!((min | max) & 1)) { + min >>= 1; + max >>= 1; + } + while (!(min & 1)) { + min >>= 1; + left += left; + } + + /* now, the remaining primes */ + { + int prime = 0; + const unsigned char *p = primediffs; + while (*p) { + prime += *p++; + if (min < prime * prime) + break; + while (min % prime == 0) { + min /= prime; + if (max % prime != 0) { + left *= prime; + break; + } + max /= prime; + } + while (min % prime == 0) { + min /= prime; + left *= prime; + } + } + } + /* after stripping all prime factors up to sqrt (min), the rest _is_ prime */ + if (max % min == 0) { + max /= min; + min = 1; + } + min *= left; + if (num < 0) { + if (-num < den) { + value->num = -min; + value->den = max; + } else { + value->num = -max; + value->den = min; + } + } else { + if (num < den) { + value->num = min; + value->den = max; + } else { + value->num = max; + value->den = min; + } + } +} +
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor