@@ -3,75 +3,93 @@ import { getFolder, type Folder } from "#src/services/resources.ts";
33import { Logger } from "#src/utils/utils.ts" ;
44
55import type { Channel } from "./channel" ;
6+ import { FFMPEG } from "#src/models/ffmpeg.ts" ;
67
78export enum RECORDER_STATE {
89 STARTED = "started" ,
10+ STOPPING = "stopping" ,
911 STOPPED = "stopped"
1012}
1113const logger = new Logger ( "RECORDER" ) ;
1214
1315export class Recorder extends EventEmitter {
1416 channel : Channel ;
1517 folder : Folder | undefined ;
16- ffmpeg = null ;
18+ ffmpeg : FFMPEG | undefined ;
1719 /** Path to which the final recording will be uploaded to */
1820 recordingAddress : string ;
1921 private _state : RECORDER_STATE = RECORDER_STATE . STOPPED ;
2022
23+ get isRecording ( ) : boolean {
24+ return this . state === RECORDER_STATE . STARTED ;
25+ }
26+ get state ( ) : RECORDER_STATE {
27+ return this . _state ;
28+ }
29+ set state ( state : RECORDER_STATE ) {
30+ this . _state = state ;
31+ this . emit ( "stateChange" , state ) ;
32+ }
33+
2134 constructor ( channel : Channel , recordingAddress : string ) {
2235 super ( ) ;
2336 this . channel = channel ;
2437 this . recordingAddress = recordingAddress ;
2538 }
2639
2740 async start ( ) {
28- if ( this . state === RECORDER_STATE . STOPPED ) {
29- this . folder = getFolder ( ) ;
30- this . state = RECORDER_STATE . STARTED ;
31- logger . trace ( "TO IMPLEMENT" ) ;
32- // TODO ffmpeg instance creation for recording to folder.path with proper name, start, build timestamps object
41+ if ( ! this . isRecording ) {
42+ try {
43+ await this . _start ( ) ;
44+ } catch {
45+ await this . _stop ( ) ;
46+ }
3347 }
34- this . _record ( ) ;
3548 return this . isRecording ;
3649 }
3750
3851 async stop ( ) {
39- if ( this . state === RECORDER_STATE . STARTED ) {
40- logger . trace ( "TO IMPLEMENT" ) ;
52+ if ( this . isRecording ) {
4153 try {
42- await this . folder ! . seal ( "test-name" ) ;
54+ await this . _stop ( { save : true } ) ;
4355 } catch {
4456 logger . verbose ( "failed to save the recording" ) ; // TODO maybe warn and give more info
4557 }
46- this . folder = undefined ;
47- // TODO ffmpeg instance stop, cleanup,
48- // only resolve promise and switch state when completely ready to start a new recording.
49- this . state = RECORDER_STATE . STOPPED ;
5058 }
5159 return this . isRecording ;
5260 }
5361
54- get isRecording ( ) : boolean {
55- return this . state === RECORDER_STATE . STARTED ;
56- }
57-
58- get state ( ) : RECORDER_STATE {
59- return this . _state ;
60- }
61-
62- set state ( state : RECORDER_STATE ) {
63- this . _state = state ;
64- this . emit ( "stateChange" , state ) ;
65- }
66-
6762 /**
6863 * @param video whether we want to record videos or not (will always record audio)
6964 */
70- _record ( video : boolean = false ) {
65+ private async _start ( { video = true } : { video ?: boolean } = { } ) {
66+ this . state = RECORDER_STATE . STARTED ;
67+ this . folder = getFolder ( ) ;
7168 logger . trace ( `TO IMPLEMENT: recording channel ${ this . channel . name } , video: ${ video } ` ) ;
69+ this . ffmpeg = new FFMPEG ( ) ;
7270 // iterate all producers on all sessions of the channel, create a ffmpeg for each,
7371 // save them on a map by session id+type.
7472 // check if recording for that session id+type is already in progress
7573 // add listener to the channel for producer creation (and closure).
7674 }
75+
76+ private async _stop ( { save = false } : { save ?: boolean } = { } ) {
77+ this . state = RECORDER_STATE . STOPPING ;
78+ // remove all listener from the channel
79+ let failure = false ;
80+ try {
81+ await this . ffmpeg ?. kill ( ) ;
82+ } catch ( error ) {
83+ logger . error ( `failed to kill ffmpeg: ${ error } ` ) ;
84+ failure = true ;
85+ }
86+ this . ffmpeg = undefined ;
87+ if ( save && ! failure ) {
88+ await this . folder ?. seal ( "test-name" ) ;
89+ } else {
90+ await this . folder ?. delete ( ) ;
91+ }
92+ this . folder = undefined ;
93+ this . state = RECORDER_STATE . STOPPED ;
94+ }
7795}
0 commit comments