Update of /cvsroot/xine/xine-lib/src/demuxers
In directory sc8-pr-cvs10.sourceforge.net:/tmp/cvs-serv15815

Modified Files:
        demux_flv.c 
Log Message:
Fixed video codec parsing (NOTE: we need vp6 codec from ffmpeg).
Added support for audio packet demuxing.
Added support for seeking.


Index: demux_flv.c
===================================================================
RCS file: /cvsroot/xine/xine-lib/src/demuxers/demux_flv.c,v
retrieving revision 1.9
retrieving revision 1.10
diff -u -r1.9 -r1.10
--- demux_flv.c 10 Jul 2006 22:08:13 -0000      1.9
+++ demux_flv.c 14 Dec 2006 18:29:02 -0000      1.10
@@ -20,7 +20,7 @@
 
 /*
  * Flash Video (.flv) File Demuxer
- *   by Mike Melanson ([EMAIL PROTECTED])
+ *   by Mike Melanson ([EMAIL PROTECTED]) and Claudio Ciccani ([EMAIL 
PROTECTED])
  * For more information on the FLV file format, visit:
  * http://download.macromedia.com/pub/flash/flash_file_format_specification.pdf
  *
@@ -53,101 +53,232 @@
 typedef struct {
   demux_plugin_t       demux_plugin;
 
+  xine_t              *xine;
   xine_stream_t       *stream;
   fifo_buffer_t       *video_fifo;
   fifo_buffer_t       *audio_fifo;
   input_plugin_t      *input;
   int                  status;
 
-  unsigned int         video_type;
-  unsigned int         audio_type;
-
-  off_t                data_start;
-  off_t                data_size;
-
-  unsigned char        bih[sizeof(xine_bmiheader)];
-  xine_waveformatex    wave;
-
+  unsigned char        flags;
+  unsigned int         movie_start;
+  
+  unsigned char        got_video;
+  unsigned char        got_audio;
+  
+  unsigned int         cur_pts;
+  
+  int64_t              last_pts[2];
+  int                  send_newpts;
+  int                  buf_flag_seek;
 } demux_flv_t ;
 
 typedef struct {
   demux_class_t     demux_class;
 } demux_flv_class_t;
 
+
+#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_NELLY8  0x05 /* Nellymoser 8KHz */
+#define FLV_SOUND_FORMAT_NELLY   0x06 /* Nellymoser */
+
+#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
+
+
+/* redefine abs as macro to handle 64-bit diffs.
+   i guess llabs may not be available everywhere */
+#define abs(x) ( ((x)<0) ? -(x) : (x) )
+
+#define WRAP_THRESHOLD           220000
+#define PTS_AUDIO                0
+#define PTS_VIDEO                1
+
+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 %lld\n", pts);
+
+  if (pts && (this->send_newpts || (this->last_pts[video] && 
abs(diff)>WRAP_THRESHOLD))) {
+    lprintf ("diff=%lld\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;
+}
+
 /* returns 1 if the FLV file was opened successfully, 0 otherwise */
 static int open_flv_file(demux_flv_t *this) {
+  unsigned char buffer[9];
 
-  unsigned char buffer[4];
-  off_t first_offset;
-
-  if (_x_demux_read_header(this->input, buffer, 4) != 4)
+  if (_x_demux_read_header(this->input, buffer, 9) != 9)
     return 0;
 
   if ((buffer[0] != 'F') || (buffer[1] != 'L') || (buffer[2] != 'V'))
     return 0;
+    
+  if (buffer[3] != 0x01) {
+    xprintf(this->xine, XINE_VERBOSITY_LOG,
+      _("unsupported FLV version (%d).\n"), buffer[3]);
+    return 0;
+  }
 
-  this->video_type = this->audio_type = 0;
-  if (buffer[3] & 0x1)
-    this->video_type = BUF_VIDEO_FLV1;
-/* buffer[3] * 0x4 indicates audio, possibly always MP3; deal with
-   that later */
-
-  /* file is qualified at this point; position to start of first packet */
-  this->input->seek(this->input, 5, SEEK_SET);
-  if (this->input->read(this->input, buffer, 4) != 4)
+  this->flags = buffer[4];
+  if ((this->flags & (FLV_FLAG_HAS_VIDEO | FLV_FLAG_HAS_AUDIO)) == 0) {
+    xprintf(this->xine, XINE_VERBOSITY_LOG,
+      _("neither video nor audio stream in this file.\n"));
     return 0;
+  }
 
-  first_offset = BE_32(buffer);
-  this->input->seek(this->input, first_offset, SEEK_SET);
+  this->movie_start = BE_32(&buffer[5]);
+  this->input->seek(this->input, this->movie_start, SEEK_SET);
+  
   lprintf("  qualified FLV file, repositioned @ offset 0x%" PRIxMAX "\n", 
-          (intmax_t)first_offset);
+          (intmax_t)this->movie_start);
 
   return 1;
 }
 
