Learn how easy it is to sync an existing GitHub or Google Code repo to a SourceForge project! See Demo

Close

Diff of /src/demuxers/demux_flv.c [7d8f10] .. [271a94] Maximize Restore

  Switch to side-by-side view

--- a/src/demuxers/demux_flv.c
+++ b/src/demuxers/demux_flv.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2004 the xine project
+ * Copyright (C) 2004-2013 the xine project
  *
  * This file is part of xine, a free video player.
  *
@@ -22,9 +22,16 @@
  * Flash Video (.flv) File Demuxer
  *   by Mike Melanson (melanson@pcisys.net) and
  *      Claudio Ciccani (klan@users.sf.net)
+ *   rewritten by Torsten Jager (t.jager@gmx.de)
  *
  * For more information on the FLV file format, visit:
- * http://www.adobe.com/devnet/flv/pdf/video_file_format_spec_v9.pdf
+ * http://www.adobe.com/devnet/flv/pdf/video_file_format_spec_v10.pdf
+ *
+ * TJ. FLV actually is a persistent variant of Realtime Messaging Protocol
+ * (rtmp). Some features, most notably message interleaving and relative
+ * timestamps, have been removed. Official spec imposes further restrictions.
+ * We should nevertheless be prepared for more general stuff left by rtmp
+ * stream recorders.
  */
 
 #ifdef HAVE_CONFIG_H
@@ -47,11 +54,10 @@
 #include <xine/compat.h>
 #include <xine/demux.h>
 #include "bswap.h"
-#include "group_games.h"
 
 typedef struct {
   unsigned int         pts;
-  unsigned int         offset;
+  off_t                offset;
 } flv_index_entry_t;
 
 typedef struct {
@@ -68,8 +74,7 @@
   off_t                start;  /* in bytes */
   off_t                size;   /* in bytes */
 
-  unsigned char        got_video_header;
-  unsigned char        got_audio_header;
+  unsigned char        got_video_header, got_audio_header, got_info;
 
   unsigned int         length; /* in ms */
   int                  width;
@@ -79,7 +84,7 @@
 
   int                  samplerate;
   int                  samplesize;
-  int                  stereo;
+  int                  audio_channels;
   int                  audiocodec;
 
   off_t                filesize;
@@ -92,51 +97,63 @@
   int64_t              last_pts[2];
   int                  send_newpts;
   int                  buf_flag_seek;
+
+  int                  audiodelay;   /* fine tune a/v sync */
+
+  unsigned int         zero_pts_count;
 } demux_flv_t ;
 
 typedef struct {
   demux_class_t     demux_class;
 } demux_flv_class_t;
 
+/* an early FLV specification had 24bit bigendian timestamps. This
+   limited clip duration to 4:39:37.215. The backwards compatible solution:
+   hand over 1 byte from stream ID. Hence the weird byte order 2-1-0-3 */
+#define gettimestamp(p,o) (((((((uint32_t)p[o+3]<<8)|p[o])<<8)|p[o+1])<<8)|p[o+2])
 
 #define FLV_FLAG_HAS_VIDEO       0x01
 #define FLV_FLAG_HAS_AUDIO       0x04
 
 #define FLV_TAG_TYPE_AUDIO       0x08
 #define FLV_TAG_TYPE_VIDEO       0x09
-#define FLV_TAG_TYPE_SCRIPT      0x12
-
-#define FLV_SOUND_FORMAT_PCM_BE  0x00
-#define FLV_SOUND_FORMAT_ADPCM   0x01
-#define FLV_SOUND_FORMAT_MP3     0x02
-#define FLV_SOUND_FORMAT_PCM_LE  0x03
-#define FLV_SOUND_FORMAT_NELLY16 0x04 /* Nellymoser 16KHz */
-#define FLV_SOUND_FORMAT_NELLY8  0x05 /* Nellymoser 8KHz */
-#define FLV_SOUND_FORMAT_NELLY   0x06 /* Nellymoser */
-#define FLV_SOUND_FORMAT_ALAW    0x07 /* G.711 A-LAW */
-#define FLV_SOUND_FORMAT_MULAW   0x08 /* G.711 MU-LAW */
-#define FLV_SOUND_FORMAT_AAC     0x0a
-#define FLV_SOUND_FORMAT_MP38    0x0e /* MP3 8KHz */
-
-#define FLV_VIDEO_FORMAT_FLV1    0x02 /* Sorenson H.263 */
-#define FLV_VIDEO_FORMAT_SCREEN  0x03
-#define FLV_VIDEO_FORMAT_VP6     0x04 /* On2 VP6 */
-#define FLV_VIDEO_FORMAT_VP6A    0x05 /* On2 VP6 with alphachannel */
-#define FLV_VIDEO_FORMAT_SCREEN2 0x06
-#define FLV_VIDEO_FORMAT_H264    0x07
-
-#define FLV_DATA_TYPE_NUMBER     0x00
-#define FLV_DATA_TYPE_BOOL       0x01
-#define FLV_DATA_TYPE_STRING     0x02
-#define FLV_DATA_TYPE_OBJECT     0x03
-#define FLC_DATA_TYPE_CLIP       0x04
-#define FLV_DATA_TYPE_REFERENCE  0x07
-#define FLV_DATA_TYPE_ECMARRAY   0x08
-#define FLV_DATA_TYPE_ENDOBJECT  0x09
-#define FLV_DATA_TYPE_ARRAY      0x0a
-#define FLV_DATA_TYPE_DATE       0x0b
-#define FLV_DATA_TYPE_LONGSTRING 0x0c
-
+#define FLV_TAG_TYPE_NOTIFY      0x12
+
+typedef enum {
+  AF_PCM_BE,  /* officially "native endian"?? */
+  AF_ADPCM,
+  AF_MP3,
+  AF_PCM_LE,  /* little endian */
+  AF_NELLY16, /* Nellymoser 16KHz */
+  AF_NELLY8,  /* Nellymoser 8KHz */
+  AF_NELLY,   /* Nellymoser */
+  AF_ALAW,    /* G.711 A-LAW */
+  AF_MULAW,   /* G.711 MU-LAW */
+  AF_reserved9,
+  AF_AAC,     /* mp4a with global header */
+  AF_SPEEX,
+  AF_reserved12,
+  AF_reserved13,
+  AF_MP38,    /* MP3 8KHz */
+  AF_DS       /* device specific sound */
+} af_t;
+
+/* audio types that support free samplerate from header */
+/* got the message ? ;-) */
+#define IS_PCM(id) ((((1<<AF_PCM_BE)|(1<<AF_ADPCM)|(1<<AF_PCM_LE)|(1<<AF_ALAW)|(1<<AF_MULAW))>>(id))&1)
+
+typedef enum {
+  VF_reserved0,
+  VF_JPEG,
+  VF_FLV1,    /* modified Sorenson H.263 */
+  VF_SCREEN,  /* Macromedia screen video v1 */
+  VF_VP6,     /* On2 VP6 */
+  VF_VP6A,    /* On2 VP6 with alphachannel */
+  VF_SCREEN2, /* v2 */
+  VF_H264,    /* MPEG4 part 10, usually with global sequence parameter set */
+  VF_H263,
+  VF_MP4      /* MPEG4 part 2, usually with global sequence parameter set */
+} vf_t;
 
 /* redefine abs as macro to handle 64-bit diffs.
    i guess llabs may not be available everywhere */
@@ -146,27 +163,24 @@
 #define PTS_AUDIO                0
 #define PTS_VIDEO                1
 
