Skip to content

Commit 3e3086c

Browse files
committed
添加BGM功能。
1 parent 9ea46d7 commit 3e3086c

9 files changed

Lines changed: 188 additions & 25 deletions

File tree

Image2Video/Image2Video.csproj

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,10 @@
2121
<PackageReference Include="OpenCvSharp4.runtime.win" Version="4.10.0.20240616" />
2222
</ItemGroup>
2323

24+
<ItemGroup>
25+
<Reference Include="Interop.shell32">
26+
<HintPath>Interop.Shell32.dll</HintPath>
27+
</Reference>
28+
</ItemGroup>
29+
2430
</Project>

Image2Video/Interop.Shell32.dll

52 KB
Binary file not shown.

Image2Video/MainWindow.xaml

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,14 @@
55
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
66
xmlns:local="clr-namespace:Image2Video"
77
mc:Ignorable="d"
8-
Title="图片生成视频展示工具" Height="110" Width="600">
8+
Title="图片生成视频展示工具" Height="130" Width="600">
99
<Grid>
1010
<Grid.RowDefinitions>
1111
<RowDefinition Height="Auto"></RowDefinition>
1212
<RowDefinition Height="Auto"></RowDefinition>
1313
<RowDefinition Height="Auto"></RowDefinition>
1414
<RowDefinition Height="Auto"></RowDefinition>
15+
<RowDefinition Height="Auto"></RowDefinition>
1516
</Grid.RowDefinitions>
1617
<Grid Grid.Row="0" >
1718
<Grid.ColumnDefinitions>
@@ -20,8 +21,17 @@
2021
</Grid.ColumnDefinitions>
2122
<TextBox Grid.Column="0" x:Name="files_dir" ></TextBox>
2223
<Button Grid.Column="1" Name="open_files" Height="19" VerticalAlignment="Bottom" Click="Open_files_Click">打开文件夹</Button>
24+
25+
</Grid>
26+
<Grid Grid.Row="1" >
27+
<Grid.ColumnDefinitions>
28+
<ColumnDefinition Width="3.5*"></ColumnDefinition>
29+
<ColumnDefinition Width="1*"></ColumnDefinition>
30+
</Grid.ColumnDefinitions>
31+
<TextBox Grid.Column="0" x:Name="bgm_dir" ></TextBox>
32+
<Button Grid.Column="1" Name="open_bgm" Click="open_bgm_Click">打开BGM</Button>
2333
</Grid>
24-
<Grid Grid.Row="1">
34+
<Grid Grid.Row="2">
2535
<Grid>
2636
<Grid.ColumnDefinitions>
2737
<ColumnDefinition Width="Auto"></ColumnDefinition>
@@ -85,7 +95,7 @@
8595
</Grid>
8696
</Grid>
8797
</Grid>
88-
<Grid Grid.Row="2">
98+
<Grid Grid.Row="3">
8999
<Grid.ColumnDefinitions>
90100
<ColumnDefinition Width="2*"></ColumnDefinition>
91101
<ColumnDefinition Width="2*"></ColumnDefinition>
@@ -109,7 +119,7 @@
109119
<CheckBox Grid.Column="3" IsChecked="False" HorizontalAlignment="Center" VerticalAlignment="Center" Name="has_end">带尾图</CheckBox>
110120
<Button Grid.Column="4" HorizontalAlignment="Center" Name="all_do" Height="19" VerticalAlignment="Top" Click="All_do_Click" >开始</Button>
111121
</Grid>
112-
<Grid Grid.Row="3" >
122+
<Grid Grid.Row="4" >
113123
<ProgressBar x:Name="progressBar" Height="10" />
114124
</Grid>
115125
</Grid>

Image2Video/MainWindow.xaml.cs

