ChandimaPrabath commited on
Commit
49e20ff
·
1 Parent(s): c9b5d14
Files changed (1) hide show
  1. app/services/encoder_service.py +58 -47
app/services/encoder_service.py CHANGED
@@ -9,6 +9,7 @@ import re
9
  import threading
10
  import queue
11
  import signal
 
12
 
13
  # Configure logging
14
  logging.basicConfig(level=logging.INFO)
@@ -100,7 +101,7 @@ class EncoderService:
100
  return {'status': 'failed', 'error': str(e)}
101
 
102
  def _encode_video(self, filename, job_id):
103
- """Internal method to handle video encoding"""
104
  try:
105
  upload_path = Path(os.getenv('UPLOAD_FOLDER', 'uploads'))
106
  encoded_path = Path(os.getenv('ENCODED_FOLDER', 'encoded'))
@@ -122,13 +123,17 @@ class EncoderService:
122
  raise Exception("Could not determine video duration")
123
 
124
  for quality, settings in qualities.items():
125
- try:
 
 
 
 
 
126
  self.jobs[job_id].update({
127
  'status': 'processing',
128
  'current_quality': quality,
129
  'progress': (completed_steps / total_steps) * 100
130
  })
131
-
132
  output_file = output_dir / f"{output_name}_{quality}.mp4"
133
 
134
  # FFmpeg command with optimized settings for web streaming
@@ -146,59 +151,65 @@ class EncoderService:
146
  '-vf', f"scale={settings['width']}:{settings['height']}",
147
  '-g', settings['keyframe'],
148
  '-keyint_min', settings['keyframe'],
149
- '-sc_threshold', '0', # Disable scene cut detection
150
  '-c:a', 'aac',
151
  '-b:a', settings['audio_bitrate'],
152
- '-ar', '48000', # Audio sample rate
153
- '-ac', '2', # Stereo audio
154
- '-movflags', '+faststart', # Enable fast start for web playback
155
  '-progress', 'pipe:1',
156
  str(output_file)
157
  ]
158
 
159
- process = subprocess.Popen(
160
- cmd,
161
- stdout=subprocess.PIPE,
162
- stderr=subprocess.PIPE,
163
- universal_newlines=True
164
- )
 
 
165
 
166
- self.active_processes[job_id] = process
 
 
 
 
 
 
 
 
 
167
 
168
- # Monitor FFmpeg progress
169
- while True:
170
- output = process.stdout.readline()
171
- if output == '' and process.poll() is not None:
172
- break
173
- if output:
174
- progress = self._parse_ffmpeg_progress(output, duration)
175
- if progress is not None:
176
- quality_progress = (completed_steps + progress/100) / total_steps * 100
177
- self.jobs[job_id]['progress'] = quality_progress
178
-
179
- if process.returncode == 0:
180
- outputs.append({
181
- 'quality': quality,
182
- 'path': str(output_file),
183
- 'settings': settings
184
- })
185
- completed_steps += 1
186
- else:
187
- error_output = process.stderr.read()
188
- logger.error(f"FFmpeg error: {error_output}")
189
- raise Exception(f"FFmpeg failed for quality {quality}")
190
-
191
- except Exception as e:
192
- logger.error(f"Error encoding {quality}: {str(e)}")
193
- self.jobs[job_id].update({
194
- 'status': 'failed',
195
- 'error': str(e)
196
- })
197
- return
198
 
199
- finally:
200
- if job_id in self.active_processes:
201
- del self.active_processes[job_id]
 
 
 
 
 
 
 
 
 
 
 
 
202
 
203
  self.jobs[job_id].update({
204
  'status': 'completed',
 
9
  import threading
10
  import queue
11
  import signal
12
+ import time # Added for retry delays
13
 
14
  # Configure logging
15
  logging.basicConfig(level=logging.INFO)
 
101
  return {'status': 'failed', 'error': str(e)}
102
 
103
  def _encode_video(self, filename, job_id):
104
+ """Internal method to handle video encoding with retries and enhanced error handling"""
105
  try:
106
  upload_path = Path(os.getenv('UPLOAD_FOLDER', 'uploads'))
107
  encoded_path = Path(os.getenv('ENCODED_FOLDER', 'encoded'))
 
123
  raise Exception("Could not determine video duration")
124
 
125
  for quality, settings in qualities.items():
126
+ max_retries = 3
127
+ attempts = 0
128
+ success = False
129
+
130
+ while attempts < max_retries and not success:
131
+ logger.info(f"Starting encoding for quality {quality}, attempt {attempts+1}")
132
  self.jobs[job_id].update({
133
  'status': 'processing',
134
  'current_quality': quality,
135
  'progress': (completed_steps / total_steps) * 100
136
  })
 
137
  output_file = output_dir / f"{output_name}_{quality}.mp4"
138
 
139
  # FFmpeg command with optimized settings for web streaming
 
151
  '-vf', f"scale={settings['width']}:{settings['height']}",
152
  '-g', settings['keyframe'],
153
  '-keyint_min', settings['keyframe'],
154
+ '-sc_threshold', '0',
155
  '-c:a', 'aac',
156
  '-b:a', settings['audio_bitrate'],
157
+ '-ar', '48000',
158
+ '-ac', '2',
159
+ '-movflags', '+faststart',
160
  '-progress', 'pipe:1',
161
  str(output_file)
162
  ]
163
 
164
+ try:
165
+ process = subprocess.Popen(
166
+ cmd,
167
+ stdout=subprocess.PIPE,
168
+ stderr=subprocess.PIPE,
169
+ universal_newlines=True
170
+ )
171
+ self.active_processes[job_id] = process
172
 
173
+ # Monitor FFmpeg progress
174
+ while True:
175
+ line = process.stdout.readline()
176
+ if line == '' and process.poll() is not None:
177
+ break
178
+ if line:
179
+ progress = self._parse_ffmpeg_progress(line, duration)
180
+ if progress is not None:
181
+ quality_progress = ((completed_steps + progress / 100) / total_steps) * 100
182
+ self.jobs[job_id]['progress'] = quality_progress
183
 
184
+ if process.returncode == 0:
185
+ logger.info(f"Encoding successful for quality {quality} on attempt {attempts+1}")
186
+ outputs.append({
187
+ 'quality': quality,
188
+ 'path': str(output_file),
189
+ 'settings': settings
190
+ })
191
+ success = True
192
+ completed_steps += 1
193
+ else:
194
+ error_output = process.stderr.read()
195
+ logger.error(f"FFmpeg error on quality {quality}, attempt {attempts+1}: {error_output}")
196
+ raise Exception(f"FFmpeg failed for quality {quality}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
197
 
198
+ except Exception as e:
199
+ logger.error(f"Error encoding {quality} on attempt {attempts+1}: {str(e)}")
200
+ attempts += 1
201
+ if attempts < max_retries:
202
+ logger.info(f"Retrying encoding for quality {quality} (attempt {attempts+1} of {max_retries})")
203
+ time.sleep(2) # Wait before retrying
204
+ else:
205
+ self.jobs[job_id].update({
206
+ 'status': 'failed',
207
+ 'error': f"Failed encoding {quality} after {max_retries} attempts: {str(e)}"
208
+ })
209
+ return
210
+ finally:
211
+ if job_id in self.active_processes:
212
+ del self.active_processes[job_id]
213
 
214
  self.jobs[job_id].update({
215
  'status': 'completed',