11import { spawn } from 'child_process' ;
22import fs from 'fs' ;
33import path from 'path' ;
4- import { uploadObjectFromDir } from './object ' ;
4+ import { addVideoToQueue } from './queue ' ;
55
66export const createFfmpegProcess = (
77 videoPort : number ,
@@ -11,23 +11,26 @@ export const createFfmpegProcess = (
1111 audioPort ?: number ,
1212) => {
1313 if ( type === 'record' ) {
14- fs . mkdirSync ( `${ assetsDirPath } /records /${ roomId } ` , { recursive : true } ) ;
14+ fs . mkdirSync ( `${ assetsDirPath } /videos /${ roomId } ` , { recursive : true } ) ;
1515 }
16+ const randomStr = crypto . randomUUID ( ) ;
1617 const sdpString = audioPort ? createRecordSdpText ( videoPort , audioPort ) : createThumbnailSdpText ( videoPort ) ;
17- const args = type === 'thumbnail' ? thumbnailArgs ( assetsDirPath , roomId ) : recordArgs ( assetsDirPath , roomId ) ;
18+ const args =
19+ type === 'thumbnail' ? thumbnailArgs ( assetsDirPath , roomId ) : recordArgs ( assetsDirPath , roomId , randomStr ) ;
1820 const ffmpegProcess = spawn ( 'ffmpeg' , args ) ;
1921
2022 ffmpegProcess . stdin . write ( sdpString ) ;
2123 ffmpegProcess . stdin . end ( ) ;
2224
23- handleFfmpegProcess ( ffmpegProcess , type , roomId , assetsDirPath ) ;
25+ handleFfmpegProcess ( ffmpegProcess , type , roomId , assetsDirPath , randomStr ) ;
2426} ;
2527
2628const handleFfmpegProcess = (
2729 process : ReturnType < typeof spawn > ,
2830 type : string ,
2931 roomId : string ,
3032 assetsDirPath : string ,
33+ uuid : string ,
3134) => {
3235 if ( process . stderr ) {
3336 process . stderr . setEncoding ( 'utf-8' ) ;
@@ -40,7 +43,7 @@ const handleFfmpegProcess = (
4043 process . on ( 'error' , error => console . error ( `[FFmpeg ${ type } ] error:` , error ) ) ;
4144 process . on ( 'close' , async code => {
4245 if ( type === 'record' ) {
43- await stopRecord ( assetsDirPath , roomId ) ;
46+ await stopRecord ( assetsDirPath , roomId , uuid ) ;
4447 } else {
4548 await stopMakeThumbnail ( assetsDirPath , roomId ) ;
4649 }
@@ -98,49 +101,67 @@ const thumbnailArgs = (dirPath: string, roomId: string) => {
98101 return commandArgs ;
99102} ;
100103
101- const recordArgs = ( dirPath : string , roomId : string ) => {
104+ const recordArgs = ( dirPath : string , roomId : string , randomStr : string ) => {
102105 const commandArgs = [
103106 '-loglevel' ,
104107 'info' , // 로그 활성화
105108 '-protocol_whitelist' ,
106109 'pipe,udp,rtp' , // 허용할 프로토콜 정의
107110 '-f' ,
108- 'sdp' , // SDP 입력 포맷
111+ 'sdp' , // 입력 포맷
109112 '-i' ,
110113 'pipe:0' , // SDP를 파이프로 전달
111- '-c:v' ,
112- 'libx264' , // 비디오 코덱
113- '-preset' ,
114- 'superfast' ,
115- '-profile:v' ,
116- 'high' , // H.264 High 프로필
117- '-level:v' ,
118- '4.1' , // H.264 레벨 설정 (4.1)
119- '-crf' ,
120- '23' , // 비디오 품질 설정
121- '-c:a' ,
122- 'libmp3lame' , // 오디오 코덱
123- '-b:a' ,
124- '128k' , // 오디오 비트레이트
125- '-ar' ,
126- '48000' , // 오디오 샘플링 레이트
127- '-af' ,
128- 'aresample=async=1' , // 오디오 샘플 동기화
129- '-ac' ,
130- '2' ,
131- '-f' ,
132- 'hls' , // HLS 출력 포맷
133- '-hls_time' ,
134- '15' , // 각 세그먼트 길이 (초)
135- '-hls_list_size' ,
136- '0' , // 유지할 세그먼트 개수
137- '-hls_segment_filename' ,
138- `${ dirPath } /records/${ roomId } /segment_%03d.ts` , // HLS 세그먼트 파일 이름
139- `${ dirPath } /records/${ roomId } /video.m3u8` , // HLS 플레이리스트 파일 이름
114+ '-c' ,
115+ 'copy' , // 코덱 재인코딩 없이 원본 저장
116+ '-y' , // 파일 덮어쓰기 허용
117+ `${ dirPath } /videos/${ roomId } /${ randomStr } .webm` ,
140118 ] ;
141119 return commandArgs ;
142120} ;
143121
122+ // const recordArgs = (dirPath: string, roomId: string) => {
123+ // const commandArgs = [
124+ // '-loglevel',
125+ // 'info', // 로그 활성화
126+ // '-protocol_whitelist',
127+ // 'pipe,udp,rtp', // 허용할 프로토콜 정의
128+ // '-f',
129+ // 'sdp', // SDP 입력 포맷
130+ // '-i',
131+ // 'pipe:0', // SDP를 파이프로 전달
132+ // '-c:v',
133+ // 'libx264', // 비디오 코덱
134+ // '-preset',
135+ // 'superfast',
136+ // '-profile:v',
137+ // 'high', // H.264 High 프로필
138+ // '-level:v',
139+ // '4.1', // H.264 레벨 설정 (4.1)
140+ // '-crf',
141+ // '23', // 비디오 품질 설정
142+ // '-c:a',
143+ // 'libmp3lame', // 오디오 코덱
144+ // '-b:a',
145+ // '128k', // 오디오 비트레이트
146+ // '-ar',
147+ // '48000', // 오디오 샘플링 레이트
148+ // '-af',
149+ // 'aresample=async=1', // 오디오 샘플 동기화
150+ // '-ac',
151+ // '2',
152+ // '-f',
153+ // 'hls', // HLS 출력 포맷
154+ // '-hls_time',
155+ // '15', // 각 세그먼트 길이 (초)
156+ // '-hls_list_size',
157+ // '0', // 유지할 세그먼트 개수
158+ // '-hls_segment_filename',
159+ // `${dirPath}/records/${roomId}/segment_%03d.ts`, // HLS 세그먼트 파일 이름
160+ // `${dirPath}/records/${roomId}/video.m3u8`, // HLS 플레이리스트 파일 이름
161+ // ];
162+ // return commandArgs;
163+ // };
164+
144165async function stopMakeThumbnail ( assetsDirPath : string , roomId : string ) {
145166 const thumbnailPath = path . join ( assetsDirPath , 'thumbnails' , `${ roomId } .jpg` ) ;
146167 fs . unlink ( thumbnailPath , err => {
@@ -150,29 +171,35 @@ async function stopMakeThumbnail(assetsDirPath: string, roomId: string) {
150171 } ) ;
151172}
152173
153- async function stopRecord ( assetsDirPath : string , roomId : string ) {
154- const roomDirPath = path . join ( assetsDirPath , 'records' , roomId ) ;
155- await uploadObjectFromDir ( roomId , assetsDirPath ) ;
156- if ( fs . existsSync ( roomDirPath ) ) {
157- await deleteAllFiles ( roomDirPath ) ;
158- console . log ( `All files in ${ roomDirPath } deleted successfully.` ) ;
159- }
174+ async function stopRecord ( assetsDirPath : string , roomId : string , uuid : string ) {
175+ const video = {
176+ roomId,
177+ randomStr : uuid ,
178+ title : 'title' ,
179+ } ;
180+ addVideoToQueue ( video ) ;
181+ // const roomDirPath = path.join(assetsDirPath, 'records', roomId, `${uuid}.webm`);
182+ // await uploadObjectFromDir(roomId, assetsDirPath);
183+ // if (fs.existsSync(roomDirPath)) {
184+ // await deleteAllFiles(roomDirPath);
185+ // console.log(`All files in ${roomDirPath} deleted successfully.`);
186+ // }
160187}
161188
162- async function deleteAllFiles ( directoryPath : string ) : Promise < void > {
163- try {
164- const files = await fs . promises . readdir ( directoryPath , { withFileTypes : true } ) ;
165- for ( const file of files ) {
166- const fullPath = path . join ( directoryPath , file . name ) ;
167- if ( file . isDirectory ( ) ) {
168- await deleteAllFiles ( fullPath ) ; // 재귀적으로 디렉토리 삭제
169- await fs . promises . rmdir ( fullPath ) ; // 빈 디렉토리 삭제
170- } else {
171- await fs . promises . unlink ( fullPath ) ; // 파일 삭제
172- }
173- }
174- } catch ( error ) {
175- console . error ( `Error deleting files in directory: ${ directoryPath } ` , error ) ;
176- throw error ;
177- }
178- }
189+ // async function deleteAllFiles(directoryPath: string): Promise<void> {
190+ // try {
191+ // const files = await fs.promises.readdir(directoryPath, { withFileTypes: true });
192+ // for (const file of files) {
193+ // const fullPath = path.join(directoryPath, file.name);
194+ // if (file.isDirectory()) {
195+ // await deleteAllFiles(fullPath); // 재귀적으로 디렉토리 삭제
196+ // await fs.promises.rmdir(fullPath); // 빈 디렉토리 삭제
197+ // } else {
198+ // await fs.promises.unlink(fullPath); // 파일 삭제
199+ // }
200+ // }
201+ // } catch (error) {
202+ // console.error(`Error deleting files in directory: ${directoryPath}`, error);
203+ // throw error;
204+ // }
205+ // }
0 commit comments