@@ -197,7 +197,7 @@ export class RecordingDelegate implements CameraRecordingDelegate {
197
197
default :
198
198
this . log . warn ( `❓ HKSV: Unknown reason ${ reason } ` , this . cameraName )
199
199
}
200
-
200
+
201
201
// Abort the stream generator
202
202
const abortController = this . streamAbortControllers . get ( streamId )
203
203
if ( abortController ) {
@@ -273,14 +273,35 @@ export class RecordingDelegate implements CameraRecordingDelegate {
273
273
let fragmentCount = 0
274
274
275
275
this . log . debug ( 'HKSV: Starting recording request' , this . cameraName )
276
+ const audioArgs : Array < string > = [
277
+ '-acodec' ,
278
+ 'aac' ,
279
+ ...( configuration . audioCodec . type === AudioRecordingCodecType . AAC_LC
280
+ ? [ '-profile:a' , 'aac_low' ]
281
+ : [ '-profile:a' , 'aac_eld' ] ) ,
282
+ '-ar' , '32000' ,
283
+ //`${configuration.audioCodec.samplerate * 1000}`, // i see 3k here before, 3000 also will not work
284
+ '-b:a' ,
285
+ `${ configuration . audioCodec . bitrate } k` ,
286
+ '-ac' ,
287
+ `${ configuration . audioCodec . audioChannels } ` ,
288
+ ]
289
+
290
+ const profile = configuration . videoCodec . parameters . profile === H264Profile . HIGH
291
+ ? 'high'
292
+ : configuration . videoCodec . parameters . profile === H264Profile . MAIN ? 'main' : 'baseline'
293
+
294
+ const level = configuration . videoCodec . parameters . level === H264Level . LEVEL4_0
295
+ ? '4.0'
296
+ : configuration . videoCodec . parameters . level === H264Level . LEVEL3_2 ? '3.2' : '3.1'
276
297
277
298
// Clean H.264 parameters for HKSV compatibility
278
299
const videoArgs : Array < string > = [
279
300
'-an' , '-sn' , '-dn' , // Disable audio/subtitles/data (audio handled separately)
280
301
'-vcodec' , 'libx264' ,
281
302
'-pix_fmt' , 'yuv420p' ,
282
- '-profile:v' , 'baseline' ,
283
- '-level:v' , '3.1' ,
303
+ '-profile:v' , profile , // 'baseline' tested
304
+ '-level:v' , level , // '3.1' tested
284
305
'-preset' , 'ultrafast' ,
285
306
'-tune' , 'zerolatency' ,
286
307
'-b:v' , '600k' ,
@@ -292,6 +313,14 @@ export class RecordingDelegate implements CameraRecordingDelegate {
292
313
'-force_key_frames' , 'expr:gte(t,n_forced*1)'
293
314
]
294
315
316
+ if ( configuration ?. audioCodec ) {
317
+ // Remove the '-an' flag to enable audio
318
+ const anIndex = videoArgs . indexOf ( '-an' )
319
+ if ( anIndex !== - 1 ) {
320
+ videoArgs . splice ( anIndex , 1 , ...audioArgs )
321
+ }
322
+ }
323
+
295
324
// Get input configuration
296
325
const ffmpegInput : Array < string > = [ ]
297
326
if ( this . videoConfig ?. prebuffer ) {
@@ -302,7 +331,7 @@ export class RecordingDelegate implements CameraRecordingDelegate {
302
331
if ( ! this . videoConfig ?. source ) {
303
332
throw new Error ( 'No video source configured' )
304
333
}
305
- ffmpegInput . push ( ...this . videoConfig . source . split ( ' ' ) )
334
+ ffmpegInput . push ( ...this . videoConfig . source . trim ( ) . split ( / \s + / ) . filter ( arg => arg . length > 0 ) )
306
335
}
307
336
308
337
if ( ffmpegInput . length === 0 ) {
@@ -361,14 +390,12 @@ export class RecordingDelegate implements CameraRecordingDelegate {
361
390
cp : import ( 'node:child_process' ) . ChildProcess ;
362
391
} > {
363
392
return new Promise ( ( resolve , reject ) => {
364
- const args : string [ ] = [ ...ffmpegInput ]
393
+ const args : string [ ] = [ '-hide_banner' , ...ffmpegInput ]
365
394
366
395
// Add dummy audio for HKSV compatibility if needed
367
396
if ( this . videoConfig ?. audio === false ) {
368
397
args . push (
369
- '-f' , 'lavfi' , '-i' , 'anullsrc=cl=mono:r=16000' ,
370
- '-c:a' , 'aac' , '-profile:a' , 'aac_low' ,
371
- '-ac' , '1' , '-ar' , '16000' , '-b:a' , '32k' , '-shortest'
398
+ '-f' , 'lavfi' , '-i' , 'anullsrc=cl=mono:r=32000' ,
372
399
)
373
400
}
374
401
@@ -428,13 +455,25 @@ export class RecordingDelegate implements CameraRecordingDelegate {
428
455
cp . on ( 'spawn' , ( ) => {
429
456
resolve ( { generator : generator ( ) , cp } )
430
457
} )
431
-
458
+
432
459
cp . on ( 'error' , reject )
433
460
434
461
cp . on ( 'exit' , ( code , signal ) => {
435
462
if ( code !== 0 && ! processKilledIntentionally && code !== 255 ) {
436
463
this . log . warn ( `FFmpeg exited with code ${ code } ` , this . cameraName )
437
464
}
465
+
466
+ // Enhanced process cleanup and error handling
467
+ cp . on ( 'exit' , ( code , signal ) => {
468
+ this . log . debug ( `DEBUG: FFmpeg process ${ cp . pid } exited with code ${ code } , signal ${ signal } ` , this . cameraName )
469
+ if ( code !== 0 && code !== null ) {
470
+ this . log . warn ( `HKSV: FFmpeg exited with non-zero code ${ code } , this may indicate stream issues` , this . cameraName )
471
+ }
472
+ } )
473
+
474
+ cp . on ( 'error' , ( error ) => {
475
+ this . log . error ( `DEBUG: FFmpeg process error: ${ error } ` , this . cameraName )
476
+ } )
438
477
} )
439
478
440
479
// Fast cleanup
0 commit comments