| @@ -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 | } |