@@ -5,7 +5,6 @@ import { resolve } from 'path';
55import { RtpPacket } from 'werift' ;
66
77import {
8- AV_CODEC_PROP_FIELDS ,
98 AV_NOPTS_VALUE ,
109 AV_PIX_FMT_NONE ,
1110 AV_ROUND_NEAR_INF ,
@@ -29,7 +28,7 @@ import { InputFormat } from '../lib/input-format.js';
2928import { IOContext } from '../lib/io-context.js' ;
3029import { Packet } from '../lib/packet.js' ;
3130import { Rational } from '../lib/rational.js' ;
32- import { avGetPixFmtName , avGetSampleFmtName , avInvQ , avMulQ , avRescaleQ , avRescaleQRnd } from '../lib/utilities.js' ;
31+ import { avGetPixFmtName , avGetSampleFmtName , avRescaleQ , avRescaleQRnd , dtsPredict as nativeDtsPredict } from '../lib/utilities.js' ;
3332import { DELTA_THRESHOLD , DTS_ERROR_THRESHOLD , IO_BUFFER_SIZE , MAX_INPUT_QUEUE_SIZE } from './constants.js' ;
3433import { IOStream } from './io-stream.js' ;
3534import { StreamingUtils } from './utilities/streaming.js' ;
@@ -1667,6 +1666,14 @@ export class Demuxer implements AsyncDisposable, Disposable {
16671666
16681667 // Read next packet
16691668 const ret = await this . formatContext . readFrame ( packet ) ;
1669+
1670+ // IMPORTANT: Check isClosed again after await - the demuxer may have been
1671+ // closed while we were waiting for readFrame(). If closed, the native
1672+ // AVStreams have been freed and accessing them would cause use-after-free!
1673+ if ( this . isClosed ) {
1674+ break ;
1675+ }
1676+
16701677 if ( ret < 0 ) {
16711678 // End of stream - notify all waiting consumers
16721679 this . demuxEof = true ;
@@ -1876,74 +1883,20 @@ export class Demuxer implements AsyncDisposable, Disposable {
18761883 */
18771884 private dtsPredict ( packet : Packet , stream : Stream ) : void {
18781885 const state = this . getStreamState ( packet . streamIndex ) ;
1879- const par = stream . codecpar ;
1880-
1881- // First timestamp seen
1882- if ( ! state . sawFirstTs ) {
1883- // For video with avg_frame_rate, account for video_delay
1884- const avgFrameRate = stream . avgFrameRate ;
1885- if ( avgFrameRate && avgFrameRate . num > 0 ) {
1886- const frameRateD = Number ( avgFrameRate . num ) / Number ( avgFrameRate . den ) ;
1887- state . firstDts = state . dts = BigInt ( Math . floor ( ( - par . videoDelay * Number ( AV_TIME_BASE ) ) / frameRateD ) ) ;
1888- } else {
1889- state . firstDts = state . dts = 0n ;
1890- }
1891-
1892- if ( packet . pts !== AV_NOPTS_VALUE ) {
1893- const ptsDts = avRescaleQ ( packet . pts , packet . timeBase , AV_TIME_BASE_Q ) ;
1894- state . firstDts += ptsDts ;
1895- state . dts += ptsDts ;
1896- }
1897- state . sawFirstTs = true ;
1898- }
1899-
1900- // Initialize next_dts if not set
1901- if ( state . nextDts === AV_NOPTS_VALUE ) {
1902- state . nextDts = state . dts ;
1903- }
19041886
1905- // Update from packet DTS if available
1906- if ( packet . dts !== AV_NOPTS_VALUE ) {
1907- state . nextDts = state . dts = avRescaleQ ( packet . dts , packet . timeBase , AV_TIME_BASE_Q ) ;
1908- }
1909-
1910- state . dts = state . nextDts ;
1911-
1912- // Predict next DTS based on codec type
1913- switch ( par . codecType ) {
1914- case AVMEDIA_TYPE_AUDIO :
1915- // Audio: duration from sample_rate or packet duration
1916- if ( par . sampleRate >= 1 && par . frameSize > 0 ) {
1917- state . nextDts += ( BigInt ( AV_TIME_BASE ) * BigInt ( par . frameSize ) ) / BigInt ( par . sampleRate ) ;
1918- } else {
1919- state . nextDts += avRescaleQ ( packet . duration , packet . timeBase , AV_TIME_BASE_Q ) ;
1920- }
1921- break ;
1922-
1923- case AVMEDIA_TYPE_VIDEO : {
1924- // Video: various methods depending on available metadata
1925- // Note: FFmpeg has ist->framerate (forced with -r), but we don't support that option
1926- if ( packet . duration > 0n ) {
1927- // Use packet duration
1928- state . nextDts += avRescaleQ ( packet . duration , packet . timeBase , AV_TIME_BASE_Q ) ;
1929- } else if ( par . frameRate && par . frameRate . num > 0 ) {
1930- // Use codec framerate with field handling
1931- const fieldRate = avMulQ ( par . frameRate , { num : 2 , den : 1 } ) ;
1932- let fields = 2 ; // Default: 2 fields (progressive or standard interlaced)
1933-
1934- // Check if codec has fields property and parser is available
1935- const parser = stream . parser ;
1936- if ( par . hasProperties ( AV_CODEC_PROP_FIELDS ) && parser ) {
1937- // Get repeat_pict from parser for accurate field count
1938- fields = 1 + parser . repeatPict ;
1939- }
1940-
1941- const invFieldRate = avInvQ ( fieldRate ) ;
1942- state . nextDts += avRescaleQ ( BigInt ( fields ) , invFieldRate , AV_TIME_BASE_Q ) ;
1943- }
1944- break ;
1945- }
1946- }
1887+ // Call native implementation with native objects
1888+ const newState = nativeDtsPredict ( packet , stream , {
1889+ sawFirstTs : state . sawFirstTs ,
1890+ dts : state . dts ,
1891+ nextDts : state . nextDts ,
1892+ firstDts : state . firstDts ,
1893+ } ) ;
1894+
1895+ // Update state with results
1896+ state . sawFirstTs = newState . sawFirstTs ;
1897+ state . dts = newState . dts ;
1898+ state . nextDts = newState . nextDts ;
1899+ state . firstDts = newState . firstDts ;
19471900 }
19481901
19491902 /**
0 commit comments