| @@ -14,9 +14,12 @@ int mainstacksize = 128*1024; · 13 unmodified lines |
| 14 | | 14 | |
| 15 | uvlong trackpref; | 15 | uvlong trackpref; |
| 16 | | 16 | |
| | 17 | static Streamframe subfr[8]; |
| | 18 | static int nsubfr; |
| 17 | static Image *curim; | 19 | static Image *curim; |
| | 20 | static Rectangle videor; |
| 18 | static u8int *curpx; | 21 | static u8int *curpx; |
| 19 | static uvlong lastframe; | 22 | static uvlong firstframe, lastframe; |
| 20 | static int audiofd = -1; | 23 | static int audiofd = -1; |
| 21 | static Channel *audiosync; | 24 | static Channel *audiosync; |
| 22 | static Channel *audiofinished; | 25 | static Channel *audiofinished; |
| @@ -59,6 +62,63 @@ audioproc(void *x) · 36 unmodified lines |
| 59 | threadexits(nil); | 62 | threadexits(nil); |
| 60 | } | 63 | } |
| 61 | | 64 | |
| | 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 | |
| 62 | static void | 122 | static void |
| 63 | drawframe(Frame *f) | 123 | drawframe(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 | else | 189 | 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; |
| 135 | drop: | 199 | drop: |
| @@ -144,7 +208,7 @@ drop: · 8 unmodified lines |
| 144 | static void | 208 | static void |
| 145 | usage(void) | 209 | usage(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 | else | 322 | 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); |