@@ -42,6 +42,7 @@ import {
4242 parseFrame ,
4343 checkCodecUpdate ,
4444 reset ,
45+ isLastPage ,
4546} from "./constants.js" ;
4647import HeaderCache from "./codecs/HeaderCache.js" ;
4748import MPEGParser from "./codecs/mpeg/MPEGParser.js" ;
@@ -229,12 +230,37 @@ export default class CodecParser {
229230 [ mapFrameStats ] ( frame ) {
230231 if ( frame [ codecFrames ] ) {
231232 // Ogg container
232- frame [ codecFrames ] . forEach ( ( codecFrame ) => {
233- frame [ duration ] += codecFrame [ duration ] ;
234- frame [ samples ] += codecFrame [ samples ] ;
235- this [ mapCodecFrameStats ] ( codecFrame ) ;
236- } ) ;
233+ if ( frame [ isLastPage ] ) {
234+ // cut any excess samples that fall outside of the absolute granule position
235+ // some streams put invalid data in absolute granule position, so only do this
236+ // for the end of the stream
237+ let absoluteGranulePositionSamples = frame [ samples ] ;
238+
239+ frame [ codecFrames ] . forEach ( ( codecFrame ) => {
240+ const untrimmedCodecSamples = codecFrame [ samples ] ;
241+
242+ if ( absoluteGranulePositionSamples < untrimmedCodecSamples ) {
243+ codecFrame [ samples ] =
244+ absoluteGranulePositionSamples > 0
245+ ? absoluteGranulePositionSamples
246+ : 0 ;
247+ codecFrame [ duration ] =
248+ ( codecFrame [ samples ] / codecFrame [ header ] [ sampleRate ] ) * 1000 ;
249+ }
250+
251+ absoluteGranulePositionSamples -= untrimmedCodecSamples ;
252+
253+ this [ mapCodecFrameStats ] ( codecFrame ) ;
254+ } ) ;
255+ } else {
256+ frame [ samples ] = 0 ;
257+ frame [ codecFrames ] . forEach ( ( codecFrame ) => {
258+ frame [ samples ] += codecFrame [ samples ] ;
259+ this [ mapCodecFrameStats ] ( codecFrame ) ;
260+ } ) ;
261+ }
237262
263+ frame [ duration ] = ( frame [ samples ] / this . _sampleRate ) * 1000 || 0 ;
238264 frame [ totalSamples ] = this . _totalSamples ;
239265 frame [ totalDuration ] =
240266 ( this . _totalSamples / this . _sampleRate ) * 1000 || 0 ;
0 commit comments