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
14 14
15uvlong trackpref;15uvlong trackpref;
16 16
17static Streamframe subfr[8];
18static int nsubfr;
17static Image *curim;19static Image *curim;
20static Rectangle videor;
18static u8int *curpx;21static u8int *curpx;
19static uvlong lastframe;22static uvlong firstframe, lastframe;
20static int audiofd = -1;23static int audiofd = -1;
21static Channel *audiosync;24static Channel *audiosync;
22static Channel *audiofinished;25static Channel *audiofinished;
@@ -59,6 +62,63 @@ audioproc(void *x) · 36 unmodified lines
59 threadexits(nil);62 threadexits(nil);
60}63}
61 64
65static void
66drawsub(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
62static void122static void
63drawframe(Frame *f)123drawframe(Frame *f)
64{124{
@@ -67,8 +127,10 @@ drawframe(Frame *f) · 2 unmodified lines
67 int y;127 int y;
68 128
69 x = nanosec();129 x = nanosec();
70 if(lastframe == 0)130 if(lastframe == 0){
131 firstframe = x;
71 lastframe = x;132 lastframe = x;
133 }
72 134
73 end = lastframe + f->dt;135 end = lastframe + f->dt;
74 if(x+dispdelay >= end){136 if(x+dispdelay >= end){
@@ -127,9 +189,11 @@ drawframe(Frame *f) · 52 unmodified lines
127 else189 else
128 r = screen->r;190 r = screen->r;
129 draw(screen, r, curim, nil, ZP);191 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);
130 if(showinfo)195 if(showinfo)
131 stringbg(screen, screen->r.min, display->white, ZP, font, info, display->black, ZP);196 stringbg(screen, screen->r.min, display->white, ZP, font, info, display->black, ZP);
132 flushimage(display, 1);
133 197
134 dispdelay += nanosec() - x;198 dispdelay += nanosec() - x;
135drop:199drop:
@@ -144,7 +208,7 @@ drop: · 8 unmodified lines
144static void208static void
145usage(void)209usage(void)
146{210{
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);
148 threadexitsall("usage");212 threadexitsall("usage");
149}213}
150 214
@@ -164,8 +228,8 @@ threadmain(int argc, char **argv) · 13 unmodified lines
164 Keyboardctl *kctl;228 Keyboardctl *kctl;
165 Rune key;229 Rune key;
166 Mouse m;230 Mouse m;
167 char *s, *audio, *status;231 char *s, *audio, *sub, *status;
168 Stream *stream, *svideo, *saudio;232 Stream *stream, *svideo, *saudio, *ssub;
169 Decoder *d;233 Decoder *d;
170 int i, j, end, done, forced, res, nstreams;234 int i, j, end, done, forced, res, nstreams;
171 Alt a[Cnum+1] =235 Alt a[Cnum+1] =
@@ -183,6 +247,7 @@ threadmain(int argc, char **argv) · 11 unmodified lines
183 247
184 debug = 0;248 debug = 0;
185 audio = nil;249 audio = nil;
250 sub = nil;
186 trackpref = 0;251 trackpref = 0;
187 ARGBEGIN{252 ARGBEGIN{
188 case 'a':253 case 'a':
@@ -192,6 +257,13 @@ threadmain(int argc, char **argv) · 3 unmodified lines
192 }257 }
193 audio = EARGF(usage());258 audio = EARGF(usage());
194 break;259 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;
195 case 'B':267 case 'B':
196 benchmark++;268 benchmark++;
197 break;269 break;
@@ -230,9 +302,8 @@ threadmain(int argc, char **argv) · 32 unmodified lines
230 a[Cresize].c = mctl->resizec;302 a[Cresize].c = mctl->resizec;
231 a[Ckeyboard].c = kctl->c;303 a[Ckeyboard].c = kctl->c;
232 304
233 saudio = nil;305 saudio = audio ? Sopen(audio, &nstreams) : nil;
234 if(audio != nil)306 ssub = sub ? subopen(sub, &nstreams, &i) : nil;
235 saudio = Sopen(audio, &nstreams);
236 307
237 for(end = i = 0; !end && i < argc; i++){308 for(end = i = 0; !end && i < argc; i++){
238 draw(screen, screen->r, display->black, nil, ZP);309 draw(screen, screen->r, display->black, nil, ZP);
@@ -246,6 +317,8 @@ threadmain(int argc, char **argv) · 7 unmodified lines
246 svideo = stream+j;317 svideo = stream+j;
247 else if(stream[j].type == Saudio && saudio == nil && !benchmark)318 else if(stream[j].type == Saudio && saudio == nil && !benchmark)
248 saudio = stream+j;319 saudio = stream+j;
320 else if(stream[j].type == Ssubtitles && ssub == nil)
321 ssub = stream+j;
249 else322 else
250 Sclose(stream+j);323 Sclose(stream+j);
251 }324 }
@@ -258,6 +331,7 @@ threadmain(int argc, char **argv) · 6 unmodified lines
258 a[Cframe].c = d->frames;331 a[Cframe].c = d->frames;
259 a[Cplayerdone].c = d->finished;332 a[Cplayerdone].c = d->finished;
260 lastframe = 0;333 lastframe = 0;
334 nsubfr = 0;
261 335
262 if(saudio != nil){336 if(saudio != nil){
263 audiosync = chancreate(sizeof(void*), 1);337 audiosync = chancreate(sizeof(void*), 1);
@@ -275,7 +349,7 @@ threadmain(int argc, char **argv) · 11 unmodified lines
275 if(showinfo){349 if(showinfo){
276 snprint(350 snprint(
277 info, sizeof(info),351 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",
279 d->decodetime/1000000ULL,353 d->decodetime/1000000ULL,
280 dispdelay/1000000ULL,354 dispdelay/1000000ULL,
281 yuv→rgb/1000000ULL,355 yuv→rgb/1000000ULL,
@@ -284,10 +358,13 @@ threadmain(int argc, char **argv) · 2 unmodified lines
284 frame->w, frame->h,358 frame->w, frame->h,
285 d->info,359 d->info,
286 d->s->info,360 d->s->info,
287 saudio ? saudio->info : "no audio"361 saudio ? saudio->info : "no audio",
362 ssub ? ssub->info : "no subtitles"
288 );363 );
289 }364 }
290 drawframe(frame);365 drawframe(frame);
366 drawsub(ssub);
367 flushimage(display, 1);
291 break;368 break;
292 369
293 case Cplayerdone:370 case Cplayerdone:
@@ -328,6 +405,12 @@ threadmain(int argc, char **argv) · 34 unmodified lines
328 Sclose(saudio);405 Sclose(saudio);
329 saudio = nil;406 saudio = nil;
330 407
408 if(ssub != nil){
409 Sclose(ssub);
410 if(sub != nil)
411 free(ssub);
412 }
413
331 Dclose(d);414 Dclose(d);
332 for(i = 0; i < nstreams; i++)415 for(i = 0; i < nstreams; i++)
333 Sclose(stream+i);416 Sclose(stream+i);
misc.c+1−0
@@ -13,6 +13,7 @@ static char *fmts[Numfmt] = { · 12 unmodified lines
13 [FmtH264] = "h264",13 [FmtH264] = "h264",
14 [FmtMp3] = "mp3",14 [FmtMp3] = "mp3",
15 [FmtOpus] = "opus",15 [FmtOpus] = "opus",
16 [FmtSrt] = "srt",
16 [FmtVP8] = "vp8",17 [FmtVP8] = "vp8",
17 [FmtVP9] = "vp9",18 [FmtVP9] = "vp9",
18};19};
mkfile+1−0
@@ -27,6 +27,7 @@ OFILES=\ · 26 unmodified lines
27 stream_audio.$O\27 stream_audio.$O\
28 stream_ivf.$O\28 stream_ivf.$O\
29 stream_mc.$O\29 stream_mc.$O\
30 stream_sub.$O\
30 yuv.$O\31 yuv.$O\
31 32
32default:V: all33default:V: all
stream.c+3−1
@@ -57,7 +57,9 @@ Sclose(Stream *s) · 56 unmodified lines
57int57int
58Sread(Stream *s, Streamframe *f)58Sread(Stream *s, Streamframe *f)
59{59{
60 if(s == nil || s->type < 0)60 if(s == nil || s->type < 0){
61 werrstr("invalid stream");
61 return -1;62 return -1;
63 }
62 return s->ops.read(s, f);64 return s->ops.read(s, f);
63}65}
stream.h+16−2
@@ -8,6 +8,7 @@ typedef struct Streamops Streamops; · 7 unmodified lines
8enum {8enum {
9 Svideo,9 Svideo,
10 Saudio,10 Saudio,
11 Ssubtitles,
11 12
12 /* video */13 /* video */
13 FmtAV1 = 0,14 FmtAV1 = 0,
@@ -20,6 +21,8 @@ enum { · 6 unmodified lines
20 FmtOpus,21 FmtOpus,
21 FmtVorbis,22 FmtVorbis,
22 FmtFlac,23 FmtFlac,
24 /* subtitles */
25 FmtSrt,
23 26
24 Numfmt,27 Numfmt,
25};28};
@@ -27,8 +30,16 @@ enum { · 1 unmodified line
27struct Streamframe {30struct Streamframe {
28 uvlong timestamp;31 uvlong timestamp;
29 uvlong dt;32 uvlong dt;
30 u8int *buf;33 union {
31 int sz;34 struct { /* video/audio */
35 u8int *buf;
36 int sz;
37 };
38 struct { /* subtitles */
39 char *lines[16];
40 int nlines;
41 };
42 };
32};43};
33 44
34struct Streamops {45struct Streamops {
@@ -81,3 +92,6 @@ int Sread(Stream *s, Streamframe *f); · 46 unmodified lines
81 92
82int ivfopenb(void *bio, Stream *s, int *failed);93int ivfopenb(void *bio, Stream *s, int *failed);
83int audopenfd(int fd, Stream *s, int *failed);94int audopenfd(int fd, Stream *s, int *failed);
95int subopenfd(int fd, Stream *s, int *failed);
96
97Stream *subopen(char *path, int *num, int *failed);
stream_mc.c+12−6
@@ -5,7 +5,7 @@ · 4 unmodified lines
5#include "misc.h"5#include "misc.h"
6 6
7enum {7enum {
8 Maxstreams = 2,8 Maxstreams = 3,
9 Bufsz = 64*1024,9 Bufsz = 64*1024,
10};10};
11 11
@@ -66,7 +66,7 @@ mcopen(char *filename, int *num, int *failed) · 54 unmodified lines
66 nil,66 nil,
67 };67 };
68 Stream *s, *streams;68 Stream *s, *streams;
69 int nvideo, naudio, sp;69 int nvideo, naudio, nsub, sp;
70 char *v[8];70 char *v[8];
71 71
72 if((pid = mcfs(argv, &p)) < 0)72 if((pid = mcfs(argv, &p)) < 0)
@@ -75,17 +75,17 @@ mcopen(char *filename, int *num, int *failed) · 2 unmodified lines
75 return nil;75 return nil;
76 76
77 Binit(&b, p, OREAD);77 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;){
79 n = tokenize(line, v, nelem(v));79 n = tokenize(line, v, nelem(v));
80 if(trackpref != 0 && (trackpref & (1<<atoi(v[0]))) == 0)80 if(trackpref != 0 && (trackpref & (1<<atoi(v[0]))) == 0)
81 continue;81 continue;
82 if(n > 4 && str2fmt(v[2]) >= 0){82 if(n >= 3 && str2fmt(v[2]) >= 0){
83 argv[1] = "-t";83 argv[1] = "-t";
84 argv[2] = v[0]; /* stream id */84 argv[2] = v[0]; /* stream id */
85 argv[3] = filename;85 argv[3] = filename;
86 s = streams+ns;86 s = streams+ns;
87 87
88 if(nvideo < 1 && strcmp(v[1], "video") == 0){88 if(n >= 5 && nvideo < 1 && strcmp(v[1], "video") == 0){
89 s->video.w = atoi(v[3]);89 s->video.w = atoi(v[3]);
90 s->video.h = atoi(v[4]);90 s->video.h = atoi(v[4]);
91 if(mcfs(argv, &sp) > 0){91 if(mcfs(argv, &sp) > 0){
@@ -99,7 +99,7 @@ mcopen(char *filename, int *num, int *failed) · 7 unmodified lines
99 ns++;99 ns++;
100 }100 }
101 }101 }
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){
103 s->audio.nchan = atoi(v[3]);103 s->audio.nchan = atoi(v[3]);
104 s->audio.srate = atoi(v[4]);104 s->audio.srate = atoi(v[4]);
105 snprint(s->info, sizeof(s->info), "%s (%d %d)", v[2], s->audio.nchan, s->audio.srate);105 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
107 naudio++;107 naudio++;
108 ns++;108 ns++;
109 }109 }
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 }
110 }116 }
111 }117 }
112 free(line);118 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