[MPlayer-dev-eng] [PATCH] - fix for audio and video in dvr-ms asf files

John Donaghy johnfdonaghy at gmail.com
Fri Mar 31 20:07:05 CEST 2006


Hi

I've been doing some work on the asf demuxer in ffmpeg and apparently
there's some interest in making the mplayer demuxer work with dvr-ms
files too. So I've attached a patch that fixes most of the audio and
video problems that occur when attempting to play dvr-ms in mplayer.

The audio fix is a port of the work I did in ffmpeg. The way I've done
it here is a little different though... if the dvr-ms guid is
discovered in the header then some special case code is executed to
initialise the audio. This shouldn't affect regular asf files and has
minimal impact on the existing code.

The video fix is basically a lift of the work done by others on the
ffmpeg asf demuxer. The reason dvr-ms files haven't worked until now
is that the mpeg2 video frames need to be parsed out of the data
segments. The existing code assumes that when the media object number
changes then you've got your frame. This is not the case with dvr-ms
files though. FFmpeg has a generic way of handling this kind of
parsing and there may well be some equivalent in mplayer. I haven't
had time to look though so I've just taken the relevant bits of code
from ffmpeg and dropped them into demux_asf.c. I dont know if it's
acceptable to do that but I'm sure you'll let me know. If there's a
better way to do it please point me in the right direction and I'll
have a go at some point.

There are still at least the following issues to be resolved that I
hope to get to at some point... seeking is broken, the frame height is
wrong, the fps and bit rate displayed at the start is wrong and
there's no support for subtitles. If you're going to tackle these,
please let me know so as we don't duplicate effort.

Regards,

John
-------------- next part --------------
Index: asfheader.c
===================================================================
RCS file: /cvsroot/mplayer/main/libmpdemux/asfheader.c,v
retrieving revision 1.49
diff -u -r1.49 asfheader.c
--- asfheader.c	30 Mar 2006 23:06:18 -0000	1.49
+++ asfheader.c	31 Mar 2006 17:35:15 -0000
@@ -34,6 +34,8 @@
 #define ASF_GUID_PREFIX_file_header	0x8CABDCA1
 #define	ASF_GUID_PREFIX_content_desc	0x75b22633
 #define	ASF_GUID_PREFIX_stream_group	0x7bf875ce
