Subtitles demuxer

Alban Bedel <[email protected]>
Newsgroups gmane.comp.video.mplayer.g2.devel
Message-ID <[email protected]>
Hi,

I started to do some g2 hacking with $SUBJ. I added subtitles stream to
the demux_find_streams function and wrote a simple mpsub demuxer.

To test that i added a quick hack to test-play to show the subs on the
terminal. Sadly i have atm no good movie / subtitle combination
to do proper test about the sync but it seems to be ok.

The demuxer don't do autodetection atm so you have to force it :
test-play -sub subfile -subdemuxer txtsub

Now this is only a quick hack to have some thing comming out of the
demuxer level. I'd like to know what are the plans for the subtitles
decoding/filtering/output. If there are any ;)
	Albeu
-- 

Everything is controlled by a small evil group
to which, unfortunately, no one we know belongs.

_______________________________________________
MPlayer-G2-dev mailing list
[email protected]
http://mplayerhq.hu/mailman/listinfo/mplayer-g2-dev
g2-subs.diff (application/octet-stream, 10.6 KB)
--- /home/alban/MP/g2/demux/Makefile	Sun May 18 01:53:18 2003
+++ demux/Makefile	Wed Jul  9 19:41:34 2003
@@ -3,7 +3,7 @@
 
 include ../config.mak
 
-SRCS = demuxer.c demux_avi.c demux_asf.c print.c demux_mpeg.c mp3_hdr.c demux_ogg.c demux_nsv.c
+SRCS = demuxer.c demux_avi.c demux_asf.c print.c demux_mpeg.c mp3_hdr.c demux_ogg.c demux_nsv.c demux_txtsub.c
 
 OBJS	= $(SRCS:.c=.o)
 INCLUDE = -I.. $(EXTRA_INC)