Lines changed: 163 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
using Microsoft.WindowsAPICodePack.Dialogs;
22
using OpenCvSharp;
3+
using Shell32;
34
using System.Diagnostics;
45
using System.IO;
6+
using System.Text;
7+
using System.Text.RegularExpressions;
58
using System.Windows;
69
using Path = System.IO.Path;
710
using Rect = OpenCvSharp.Rect;
@@ -70,6 +73,10 @@ private void All_do_Click(object sender, RoutedEventArgs e)
7073
// 输出文件夹
7174
if (!Directory.Exists("./Res"))
7275
Directory.CreateDirectory("./Res");
76+
if (files_dir.Text == null || files_dir.Text == "")
77+
{
78+
return;
79+
}
7380
string[] filedirs;
7481
if (is_all.IsChecked == true)
7582
filedirs = Directory.GetFiles(files_dir.Text, "*", SearchOption.AllDirectories);
@@ -84,7 +91,7 @@ private void All_do_Click(object sender, RoutedEventArgs e)
8491
Dispatcher.Invoke(() => setting.UpdateSetting());
8592
setting.SaveSetting();
8693

87-
Task.Run(() =>
94+
_ = Task.Run(() =>
8895
{
8996
bool btn_has_start = true, btn_has_end = false;
9097
Dispatcher.Invoke(() => btn_has_start = (bool)has_start.IsChecked);
@@ -135,27 +142,110 @@ private void All_do_Click(object sender, RoutedEventArgs e)
135142
Dispatcher.Invoke(() => progressBar.Value = 100);
136143
}
137144
Dispatcher.Invoke(() => progressBar.IsIndeterminate = true);
138-
string arg = "";
139-
bool gpu_check = true, cpu_check = false;
140-
string fps_text = "60", file_dir_text = "";
145+
string fps_text = "60", file_dir_text = "", bgm_dir_text = "";
141146
Dispatcher.Invoke(() => fps_text = fps.Text);
142147
Dispatcher.Invoke(() => file_dir_text = files_dir.Text);
148+
Dispatcher.Invoke(() => bgm_dir_text = bgm_dir.Text);
143149
string ffmpeg_path = System.Environment.CurrentDirectory + @"\ffmpeg\ffmpeg.exe";
144-
ProcessStartInfo info = new ProcessStartInfo(ffmpeg_path, $" -y -r {fps_text} -i {System.Environment.CurrentDirectory}\\Cache\\%d.jpg {System.Environment.CurrentDirectory}\\Cache\\output.mp4");
145-
info.UseShellExecute = true;
146-
info.RedirectStandardInput = false;
147-
info.RedirectStandardOutput = false;
148-
info.RedirectStandardError = false;
149-
info.CreateNoWindow = true;
150+
string cache_dir = "Cache";
151+
152+
//合成MP4(无声)
153+
ProcessStartInfo info = new ProcessStartInfo(ffmpeg_path, $" -y -r {fps_text} -i {cache_dir}\\%d.jpg {cache_dir}\\output.mp4")
154+
{
155+
UseShellExecute = true,
156+
RedirectStandardInput = false,
157+
RedirectStandardOutput = false,
158+
RedirectStandardError = false,
159+
CreateNoWindow = true
160+
};
150161
Process AppProcess = System.Diagnostics.Process.Start(info);
151162
AppProcess.WaitForExit();
152-
var files_dir_list = file_dir_text.Split("\\");
153-
if (File.Exists($"Res/{files_dir_list[files_dir_list.Length - 1]}.mp4"))
163+
164+
// 处理MP3 生成和MP4同样长的MP3文件
165+
if (bgm_dir_text != "" || bgm_dir_text != null)
154166
{
155-
File.Delete($"Res/{files_dir_list[files_dir_list.Length - 1]}.mp4");
167+
int mp4sec = ReadMp4During(System.Environment.CurrentDirectory + "\\Cache\\output.mp4");
168+
int mp3sec = ReadMp3During(bgm_dir_text);
169+
FileInfo file = new FileInfo(bgm_dir_text);
170+
if (file.Exists) //可以判断源文件是否存在
171+
{
172+
// 这里是true的话覆盖
173+
file.CopyTo($"{System.Environment.CurrentDirectory}\\Cache\\1.mp3", true);
174+
}
175+
string concat_str = $"concat:{cache_dir}\\1.mp3";
176+
if (mp4sec > mp3sec)
177+
{
178+
for (i = 0; i < Math.Ceiling((double)mp4sec / (double)mp3sec); i++)
179+
{
180+
concat_str += $"|{cache_dir}\\1.mp3";
181+
}
182+
ProcessStartInfo info2 = new ProcessStartInfo(ffmpeg_path, $" -i \"{concat_str}\" -y -acodec copy {cache_dir}\\b.mp3")
183+
{
184+
UseShellExecute = true,
185+
RedirectStandardInput = false,
186+
RedirectStandardOutput = false,
187+
RedirectStandardError = false,
188+
CreateNoWindow = true
189+
};
190+
Process AppProcess2 = System.Diagnostics.Process.Start(info2);
191+
AppProcess2.WaitForExit();
192+
193+
ProcessStartInfo info3 = new ProcessStartInfo(ffmpeg_path, $" -i {cache_dir}\\b.mp3 -t {mp4sec} -y -acodec copy {cache_dir}\\c.mp3")
194+
{
195+
UseShellExecute = true,
196+
RedirectStandardInput = false,
197+
RedirectStandardOutput = false,
198+
RedirectStandardError = false,
199+
CreateNoWindow = true
200+
};
201+
Process AppProcess3 = System.Diagnostics.Process.Start(info3);
202+
AppProcess3.WaitForExit();
203+
}
204+
else if (mp4sec < mp3sec)
205+
{
206+
ProcessStartInfo info2 = new ProcessStartInfo(ffmpeg_path, $" -i {cache_dir}\\1.mp3 -y -t {mp4sec} -acodec copy {cache_dir}\\c.mp3")
207+
{
208+
UseShellExecute = true,
209+
RedirectStandardInput = false,
210+
RedirectStandardOutput = false,
211+
RedirectStandardError = false,
212+
CreateNoWindow = true
213+
};
214+
Process AppProcess2 = System.Diagnostics.Process.Start(info2);
215+
AppProcess2.WaitForExit();
216+
}
217+
218+
ProcessStartInfo info4 = new ProcessStartInfo(ffmpeg_path, $" -i {cache_dir}\\c.mp3 -i {cache_dir}\\output.mp4 -y {cache_dir}\\d.mp4")
219+
{
220+
UseShellExecute = true,
221+
RedirectStandardInput = false,
222+
RedirectStandardOutput = false,
223+
RedirectStandardError = false,
224+
CreateNoWindow = true
225+
};
226+
Process AppProcess4 = System.Diagnostics.Process.Start(info4);
227+
AppProcess4.WaitForExit();
228+
229+
//导出带BGM的mp4
230+
var files_dir_list = file_dir_text.Split("\\");
231+
if (File.Exists($"Res/{files_dir_list[files_dir_list.Length - 1]}.mp4"))
232+
{
233+
File.Delete($"Res/{files_dir_list[files_dir_list.Length - 1]}.mp4");
234+
}
235+
File.Move("./Cache/d.mp4", $"Res/{files_dir_list[files_dir_list.Length - 1]}.mp4");
236+
}
237+
else
238+
{
239+
// 导出无声MP4
240+
var files_dir_list = file_dir_text.Split("\\");
241+
if (File.Exists($"Res/{files_dir_list[files_dir_list.Length - 1]}.mp4"))
242+
{
243+
File.Delete($"Res/{files_dir_list[files_dir_list.Length - 1]}.mp4");
244+
}
245+
File.Move("./Cache/output.mp4", $"Res/{files_dir_list[files_dir_list.Length - 1]}.mp4");
156246
}
157-
File.Move("./Cache/output.mp4", $"Res/{files_dir_list[files_dir_list.Length - 1]}.mp4");
158247
Dispatcher.Invoke(() => progressBar.IsIndeterminate = false);
248+
//清理缓存
159249
if (Directory.Exists("./Cache"))
160250
Directory.Delete("./Cache", true);
161251
});
@@ -225,7 +315,7 @@ private void LongImg2frame(Mat mat, ref int now_frame)
225315
int sec = (dst.Height - canvas_height) / velocity;
226316
if (sec <= _sec)
227317
sec = _sec;
228-
else if (sec > 30) sec = 30;
318+
else if (sec > 60) sec = 60;
229319
int ratio = (dst.Height - canvas_height) / (sec * f) + 1;
230320
int end_frame = 0;
231321
//长图头停顿
@@ -278,5 +368,63 @@ private void Img2frame(Mat mat, ref int now_frame)
278368