+#define ASF_GUID_PREFIX_ext_audio_stream	0x9D8C1731
+#define ASF_GUID_PREFIX_ext_stream_embed_stream_header	0x3AFB65E2
 
 /*
 const char asf_audio_stream_guid[16] = {0x40, 0x9e, 0x69, 0xf8,
@@ -51,6 +53,12 @@
   0x8d, 0x46, 0xd1, 0x11, 0x8d, 0x82, 0x00, 0x60, 0x97, 0xc9, 0xa2, 0xb2};
 const char asf_data_chunk_guid[16] = {0x36, 0x26, 0xb2, 0x75,
   0x8e, 0x66, 0xcf, 0x11, 0xa6, 0xd9, 0x00, 0xaa, 0x00, 0x62, 0xce, 0x6c};
+const char asf_ext_stream_embed_stream_header[16] = {0xe2, 0x65, 0xfb, 0x3a,
+  0xef, 0x47, 0xf2, 0x40, 0xac, 0x2c, 0x70, 0xa9, 0x0d, 0x71, 0xd3, 0x43};
+const char asf_ext_stream_audio[16] = {0x9d, 0x8c, 0x17, 0x31,
+  0xe1, 0x03, 0x28, 0x45, 0xb5, 0x82, 0x3d, 0xf9, 0xdb, 0x22, 0xf5, 0x03};
+const char asf_ext_stream_header[16] = {0xCB, 0xA5, 0xE6, 0x14,
+  0x72, 0xC6, 0x32, 0x43, 0x83, 0x99, 0xA9, 0x69, 0x52, 0x06, 0x5B, 0x5A};
 
 
 // the variable string is modify in this function
@@ -77,6 +85,10 @@
   switch(ASF_LOAD_GUID_PREFIX(guid)){
     case ASF_GUID_PREFIX_audio_stream:
       return "guid_audio_stream";
+    case ASF_GUID_PREFIX_ext_audio_stream:
+      return "guid_ext_audio_stream";
+    case ASF_GUID_PREFIX_ext_stream_embed_stream_header:
+      return "guid_ext_stream_embed_stream_header";
     case ASF_GUID_PREFIX_video_stream: 
       return "guid_video_stream";
     case ASF_GUID_PREFIX_audio_conceal_none:
@@ -144,6 +156,42 @@
   return -1;
 }
 
+static int find_backwards_asf_guid(char *buf, const char *guid, int cur_pos)
+{
+  int i;
+  for (i=cur_pos; i>0; i--) {
+    if (memcmp(&buf[i], guid, 16) == 0)
+      return i + 16 + 8; // point after guid + length
+  }
+  return -1;
+}
+
+static int asf_init_audio_stream(demuxer_t *demuxer,struct asf_priv* asf, sh_audio_t* sh_audio, ASF_stream_header_t *streamh, int *ppos, uint8_t** buf, char *hdr, unsigned int hdr_len)
+{
+  uint8_t *buffer = *buf;
+  int pos = *ppos;
+  
+  sh_audio->wf=calloc((streamh->type_size<sizeof(WAVEFORMATEX))?sizeof(WAVEFORMATEX):streamh->type_size,1);
+  memcpy(sh_audio->wf,buffer,streamh->type_size);
+  le2me_WAVEFORMATEX(sh_audio->wf);
+  if( mp_msg_test(MSGT_HEADER,MSGL_V) ) print_wave_header(sh_audio->wf,MSGL_V);
+  if(ASF_LOAD_GUID_PREFIX(streamh->concealment)==ASF_GUID_PREFIX_audio_conceal_interleave){
+    buffer = &hdr[pos];
+    pos += streamh->stream_size;
+    if (pos > hdr_len) return 0;
+    asf->scrambling_h=buffer[0];
+    asf->scrambling_w=(buffer[2]<<8)|buffer[1];
+    asf->scrambling_b=(buffer[4]<<8)|buffer[3];
+    if(asf->scrambling_b>0){
+      asf->scrambling_w/=asf->scrambling_b;
+    }
+  } else {
+    asf->scrambling_b=asf->scrambling_h=asf->scrambling_w=1;
+  }
+  mp_msg(MSGT_HEADER,MSGL_V,"ASF: audio scrambling: %d x %d x %d\n",asf->scrambling_h,asf->scrambling_w,asf->scrambling_b);
+  return 1;
+}
+
 int read_asf_header(demuxer_t *demuxer,struct asf_priv* asf){
   int hdr_len = asf->header.objh.size - sizeof(asf->header);
   char *hdr = NULL;
@@ -156,6 +204,8 @@
   int best_video = -1;
   int best_audio = -1;
   uint64_t data_len;
+  ASF_stream_header_t *streamh;
+  uint8_t *buffer;
 
   if(hdr_len < 0) {
     mp_msg(MSGT_HEADER, MSGL_FATAL, "Header size is too small.\n");
@@ -179,12 +229,35 @@
     goto err_out;
   }
 
+  pos=0;
+  if ((pos = find_asf_guid(hdr, asf_ext_stream_audio, pos, hdr_len)) >= 0)
+  {
+    // Special case: found GUID for dvr-ms audio.
+    // Now skip back to associated stream header.
+    int sh_pos=0;
+    int audio_pos=0;
+    
+    sh_pos = find_backwards_asf_guid(hdr, asf_stream_header_guid, pos);
+     
+    if (sh_pos > 0) {
+      // found audio stream header - following code reads header and
+      // initializes audio stream.
+      audio_pos = pos - 16 - 8;
+      streamh = (ASF_stream_header_t *)&hdr[sh_pos];
+      le2me_ASF_stream_header_t(streamh);
+      audio_pos += 64; //16+16+4+4+4+16+4;
+      buffer = &hdr[audio_pos];
+      sh_audio_t* sh_audio=new_sh_audio(demuxer,streamh->stream_no & 0x7F);
+      ++audio_streams;
+      if (!asf_init_audio_stream(demuxer, asf, sh_audio, streamh, &audio_pos, &buffer, hdr, hdr_len))
+        goto len_err_out;
+    }
+  }
   // find stream headers
   pos = 0;
   while ((pos = find_asf_guid(hdr, asf_stream_header_guid, pos, hdr_len)) >= 0)
   {
-    ASF_stream_header_t *streamh = (ASF_stream_header_t *)&hdr[pos];
-    uint8_t *buffer;
+    streamh = (ASF_stream_header_t *)&hdr[pos];
     pos += sizeof(ASF_stream_header_t);
     if (pos > hdr_len) goto len_err_out;
     le2me_ASF_stream_header_t(streamh);
@@ -206,24 +279,8 @@
       case ASF_GUID_PREFIX_audio_stream: {
         sh_audio_t* sh_audio=new_sh_audio(demuxer,streamh->stream_no & 0x7F);
         ++audio_streams;
-        sh_audio->wf=calloc((streamh->type_size<sizeof(WAVEFORMATEX))?sizeof(WAVEFORMATEX):streamh->type_size,1);
-        memcpy(sh_audio->wf,buffer,streamh->type_size);
-	le2me_WAVEFORMATEX(sh_audio->wf);
-        if( mp_msg_test(MSGT_HEADER,MSGL_V) ) print_wave_header(sh_audio->wf,MSGL_V);
-	if(ASF_LOAD_GUID_PREFIX(streamh->concealment)==ASF_GUID_PREFIX_audio_conceal_interleave){
-          buffer = &hdr[pos];
-          pos += streamh->stream_size;
-          if (pos > hdr_len) goto len_err_out;
-          asf->scrambling_h=buffer[0];
-          asf->scrambling_w=(buffer[2]<<8)|buffer[1];
-          asf->scrambling_b=(buffer[4]<<8)|buffer[3];
-          if(asf->scrambling_b>0){
-            asf->scrambling_w/=asf->scrambling_b;
-          }
-	} else {
-	  asf->scrambling_b=asf->scrambling_h=asf->scrambling_w=1;
-	}
-	mp_msg(MSGT_HEADER,MSGL_V,"ASF: audio scrambling: %d x %d x %d\n",asf->scrambling_h,asf->scrambling_w,asf->scrambling_b);
+        if (!asf_init_audio_stream(demuxer, asf, sh_audio, streamh, &pos, &buffer, hdr, hdr_len))
+          goto len_err_out;
 	//if(demuxer->audio->id==-1) demuxer->audio->id=streamh.stream_no & 0x7F;
         break;
         }
@@ -235,10 +292,14 @@
         sh_video->bih=calloc((len<sizeof(BITMAPINFOHEADER))?sizeof(BITMAPINFOHEADER):len,1);
         memcpy(sh_video->bih,&buffer[4+4+1+2],len);
 	le2me_BITMAPINFOHEADER(sh_video->bih);
-	if (sh_video->bih->biCompression == mmioFOURCC('D', 'V', 'R', ' '))
-	  mp_msg(MSGT_DEMUXER, MSGL_WARN, MSGTR_MPDEMUX_ASFHDR_DVRWantsLibavformat);
-        //sh_video->fps=(float)sh_video->video.dwRate/(float)sh_video->video.dwScale;
-        //sh_video->frametime=(float)sh_video->video.dwScale/(float)sh_video->video.dwRate;
+        if (sh_video->bih->biCompression == mmioFOURCC('D', 'V', 'R', ' ')) {
+          //mp_msg(MSGT_DEMUXER, MSGL_WARN, MSGTR_MPDEMUX_ASFHDR_DVRWantsLibavformat);
+          //sh_video->fps=(float)sh_video->video.dwRate/(float)sh_video->video.dwScale;
+          //sh_video->frametime=(float)sh_video->video.dwScale/(float)sh_video->video.dwRate;
+          demuxer->video->asf_frame_state=-1;
+          demuxer->video->asf_frame_start_found=0;
+          demuxer->video->asf_is_dvr_ms=1;
+        } else if (demuxer->video) demuxer->video->asf_is_dvr_ms=0;
         if( mp_msg_test(MSGT_DEMUX,MSGL_V) ) print_video_header(sh_video->bih, MSGL_V);
         //asf_video_id=streamh.stream_no & 0x7F;
 	//if(demuxer->video->id==-1) demuxer->video->id=streamh.stream_no & 0x7F;
Index: demux_asf.c
===================================================================
RCS file: /cvsroot/mplayer/main/libmpdemux/demux_asf.c,v
retrieving revision 1.47
diff -u -r1.47 demux_asf.c
--- demux_asf.c	30 Mar 2006 23:06:18 -0000	1.47
+++ demux_asf.c	31 Mar 2006 17:35:17 -0000
@@ -14,6 +14,11 @@
 
 #include "libvo/fastmemcpy.h"
 
+#define ASFMIN(a,b) ((a) > (b) ? (b) : (a))
+#define SLICE_MIN_START_CODE    0x00000101
+#define SLICE_MAX_START_CODE    0x000001af
+#define END_NOT_FOUND -100
+
 /*
  * Load 16/32-bit values in little endian byte order
  * from an unaligned address
@@ -30,6 +35,28 @@
 			 ((unsigned char*)(p))[1]<<8)
 #endif
 
+#if defined(ARCH_X86) || defined(ARCH_X86_64)
+#    define unaligned32(a) (*(const uint32_t*)(a))
+#else
+#    ifdef __GNUC__
+static inline uint32_t unaligned32(const void *v) {
+    struct Unaligned {
+        uint32_t i;
+    } __attribute__((packed));
+
+    return ((const struct Unaligned *) v)->i;
+}
+#    elif defined(__DECC)
+static inline uint32_t unaligned32(const void *v) {
+    return *(const __unaligned uint32_t *) v;
+}
+#    else
+static inline uint32_t unaligned32(const void *v) {
+    return *(const uint32_t *) v;
+}
+#    endif
+#endif //!ARCH_X86
+
 // defined at asfheader.c:
 
 extern int asf_check_header(demuxer_t *demuxer);
@@ -64,9 +91,85 @@
 #define FF_INPUT_BUFFER_PADDING_SIZE 8
 #endif
 
+static const uint8_t *find_start_code(const uint8_t * restrict p, const uint8_t *end, uint32_t * restrict state){
+  int i;
+  if(p>=end)
+    return end;
+
+  for(i=0; i<3; i++){
+    uint32_t tmp= *state << 8;
+    *state= tmp + *(p++);
+    if(tmp == 0x100 || p==end)
+      return p;
+  }
+
+  while(p<end){
+    if     (p[-1] > 1      ) p+= 3;
+    else if(p[-2]          ) p+= 2;
+    else if(p[-3]|(p[-1]-1)) p++;
+    else{
+      p++;
+      break;
+    }
+  }
+
+  p= ASFMIN(p, end)-4;
+  *state=  be2me_32(unaligned32(p));
+
+  return p+4;
+}
+
+static int mpeg1_find_frame_end(demux_stream_t *ds, const uint8_t *buf, int buf_size)
+{
+  int i;
+
+  i=0;
+   if(!ds->asf_frame_start_found){
+    for(i=0; i<buf_size; i++){
+      i= find_start_code(buf+i, buf+buf_size, &ds->asf_frame_state) - buf - 1;
+      if(ds->asf_frame_state >= SLICE_MIN_START_CODE && ds->asf_frame_state <= SLICE_MAX_START_CODE){
+        i++;
+        ds->asf_frame_start_found=1;
+        break;
+      }
+    }
+  }
+
+  if(ds->asf_frame_start_found){
+    /* EOF considered as end of frame */
+      if (buf_size == 0)
+          return 0;
+            
+    for(; i<buf_size; i++){
+      i= find_start_code(buf+i, buf+buf_size, &ds->asf_frame_state) - buf - 1;
+      if((ds->asf_frame_state&0xFFFFFF00) == 0x100){
+        //if NOT in range 257 - 431
+        if(ds->asf_frame_state < SLICE_MIN_START_CODE || ds->asf_frame_state > SLICE_MAX_START_CODE){
+          ds->asf_frame_start_found=0;
+          ds->asf_frame_state=-1;
+          return i-3;
+        }
+      }
+    }
+  }
+  return END_NOT_FOUND;
+}
+
+static void demux_asf_append_to_packet(demux_packet_t* dp,unsigned char *data,int len,int offs)
+{
+  if(dp->len!=offs && offs!=-1) mp_msg(MSGT_DEMUX,MSGL_V,"warning! fragment.len=%d BUT next fragment offset=%d  \n",dp->len,offs);
+  dp->buffer=realloc(dp->buffer,dp->len+len+FF_INPUT_BUFFER_PADDING_SIZE);
+  memcpy(dp->buffer+dp->len,data,len);
+  memset(dp->buffer+dp->len+len, 0, FF_INPUT_BUFFER_PADDING_SIZE);
+  mp_dbg(MSGT_DEMUX,MSGL_DBG4,"data appended! %d+%d\n",dp->len,len);
+  dp->len+=len;
+}
+
 static int demux_asf_read_packet(demuxer_t *demux,unsigned char *data,int len,int id,int seq,unsigned long time,unsigned short dur,int offs,int keyframe){
   struct asf_priv* asf = demux->priv;
   demux_stream_t *ds=NULL;
+  int close_seg=0;
+  int frame_end_pos=END_NOT_FOUND;
   
   mp_dbg(MSGT_DEMUX,MSGL_DBG4,"demux_asf.read_packet: id=%d seq=%d len=%d\n",id,seq,len);
   
@@ -97,7 +200,23 @@
   
   if(ds){
     if(ds->asf_packet){
-      if(ds->asf_seq!=seq){
+      demux_packet_t* dp=ds->asf_packet;
+
+      if (ds==demux->video && demux->video->asf_is_dvr_ms) {
+        frame_end_pos=mpeg1_find_frame_end(ds, data, len);
+        
+        if (frame_end_pos != END_NOT_FOUND) {
+          dp->pos=demux->filepos;
+          if (frame_end_pos > 0) {
+            demux_asf_append_to_packet(dp,data,frame_end_pos,offs);
+            data += frame_end_pos;
+            len -= frame_end_pos;
+          }
+          close_seg = 1;
+        } else seq = ds->asf_seq;
+      } else close_seg = ds->asf_seq!=seq;
+
+      if(close_seg){
         // closed segment, finalize packet:
 		if(ds==demux->audio)
 		  if(asf->scrambling_h>1 && asf->scrambling_w>1 && asf->scrambling_b>0)
@@ -105,16 +224,10 @@
         ds_add_packet(ds,ds->asf_packet);
         ds->asf_packet=NULL;
       } else {
-        // append data to it!
-        demux_packet_t* dp=ds->asf_packet;
-        if(dp->len!=offs && offs!=-1) mp_msg(MSGT_DEMUX,MSGL_V,"warning! fragment.len=%d BUT next fragment offset=%d  \n",dp->len,offs);
-        dp->buffer=realloc(dp->buffer,dp->len+len+FF_INPUT_BUFFER_PADDING_SIZE);
-        memcpy(dp->buffer+dp->len,data,len);
-        memset(dp->buffer+dp->len+len, 0, FF_INPUT_BUFFER_PADDING_SIZE);
-        mp_dbg(MSGT_DEMUX,MSGL_DBG4,"data appended! %d+%d\n",dp->len,len);
-        dp->len+=len;
-        // we are ready now.
-        return 1;
+         // append data to it!
+         demux_asf_append_to_packet(dp,data,len,offs);
+         // we are ready now.
+         return 1;
       }
     }
     // create new packet:
Index: demuxer.h
===================================================================
RCS file: /cvsroot/mplayer/main/libmpdemux/demuxer.h,v
retrieving revision 1.91
diff -u -r1.91 demuxer.h
--- demuxer.h	17 Feb 2006 01:57:41 -0000	1.91
+++ demuxer.h	31 Mar 2006 17:35:25 -0000
@@ -110,6 +110,9 @@
 // ---- asf -----
   demux_packet_t *asf_packet;  // read asf fragments here
   int asf_seq;
+  int asf_is_dvr_ms;
+  uint32_t asf_frame_state;
+  int asf_frame_start_found;
 // ---- mov -----
   unsigned int ss_mul,ss_div;
 // ---- stream header ----





















More information about the MPlayer-dev-eng mailing list