History

add SRT subtitles support

Sigrid Solveig Haflínudóttir committed 5 years ago

Commit 3d302081 parent d0d30996 files · +127 −20Browse files

Unified · Split

6 files
  1. main.c +94 −11
  2. misc.c +1 −0
  3. mkfile +1 −0
  4. stream.c +3 −1
  5. stream.h +16 −2
  6. stream_mc.c +12 −6
main.c+94−11
@@ -14,9 +14,12 @@ int mainstacksize = 128*1024; · 13 unmodified lines
1414
1515 uvlong trackpref;
1616
17+static Streamframe subfr[8];
18+static int nsubfr;
1719 static Image *curim;
20+static Rectangle videor;
1821 static u8int *curpx;
19−static uvlong lastframe;
22+static uvlong firstframe, lastframe;
2023 static int audiofd = -1;
2124 static Channel *audiosync;
2225 static Channel *audiofinished;
@@ -59,6 +62,63 @@ audioproc(void *x) · 36 unmodified lines
5962 threadexits(nil);
6063 }
6164
65+static void
66+drawsub(Stream *s)
67+{
68+ Streamframe *f;
69+ int i, l, h;
70+ uvlong ts;
71+ Point p;
72+
73+ if(s == nil)
74+ return;
75+
76+ /* read until we have something to show, but don't go too far */
77+ ts = lastframe - firstframe;
78+ for(; nsubfr < nelem(subfr) && (nsubfr == 0 || subfr[nsubfr-1].timestamp <= ts); nsubfr++){
79+ f = &subfr[nsubfr];
80+ if(Sread(s, f) != 0){
81+ fprint(2, "%r\n");
82+ break;
83+ }
84+ if(f->nlines == 0)
85+ return;
86+ }
87+
88+ /* remove stale text, calculate height of the lines currently displayed */
89+ h = 0;
90+ for(i = 0; i < nsubfr; i++){
91+ f = &subfr[i];
92+
93+ if(f->timestamp + f->dt < ts){
94+ for(l = 0; l < nelem(f->lines) && f->lines[l] != nil; l++)
95+ free(f->lines[l]);
96+ ts = lastframe - firstframe;
97+ memmove(subfr+i, subfr+i+1, sizeof(Streamframe)*(nsubfr-i));
98+ nsubfr--;
99+ i--;
100+ continue;
101+ }
102+
103+ if(f->timestamp <= ts && f->timestamp+f->dt >= ts)
104+ h += font->height*(f->nlines + 1); /* one extra for spacing */
105+ }
106+
107+ /* draw */
108+ p.y = videor.max.y - h;
109+ for(i = 0; i < nsubfr; i++){
110+ f = &subfr[i];
111+ if(f->timestamp <= ts && f->timestamp+f->dt >= ts){
112+ for(l = 0; l < nelem(f->lines) && f->lines[l] != nil; l++){
113+ p.x = screen->r.min.x + Dx(screen->r)/2 - stringwidth(font, f->lines[l])/2;
114+ stringbg(screen, p, display->white, ZP, font, f->lines[l], display->black, ZP);
115+ p.y += font->height;
116+ }
117+ p.y += font->height; /* spacing */
118+ }
119+ }
120+}
121+
62122 static void
63123 drawframe(Frame *f)
64124 {
@@ -67,8 +127,10 @@ drawframe(Frame *f) · 2 unmodified lines
67127 int y;
68128
69129 x = nanosec();
70− if(lastframe == 0)
130+ if(lastframe == 0){
131+ firstframe = x;
71132 lastframe = x;
133+ }
72134
73135 end = lastframe + f->dt;
74136 if(x+dispdelay >= end){
@@ -127,9 +189,11 @@ drawframe(Frame *f) · 52 unmodified lines
127189 else
128190 r = screen->r;
129191 draw(screen, r, curim, nil, ZP);
192+ videor.min = r.min;
193+ videor.max.x = r.min.x + Dx(clip);
194+ videor.max.y = r.min.y + Dy(clip);
130195 if(showinfo)
131196 stringbg(screen, screen->r.min, display->white, ZP, font, info, display->black, ZP);
132− flushimage(display, 1);
133197
134198 dispdelay += nanosec() - x;
135199 drop:
@@ -144,7 +208,7 @@ drop: · 8 unmodified lines
144208 static void
145209 usage(void)
146210 {
147− fprint(2, "usage: %s [-a AUDIOFILE] [-t N -t M ...] file.mp4\n", argv0);
211+ fprint(2, "usage: %s [-a AUDIOFILE] [-S subtitles] [-t N -t M ...] file.mp4\n", argv0);
148212 threadexitsall("usage");
149213 }
150214
@@ -164,8 +228,8 @@ threadmain(int argc, char **argv) · 13 unmodified lines
164228 Keyboardctl *kctl;
165229 Rune key;
166230 Mouse m;
167− char *s, *audio, *status;
168− Stream *stream, *svideo, *saudio;
231+ char *s, *audio, *sub, *status;
232+ Stream *stream, *svideo, *saudio, *ssub;
169233 Decoder *d;
170234 int i, j, end, done, forced, res, nstreams;
171235 Alt a[Cnum+1] =
@@ -183,6 +247,7 @@ threadmain(int argc, char **argv) · 11 unmodified lines
183247
184248 debug = 0;
185249 audio = nil;
250+ sub = nil;
186251 trackpref = 0;
187252 ARGBEGIN{
188253 case 'a':
@@ -192,6 +257,13 @@ threadmain(int argc, char **argv) · 3 unmodified lines
192257 }
193258 audio = EARGF(usage());
194259 break;
260+ case 'S':
261+ if(sub != nil){
262+ fprint(2, "only one subtitle file can be specified\n");
263+ usage();
264+ }
265+ sub = EARGF(usage());
266+ break;
195267 case 'B':
196268 benchmark++;
197269 break;
@@ -230,9 +302,8 @@ threadmain(int argc, char **argv) · 32 unmodified lines
230302 a[Cresize].c = mctl->resizec;
231303 a[Ckeyboard].c = kctl->c;
232304
233− saudio = nil;
234− if(audio != nil)
235− saudio = Sopen(audio, &nstreams);
305+ saudio = audio ? Sopen(audio, &nstreams) : nil;
306+ ssub = sub ? subopen(sub, &nstreams, &i) : nil;
236307
237308 for(end = i = 0; !end && i < argc; i++){
238309 draw(screen, screen->r, display->black, nil, ZP);
@@ -246,6 +317,8 @@ threadmain(int argc, char **argv) · 7 unmodified lines
246317 svideo = stream+j;
247318 else if(stream[j].type == Saudio && saudio == nil && !benchmark)
248319 saudio = stream+j;
320+ else if(stream[j].type == Ssubtitles && ssub == nil)
321+ ssub = stream+j;
249322 else
250323 Sclose(stream+j);
251324 }
@@ -258,6 +331,7 @@ threadmain(int argc, char **argv) · 6 unmodified lines
258331 a[Cframe].c = d->frames;
259332 a[Cplayerdone].c = d->finished;
260333 lastframe = 0;
334+ nsubfr = 0;
261335
262336 if(saudio != nil){
263337 audiosync = chancreate(sizeof(void*), 1);
@@ -275,7 +349,7 @@ threadmain(int argc, char **argv) · 11 unmodified lines
275349 if(showinfo){
276350 snprint(
277351 info, sizeof(info),
278− "decode=%-5lld yuv→rgb=%-3lld display=%-3lld total=%-3lld late=%-3lld %dx%d %s %s %s",
352+ "decode=%-5lld yuv→rgb=%-3lld display=%-3lld total=%-3lld late=%-3lld %dx%d %s %s %s %s",
279353 d->decodetime/1000000ULL,
280354 dispdelay/1000000ULL,
281355 yuv→rgb/1000000ULL,
@@ -284,10 +358,13 @@ threadmain(int argc, char **argv) · 2 unmodified lines
284358 frame->w, frame->h,
285359 d->info,
286360 d->s->info,
287− saudio ? saudio->info : "no audio"
361+ saudio ? saudio->info : "no audio",
362+ ssub ? ssub->info : "no subtitles"
288363 );
289364 }
290365 drawframe(frame);
366+ drawsub(ssub);
367+ flushimage(display, 1);
291368 break;
292369
293370 case Cplayerdone:
@@ -328,6 +405,12 @@ threadmain(int argc, char **argv) · 34 unmodified lines
328405 Sclose(saudio);
329406 saudio = nil;
330407
408+ if(ssub != nil){
409+ Sclose(ssub);
410+ if(sub != nil)
411+ free(ssub);
412+ }
413+
331414 Dclose(d);
332415 for(i = 0; i < nstreams; i++)
333416 Sclose(stream+i);
misc.c+1−0
@@ -13,6 +13,7 @@ static char *fmts[Numfmt] = { · 12 unmodified lines
1313 [FmtH264] = "h264",
1414 [FmtMp3] = "mp3",
1515 [FmtOpus] = "opus",
16+ [FmtSrt] = "srt",
1617 [FmtVP8] = "vp8",
1718 [FmtVP9] = "vp9",
1819 };
mkfile+1−0
@@ -27,6 +27,7 @@ OFILES=\ · 26 unmodified lines
2727 stream_audio.$O\
2828 stream_ivf.$O\
2929 stream_mc.$O\
30+ stream_sub.$O\
3031 yuv.$O\
3132
3233 default:V: all
stream.c+3−1
@@ -57,7 +57,9 @@ Sclose(Stream *s) · 56 unmodified lines
5757 int
5858 Sread(Stream *s, Streamframe *f)
5959 {
60− if(s == nil || s->type < 0)
60+ if(s == nil || s->type < 0){
61+ werrstr("invalid stream");
6162 return -1;
63+ }
6264 return s->ops.read(s, f);
6365 }
stream.h+16−2
@@ -8,6 +8,7 @@ typedef struct Streamops Streamops; · 7 unmodified lines
88 enum {
99 Svideo,
1010 Saudio,
11+ Ssubtitles,
1112
1213 /* video */
1314 FmtAV1 = 0,
@@ -20,6 +21,8 @@ enum { · 6 unmodified lines
2021 FmtOpus,
2122 FmtVorbis,
2223 FmtFlac,
24+ /* subtitles */
25+ FmtSrt,
2326
2427 Numfmt,
2528 };
@@ -27,8 +30,16 @@ enum { · 1 unmodified line
2730 struct Streamframe {
2831 uvlong timestamp;
2932 uvlong dt;
30− u8int *buf;
31− int sz;
33+ union {
34+ struct { /* video/audio */
35+ u8int *buf;
36+ int sz;
37+ };
38+ struct { /* subtitles */
39+ char *lines[16];
40+ int nlines;
41+ };
42+ };
3243 };
3344
3445 struct Streamops {
@@ -81,3 +92,6 @@ int Sread(Stream *s, Streamframe *f); · 46 unmodified lines
8192
8293 int ivfopenb(void *bio, Stream *s, int *failed);
8394 int audopenfd(int fd, Stream *s, int *failed);
95+int subopenfd(int fd, Stream *s, int *failed);
96+
97+Stream *subopen(char *path, int *num, int *failed);
stream_mc.c+12−6
@@ -5,7 +5,7 @@ · 4 unmodified lines
55 #include "misc.h"
66
77 enum {
8− Maxstreams = 2,
8+ Maxstreams = 3,
99 Bufsz = 64*1024,
1010 };
1111
@@ -66,7 +66,7 @@ mcopen(char *filename, int *num, int *failed) · 54 unmodified lines
6666 nil,
6767 };
6868 Stream *s, *streams;
69− int nvideo, naudio, sp;
69+ int nvideo, naudio, nsub, sp;
7070 char *v[8];
7171
7272 if((pid = mcfs(argv, &p)) < 0)
@@ -75,17 +75,17 @@ mcopen(char *filename, int *num, int *failed) · 2 unmodified lines
7575 return nil;
7676
7777 Binit(&b, p, OREAD);
78− for(ns = naudio = nvideo = 0; ns < Maxstreams && (line = Brdstr(&b, '\n', 1)) != nil;){
78+ for(ns = naudio = nvideo = nsub = 0; ns < Maxstreams && (line = Brdstr(&b, '\n', 1)) != nil;){
7979 n = tokenize(line, v, nelem(v));
8080 if(trackpref != 0 && (trackpref & (1<<atoi(v[0]))) == 0)
8181 continue;
82− if(n > 4 && str2fmt(v[2]) >= 0){
82+ if(n >= 3 && str2fmt(v[2]) >= 0){
8383 argv[1] = "-t";
8484 argv[2] = v[0]; /* stream id */
8585 argv[3] = filename;
8686 s = streams+ns;
8787
88− if(nvideo < 1 && strcmp(v[1], "video") == 0){
88+ if(n >= 5 && nvideo < 1 && strcmp(v[1], "video") == 0){
8989 s->video.w = atoi(v[3]);
9090 s->video.h = atoi(v[4]);
9191 if(mcfs(argv, &sp) > 0){
@@ -99,7 +99,7 @@ mcopen(char *filename, int *num, int *failed) · 7 unmodified lines
9999 ns++;
100100 }
101101 }
102− }else if(naudio < 1 && strcmp(v[1], "audio") == 0 && (s->fmt = str2fmt(v[2])) >= 0){
102+ }else if(n >= 5 && naudio < 1 && strcmp(v[1], "audio") == 0 && (s->fmt = str2fmt(v[2])) >= 0){
103103 s->audio.nchan = atoi(v[3]);
104104 s->audio.srate = atoi(v[4]);
105105 snprint(s->info, sizeof(s->info), "%s (%d %d)", v[2], s->audio.nchan, s->audio.srate);
@@ -107,6 +107,12 @@ mcopen(char *filename, int *num, int *failed) · 1 unmodified line
107107 naudio++;
108108 ns++;
109109 }
110+ }else if(n >= 3 && nsub < 1 && strcmp(v[1], "subtitle") == 0 && (s->fmt = str2fmt(v[2])) >= 0){
111+ snprint(s->info, sizeof(s->info), "%s (%s)", v[2], (n > 3 && *v[3]) ? v[3] : "default");
112+ if(mcfs(argv, &sp) > 0 && subopenfd(sp, s, failed) == 0){
113+ nsub++;
114+ ns++;
115+ }
110116 }
111117 }
112118 free(line);

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t