279369
}
280370

371+
private void open_bgm_Click(object sender, RoutedEventArgs e)
372+
{
373+
System.Windows.Forms.OpenFileDialog openFileDialog = new System.Windows.Forms.OpenFileDialog();
374+
openFileDialog.Filter = "音频文件|*.mp3|所有文件|*.*";
375+
openFileDialog.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
376+
openFileDialog.Title = "打开BGM";
377+
openFileDialog.Multiselect = false;
378+
openFileDialog.InitialDirectory = "../";
379+
// 显示文件对话框
380+
if (openFileDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
381+
{
382+
// 获取用户选择的文件路径
383+
bgm_dir.Text = openFileDialog.FileName;
384+
}
385+
386+
}
387+
//获取MP4视频时长
388+
private int ReadMp4During(string filePath)
389+
{
390+
Debug.WriteLine(filePath);
391+
ShellClass sc = new ShellClass();
392+
Folder dir = sc.NameSpace(Path.GetDirectoryName(filePath));
393+
FolderItem item = dir.ParseName(Path.GetFileName(filePath));
394+
StringBuilder sb = new StringBuilder();
395+
string pattern = @"\d+";
396+
int i = 0, hour = 0, min = 0, sec = 0;
397+
foreach (Match match in Regex.Matches(dir.GetDetailsOf(item, 27), pattern))
398+
{
399+
if (i == 0) hour = int.Parse(match.Value);
400+
else if (i == 1) min = int.Parse(match.Value);
401+
else if (i == 2) sec = int.Parse(match.Value);
402+
i++;
403+
}
404+
int all_sec = hour * 3600 + min * 60 + sec;
405+
Debug.WriteLine($"{filePath}->{dir.GetDetailsOf(item, 27)}{all_sec}秒");
406+
return all_sec;
407+
}
408+
409+
//获取MP3时长
410+
private int ReadMp3During(string filePath)
411+
{
412+
ShellClass sc = new ShellClass();
413+
Folder dir = sc.NameSpace(Path.GetDirectoryName(filePath));
414+
FolderItem item = dir.ParseName(Path.GetFileName(filePath));
415+
StringBuilder sb = new StringBuilder();
416+
string pattern = @"\d+";
417+
int i = 0, hour = 0, min = 0, sec = 0;
418+
foreach (Match match in Regex.Matches(dir.GetDetailsOf(item, 27), pattern))
419+
{
420+
if (i == 0) hour = int.Parse(match.Value);
421+
else if (i == 1) min = int.Parse(match.Value);
422+
else if (i == 2) sec = int.Parse(match.Value);
423+
i++;
424+
}
425+
int all_sec = hour * 3600 + min * 60 + sec;
426+
Debug.WriteLine($"{filePath}->{dir.GetDetailsOf(item, 27)}{all_sec}秒");
427+
return all_sec;
428+
}
281429
}
282430
}

