Skip to content

Commit 2dc979a

Browse files
committed
Update Fix File Hasn't Audio
1 parent 11cbf35 commit 2dc979a

File tree

2 files changed

+98
-49
lines changed

2 files changed

+98
-49
lines changed

FileService/.idea/.idea.FileService/.idea/workspace.xml

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

FileService/FileService.Service/Implementation/FfmpegService.cs

Lines changed: 96 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using Microsoft.Extensions.Logging;
66
using Microsoft.Extensions.Options;
77
using System.Diagnostics;
8+
using System.Text;
89

910
namespace FileService.Service.Implementation
1011
{
@@ -13,6 +14,86 @@ public class FfmpegService : IFfmpegService
1314
private readonly ILogger<FfmpegService> _logger;
1415
private readonly IMinioService _minioService;
1516
private readonly string _tempFolder;
17+
18+
private bool HasAudioStream(string videoPath)
19+
{
20+
var psi = new ProcessStartInfo
21+
{
22+
FileName = "ffprobe",
23+
Arguments = $"-v error -select_streams a:0 -show_entries stream=codec_type -of csv=p=0 \"{videoPath}\"",
24+
RedirectStandardOutput = true,
25+
RedirectStandardError = true,
26+
UseShellExecute = false,
27+
CreateNoWindow = true
28+
};
29+
using var p = Process.Start(psi);
30+
string stdout = p.StandardOutput.ReadToEnd();
31+
p.WaitForExit();
32+
return !string.IsNullOrWhiteSpace(stdout); // có ít nhất 1 audio stream
33+
}
34+
35+
36+
private string BuildFfmpegArgs(string inputPath, string outputFolder, bool hasAudio)
37+
{
38+
// Scale 6 mức như cũ
39+
var filterComplex =
40+
"[0:v]split=6[v144][v240][v360][v480][v720][v1080];" +
41+
"[v144]scale=w=256:h=144:force_original_aspect_ratio=decrease:force_divisible_by=2[v144out];" +
42+
"[v240]scale=w=426:h=240:force_original_aspect_ratio=decrease:force_divisible_by=2[v240out];" +
43+
"[v360]scale=w=640:h=360:force_original_aspect_ratio=decrease:force_divisible_by=2[v360out];" +
44+
"[v480]scale=w=854:h=480:force_original_aspect_ratio=decrease:force_divisible_by=2[v480out];" +
45+
"[v720]scale=w=1280:h=720:force_original_aspect_ratio=decrease:force_divisible_by=2[v720out];" +
46+
"[v1080]scale=w=1920:h=1080:force_original_aspect_ratio=decrease:force_divisible_by=2[v1080out]";
47+
48+
// map + encode per-variant
49+
var maps = new StringBuilder();
50+
// Video bitrates/profiles tương ứng index
51+
var v = new (string label, string br, string profile)[]
52+
{
53+
("[v144out]","300k","baseline"),
54+
("[v240out]","700k","baseline"),
55+
("[v360out]","1000k","main"),
56+
("[v480out]","1500k","main"),
57+
("[v720out]","2500k","main"),
58+
("[v1080out]","4000k","high"),
59+
};
60+
61+
for (int i = 0; i < v.Length; i++)
62+
{
63+
maps.Append(
64+
$" -map \"{v[i].label}\" -c:v:{i} libx264 -b:v:{i} {v[i].br} -profile:v:{i} {v[i].profile} " +
65+
$"-g 48 -keyint_min 48 -sc_threshold 0 -pix_fmt yuv420p"
66+
);
67+
68+
if (hasAudio)
69+
{
70+
// audio từ input (nếu có kênh), downmix stereo cho tương thích
71+
maps.Append($" -map 0:a:0? -c:a:{i} aac -b:a:{i} 128k -ac 2 -ar 48000");
72+
}
73+
}
74+
75+
// var_stream_map tương ứng
76+
var varStreamMap = hasAudio
77+
? "\"v:0,a:0 v:1,a:1 v:2,a:2 v:3,a:3 v:4,a:4 v:5,a:5\""
78+
: "\"v:0 v:1 v:2 v:3 v:4 v:5\"";
79+
80+
// HLS options (giữ output như cũ, thêm independent_segments cho keyframe boundary)
81+
var hls =
82+
$"-f hls -hls_time 6 -hls_list_size 0 -hls_flags independent_segments " +
83+
$"-var_stream_map {varStreamMap} " +
84+
$"-master_pl_name master.m3u8 " +
85+
$"-hls_segment_filename \"{outputFolder}/v%v/segment%d.ts\" " +
86+
$"\"{outputFolder}/v%v/playlist.m3u8\"";
87+
88+
// Nếu hoàn toàn không có audio, loại phụ đề nếu có để tránh rắc rối
89+
var extra = hasAudio ? "" : " -sn";
90+
91+
// max_muxing_queue_size để tránh lỗi queue với vài file lạ
92+
var safety = " -max_muxing_queue_size 1024";
93+
94+
return
95+
$"-i \"{inputPath}\" -filter_complex \"{filterComplex}\"{maps}{safety}{extra} {hls}";
96+
}
1697

1798
public FfmpegService(ILogger<FfmpegService> logger, IOptions<FfmpegSettings> settings, IMinioService minioService)
1899
{
@@ -88,76 +169,42 @@ public async Task<VideoProcessResultModel> ProcessVideoAsync(IFormFile videoFile
88169

89170
try
90171
{
172+
// tạo thư mục variant
91173
for (int i = 0; i <= 5; i++)
92174
{
93-
var resolutionFolder = Path.Combine(outputFolder, $"v{i}");
94-
Directory.CreateDirectory(resolutionFolder);
175+
Directory.CreateDirectory(Path.Combine(outputFolder, $"v{i}"));
95176
}
96177

97-
// Save input video to temp file
178+
// Save input
98179
await using (var stream = new FileStream(tempInputPath, FileMode.Create))
99180
{
100181
await videoFile.CopyToAsync(stream);
101182
}
102183

103-
// Create thumbnail
184+
// Thumbnail
104185
var thumbnailArgs = $"-ss 00:00:01 -i \"{tempInputPath}\" -frames:v 1 -q:v 2 \"{thumbnailPath}\"";
105186
await RunFfmpegAsync(thumbnailArgs);
106187

107-
//FFmpeg CLI arguments for HLS Adaptive Bitrate
108-
var ffmpegArgs = $"-i \"{tempInputPath}\" " +
109-
"-filter_complex " +
110-
"\"[0:v]split=6[v144][v240][v360][v480][v720][v1080]; " +
111-
"[v144]scale=w=256:h=144:force_original_aspect_ratio=decrease:force_divisible_by=2[v144out]; " +
112-
"[v240]scale=w=426:h=240:force_original_aspect_ratio=decrease:force_divisible_by=2[v240out]; " +
113-
"[v360]scale=w=640:h=360:force_original_aspect_ratio=decrease:force_divisible_by=2[v360out]; " +
114-
"[v480]scale=w=854:h=480:force_original_aspect_ratio=decrease:force_divisible_by=2[v480out]; " +
115-
"[v720]scale=w=1280:h=720:force_original_aspect_ratio=decrease:force_divisible_by=2[v720out]; " +
116-
"[v1080]scale=w=1920:h=1080:force_original_aspect_ratio=decrease:force_divisible_by=2[v1080out]\" " +
117-
118-
"-map \"[v144out]\" -map 0:a -c:v:0 libx264 -b:v:0 300k -profile:v:0 baseline -c:a:0 aac -b:a:0 96k " +
119-
"-map \"[v240out]\" -map 0:a -c:v:1 libx264 -b:v:1 700k -profile:v:1 baseline -c:a:1 aac -b:a:1 96k " +
120-
"-map \"[v360out]\" -map 0:a -c:v:2 libx264 -b:v:2 1000k -profile:v:2 main -c:a:2 aac -b:a:2 128k " +
121-
"-map \"[v480out]\" -map 0:a -c:v:3 libx264 -b:v:3 1500k -profile:v:3 main -c:a:3 aac -b:a:3 128k " +
122-
"-map \"[v720out]\" -map 0:a -c:v:4 libx264 -b:v:4 2500k -profile:v:4 main -c:a:4 aac -b:a:4 128k " +
123-
"-map \"[v1080out]\" -map 0:a -c:v:5 libx264 -b:v:5 4000k -profile:v:5 high -c:a:5 aac -b:a:5 128k " +
124-
125-
"-f hls -var_stream_map " +
126-
"\"v:0,a:0 v:1,a:1 v:2,a:2 v:3,a:3 v:4,a:4 v:5,a:5\" " +
127-
$"-master_pl_name master.m3u8 -hls_time 6 -hls_list_size 0 " +
128-
$"-hls_segment_filename \"{outputFolder}/v%v/segment%d.ts\" " +
129-
$"\"{outputFolder}/v%v/playlist.m3u8\"";
130-
188+
// >>> PHÁT HIỆN AUDIO & BUILD LỆNH PHÙ HỢP <<<
189+
bool hasAudio = HasAudioStream(tempInputPath);
190+
var ffmpegArgs = BuildFfmpegArgs(tempInputPath, outputFolder, hasAudio);
131191
await RunFfmpegAsync(ffmpegArgs);
132192

133193
// Upload thumbnail
134194
var thumbKey = $"thumbnails/{fileId}.jpg";
135195
await using (var thumbStream = File.OpenRead(thumbnailPath))
136-
{
137196
await _minioService.UploadStreamAsync(thumbKey, thumbStream, "image/jpeg");
138-
}
139197

140198
// Upload master playlist
141199
var baseVideoPath = $"videos/{fileId}/master.m3u8";
142200
await using (var masterStream = File.OpenRead(masterPlaylistPath))
143-
{
144201
await _minioService.UploadStreamAsync(baseVideoPath, masterStream, "application/x-mpegURL");
145-
}
146202

147-
// Upload HLS segments and playlists
203+
// Upload từng playlist + segment
148204
for (int i = 0; i <= 5; i++)
149205
{
150206
var segmentFolder = Path.Combine(outputFolder, $"v{i}");
151-
var segmentFiles = Directory.GetFiles(segmentFolder, "segment*.ts");
152207

153-
foreach (var segmentFile in segmentFiles)
154-
{
155-
var segKey = $"videos/{fileId}/v{i}/{Path.GetFileName(segmentFile)}";
156-
await using var segStream = File.OpenRead(segmentFile);
157-
await _minioService.UploadStreamAsync(segKey, segStream, "video/MP2T");
158-
}
159-
160-
// Upload playlist.m3u8 for each resolution folder
161208
var playlistFile = Path.Combine(segmentFolder, "playlist.m3u8");
162209
if (File.Exists(playlistFile))
163210
{
@@ -166,9 +213,14 @@ public async Task<VideoProcessResultModel> ProcessVideoAsync(IFormFile videoFile
166213
await _minioService.UploadStreamAsync(playlistKey, playlistStream, "application/x-mpegURL");
167214
}
168215

216+
foreach (var segmentFile in Directory.GetFiles(segmentFolder, "segment*.ts"))
217+
{
218+
var segKey = $"videos/{fileId}/v{i}/{Path.GetFileName(segmentFile)}";
219+
await using var segStream = File.OpenRead(segmentFile);
220+
await _minioService.UploadStreamAsync(segKey, segStream, "video/mp2t"); // <— sửa MIME
221+
}
169222
}
170223

171-
// get duration using ffprobe
172224
var duration = GetVideoDuration(tempInputPath);
173225
var thumbnailUrl = await _minioService.GetPublicFileUrlAsync(thumbKey);
174226
var hlsUrl = await _minioService.GetPublicFileUrlAsync(baseVideoPath);
@@ -186,10 +238,7 @@ public async Task<VideoProcessResultModel> ProcessVideoAsync(IFormFile videoFile
186238
Console.WriteLine($"Video processing failed: {ex.Message}");
187239
return new VideoProcessResultModel
188240
{
189-
ThumbnailUrl = null,
190-
Duration = null,
191-
Status = "failed",
192-
HlsUrl = null
241+
Status = "failed"
193242
};
194243
}
195244
finally

0 commit comments

Comments
 (0)