-static int demux_flv_send_chunk(demux_plugin_t *this_gen) {
-
-  demux_flv_t *this = (demux_flv_t *) this_gen;
-  buf_element_t *buf = NULL;
-  unsigned int remaining_bytes;
-  unsigned char chunk_type, sub_type;
-  int64_t pts;
-
-  unsigned char buffer[12];
-
-  lprintf ("  sending FLV chunk...\n");
-  this->input->seek(this->input, 4, SEEK_CUR);
-  if (this->input->read(this->input, buffer, 12) != 12) {
-    this->status = DEMUX_FINISHED;
-    return this->status;
-  }
+static int read_flv_packet(demux_flv_t *this) {
+  fifo_buffer_t *fifo = NULL;
+  buf_element_t *buf  = NULL;
+  unsigned char  buffer[12];
+  unsigned char  tag_type;
+  unsigned int   remaining_bytes;
+  unsigned int   buf_type = 0;
+  int64_t        pts;
+ 
+  while (1) {
+    lprintf ("  reading FLV tag...\n");
+    this->input->seek(this->input, 4, SEEK_CUR);
+    if (this->input->read(this->input, buffer, 11) != 11) {
+      this->status = DEMUX_FINISHED;
+      return this->status;
+    }
 
-  chunk_type = buffer[0];
-  remaining_bytes = BE_32(&buffer[0]);
-  remaining_bytes &= 0x00FFFFFF;
-  pts = BE_32(&buffer[3]);
-  pts &= 0x00FFFFFF;
-  sub_type = buffer[11];
-
-  /* Flash timestamps are in milliseconds; multiply by 90 to get xine pts */
-  pts *= 90;
-
-  lprintf ("  chunk_type = %X, 0x%X -1 bytes, pts %lld, sub-type = %X\n",
-           chunk_type, remaining_bytes, pts, sub_type);
-
-  /* only handle the chunk right now if chunk type is 9 and lower nibble
-   * of sub-type is 2 */
-  if ((chunk_type != 9) || ((sub_type & 0x0F) != 2)) {
-    this->input->seek(this->input, remaining_bytes - 1, SEEK_CUR);
-  } else {
-    /* send the chunk off to the video demuxer */
-    remaining_bytes--;  /* sub-type byte does not count */
+    tag_type = buffer[0];
+    remaining_bytes = BE_24(&buffer[1]);
+    pts = BE_24(&buffer[4]) | (buffer[7] << 24);
+    
+    lprintf("  tag_type = 0x%02X, 0x%X bytes, pts %lld\n",
+            tag_type, remaining_bytes, pts/90);
+
+    switch (tag_type) {
+      case FLV_TAG_TYPE_AUDIO:
+        lprintf("  got audio tag..\n");
+        if (this->input->read(this->input, buffer, 1) != 1) {
+          this->status = DEMUX_FINISHED;
+          return this->status; 
+        }
+        remaining_bytes--;
+        
+        switch (buffer[0] >> 4) {
+          case FLV_SOUND_FORMAT_PCM_BE:
+            buf_type = BUF_AUDIO_LPCM_BE;
+            break;
+          case FLV_SOUND_FORMAT_MP3:
+            buf_type = BUF_AUDIO_MPEG;
+            break;
+          case FLV_SOUND_FORMAT_PCM_LE:
+            buf_type = BUF_AUDIO_LPCM_LE;
+            break;
+          default:
+            lprintf("  unsupported audio format (%d)...\n", buffer[0] >> 4);
+            buf_type = BUF_AUDIO_UNKNOWN;
+            break;
+        }
+        
+        fifo = this->audio_fifo;
+        if (!this->got_audio) {
+          /* 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 - ((buffer[0] >> 2) & 3)); /* 
samplerate */
+          buf->decoder_info[2] = (buffer[0] & 2) ? 16 : 8; /* bits per sample 
*/
+          buf->decoder_info[3] = (buffer[0] & 1) + 1; /* channels */
+          buf->size = 0; /* no extra data */
+          buf->type = buf_type;
+          fifo->put(fifo, buf);
+          this->got_audio = 1;
+        }
+        break;
+        
+      case FLV_TAG_TYPE_VIDEO:
+        lprintf("  got video tag..\n");
+        if (this->input->read(this->input, buffer, 1) != 1) {
+          this->status = DEMUX_FINISHED;
+          return this->status;
+        }
+        remaining_bytes--;
+        
+        switch (buffer[0] & 0x0F) {
+          case FLV_VIDEO_FORMAT_FLV1:
+            buf_type = BUF_VIDEO_FLV1;
+            break;
+          case FLV_VIDEO_FORMAT_VP6:
+            buf_type = BUF_VIDEO_VP6;
+            break;
+          default:
+            lprintf("  unsupported video format (%d)...\n", buffer[0] & 0x0F);
+            buf_type = BUF_VIDEO_UNKNOWN;
+            break;
+        }
+        
+        fifo = this->video_fifo;        
+        if (!this->got_video) {
+          /* 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_FRAME_END;
+          buf->decoder_info[0] = 7470;  /* initial duration */
+          buf->size = 0;
+          buf->type = buf_type;
+          fifo->put(fifo, buf);
+          this->got_video = 1;
+        }
+        break;
+        
+      default:
+        lprintf("  skipping packet...\n");
+        this->input->seek(this->input, remaining_bytes, SEEK_CUR);
+        continue;
+    }
+    
     while (remaining_bytes) {
-      buf = this->video_fifo->buffer_pool_alloc (this->video_fifo);
-      buf->type = BUF_VIDEO_FLV1;
-      if( this->input->get_length (this->input) )
-        buf->extra_info->input_normpos = (int)( (double) 
this->input->get_current_pos (this->input) * 
-                                         65535 / this->input->get_length 
(this->input) );
+      buf = fifo->buffer_pool_alloc(fifo);
+      buf->type = buf_type;
+      buf->pts = (int64_t) pts * 90;
+      check_newpts(this, buf->pts, (tag_type == FLV_TAG_TYPE_VIDEO));
+      
+      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 / 
this->input->get_length(this->input) );
+      }
 
       if (remaining_bytes > buf->max_size)
         buf->size = buf->max_size;
@@ -158,25 +289,93 @@
       if (!remaining_bytes)
         buf->decoder_flags |= BUF_FLAG_FRAME_END;
 
-      if (this->input->read(this->input, buf->content, buf->size) !=
-        buf->size) {
+      if (this->input->read(this->input, buf->content, buf->size) != 
buf->size) {
         buf->free_buffer(buf);
         this->status = DEMUX_FINISHED;
         break;
       }
 
-      buf->pts = pts;
-      buf->extra_info->input_time = buf->pts / 90;
-      this->video_fifo->put(this->video_fifo, buf);
+      fifo->put(fifo, buf);
     }
+    
+    this->cur_pts = pts;
+    break;
   }
+    
+  return this->status;
+}
+
+static int seek_flv_file(demux_flv_t *this, int seek_pts) {
+  unsigned char buffer[16];
+  int           next_tag  = 0;
+  int           do_rewind = (seek_pts < this->cur_pts);
+  
+  if (this->cur_pts == seek_pts)
+    return this->status;
+    
+  if (seek_pts == 0) {
+    this->input->seek(this->input, this->movie_start, SEEK_SET);
+    this->cur_pts = 0;
+    return this->status;
+  }
+  
+  lprintf("  seeking %s to %d...\n", 
+          do_rewind ? "backward" : "forward", 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;
+    unsigned int  pts;
+    
+    if (next_tag)
+      this->input->seek(this->input, next_tag, SEEK_CUR);
+    
+    if (this->input->read(this->input, buffer, 16) != 16) {
+      this->status = DEMUX_FINISHED;
+      return this->status;
+    }
+          
+    ptag_size = BE_32(&buffer[0]);
+    tag_type = buffer[4];
+    data_size = BE_24(&buffer[5]);
+    pts = BE_24(&buffer[8]) | (buffer[11] << 24);
+    
+    if (do_rewind) {
+      if (!ptag_size) break;
+      next_tag = -(ptag_size + 16 + 4);
+    }
+    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, -16, SEEK_CUR);
+  
+  lprintf( "  seeked to %d.\n", this->cur_pts);
 
   return this->status;
 }
 
+
+static int demux_flv_send_chunk(demux_plugin_t *this_gen) {
+  demux_flv_t *this = (demux_flv_t *) this_gen;
+  
+  return read_flv_packet(this);
+}
+
 static void demux_flv_send_headers(demux_plugin_t *this_gen) {
   demux_flv_t *this = (demux_flv_t *) this_gen;
-  buf_element_t *buf;
+  int          i;
 
   this->video_fifo  = this->stream->video_fifo;
   this->audio_fifo  = this->stream->audio_fifo;
@@ -185,24 +384,23 @@
 
   /* load stream information */
   _x_stream_info_set(this->stream, XINE_STREAM_INFO_HAS_VIDEO, 
-    (this->video_type ? 1 : 0));
+                    (this->flags & FLV_FLAG_HAS_VIDEO) ? 1 : 0);
   _x_stream_info_set(this->stream, XINE_STREAM_INFO_HAS_AUDIO,
-    (this->audio_type ? 1 : 0));
+                    (this->flags & FLV_FLAG_HAS_AUDIO) ? 1 : 0);
 
   /* send start buffers */
   _x_demux_control_start(this->stream);
 
