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);