Image2Video/Setting.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public void LoadSetting()
3434
this.has_start = tmp.has_start;
3535
this.has_end = tmp.has_end;
3636
}
37-
catch (Exception ex)
37+
catch (Exception)
3838
{
3939
this.ret_width = 1080;
4040
this.ret_height = 1920;

README.md

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,9 @@
22

33
## 功能介绍
44

5-
能把选中文件夹内的图片转换成自己设定的分辨率视频并导出,短图采取等比缩放并填充黑白形式,长图采取滚动形式。
6-
7-
支持png、jpg格式图片。
8-
5+
能把选中文件夹内的图片转换成自己设定的分辨率视频并导出,短图采取等比缩放并填充黑边形式,长图采取滚动形式。
6+
图片支持png、jpg格式图片。BGM支持MP3格式,不打开BGM则无BGM。
7+
s
98
## 使用方法
109

1110
1. 下载源码。
@@ -17,6 +16,6 @@
1716

1817
## 界面展示
1918

20-
![1731067084807](images/README/1731067084807.png)![1731067683390](images/README/1731067683390.png)
19+
![1731067084807](images/README/show.png)![1731067683390](images/README/1731067683390.png)
2120

2221
## 展示视频见目录show.mp4

images/README/1731067084807.png

-33.4 KB
Binary file not shown.

images/README/show.png

37.5 KB
Loading

show.mp4

13.4 MB
Binary file not shown.

0 commit comments

Comments
 (0)