-  /* send init info to decoders; send the bitmapinfo header to the decoder
-   * primarily as a formality since there is no real data inside */
-  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] = 7470;  /* initial duration */
-  memcpy(buf->content, this->bih, sizeof(xine_bmiheader));
-  buf->size = sizeof(xine_bmiheader);
-  buf->type = BUF_VIDEO_FLV1;
-  this->video_fifo->put (this->video_fifo, buf);
-
+  /* find first audio/video packets and send headers */
+  for (i = 0; i < 20; i++) {
+    if (read_flv_packet(this) != DEMUX_OK)
+      break;
+    if (((this->flags & FLV_FLAG_HAS_VIDEO) && this->got_video) &&
+        ((this->flags & FLV_FLAG_HAS_AUDIO) && this->got_audio)) {
+      lprintf("  headers sent...\n");
+      break;
+    }
+  }
 }
 
 static int demux_flv_seek (demux_plugin_t *this_gen,
@@ -210,10 +408,16 @@
 
   demux_flv_t *this = (demux_flv_t *) this_gen;
 
-  /* if thread is not running, initialize demuxer */
-  if( !playing ) {
-    this->status = DEMUX_OK;
-  }
+  this->status = DEMUX_OK;
+
+  if (this->input->get_capabilities(this->input) & INPUT_CAP_SEEKABLE) {
+    seek_flv_file(this, start_time);
+    
+    if (playing) {
+      this->buf_flag_seek = 1;
+      _x_demux_flush_engine(this->stream);
+    }
+  }  
 
   return this->status;
 }