--- /dev/null	Mon Jul 18 01:46:18 1994
+++ demux/demux_txtsub.c	Thu Jul 10 12:19:10 2003
@@ -0,0 +1,138 @@
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "config.h"
+#include "mp_msg.h"
+#include "help_mp.h"
+
+#include "stream/stream.h"
+#include "demuxer.h"
+
+
+typedef struct mp_txt_sub_packet_s {
+  float duration; // in ms
+  char txt[0];
+} mp_txt_sub_packet_t;
+
+struct demuxer_priv_s {
+  float pos;
+  char* line;
+};
+
+static void kill_space(char *s) {
+	int i = 0;
+	while (isspace(s[i])) ++i;
+	if (i) strcpy(s, s + i);
+	i = strlen(s) - 1;
+	while (i > 0 && isspace(s[i])) s[i--] = '\0';
+}
+
+static int driver_fill(demuxer_t *demuxer, demux_stream_t* ds){
+  stream_t* s = demuxer->stream;
+  struct demuxer_priv_s* p = demuxer->priv;
+  mp_txt_sub_packet_t* pack = NULL;
+  float off=-1,dur=-1,pts=-1;
+  demux_packet_t* dp;
+  
+  ds = demuxer->streams[0];
+
+  while(!s->eof) {
+    // No line, so try to get one
+    if(!p->line) {
+      p->line = stream_read_line(s);
+      if(!p->line) return 0; // bye
+      kill_space(p->line);
+      if(p->line[0] == '\0') { // ignore empty lines
+	free(p->line);
+	p->line = NULL;
+	continue;
+      }
+    }
+
+    // Is it a block start ??
+    if(sscanf(p->line,"%f %f",&off,&dur) == 2) {
+      // If we have a header + some text we can output a packet
+      if(pts >= 0 && pack)
+	break;
+    }
+
+    if(off < 0 || dur < 0) { // Ignore line
+          free(p->line);		    
+	  p->line = NULL;
+	  continue;
+    }
+      
+    if(pack) {
+      pack = realloc(pack,sizeof(mp_txt_sub_packet_t) + strlen(pack->txt) + 1 + strlen(p->line) + 1);
+      sprintf(pack->txt + strlen(pack->txt),"\n%s",p->line);
+    } else {
+      pack = malloc(sizeof(mp_txt_sub_packet_t) + strlen(p->line) + 1);
+      strcpy(pack->txt,p->line);
+      pack->duration = dur*ds->rate_d/ds->rate_m;
+      pts = p->pos +off*ds->rate_d/ds->rate_m;
+    }
+    free(p->line);		    
+    p->line = NULL;
+  }
+
+  if(pts <= 0) return 0;
+
+  p->pos = pts + pack->duration;
+
+  dp = new_demux_packet(0);
+  dp->buffer = (char*)pack;
+  dp->len = sizeof(mp_txt_sub_packet_t) + strlen(pack->txt) + 1;
+  dp->pts = p->pos;
+
+  // printf("Give sub packet pts=%f\n",dp->pts);
+  ds_add_packet(ds,dp);
+  return 1;
+
+}
+
+static int driver_open(demuxer_t* demuxer,int level){
+  char* line;
+  double fps = 24;
+  demux_stream_t* ds;
+  stream_t* s = demuxer->stream;
+
+  if(level != DEMUX_CHECK_LEVEL_FORCE)
+    return -1;
+
+  line = stream_read_line(s);
+  if(!line) return 0;
+
+  demuxer->priv = calloc(1,sizeof(struct demuxer_priv_s));
+  demuxer->fill = driver_fill;
+
+  if(!strncmp(line,"FORMAT=",7)) {
+    fps = strtod(line+7,NULL);
+    free(line);
+  } else
+    demuxer->priv->line = line;
+
+  printf("Subtitle fps is %f\n",fps);
+
+  ds = new_demuxer_stream(demuxer,0,DEMUX_STREAM_TYPE_SUB);
+  ds->format = ('T' << 24) | ('x' << 16) | ('T' << 8) | '8';
+  ds->rate_m = fps*1000;
+  ds->rate_d = 1000;
+
+
+  return 1;
+}
+
+static char* ext_list[]={"sub","mpsub",NULL};
+
+demux_driver_t demux_driver_txtsub = {
+    NULL, // url
+    "txtsub",
+    "Text subtitles",
+    "Albeu",
+    ext_list,
+    driver_open
+};
--- /home/alban/MP/g2/demux/demuxer.c	Sun May 18 01:53:32 2003
+++ demux/demuxer.c	Wed Jul  9 19:45:34 2003
@@ -16,6 +16,7 @@
 extern demux_driver_t demux_driver_nsv;
 extern demux_driver_t demux_driver_ts;
 extern demux_driver_t demux_driver_mpeg;
+extern demux_driver_t demux_driver_txtsub;
 
 demux_driver_t* demux_driver_list[]={
     &demux_driver_avi,
@@ -24,6 +25,7 @@
     &demux_driver_nsv,
     &demux_driver_ts,
     &demux_driver_mpeg,
+    &demux_driver_txtsub,
     NULL
 };
 
@@ -411,9 +413,10 @@
     mde->len=len;
 }
 
-int demux_find_streams(demuxer_t* demuxer, demux_stream_t** d_audio_ptr, demux_stream_t** d_video_ptr){
+int demux_find_streams(demuxer_t* demuxer, demux_stream_t** d_audio_ptr, demux_stream_t** d_video_ptr,demux_stream_t** d_sub_ptr){
     demux_stream_t* d_audio=NULL;
     demux_stream_t* d_video=NULL;
+    demux_stream_t* d_sub=NULL;
     int num=0;
     int i;
 
@@ -439,10 +442,15 @@
 		(ds->bytes>d_video->bytes*0.8 && ds->id<d_video->id))
 		d_video=ds;
 	    break;
-	}
+      	case DEMUX_STREAM_TYPE_SUB:
+	    // TODO: check resolution/fps too:
+	    if(!d_sub) d_sub=ds;
+	    break;
+  	}  
     }
     
     if(d_audio_ptr) *d_audio_ptr=d_audio;
     if(d_video_ptr) *d_video_ptr=d_video;
+    if(d_sub_ptr) *d_sub_ptr=d_sub;
     return num;
 }
