| @@ -0,0 +1,93 @@ |
| 1 | +#include "tagspriv.h" |
| 2 | + |
| 3 | +#define leuint(d) (uint)(((uchar*)(d))[3]<<24 | ((uchar*)(d))[2]<<16 | ((uchar*)(d))[1]<<8 | ((uchar*)(d))[0]<<0) |
| 4 | + |
| 5 | +int |
| 6 | +tagopus(Tagctx *ctx) |
| 7 | +{ |
| 8 | + char *v; |
| 9 | + uchar *d, h[4]; |
| 10 | + int sz, numtags, i, npages; |
| 11 | + |
| 12 | + d = (uchar*)ctx->buf; |
| 13 | + /* need to find vorbis frame with type=3 */ |
| 14 | + for(npages = 0; npages < 2; npages++){ /* vorbis comment is the second header */ |
| 15 | + int nsegs; |
| 16 | + if(ctx->read(ctx, d, 27) != 27) |
| 17 | + return -1; |
| 18 | + if(memcmp(d, "OggS", 4) != 0) |
| 19 | + return -1; |
| 20 | + |
| 21 | + /* calculate the size of the packet */ |
| 22 | + nsegs = d[26]; |
| 23 | + if(ctx->read(ctx, d, nsegs+8) != nsegs+8) |
| 24 | + return -1; |
| 25 | + for(sz = i = 0; i < nsegs; sz += d[i++]); |
| 26 | + |
| 27 | + if(memcmp(&d[nsegs], "OpusHead", 8) == 0){ |
| 28 | + if(ctx->read(ctx, d, 8) != 8 || d[0] != 1) |
| 29 | + return -1; |
| 30 | + sz -= 8; |
| 31 | + ctx->channels = d[1]; |
| 32 | + ctx->samplerate = leuint(&d[4]); |
| 33 | + }else if(memcmp(&d[nsegs], "OpusTags", 8) == 0){ |
| 34 | + break; |
| 35 | + } |
| 36 | + |
| 37 | + ctx->seek(ctx, sz-8, 1); |
| 38 | + } |
| 39 | + |
| 40 | + if(npages < 3){ |
| 41 | + if(ctx->read(ctx, d, 4) != 4) |
| 42 | + return -1; |
| 43 | + sz = leuint(d); |
| 44 | + if(ctx->seek(ctx, sz, 1) < 0 || ctx->read(ctx, h, 4) != 4) |
| 45 | + return -1; |
| 46 | + numtags = leuint(h); |
| 47 | + |
| 48 | + for(i = 0; i < numtags; i++){ |
| 49 | + if(ctx->read(ctx, h, 4) != 4) |
| 50 | + return -1; |
| 51 | + if((sz = leuint(h)) < 0) |
| 52 | + return -1; |
| 53 | + |
| 54 | + if(ctx->bufsz < sz+1){ |
| 55 | + if(ctx->seek(ctx, sz, 1) < 0) |
| 56 | + return -1; |
| 57 | + continue; |
| 58 | + } |
| 59 | + if(ctx->read(ctx, ctx->buf, sz) != sz) |
| 60 | + return -1; |
| 61 | + ctx->buf[sz] = 0; |
| 62 | + |
| 63 | + if((v = strchr(ctx->buf, '=')) == nil) |
| 64 | + return -1; |
| 65 | + *v++ = 0; |
| 66 | + cbvorbiscomment(ctx, ctx->buf, v); |
| 67 | + } |
| 68 | + } |
| 69 | + |
| 70 | + /* calculate the duration */ |
| 71 | + if(ctx->samplerate > 0){ |
| 72 | + sz = ctx->bufsz <= 4096 ? ctx->bufsz : 4096; |
| 73 | + for(i = sz; i < 65536+16; i += sz - 16){ |
| 74 | + if(ctx->seek(ctx, -i, 2) <= 0) |
| 75 | + break; |
| 76 | + v = ctx->buf; |
| 77 | + if(ctx->read(ctx, v, sz) != sz) |
| 78 | + break; |
| 79 | + for(; v != nil && v < ctx->buf+sz;){ |
| 80 | + v = memchr(v, 'O', ctx->buf+sz - v - 14); |
| 81 | + if(v != nil && v[1] == 'g' && v[2] == 'g' && v[3] == 'S' && (v[5] & 4) == 4){ /* last page */ |
| 82 | + uvlong g = leuint(v+6) | (uvlong)leuint(v+10)<<32; |
| 83 | + ctx->duration = g * 1000 / ctx->samplerate; |
| 84 | + return 0; |
| 85 | + } |
| 86 | + if(v != nil) |
| 87 | + v++; |
| 88 | + } |
| 89 | + } |
| 90 | + } |
| 91 | + |
| 92 | + return 0; |
| 93 | +} |