code: plan9front

Download patch

ref: 87a6e1873b3ebd90a6cc075b3a833cca5b675f1a
parent: 7f071685e9b7d9e32c1a9675f7fca0407d18e6e1
author: Sigrid Solveig Haflínudóttir <sigrid@ftrv.se>
date: Sat Feb 11 11:52:56 EST 2023

libtags: work around encoders producing Ogg containers with first granule position set to non-zero outside of the first page

--- a/sys/src/cmd/audio/libtags/opus.c
+++ b/sys/src/cmd/audio/libtags/opus.c
@@ -71,7 +71,29 @@
 
 	/* calculate the duration */
 	if(ctx->samplerate > 0){
+		uvlong first = 0;
 		sz = ctx->bufsz <= 4096 ? ctx->bufsz : 4096;
+		/* go back a bit but make sure first page is skipped */
+		ctx->seek(ctx, -sz, 1);
+		ctx->seek(ctx, 16, 1);
+		for(i = -sz; i < 65536; i += sz - 16){
+			if(ctx->seek(ctx, sz - 16, 1) <= 0)
+				break;
+			v = ctx->buf;
+			if(ctx->read(ctx, v, sz) != sz)
+				break;
+			for(; v != nil && v < ctx->buf+sz;){
+				v = memchr(v, 'O', ctx->buf+sz - v - 14);
+				if(v != nil && v[1] == 'g' && v[2] == 'g' && v[3] == 'S'){
+					first = leuint(v+6) | (uvlong)leuint(v+10)<<32;
+					goto found;
+				}
+				if(v != nil)
+					v++;
+			}
+		}
+
+found:
 		for(i = sz; i < 65536+16; i += sz - 16){
 			if(ctx->seek(ctx, -i, 2) <= 0)
 				break;
@@ -82,7 +104,7 @@
 				v = memchr(v, 'O', ctx->buf+sz - v - 14);
 				if(v != nil && v[1] == 'g' && v[2] == 'g' && v[3] == 'S'){
 					uvlong g = leuint(v+6) | (uvlong)leuint(v+10)<<32;
-					ctx->duration = g * 1000 / 48000; /* granule positions are always 48KHz */
+					ctx->duration = (g - first) * 1000 / 48000; /* granule positions are always 48KHz */
 				}
 				if(v != nil)
 					v++;
--- a/sys/src/cmd/audio/libtags/vorbis.c
+++ b/sys/src/cmd/audio/libtags/vorbis.c
@@ -107,7 +107,28 @@
 
 	/* calculate the duration */
 	if(ctx->samplerate > 0){
+		uvlong first = 0;
 		sz = ctx->bufsz <= 4096 ? ctx->bufsz : 4096;
+		ctx->seek(ctx, -sz, 1);
+		ctx->seek(ctx, 16, 1);
+		for(i = -sz; i < 65536; i += sz - 16){
+			if(ctx->seek(ctx, sz - 16, 1) <= 0)
+				break;
+			v = ctx->buf;
+			if(ctx->read(ctx, v, sz) != sz)
+				break;
+			for(; v != nil && v < ctx->buf+sz;){
+				v = memchr(v, 'O', ctx->buf+sz - v - 14);
+				if(v != nil && v[1] == 'g' && v[2] == 'g' && v[3] == 'S'){
+					first = leuint(v+6) | (uvlong)leuint(v+10)<<32;
+					goto found;
+				}
+				if(v != nil)
+					v++;
+			}
+		}
+
+found:
 		for(i = sz; i < 65536+16; i += sz - 16){
 			if(ctx->seek(ctx, -i, 2) <= 0)
 				break;
@@ -118,7 +139,7 @@
 				v = memchr(v, 'O', ctx->buf+sz - v - 14);
 				if(v != nil && v[1] == 'g' && v[2] == 'g' && v[3] == 'S' && (v[5] & 4) == 4){ /* last page */
 					uvlong g = leuint(v+6) | (uvlong)leuint(v+10)<<32;
-					ctx->duration = g * 1000 / ctx->samplerate;
+					ctx->duration = (g - first) * 1000 / ctx->samplerate;
 				}
 				if(v != nil)
 					v++;