--- /home/alban/MP/g2/demux/demuxer.h	Sun May 11 23:56:10 2003
+++ demux/demuxer.h	Wed Jul  9 17:04:11 2003
@@ -279,7 +279,7 @@
 demuxer_t* demux_open(stream_t *stream,char* name,int flags);
 int demux_seek(demuxer_t *demuxer,float rel_seek_secs,int flags);
 int demux_control(demuxer_t *demuxer, int cmd, void *arg);
-int demux_find_streams(demuxer_t* demuxer, demux_stream_t** d_audio_ptr, demux_stream_t** d_video_ptr);
+int demux_find_streams(demuxer_t* demuxer, demux_stream_t** d_audio_ptr, demux_stream_t** d_video_ptr, demux_stream_t** d_sub_ptr);
 
 // AVI demuxer params:
 //extern int index_mode;  // -1=untouched  0=don't use index  1=use (generate) index
--- /home/alban/MP/g2/stream/stream.c	Mon May 26 02:23:50 2003
+++ stream/stream.c	Wed Jul  9 19:50:52 2003
@@ -207,6 +207,38 @@
   //stream_seek(s,0);
 }
 
+char* stream_read_line(stream_t *s) {
+  char* ret = NULL;
+  int i;
+
+  while(!s->eof) {
+    int rlen = ret ? strlen(ret) : 0;
+    int blen;
+    for(i = s->buf_pos ; i < s->buf_len ; i++) {
+      if(s->buffer[i] == '\n') break;
+    }
+    if(i > s->buf_len)
+      i = s->buf_len;
+    blen = i-s->buf_pos;
+    ret = realloc(ret,rlen+blen+1);
+    if(blen > 0)
+      memcpy(ret + rlen,s->buffer + s->buf_pos, blen);
+    ret[rlen+blen] = '\0';
+    if(i < s->buf_len) { // eol found
+      if(i == s->buf_len-1)
+	stream_fill_buffer(s);
+      else
+	s->buf_pos = i+1;
+      break;
+    }
+    if(!stream_fill_buffer(s))
+      break;
+  }
+  return ret;
+}
+    
+    
+
 stream_t* new_memory_stream(unsigned char* data,int len){
   stream_t *s=malloc(sizeof(stream_t)+len);
   memset(s,0,sizeof(stream_t));
--- /home/alban/MP/g2/stream/stream.h	Sun May 18 02:09:23 2003
+++ stream/stream.h	Wed Jul  9 17:53:26 2003
@@ -53,6 +53,7 @@
   int (*reset)(stream_t *stream);
   int (*fill)(stream_t *stream);
   int (*seek)(stream_t *stream,off_t pos);
+  int (*control)(stream_t *stream,int ctrl,void* arg);
   int (*free)(stream_t *stream);
 } stream_driver_t;
 
@@ -148,6 +149,8 @@
   }
   return total;
 }