-static void check_newpts(demux_flv_t *this, int64_t pts, int video) {
+static void check_newpts (demux_flv_t *this, int64_t pts, int video) {
   int64_t diff;
-
-  diff = pts - this->last_pts[video];
   lprintf ("check_newpts %"PRId64"\n", pts);
-
-  if (pts && (this->send_newpts || (this->last_pts[video] && abs(diff)>WRAP_THRESHOLD))) {
-    lprintf ("diff=%"PRId64"\n", diff);
-
-    if (this->buf_flag_seek) {
-      _x_demux_control_newpts(this->stream, pts, BUF_FLAG_SEEK);
-      this->buf_flag_seek = 0;
-    } else {
-      _x_demux_control_newpts(this->stream, pts, 0);
-    }
-    this->send_newpts = 0;
-    this->last_pts[1-video] = 0;
-  }
-
-  if (pts)
-    this->last_pts[video] = pts;
+  if (this->buf_flag_seek) {
+    _x_demux_control_newpts (this->stream, pts, BUF_FLAG_SEEK);
+    this->buf_flag_seek = 0;
+    this->send_newpts   = 0;
+    this->last_pts[1 - video] = 0;
+  } else {
+    diff = pts - this->last_pts[video];
+    if (pts && this->last_pts[video] && abs (diff) > WRAP_THRESHOLD) {
+      lprintf ("diff=%"PRId64"\n", diff);
+      _x_demux_control_newpts (this->stream, pts, 0);
+      this->send_newpts = 0;
+      this->last_pts[1-video] = 0;
+    }
+  }
+  this->last_pts[video] = pts;
 }
 
 /* returns 1 if the FLV file was opened successfully, 0 otherwise */
@@ -209,250 +223,349 @@
   _tmp.d;\
 })\
 