@@ -247,10 +451,10 @@
 
 static demux_plugin_t *open_plugin (demux_class_t *class_gen, xine_stream_t 
*stream,
                                     input_plugin_t *input) {
-
-  demux_flv_t    *this;
+  demux_flv_t *this;
 
   this         = xine_xmalloc (sizeof (demux_flv_t));
+  this->xine   = stream->xine;
   this->stream = stream;
   this->input  = input;
 
@@ -267,33 +471,24 @@
   this->status = DEMUX_FINISHED;
 
   switch (stream->content_detection_method) {
+    case METHOD_BY_EXTENSION:
+      if (!_x_demux_check_extension(input->get_mrl(input), "flv")) {
+        free (this);
+        return NULL;
+      }
+  
+  /* falling through is intended */  
+    case METHOD_BY_CONTENT:
+    case METHOD_EXPLICIT:
+      if (!open_flv_file(this)) {
+        free (this);
+        return NULL;
+      }
+      break;
 
-  case METHOD_BY_EXTENSION: {
-    char *extensions, *mrl;
-
-    mrl = input->get_mrl (input);
-    extensions = class_gen->get_extensions (class_gen);
-
-    if (!_x_demux_check_extension (mrl, extensions)) {
-      free (this);
-      return NULL;
-    }
-  }
-  /* falling through is intended */
-
-  case METHOD_BY_CONTENT:
-  case METHOD_EXPLICIT:
-
-    if (!open_flv_file(this)) {
+    default:
       free (this);
       return NULL;
-    }
-
-  break;
-
-  default:
-    free (this);
-    return NULL;
   }
 
   return &this->demux_plugin;
@@ -312,7 +507,7 @@
 }
 
 static char *get_mimetypes (demux_class_t *this_gen) {
-  return NULL;
+  return "video/x-flv";
 }
 
 static void class_dispose (demux_class_t *this_gen) {


-------------------------------------------------------------------------
Take Surveys. Earn Cash. Influence the Future of IT
Join SourceForge.net's Techsay panel and you'll get the chance to share your
opinions on IT & business topics through brief surveys - and earn cash
http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV
_______________________________________________
Xine-cvslog mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/xine-cvslog

Reply via email to