+
+char* stream_read_line(stream_t *s);
 
 inline static int stream_eof(stream_t *s){
   return s->eof;
--- /home/alban/MP/g2/test-play.c	Sun Jul  6 00:29:35 2003
+++ test-play.c	Thu Jul 10 12:10:55 2003
@@ -31,6 +31,8 @@
 static int dumpstream=0;
 static char* demuxer_name=NULL;
 static int demuxer_use_file_ext=0;
+static char* sub_filename=NULL;
+static char* sub_demuxer_name=NULL;
 
 static int autosync=30;
 
@@ -60,6 +62,8 @@
 
 static config_t main_config[]={
 	{"dumpstream", &dumpstream, CONF_TYPE_FLAG, 0, 0, 1, NULL, "dump raw stream"},
+	{"sub",&sub_filename, CONF_TYPE_STRING, 0, 0, 0, NULL, "external subtitles stream"},
+	{"subdemuxer",&sub_demuxer_name, CONF_TYPE_STRING, 0, 0, 0, NULL, "force external subtitles demuxer"},
 	{"id", &stream_id, CONF_TYPE_INT, CONF_RANGE, 0, MAX_STREAMS-1, NULL, "stream ID"},
 
 	{"extbased", &demuxer_use_file_ext, CONF_TYPE_FLAG, 0, 0, 1, NULL, "extension-based format detection"},
@@ -122,6 +126,30 @@
     return filename;
 }
 
+void test_subs(demux_stream_t *d_sub,float time) {
+  static struct mp_txt_sub_packet_s {
+    float duration; // in ms
+    char txt[0];
+  } *pack = NULL;
+
+  while(1) {
+    if(!pack) {
+      if(ds_get_packet(d_sub,(unsigned char **)&pack) <= sizeof(struct mp_txt_sub_packet_s) || !pack) {
+	printf("Got too small subtitle packet ???\n");
+	return;
+      }
+    }
+    if(time < d_sub->pts) return;
+    if(time > d_sub->pts + pack->duration) {
+      printf("Warning skiped one sub\n");
+      continue;
+    }
+    break;
+  }
+  printf(" %f (%f): %s\n",d_sub->pts,time,pack->txt);
+  pack = NULL;
+}
+      
 
 static char* filename=NULL;
 static char* default_filename="/3a/MPlayer/samples/asf-wmv/Alice Deejay - Back In My Life.asf";
@@ -129,10 +157,11 @@
 extern int vo_x11_get_key(int fd);
 
 int main(int argc,char* argv[], char *envp[]){
-  stream_t* stream;
-  demuxer_t* demuxer;
+  stream_t* stream,*sub_stream;
+  demuxer_t* demuxer, *sub_demuxer;
   demux_stream_t* d_audio=NULL;
   demux_stream_t* d_video=NULL;
+  demux_stream_t* d_sub=NULL;
   sh_audio_t* sh_audio=NULL;
   sh_video_t* sh_video=NULL;
   ao_functions_t* audio_out=NULL;
@@ -181,7 +210,7 @@
     print_meta_data(&demuxer->meta);
 
     // find best(?) audio & video stream:
-    demux_find_streams(demuxer, &d_audio, &d_video);
+    demux_find_streams(demuxer, &d_audio, &d_video, sub_filename ? NULL : &d_sub);
 
     // Check audio:
     if(d_audio){
@@ -268,6 +297,27 @@
     }
 novideo:
 
+    while(sub_filename) {
+      sub_stream=open_stream(sub_filename);
+      if(!sub_stream) {
+	mp_msg(MSGT_CPLAYER,MSGL_ERR,"Couldn't open the subtitle stream %s\n",
+	       sub_filename);
+	break;
+      }
+      sub_demuxer=demux_open(sub_stream,sub_demuxer_name,0);
+      if(!sub_demuxer) {
+	mp_msg(MSGT_CPLAYER,MSGL_FATAL,"Unknown file-format: %s\n",
+	       sub_stream->filename);
+	free_stream(sub_stream);
+	break;
+      }
+      demux_find_streams(sub_demuxer, NULL, NULL, &d_sub);
+      break;
+    }
+
+    if(d_sub)
+      mp_msg(MSGT_CPLAYER,MSGL_INFO,"Found a subtitle stream\n");
+
 
 //INIT input
     mp_input_init();
@@ -426,6 +476,8 @@
 	    if(!frame_time_remaining){
 		if(mpi && mpi->priv && video_out->show_frame)
 		    video_out->show_frame(video_out,(vo_buffer_t*)mpi->priv,0);
+		if(d_sub && !d_sub->eof)
+		  test_subs(d_sub,v_pts);
 		mpi=NULL;
 	    }
 	} else if(sh_audio && !d_audio->eof){
@@ -455,7 +507,7 @@
 #endif
 	    sh_audio->delay+=x; c_total+=x;
 	    time_frame-=x; // for -autosync
-	    mp_msg(MSGT_AVSYNC,MSGL_STATUS,"A:%8.3f (%5.3f) V:%8.3f (%5.3f) A-V:%7.4f ct:%7.4f d:%5.3f x:%5.3f \r",
+	    mp_msg(MSGT_AVSYNC,MSGL_STATUS,"A:%8.3f (%5.3f) V:%8.3f (%5.3f) A-V:%+7.4f ct:%7.4f d:%5.3f x:%+5.3f \r",
 		(float)(a_pts-delay), (float)((a_pts-delay) - old_apts),
 		 (float)v_pts, (float)(v_pts - old_vpts),
 		 AV_delay, c_total, delay, x);
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.