-static int parse_flv_var(demux_flv_t *this,
-                         unsigned char *buf, int size, char *key, int keylen) {
-  unsigned char *tmp = buf;
-  unsigned char *end = buf + size;
-  char          *str;
-  unsigned char  type;
-  unsigned int   len, num;
-
-  if (size < 1)
-    return 0;
-
-  type = *tmp++;
-
-  switch (type) {
-    case FLV_DATA_TYPE_NUMBER:
-      lprintf("  got number (%f)\n", BE_F64(tmp));
-      if (key) {
-        double val = BE_F64(tmp);
-        if (keylen == 8 && !strncmp(key, "duration", 8)) {
-          this->length = val * 1000.0;
-        }
-        else if (keylen == 5 && !strncmp(key, "width", 5)) {
-          this->width = val;
-          _x_stream_info_set(this->stream, XINE_STREAM_INFO_VIDEO_WIDTH, this->width);
-        }
-        else if (keylen == 6 && !strncmp(key, "height", 6)) {
-          this->height = val;
-          _x_stream_info_set(this->stream, XINE_STREAM_INFO_VIDEO_HEIGHT, this->height);
-        }
-        else if (keylen == 9 && !strncmp(key, "framerate", 9)) {
-          if (val > 0) {
-            this->duration = 90000.0 / val;
-            _x_stream_info_set(this->stream, XINE_STREAM_INFO_FRAME_DURATION, this->duration);
+/* Action Message Format data types */
+typedef enum {
+  AMF0_NUMBER       = 0x00,  /* double_be */
+  AMF0_BOOLEAN      = 0x01,  /* 1 byte TRUE or FALSE */
+  AMF0_STRING       = 0x02,  /* u16_be length, then utf8 string without end byte */
+  AMF0_OBJECT       = 0x03,  /* name/type/data triplets, then empty name plus
+                                AMF0_OBJECT_END. name stored same way as AMF0_STRING */
+  AMF0_MOVIECLIP    = 0x04,  /* reserved */
+  AMF0_NULL_VALUE   = 0x05,  /* no data */
+  AMF0_UNDEFINED    = 0x06,  /* no data */
+  AMF0_REFERENCE    = 0x07,  /* u16be index into previous items table */
+  AMF0_ECMA_ARRAY   = 0x08,  /* u32_be number_of_entries, then same as AMF0_OBJECT */
+  AMF0_OBJECT_END   = 0x09,  /* end marker of AMF0_OBJECT */
+  AMF0_STRICT_ARRAY = 0x0a,  /* u32_be n, then exactly n type/value pairs */
+  AMF0_DATE         = 0x0b,  /* double_be milliseconds since Jan 01, 1970, then
+                                s16_be minutes off UTC */
+  AMF0_LONG_STRING  = 0x0c,  /* u32_be length, then utf8 string */
+  AMF0_UNSUPPORTED  = 0x0d,  /* no data */
+  AMF0_RECORD_SET   = 0x0e,  /* reserved */
+  AMF0_XML_OBJECT   = 0x0f,  /* physically same as AMF0_LONG_STRING */
+  AMF0_TYPED_OBJECT = 0x10,  /* very complex, should not appear in FLV */
+  AMF0_AMF3         = 0x11,  /* switch to AMF3 from here */
+} amf_type_t;
+
+#define MAX_AMF_LEVELS 10
+#define SPC (space + 2 * (MAX_AMF_LEVELS - level))
+#define NEEDBYTES(n) if ((unsigned long int)(end - p) < n) return 0
+
+static int parse_amf (demux_flv_t *this, unsigned char *buf, int size) {
+  unsigned char *p = buf, *end = buf + size, *name, space[2 * MAX_AMF_LEVELS + 3];
+  int level = 0, i, type, count[MAX_AMF_LEVELS], info = 0;
+  unsigned int u, c;
+  time_t tsecs;
+  struct tm *tstruct;
+  double val;
+
+  /* init prettyprinter */
+  memset (space, ' ', 2 * MAX_AMF_LEVELS + 2);
+  space[2 * MAX_AMF_LEVELS + 2] = 0x00;
+  /* top level has nameless vars */
+  count[0] = 10000;
+  while (1) {
+    if (count[level] > 0) {
+      /* next strict array item */
+      if (--count[level] == 0) {
+        /* one level up */
+        if (--level < 0) return 0;
+        xprintf (this->xine, XINE_VERBOSITY_DEBUG, "%s}\n", SPC);
+        continue;
+      }
+      if (p >= end) break;
+      type = *p++;
+      name = NULL;
+      xprintf (this->xine, XINE_VERBOSITY_DEBUG, "%s", SPC);
+    } else {
+      /* get current name */
+      NEEDBYTES (2);
+      u = _X_BE_16 (p);
+      p += 2;
+      NEEDBYTES (u);
+      name = p;
+      p += u;
+      if (u == 0) {
+        /* object end, 1 level up */
+        if (--level < 0) return 0;
+        if ((p < end) && (*p == AMF0_OBJECT_END)) p++;
+        xprintf (this->xine, XINE_VERBOSITY_DEBUG, "%s}\n", SPC);
+        continue;
+      }
+      NEEDBYTES (1);
+      type = *p;
+      *p++ = 0x00;
+      xprintf (this->xine, XINE_VERBOSITY_DEBUG, "%s%s = ", SPC, name);
+    }
+    switch (type) {
+      case AMF0_NUMBER:
+        NEEDBYTES (8);
+        val = BE_F64 (p);
+        i = val;
+        if (i == val) xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "%d\n", i);
+        else xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "%.03lf\n", val);
+        p += 8;
+        if (name && info) {
+          if (!strcmp (name, "duration")) {
+            this->length = val * (double)1000;
+          } else if (!strcmp (name, "width")) {
+            this->width = i;
+            _x_stream_info_set (this->stream, XINE_STREAM_INFO_VIDEO_WIDTH, i);
+          } else if (!strcmp (name, "height")) {
+            this->height = i;
+            _x_stream_info_set (this->stream, XINE_STREAM_INFO_VIDEO_HEIGHT, i);
+          } else if (!strcmp (name, "framerate") || !strcmp (name, "videoframerate")) {
+            if ((i > 0) && (i < 1000)) {
+              this->duration = (double)90000 / val;
+              _x_stream_info_set (this->stream, XINE_STREAM_INFO_FRAME_DURATION,
+                this->duration);
+            }
+          } else if (!strcmp (name, "videodatarate")) {
+            _x_stream_info_set (this->stream, XINE_STREAM_INFO_VIDEO_BITRATE,
+              val * (double)1000);
+          } else if (!strcmp (name, "videocodecid")) {
+            this->videocodec = i;
+          } else if (!strcmp (name, "audiosamplerate")) {
+            this->samplerate = i;
+            _x_stream_info_set (this->stream, XINE_STREAM_INFO_AUDIO_SAMPLERATE, i);
+          } else if (!strcmp (name, "audiosamplesize")) {
+            this->samplesize = i;
+            _x_stream_info_set (this->stream, XINE_STREAM_INFO_AUDIO_BITS, i);
+          } else if (!strcmp (name, "stereo")) {
+            this->audio_channels = i ? 2 : 1;
+            _x_stream_info_set (this->stream, XINE_STREAM_INFO_AUDIO_CHANNELS, i);
+          } else if (!strcmp (name, "audiodatarate")) {
+            _x_stream_info_set (this->stream, XINE_STREAM_INFO_AUDIO_BITRATE,
+              val * (double)1000);
+          } else if (!strcmp (name, "audiocodecid")) {
+            this->audiocodec = i;
+          } else if (!strcmp (name, "filesize")) {
+            this->filesize = val;
+          } else if (!strcmp (name, "audiodelay")) {
+            this->audiodelay = val * (double)-1000;
           }
         }
-        else if (keylen == 13 && !strncmp(key, "videodatarate", 13)) {
-          _x_stream_info_set(this->stream, XINE_STREAM_INFO_VIDEO_BITRATE, val*1000.0);
+      break;
+      case AMF0_BOOLEAN:
+        NEEDBYTES (1);
+        i = !!(*p++);
+        xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "%s\n", i ? "yes" : "no");
+        if (name && info) {
+          if (!strcmp (name, "stereo"))
+            this->audio_channels = i ? 2 : 1;
         }
-        else if (keylen == 12 && !strncmp(key, "videocodecid", 12)) {
-          this->videocodec = val;
-        }
-        else if (keylen == 15 && !strncmp(key, "audiosamplerate", 15)) {
-          this->samplerate = val;
-          _x_stream_info_set(this->stream, XINE_STREAM_INFO_AUDIO_SAMPLERATE, this->samplerate);
-        }
-        else if (keylen == 15 && !strncmp(key, "audiosamplesize", 15)) {
-          this->samplesize = val;
-          _x_stream_info_set(this->stream, XINE_STREAM_INFO_AUDIO_BITS, this->samplesize);
-        }
-        else if (keylen == 5 && !strncmp(key, "stereo", 5)) {
-          this->stereo = val;
-          _x_stream_info_set(this->stream, XINE_STREAM_INFO_AUDIO_CHANNELS, this->stereo ? 2 : 1);
-        }
-        else if (keylen == 13 && !strncmp(key, "audiodatarate", 13)) {
-          _x_stream_info_set(this->stream, XINE_STREAM_INFO_AUDIO_BITRATE, val*1000.0);
-        }
-        else if (keylen == 12 && !strncmp(key, "audiocodecid", 12)) {
-          this->audiocodec = val;
-        }
-        else if (keylen == 8 && !strncmp(key, "filesize", 8)) {
-          this->filesize = val;
-        }
-      }
-      tmp += 8;
-      break;
-    case FLV_DATA_TYPE_BOOL:
-      lprintf("  got bool (%d)\n", *tmp);
-      tmp++;
-      break;
-    case FLV_DATA_TYPE_STRING:
-      lprintf("  got string (%s)\n", tmp+2);
-      len = _X_BE_16(tmp);
-      tmp += len + 2;
-      break;
-    case FLV_DATA_TYPE_OBJECT:
-      while ((len = _X_BE_16(tmp)) && tmp < end) {
-        lprintf("  got object var (%s)\n", tmp+2);
-        str = tmp + 2;
-        tmp += len + 2;
-        len = parse_flv_var(this, tmp, end-tmp, str, len);
-        if (!len)
-          return 0;
-        tmp += len;
-      }
-      if (*tmp++ != FLV_DATA_TYPE_ENDOBJECT)
-        return 0;
-      break;
-    case FLV_DATA_TYPE_ECMARRAY:
-      lprintf("  got EMCA array (%d indices)\n", _X_BE_32(tmp));
-      num = _X_BE_32(tmp);
-      tmp += 4;
-      while (num-- && tmp < end) {
-        lprintf("  got array key (%s)\n", tmp+2);
-        len = _X_BE_16(tmp);
-        str = tmp + 2;
-        tmp += len + 2;
-        len = parse_flv_var(this, tmp, end-tmp, str, len);
-        if (!len)
-          return 0;
-        tmp += len;
-      }
-      break;
-    case FLV_DATA_TYPE_ARRAY:
-      lprintf("  got array (%d indices)\n", _X_BE_32(tmp));
-      num = _X_BE_32(tmp);
-      tmp += 4;
-      if (key && keylen == 5 && !strncmp(key, "times", 5)) {
-        if (!this->index || this->num_indices != num) {
-          if (this->index)
-            free(this->index);
-          this->index = calloc(num, sizeof(flv_index_entry_t));
-          if (!this->index)
-            return 0;
-          this->num_indices = num;
-        }
-        for (num = 0; num < this->num_indices && tmp < end; num++) {
-          if (*tmp++ == FLV_DATA_TYPE_NUMBER) {
-            lprintf("  got number (%f)\n", BE_F64(tmp));
-            this->index[num].pts = BE_F64(tmp) * 1000.0;
-            tmp += 8;
+      break;
+      case AMF0_STRING:
+        NEEDBYTES (2);
+        u = _X_BE_16 (p);
+        p += 2;
+        NEEDBYTES (u);
+        c = p[u];
+        p[u] = 0x00;
+        xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "\"%s\"\n", p);
+        if (!level && !strcmp (p, "onMetaData")) info = this->got_info = 1;
+        if (name && info) {
+          if ((!strcmp (name, "audiocodecid")) && (!strcmp (p, "mp4a")))
+            this->audiocodec = AF_AAC;
+          else if ((!strcmp (name, "videocodecid")) && (!strcmp (p, "avc1")))
+            this->videocodec = VF_H264;
+          else if (!strcmp (name, "stereo")) {
+            if (!strcmp (p, "true") || !strcmp (p, "yes"))
+              this->audio_channels = 2;
           }
         }
-        break;
-      }
-      if (key && keylen == 13 && !strncmp(key, "filepositions", 13)) {
-        if (!this->index || this->num_indices != num) {
-          if (this->index)
-            free(this->index);
-          this->index = calloc(num, sizeof(flv_index_entry_t));
-          if (!this->index)
-            return 0;
-          this->num_indices = num;
+        p[u] = c;
+        p += u;
+      break;
+      case AMF0_LONG_STRING:
+      case AMF0_XML_OBJECT:
+        NEEDBYTES (4);
+        u = _X_BE_32 (p);
+        p += 4;
+        NEEDBYTES (u);
+        /* avoid printf() overload */
+        if (u > 4000) p[4000] = 0x00;
+        c = p[u];
+        p[u] = 0x00;
+        xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "%s\n", p);
+        p[u] = c;
+        p += u;
+      break;
+      case AMF0_ECMA_ARRAY:
+        NEEDBYTES (4);
+        u = _X_BE_32 (p); /* this value is unreliable */
+        p += 4;
+        xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "[%d] ", u);
+        /* fall through */
+      case AMF0_OBJECT:
+        if (++level >= MAX_AMF_LEVELS) return 0;
+        count[level] = -1;
+        xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "{\n");
+      continue;
+      case AMF0_STRICT_ARRAY:
+        NEEDBYTES (4);
+        u = _X_BE_32 (p);
+        p += 4;
+        c = 0;
+        if (name) {
+          if (!strcmp (name, "times")) c = 1;
+          else if (!strcmp (name, "filepositions")) c = 2;
         }
-        for (num = 0; num < this->num_indices && tmp < end; num++) {
-          if (*tmp++ == FLV_DATA_TYPE_NUMBER) {
-            lprintf("  got number (%f)\n", BE_F64(tmp));
-            this->index[num].offset = BE_F64(tmp);
-            tmp += 8;
+        if (c) {
+          NEEDBYTES (u * 9);
+          xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "[%d] {..}\n", u);
+          if (!this->index || (this->num_indices != u)) {
+            if (this->index) free (this->index);
+            this->index = calloc (u, sizeof (flv_index_entry_t));
+            if (!this->index) return 0;
+            this->num_indices = u;
           }
+          if (c == 1) for (i = 0; i < (int)u; i++) {
+            if (*p++ != AMF0_NUMBER) return 0;
+            this->index[i].pts = BE_F64 (p) * (double)1000;
+            p += 8;
+          } else for (i = 0; i < (int)u; i++) {
+            if (*p++ != AMF0_NUMBER) return 0;
+            this->index[i].offset = BE_F64 (p);
+            p += 8;
+          }
+        } else {
+          if (++level >= MAX_AMF_LEVELS) return 0;
+          count[level] = u + 1;
+          xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "[%d] {\n", u);
         }
-        break;
-      }
-      while (num-- && tmp < end) {
-        len = parse_flv_var(this, tmp, end-tmp, NULL, 0);
-        if (!len)
-          return 0;
-        tmp += len;
-      }
-      break;
-    case FLV_DATA_TYPE_DATE:
-      lprintf("  got date (%"PRId64", %d)\n", _X_BE_64(tmp), _X_BE_16(tmp+8));
-      tmp += 10;
-      break;
-    default:
-      lprintf("  got type %d\n", type);
-      break;
-  }
-
-  return (tmp - buf);
-}
-
-static void parse_flv_script(demux_flv_t *this, int size) {
-  unsigned char *buf = malloc(size);
-  unsigned char *tmp = buf;
-  unsigned char *end = buf + size;
-  int            len;
-
-  if (!buf || this->input->read(this->input, buf, size ) != size) {
-    this->status = DEMUX_FINISHED;
-    free(buf);
-    return;
-  }
-
-  while (tmp < end) {
-    len = parse_flv_var(this, tmp, end-tmp, NULL, 0);
-    if (len < 1)
-      break;
-    tmp += len;
-  }
-
-  free(buf);
-}
+      break;
+      case AMF0_DATE:
+        NEEDBYTES (10);
+        val = BE_F64 (p) / (double)1000;
+        tsecs = val;
+        p += 8;
+        i = _X_BE_16 (p);
+        p += 2;
+        if (i & 0x8000) i |= ~0x7fff;
+        tsecs += i * 60;
+        tstruct = gmtime (&tsecs);
+        if (tstruct) {
+          char ts[200];
+          if (strftime (ts, 200, "%x %X", tstruct) >= 0)
+            xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "%s\n", ts);
+        }
+      break;
+      case AMF0_NULL_VALUE:
+        xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "(null)\n");
+      break;
+      case AMF0_UNDEFINED:
+        xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "(undefined)\n");
+      break;
+      case AMF0_REFERENCE:
+        NEEDBYTES (2);
+        u = _X_BE_16 (p);
+        p += 2;
+        xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "(see #%u)\n", u);
+      break;
+      default:
+        xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "(unhandled type %d)\n", type);
+        return 0;
+      break;
+    }
+  }
+  return level == 0;
+}
+
+#define GETBYTES(n) \
+  if (remaining_bytes < n) \
+    continue; \
+  if (this->input->read (this->input, (char *)buffer + 16, n) != n) \
+    goto fail; \
+  remaining_bytes -= n;
 
 static int read_flv_packet(demux_flv_t *this, int preview) {
   fifo_buffer_t *fifo = NULL;
   buf_element_t *buf  = NULL;
+  unsigned int   tag_type, mp4header, buf_type = 0;
+  unsigned int   buf_flags = BUF_FLAG_FRAME_START;
+  int            remaining_bytes = 0;
+
+  unsigned int   pts; /* ms */
+  int            ptsoffs = 0; /* pts ticks */
+  int64_t        buf_pts;
+  off_t          size, normpos = 0;
+
+  this->status = DEMUX_OK;
 
   while (1) {
-    unsigned char buffer[12], extrabuffer[4];
-    unsigned char tag_type, avinfo;
-    unsigned int  remaining_bytes;
-    unsigned int  buf_type = 0;
-    unsigned int  buf_flags = 0;
-    unsigned int  pts;
-
-    lprintf ("  reading FLV tag...\n");
-    this->input->seek(this->input, 4, SEEK_CUR);
-    if (this->input->read(this->input, buffer, 11) != 11) {
+    /* define this here to prevent compiler caching it across multiple this->input->read ()'s.
+       layout:
+        0 ..  3  previous tags footer (not needed here)
+        4        tag type
+        5 ..  7  payload size
+        8 .. 11  dts (= pts unless H.264 or MPEG4)
+       12 .. 14  stream id (usually 0)
+       15        codec id / frame type / audio params (A/V only)
+       16        right / bottom crop (VP6(F))
+                 frame subtype (AAC, H264, MPEG4)
+       17 .. 19  ?? (VP6F)
+                 pts - dts (H264, MPEG4) */
+    unsigned char  buffer[20];
+    /* skip rest, if any */
+    if (remaining_bytes)
+      this->input->seek (this->input, remaining_bytes, SEEK_CUR);
+    /* we have a/v tags mostly, optimize for them */
+    if (this->input->read (this->input, (char *)buffer, 16) != 16) {
+  fail:
       this->status = DEMUX_FINISHED;
       return this->status;
     }
-
-    tag_type = buffer[0];
-    remaining_bytes = _X_BE_24(&buffer[1]);
-    pts = _X_BE_24(&buffer[4]) | (buffer[7] << 24);
-
-    lprintf("  tag_type = 0x%02X, 0x%X bytes, pts %u\n",
-            tag_type, remaining_bytes, pts/90);
+    remaining_bytes = _X_BE_24(&buffer[5]);
+    /* skip empty tags */
+    if (--remaining_bytes < 0)
+      continue;
+    tag_type = buffer[4];
+    pts = gettimestamp (buffer, 8);
+    mp4header = 0;
 
     switch (tag_type) {
       case FLV_TAG_TYPE_AUDIO:
-        lprintf("  got audio tag..\n");
-        if (this->input->read(this->input, &avinfo, 1) != 1) {
-          this->status = DEMUX_FINISHED;
-          return this->status;
-        }
-        remaining_bytes--;
-
-        this->audiocodec = avinfo >> 4; /* override */
+        if (!pts)
+          this->zero_pts_count++;
+        else if (this->audiodelay > 0)
+          pts += this->audiodelay;
+        this->audiocodec = buffer[15] >> 4;
+
         switch (this->audiocodec) {
-          case FLV_SOUND_FORMAT_PCM_BE:
+          case AF_PCM_BE:
             buf_type = BUF_AUDIO_LPCM_BE;
-            break;
-          case FLV_SOUND_FORMAT_ADPCM:
+          break;
+          case AF_ADPCM:
             buf_type = BUF_AUDIO_FLVADPCM;
-            break;
-          case FLV_SOUND_FORMAT_MP3:
-          case FLV_SOUND_FORMAT_MP38:
+          break;
+          case AF_MP3:
+          case AF_MP38:
             buf_type = BUF_AUDIO_MPEG;
-            break;
-          case FLV_SOUND_FORMAT_PCM_LE:
+          break;
+          case AF_PCM_LE:
             buf_type = BUF_AUDIO_LPCM_LE;
-            break;
-          case FLV_SOUND_FORMAT_ALAW:
+          break;
+          case AF_ALAW:
             buf_type = BUF_AUDIO_ALAW;
-            break;
-          case FLV_SOUND_FORMAT_MULAW:
+          break;
+          case AF_MULAW:
             buf_type = BUF_AUDIO_MULAW;
-            break;
-          case FLV_SOUND_FORMAT_AAC:
+          break;
+#ifdef BUF_AUDIO_NELLYMOSER
+          case AF_NELLY:
+          case AF_NELLY8:
+          case AF_NELLY16:
+            buf_type = BUF_AUDIO_NELLYMOSER;
+          break;
+#endif
+          case AF_AAC:
             buf_type = BUF_AUDIO_AAC;
-            /* AAC extra header */
-            this->input->read(this->input, extrabuffer, 1 );
-            remaining_bytes--;
+            GETBYTES (1);
+            if (!buffer[16]) mp4header = 1;
+          break;
+          case AF_SPEEX:
+            buf_type = BUF_AUDIO_SPEEX;
             break;
           default:
             lprintf("  unsupported audio format (%d)...\n", this->audiocodec);
@@ -461,272 +574,205 @@
         }
 
         fifo = this->audio_fifo;
-        if (preview && !this->got_audio_header) {
+        if (!this->got_audio_header) {
+          /* prefer tag header settings, unless we hit some unofficial libavformat extension */
+          if (!IS_PCM (this->audiocodec) || (buffer[15] & 0x0c) || (this->samplerate < 4000)) {
+            this->samplerate =
+              this->audiocodec == AF_NELLY8 || this->audiocodec == AF_MP38 ? 8000 :
+              this->audiocodec == AF_NELLY16 ? 16000 :
+              44100 >> (3 - ((buffer[15] >> 2) & 3));
+            this->audio_channels = (buffer[15] & 1) + 1;
+          }
+          if (!this->audio_channels)
+            this->audio_channels = (buffer[15] & 1) + 1;
           /* send init info to audio decoder */
           buf = fifo->buffer_pool_alloc(fifo);
           buf->decoder_flags = BUF_FLAG_HEADER | BUF_FLAG_STDHEADER | BUF_FLAG_FRAME_END;
           buf->decoder_info[0] = 0;
-          buf->decoder_info[1] = 44100 >> (3 - ((avinfo >> 2) & 3)); /* samplerate */
-          buf->decoder_info[2] = (avinfo & 2) ? 16 : 8; /* bits per sample */
-          buf->decoder_info[3] = (avinfo & 1) + 1; /* channels */
+          buf->decoder_info[1] = this->samplerate;
+          buf->decoder_info[2] = (buffer[15] & 2) ? 16 : 8; /* bits per sample */
+          buf->decoder_info[3] = this->audio_channels;
           buf->size = 0; /* no extra data */
           buf->type = buf_type;
           fifo->put(fifo, buf);
           this->got_audio_header = 1;
-          if (!INPUT_IS_SEEKABLE(this->input)) {
-             /* stop preview processing immediately, this enables libfaad to
-              * initialize even without INPUT_CAP_SEEKABLE of input stream.
-              */
-             preview = 0;
-          }
         }
-        break;
+      break;
 
       case FLV_TAG_TYPE_VIDEO:
-        lprintf("  got video tag..\n");
-        if (this->input->read(this->input, &avinfo, 1) != 1) {
-          this->status = DEMUX_FINISHED;
-          return this->status;
+        if (!pts)
+          this->zero_pts_count++;
+        else if (this->audiodelay < 0)
+          pts -= this->audiodelay;
+        /* check frame type */
+        switch (buffer[15] >> 4) {
+          case 1: /* Key or seekable frame */
+          case 4: /* server generated keyframe */
+            buf_flags |= BUF_FLAG_KEYFRAME;
+          break;
+          case 5:
+            /* This is a rtmp server command.
+               One known use:
+               When doing a rtmp time seek between key frames, server may send:
+               1. a type 5 frame of one 0x00 byte
+               2. the nearest keyframe before the seek time
+               3. the following frames before the seek time, if any
+               4. a type 5 frame of one 0x01 byte */
+          continue;
+          default: ;
         }
-        remaining_bytes--;
-
-        switch ((avinfo >> 4)) {
-          case 0x01:
-            buf_flags = BUF_FLAG_KEYFRAME;
-            break;
-          case 0x05:
-            /* skip server command */
-            this->input->seek(this->input, remaining_bytes, SEEK_CUR);
-            continue;
-          default:
-            break;
-        }
-
-        this->videocodec = avinfo & 0x0F; /* override */
+        this->videocodec = buffer[15] & 0x0F;
         switch (this->videocodec) {
-          case FLV_VIDEO_FORMAT_FLV1:
+          case VF_FLV1:
             buf_type = BUF_VIDEO_FLV1;
-            break;
-          case FLV_VIDEO_FORMAT_VP6:
-            buf_type = BUF_VIDEO_VP6F;
-            /* VP6 extra header */
-            this->input->read(this->input, extrabuffer, 1 );
-            remaining_bytes--;
-            break;
-          case FLV_VIDEO_FORMAT_VP6A:
-            buf_type = BUF_VIDEO_VP6F;
-            /* VP6A extra header */
-            this->input->read(this->input, extrabuffer, 4);
-            remaining_bytes -= 4;
-            break;
-          case FLV_VIDEO_FORMAT_H264:
+          break;
+          case VF_H263:
+            buf_type = BUF_VIDEO_H263;
+          break;
+          case VF_MP4:
+            buf_type = BUF_VIDEO_MPEG4;
+          goto comm_mpeg4;
+          case VF_H264:
             buf_type = BUF_VIDEO_H264;
             /* AVC extra header */
-            this->input->read(this->input, extrabuffer, 4);
-            remaining_bytes -= 4;
-            break;
+          comm_mpeg4:
+            GETBYTES (4);
+            if (buffer[16] == 2)
+              continue; /* skip sequence footer */
+            if (!buffer[16])
+              mp4header = 1;
+            /* pts really is dts here, buffer[17..19] has (pts - dts) signed big endian. */
+            ptsoffs = _X_BE_24 (buffer + 17);
+            if (ptsoffs & 0x800000)
+              ptsoffs |= ~0xffffff;
+            /* better: +/- 16 frames, but we cannot trust header framerate */
+            if ((ptsoffs < -1000) || (ptsoffs > 1000))
+              ptsoffs = 0;
+            ptsoffs *= 90;
+          break;
+          case VF_VP6:
+            buf_type = BUF_VIDEO_VP6F;
+            GETBYTES (1);
+          break;
+          case VF_VP6A:
+            buf_type = BUF_VIDEO_VP6F;
+            GETBYTES (4);
+          break;
+          case VF_JPEG:
+            buf_type = BUF_VIDEO_JPEG;
+          break;
           default:
             lprintf("  unsupported video format (%d)...\n", this->videocodec);
             buf_type = BUF_VIDEO_UNKNOWN;
-            break;
+          break;
         }
 
         fifo = this->video_fifo;
-        if (preview && !this->got_video_header) {
+        if (!this->got_video_header) {
           xine_bmiheader *bih;
           /* send init info to video decoder; send the bitmapinfo header to the decoder
            * primarily as a formality since there is no real data inside */
           buf = fifo->buffer_pool_alloc(fifo);
-          buf->decoder_flags = BUF_FLAG_HEADER | BUF_FLAG_STDHEADER |
-                               BUF_FLAG_FRAMERATE | BUF_FLAG_FRAME_END;
-          buf->decoder_info[0] = this->duration;
+          buf->decoder_flags = BUF_FLAG_HEADER | BUF_FLAG_STDHEADER | BUF_FLAG_FRAME_END;
+          if (this->duration) {
+            buf->decoder_flags  |= BUF_FLAG_FRAMERATE;
+            buf->decoder_info[0] = this->duration;
+          }
           bih = (xine_bmiheader *) buf->content;
-          memset(bih, 0, sizeof(xine_bmiheader));
-          bih->biSize = sizeof(xine_bmiheader);
-          bih->biWidth = this->width;
+          memset (bih, 0, sizeof(xine_bmiheader));
+          bih->biSize   = sizeof(xine_bmiheader);
+          bih->biWidth  = this->width;
           bih->biHeight = this->height;
-          buf->size = sizeof(xine_bmiheader);
-          buf->type = buf_type;
+          buf->size     = sizeof(xine_bmiheader);
+          buf->type     = buf_type;
           if (buf_type == BUF_VIDEO_VP6F) {
-            *((unsigned char *)buf->content+buf->size) = extrabuffer[0];
+            *((unsigned char *)buf->content+buf->size) = buffer[16];
             bih->biSize++;
             buf->size++;
-          }
-          else if (buf_type == BUF_VIDEO_H264 && extrabuffer[0] == 0) {
-            /* AVC sequence header */
-            if (remaining_bytes > buf->max_size-buf->size) {
-              xprintf(this->xine, XINE_VERBOSITY_LOG,
-                    _("sequence header too big (%u bytes)!\n"), remaining_bytes);
-              this->input->read(this->input, buf->content+buf->size, buf->max_size-buf->size);
-              this->input->seek(this->input, remaining_bytes-buf->max_size-buf->size, SEEK_CUR);
-              bih->biSize = buf->max_size;
-              buf->size = buf->max_size;
-            }
-            else {
-              this->input->read(this->input, buf->content+buf->size, remaining_bytes);
-              bih->biSize += remaining_bytes;
-              buf->size += remaining_bytes;
-            }
-            remaining_bytes = 0;
           }
           fifo->put(fifo, buf);
           this->got_video_header = 1;
         }
-        break;
-
-      case FLV_TAG_TYPE_SCRIPT:
-        lprintf("  got script tag...\n");
-        if (preview) {
-          parse_flv_script(this, remaining_bytes);
-
-          /* send init info to decoders using script information as reference */
-          if (!this->got_audio_header && this->audiocodec) {
-            buf = this->audio_fifo->buffer_pool_alloc(this->audio_fifo);
-            buf->decoder_flags = BUF_FLAG_HEADER | BUF_FLAG_STDHEADER | BUF_FLAG_FRAME_END;
-            buf->decoder_info[0] = 0;
-            buf->decoder_info[1] = this->samplerate;
-            buf->decoder_info[2] = this->samplesize;
-            buf->decoder_info[3] = this->stereo ? 2 : 1;
-            switch (this->audiocodec) {
-              case FLV_SOUND_FORMAT_PCM_BE:
-                buf->type = BUF_AUDIO_LPCM_BE;
-                break;
-              case FLV_SOUND_FORMAT_ADPCM:
-                buf->type = BUF_AUDIO_FLVADPCM;
-                break;
-              case FLV_SOUND_FORMAT_MP3:
-              case FLV_SOUND_FORMAT_MP38:
-                buf->type = BUF_AUDIO_MPEG;
-                break;
-              case FLV_SOUND_FORMAT_PCM_LE:
-                buf->type = BUF_AUDIO_LPCM_LE;
-                break;
-              case FLV_SOUND_FORMAT_ALAW:
-                buf->type = BUF_AUDIO_ALAW;
-                break;
-              case FLV_SOUND_FORMAT_MULAW:
-                buf->type = BUF_AUDIO_MULAW;
-                break;
-              case FLV_SOUND_FORMAT_AAC:
-                buf->type = BUF_AUDIO_AAC;
-                break;
-              default:
-                buf->type = BUF_AUDIO_UNKNOWN;
-                break;
-            }
-            buf->size = 0;
-            this->audio_fifo->put(this->audio_fifo, buf);
-            this->got_audio_header = 1;
-            lprintf("  got audio header from metadata...\n");
+      break;
+
+      case FLV_TAG_TYPE_NOTIFY:
+        if (!this->got_info) {
+          unsigned char *text;
+          this->input->seek (this->input, -1, SEEK_CUR);
+          remaining_bytes++;
+          text = malloc (remaining_bytes + 1); /* 1 more byte for possible string end */
+          if (!text || this->input->read (this->input, (char *)text, remaining_bytes) != remaining_bytes) {
+            free (text);
+            goto fail;
           }
-
-          if (!this->got_video_header && this->videocodec && this->videocodec != FLV_VIDEO_FORMAT_H264) {
-            xine_bmiheader *bih;
-            buf = this->video_fifo->buffer_pool_alloc(this->video_fifo);
-            buf->decoder_flags = BUF_FLAG_HEADER | BUF_FLAG_STDHEADER |
-                                 BUF_FLAG_FRAMERATE | BUF_FLAG_FRAME_END;
-            buf->decoder_info[0] = this->duration;
-            switch (this->videocodec) {
-              case FLV_VIDEO_FORMAT_FLV1:
-                buf->type = BUF_VIDEO_FLV1;
-                break;
-              case FLV_VIDEO_FORMAT_VP6:
-              case FLV_VIDEO_FORMAT_VP6A:
-                buf->type = BUF_VIDEO_VP6F;
-                break;
-              default:
-                buf->type = BUF_VIDEO_UNKNOWN;
-                break;
-            }
-            buf->size = sizeof(xine_bmiheader);
-            bih = (xine_bmiheader *) buf->content;
-            memset(bih, 0, sizeof(xine_bmiheader));
-            bih->biSize = sizeof(xine_bmiheader);
-            bih->biWidth = this->width;
-            bih->biHeight = this->height;
-            if (buf->type == BUF_VIDEO_VP6F) {
-              *((uint8_t *)buf->content+buf->size) = ((16-(this->width&15)) << 4) |
-                                                     ((16-(this->height&15)) & 0xf);
-              bih->biSize++;
-              buf->size++;
-            }
-            this->video_fifo->put(this->video_fifo, buf);
-            this->got_video_header = 1;
-            lprintf("  got video header from metadata...\n");
-          }
-
-          return this->status;
+          xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "demux_flv: stream info:\n");
+          parse_amf (this, text, remaining_bytes);
+          free (text);
+          return (this->status);
         }
-        /* no preview */
-        this->input->seek(this->input, remaining_bytes, SEEK_CUR);
-        continue;
-
+        /* fall through */
       default:
         lprintf("  skipping packet...\n");
-        this->input->seek(this->input, remaining_bytes, SEEK_CUR);
-        continue;
-    }
-
+      continue;
+    }
+
+    /* send mpeg4 style headers in both normal and preview mode. This makes sure that
+       they get through before they are needed. And it supports multiple sequences per
+       stream (unless we seek too far). */
+    if (mp4header) {
+      buf = fifo->buffer_pool_alloc (fifo);
+      buf->type = buf_type;
+      buf->size = 0;
+      buf->decoder_flags = BUF_FLAG_SPECIAL|BUF_FLAG_HEADER;
+      buf->decoder_info[1] = BUF_SPECIAL_DECODER_CONFIG;
+      buf->decoder_info[2] = remaining_bytes > buf->max_size ?
+        buf->max_size : remaining_bytes;
+      buf->decoder_info_ptr[2] = buf->mem;
+      if ((this->input->read (this->input, (char *)buf->mem, buf->decoder_info[2]))
+        != buf->decoder_info[2]) {
+        buf->free_buffer (buf);
+        goto fail;
+      }
+      remaining_bytes -= buf->decoder_info[2];
+      if (remaining_bytes)
+        this->input->seek (this->input, remaining_bytes, SEEK_CUR);
+      remaining_bytes = 0;
+      fifo->put (fifo, buf);
+      break;
+    }
+
+    /* fkip frame contents in preview mode */
+    if (preview) {
+      if (remaining_bytes)
+        this->input->seek (this->input, remaining_bytes, SEEK_CUR);
+      return this->status;
+    }
+
+    /* send frame contents */
+    buf_pts = (int64_t)pts * 90;
+    check_newpts (this, buf_pts, (tag_type == FLV_TAG_TYPE_VIDEO));
+    size = this->input->get_length (this->input);
+    if (size > 0) {
+      this->size = size;
+      normpos = (int64_t)this->input->get_current_pos (this->input) * 65535 / size;
+    }
     while (remaining_bytes) {
-      buf = fifo->buffer_pool_alloc(fifo);
+      buf       = fifo->buffer_pool_alloc (fifo);
       buf->type = buf_type;
-
+      buf->pts  = buf_pts + ptsoffs;
       buf->extra_info->input_time = pts;
-      if (this->input->get_length(this->input)) {
-        buf->extra_info->input_normpos =
-            (int)((double)this->input->get_current_pos(this->input) * 65535.0 / this->size);
+      if (size > 0)
+        buf->extra_info->input_normpos = normpos;
+      buf->size = remaining_bytes > buf->max_size ? buf->max_size : remaining_bytes;
+      remaining_bytes -= buf->size;
+      if (!remaining_bytes)
+        buf_flags |= BUF_FLAG_FRAME_END;
+      buf->decoder_flags = buf_flags;
+      buf_flags &= ~BUF_FLAG_FRAME_START;
+      if (this->input->read (this->input, (char *)buf->content, buf->size) != buf->size) {
+        buf->free_buffer (buf);
+        goto fail;
       }
-
-      if ((buf_type == BUF_VIDEO_H264 || buf_type == BUF_AUDIO_AAC) && extrabuffer[0] == 0) {
-        /* AVC/AAC sequence header */
-        buf->pts = 0;
-        buf->size = 0;
-
-        buf->decoder_flags = BUF_FLAG_SPECIAL | BUF_FLAG_HEADER;
-        if (preview)
-          buf->decoder_flags |= BUF_FLAG_PREVIEW;
-
-        buf->decoder_info[1] = BUF_SPECIAL_DECODER_CONFIG;
-        buf->decoder_info[2] = MIN(remaining_bytes, buf->max_size);
-        buf->decoder_info_ptr[2] = buf->mem;
-
-        if (this->input->read(this->input, buf->mem, buf->decoder_info[2]) != buf->decoder_info[2]) {
-          buf->free_buffer(buf);
-          this->status = DEMUX_FINISHED;
-          break;
-        }
-
-        if (remaining_bytes > buf->max_size) {
-          xprintf(this->xine, XINE_VERBOSITY_LOG,
-                _("sequence header too big (%u bytes)!\n"), remaining_bytes);
-          this->input->seek(this->input, remaining_bytes-buf->max_size, SEEK_CUR);
-        }
-        remaining_bytes = 0;
-      }
-      else {
-        buf->pts = (int64_t) pts * 90;
-        if (!preview)
-          check_newpts(this, buf->pts, (tag_type == FLV_TAG_TYPE_VIDEO));
-
-        if (remaining_bytes > buf->max_size)
-          buf->size = buf->max_size;
-        else
-          buf->size = remaining_bytes;
-        remaining_bytes -= buf->size;
-
-        buf->decoder_flags = buf_flags;
-        if (preview)
-          buf->decoder_flags |= BUF_FLAG_PREVIEW;
-        if (!remaining_bytes)
-          buf->decoder_flags |= BUF_FLAG_FRAME_END;
-
-        if (this->input->read(this->input, buf->content, buf->size) != buf->size) {
-          buf->free_buffer(buf);
-          this->status = DEMUX_FINISHED;
-          break;
-        }
-      }
-
       fifo->put(fifo, buf);
     }
 
@@ -737,123 +783,162 @@
   return this->status;
 }
 
-static void seek_flv_file(demux_flv_t *this, off_t seek_pos, int seek_pts) {
-  unsigned char buffer[16];
-  unsigned int  pts = this->cur_pts;
-  int           len = 0;
-  int           next_tag = 0;
-  int           do_rewind = (seek_pts < this->cur_pts);
-  int           i;
-
-  lprintf("  seeking %s to %d...\n",
-          do_rewind ? "backward" : "forward", seek_pts);
-
+static void seek_flv_file (demux_flv_t *this, off_t seek_pos, int seek_pts) {
+  int i;
+  /* we start where we are */
+  off_t pos1, pos2, size, used, found;
+  unsigned char buf[4096], *p1, *p2;
+  unsigned int now = 0, fpts = this->cur_pts, try;
+
+  size = this->input->get_length (this->input);
+  if (size > 0)
+    this->size = size;
+  found = this->input->get_current_pos (this->input) + 4;
+  if (!seek_pos && this->length)
+    pos2 = (uint64_t)size * seek_pts / this->length;
+  else
+    pos2 = (uint64_t)size * seek_pos / 65535;
+
+  xprintf (this->xine, XINE_VERBOSITY_DEBUG, "demux_flv: seek (%u.%03u, %"PRId64")\n",
+    seek_pts / 1000, seek_pts % 1000, (int64_t)pos2);
+
+   /* force send newpts */
+  this->buf_flag_seek = 1;
+
+  /* neither fileposition nor time given, restart at beginning */
   if (seek_pos == 0 && seek_pts == 0) {
     this->input->seek(this->input, this->start, SEEK_SET);
     this->cur_pts = 0;
+    this->zero_pts_count = 0;
     return;
   }
-
-  if (this->index) {
-    if (do_rewind) {
-      for (i = this->num_indices-1; i > 0; i--) {
-        if (this->index[i-1].pts < seek_pts)
-          break;
-      }
-    }
-    else {
-      for (i = 0; i < (this->num_indices-1); i++) {
-        if (this->index[i+1].pts > seek_pts)
-          break;
-      }
-    }
-
-    if (this->index[i].offset >= this->start+4) {
-      lprintf("  seeking to index entry %d (pts:%u, offset:%u).\n",
-              i, this->index[i].pts, this->index[i].offset);
-
-      this->input->seek(this->input, this->index[i].offset-4, SEEK_SET);
-      this->cur_pts = this->index[i].pts;
-    }
-  }
-  else if (seek_pos && this->videocodec && abs(seek_pts-this->cur_pts) > 300000) {
-    off_t pos, size;
-
-    pos = this->input->get_current_pos(this->input);
-    size = this->filesize ? : this->input->get_length(this->input);
-    this->input->seek(this->input, (uint64_t)size * seek_pos / 65535, SEEK_SET);
-    lprintf("  resyncing...\n");
-
-    /* resync */
-    for (i = 0; i < 200000; i++) {
-      uint8_t buf[4];
-
-      if (this->input->read(this->input, buf, 1) < 1) {
-        this->status = DEMUX_FINISHED;
+ 
+  /* use file index for time based seek (if we got 1) */
+  if (seek_pts && this->index) {
+    flv_index_entry_t *x;
+    uint32_t a = 0, b, c = this->num_indices;
+    while (a + 1 < c) {
+      b = (a + c) >> 1;
+      if (this->index[b].pts <= seek_pts) a = b; else c = b;
+    }
+    x = &this->index[a];
+    if ((x->offset >= this->start + 4) && (x->offset + 15 < size)) {
+      this->input->seek (this->input, x->offset, SEEK_SET);
+      this->input->read (this->input, (char *)buf, 15);
+      if (!buf[8] && !buf[9] && !buf[10] && (
+        ((buf[0] == FLV_TAG_TYPE_VIDEO) && ((buf[11] >> 4) == 1)) ||
+        (buf[0] == FLV_TAG_TYPE_AUDIO)
+        )) {
+        xprintf (this->xine, XINE_VERBOSITY_DEBUG,
+          "demux_flv: seek_index (%u.%03u, %"PRId64")\n",
+          x->pts / 1000, x->pts % 1000, (int64_t)x->offset);
+        this->input->seek (this->input, x->offset - 4, SEEK_SET);
+        this->cur_pts = x->pts;
         return;
       }
-      if (buf[0] == FLV_TAG_TYPE_VIDEO) {
-        this->input->seek(this->input, 7, SEEK_CUR);
-        if (this->input->read(this->input, buf, 4) < 4) {
-          this->status = DEMUX_FINISHED;
-          return;
-        }
-        /* check StreamID and CodecID */
-        if ( _X_ME_32(buf) == ME_FOURCC(0, 0, 0, (this->videocodec | 0x10)) ) {
-          this->input->seek(this->input, -16, SEEK_CUR);
-          lprintf("  ...resynced after %d bytes\n", i);
-          return;
-        }
-        this->input->seek(this->input, -11, SEEK_CUR);
-      }
-    }
-
-    lprintf("  ...resync failed!\n");
-    this->input->seek(this->input, pos, SEEK_SET);
-  }
-  else if (seek_pts) {
-    while (do_rewind ? (seek_pts < this->cur_pts) : (seek_pts > this->cur_pts)) {
-      unsigned char tag_type;
-      int           data_size;
-      int           ptag_size;
-
-      if (next_tag)
-        this->input->seek(this->input, next_tag, SEEK_CUR);
-
-      len = this->input->read(this->input, buffer, 16);
-      if (len != 16) {
-        len = (len < 0) ? 0 : len;
+    }
+    xprintf (this->xine, XINE_VERBOSITY_LOG, _("demux_flv: Not using broken seek index.\n"));
+  }
+  /* Up to 4 zero pts are OK (2 AAC/AVC sequence headers, 2 av tags).
+     Otherwise, the file is non seekable. Try a size based seek. */
+  if (this->zero_pts_count > 8) {
+    xprintf (this->xine, XINE_VERBOSITY_LOG,
+      _("demux_flv: This file is non seekable. A/V lag may occur.\n"
+        "           Recommend fixing the file with some flvtool.\n"));
+    seek_pts = 0;
+  }
+
+  /* step 1: phonebook search. Estimate file position, find next tag header,
+     check time, make better estimation, repeat */
+  for (try = 4; try && (!seek_pts || abs ((int)seek_pts - (int)fpts) > 800); try--) {
+    pos1 = found;
+    found = 0;
+    this->input->seek (this->input, pos2, SEEK_SET);
+    used = this->input->read (this->input, (char *)buf + 4096 - 12, 12);
+    for (i = 0; !found && (i < 50); i++) {
+      memcpy (buf, buf + 4096 - 12, 12);
+      used = this->input->read (this->input, (char *)buf + 12, 4096 - 12);
+      if (used <= 0) break;
+      p1 = buf;
+      p2 = buf + used + 12;
+      while (!found && (p1 + 11 < p2)) switch (*p1++) {
+        case FLV_TAG_TYPE_AUDIO:
+          if (p1[7] || p1[8] || p1[9] || ((p1[10] >> 4) != this->audiocodec)) continue;
+          found = pos2 + (p1 - 1 - buf);
+        break;
+        case FLV_TAG_TYPE_VIDEO:
+          if (p1[7] || p1[8] || p1[9] || ((p1[10] & 0x0f) != this->videocodec)) continue;
+          found = pos2 + (p1 - 1 - buf);
         break;
       }
-
-      ptag_size = _X_BE_32(&buffer[0]);
-      tag_type = buffer[4];
-      data_size = _X_BE_24(&buffer[5]);
-      pts = _X_BE_24(&buffer[8]) | (buffer[11] << 24);
-
-      if (do_rewind) {
-        if (!ptag_size)
-          break; /* beginning of movie */
-        next_tag = -(ptag_size + 16 + 4);
+      pos2 += 4096 - 12;
+    }
+    if (found) {
+      fpts = gettimestamp (p1, 3);
+      if (seek_pts && fpts) pos2 = (uint64_t)found * seek_pts / fpts;
+      else try = 1;
+      xprintf (this->xine, XINE_VERBOSITY_DEBUG,
+        "demux_flv: seek_quick (%u.%03u, %"PRId64")\n",
+        fpts / 1000, fpts % 1000, (int64_t)found);
+    } else found = pos1;
+  }
+
+  /* step 2: Traverse towards the desired time */
+  if (seek_pts) {
+    pos1 = 0;
+    pos2 = found;
+    i = 0;
+    while (1) {
+      if (pos2 < this->start + 4) break;
+      this->input->seek (this->input, pos2 - 4, SEEK_SET);
+      if (this->input->read (this->input, (char *)buf, 16) != 16) break;
+      if ((buf[4] == FLV_TAG_TYPE_VIDEO) && ((buf[15] >> 4) == 1)) pos1 = pos2;
+      if ((now = gettimestamp (buf, 8)) == 0) break;
+      if (now >= seek_pts) {
+        if (i > 0) break;
+        if ((i = _X_BE_32 (buf)) == 0) break;
+        found = pos2;
+        pos2 -= i + 4;
+        i = -1;
+      } else {
+        if (i < 0) break;
+        pos2 += _X_BE_24 (&buf[5]) + 15;
+        i = 1;
       }
-      else {
-        next_tag = data_size - 1;
-      }
-
-      if (this->flags & FLV_FLAG_HAS_VIDEO) {
-        /* sync to video key frame */
-        if (tag_type != FLV_TAG_TYPE_VIDEO || (buffer[15] >> 4) != 0x01)
-          continue;
-        lprintf("  video keyframe found at %d...\n", pts);
-      }
-      this->cur_pts = pts;
-    }
-
-    /* seek back to the beginning of the tag */
-    this->input->seek(this->input, -len, SEEK_CUR);
-
-    lprintf( "  seeked to %d.\n", pts);
-  }
+    }
+    if (pos1) found = pos1;
+    xprintf (this->xine, XINE_VERBOSITY_DEBUG,
+      "demux_flv: seek_traverse (%u.%03u, %"PRId64")\n",
+      now / 1000, now % 1000, (int64_t)(pos2));
+  }
+
+  /* Go back to previous keyframe */
+  if (this->videocodec) {
+    pos1 = pos2 = found;
+    found = 0;
+    while (1) {
+      if (pos2 < this->start + 4) break;
+      this->input->seek (this->input, pos2 - 4, SEEK_SET);
+      if (this->input->read (this->input, (char *)buf, 16) != 16) break;
+      if ((buf[4] == FLV_TAG_TYPE_VIDEO) && ((buf[15] >> 4) == 1)) {found = pos2; break;}
+      if ((i = _X_BE_32 (buf)) == 0) break;
+      pos2 -= i + 4;
+    }
+    if (found) {
+      now = gettimestamp (buf, 8);
+      xprintf (this->xine, XINE_VERBOSITY_DEBUG,
+        "demux_flv: seek_keyframe (%u.%03u, %"PRId64")\n",
+        now / 1000, now % 1000, (int64_t)found);
+    } else found = pos1;
+  }
+
+  /* we are there!! */
+  this->input->seek (this->input, found + 4, SEEK_SET);
+  this->input->read (this->input, (char *)buf, 4);
+  this->cur_pts = gettimestamp (buf, 0);
+  this->input->seek (this->input, found - 4, SEEK_SET);
+
+  return;
 }
 
 
@@ -887,8 +972,8 @@
   for (i = 0; i < 20; i++) {
     if (read_flv_packet(this, 1) != DEMUX_OK)
       break;
-    if (((this->flags & FLV_FLAG_HAS_VIDEO) && this->got_video_header) &&
-        ((this->flags & FLV_FLAG_HAS_AUDIO) && this->got_audio_header)) {
+    if (((!(this->flags & FLV_FLAG_HAS_VIDEO)) || this->got_video_header) &&
+        ((!(this->flags & FLV_FLAG_HAS_AUDIO)) || this->got_audio_header)) {
       lprintf("  headers sent...\n");
       break;
     }
@@ -902,21 +987,26 @@
 
   this->status = DEMUX_OK;
 
-  if (INPUT_IS_SEEKABLE(this->input)) {
-    if (start_pos && !start_time) {
-      if (this->length)
-        start_time = (int64_t) this->length * start_pos / 65535;
-      else if (this->index)
-        start_time = this->index[(int)(start_pos * (this->num_indices-1) / 65535)].pts;
-    }
-
+  /* if demux thread is not running, do some init stuff */
+  if (!playing) {
+    this->last_pts[0] = 0;
+    this->last_pts[1] = 0;
+    _x_demux_flush_engine(this->stream);
+    seek_flv_file(this, start_pos, start_time);
+    _x_demux_control_newpts (this->stream, 0, 0);
+    return (this->status);
+  }
+
+  if (start_pos && !start_time)
+    start_time = (int64_t) this->length * start_pos / 65535;
+
+  /* always allow initial seek (this, 0, 0, 1) after send_headers ().
+     It usually works at least due to xine input cache.
+     Even if not, no problem there. */
+  if ((!start_time && !start_pos) || INPUT_IS_SEEKABLE (this->input)) {
     if (!this->length || start_time < this->length) {
+      _x_demux_flush_engine(this->stream);
       seek_flv_file(this, start_pos, start_time);
-
-      if (playing) {
-        this->buf_flag_seek = 1;
-        _x_demux_flush_engine(this->stream);
-      }
     }
   }
 
@@ -956,7 +1046,7 @@
                                     input_plugin_t *input) {
   demux_flv_t *this;
 
-  this         = calloc(1, sizeof(demux_flv_t));
+  this         = calloc(1, sizeof (demux_flv_t));
   this->xine   = stream->xine;
   this->stream = stream;
   this->input  = input;
@@ -994,7 +1084,7 @@
 static void *init_plugin (xine_t *xine, void *data) {
   demux_flv_class_t     *this;
 
-  this = calloc(1, sizeof(demux_flv_class_t));
+  this = calloc(1, sizeof (demux_flv_class_t));
 
   this->demux_class.open_plugin     = open_plugin;
   this->demux_class.description     = N_("Flash Video file demux plugin");
@@ -1020,3 +1110,4 @@
   { PLUGIN_DEMUX, 27, "flashvideo", XINE_VERSION_CODE, &demux_info_flv, init_plugin },
   { PLUGIN_NONE, 0, "", 0, NULL, NULL }
 };
+