[WPF] ffmpeg을 활용한 동영상 만들기

FFmpeg Build 다운로드하기

https://ffmpeg.org/download.html#build-windows 사이트에서 Windows EXE Files 항목의 원하는 형식으로 다운로드를 합니다.
다운로드한 후 압축해제 합니다. 프로젝트의 ffmpeg 폴더를 생성 후 실행에 사용되는 exe, dll 파일들을 복사합니다.

Bitmap들을 동영상으로 만들기

FFmpegLoader

ffmpeg를 사용하기 위해 FFMediaToolkit 패키지를 Nuget을 이용하여 설치합니다. FFmpegLoader를 사용하여 Path를 지정합니다.

1
2
// 예
FFmpegLoader.FFmpegPath = @".\ffmpeg";

동영상에 프레임 추가

아래는 예제코드입니다. StartRecordVideo 함수에 저장될 경로를 지정하여 mediaFile을 생성합니다. 그리고 AddRecordFrame 함수를 통해 Bitmap 이미지를 설정한 30fps로 동영상에 추가합니다. 이렇게 생성된 동영상은 MPEG2 형식이므로 MPEG4 형식으로 변경합니다. MPEG2 형식의 동영상은 가끔 윈도우 환경에서 오류가 발생하는 경우가 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
using FFMediaToolkit;
using FFMediaToolkit.Encoding;
using FFMediaToolkit.Graphics;

namespace Hgko.Utils
{
public class ImageService
{
private bool ffmpegInit = false;
private MediaOutput mediaFile = null;

/// <summary>
/// Start Record video
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
public void StartRecordVideo(string path)
{
if (mediaFile == null)
{
if (!ffmpegInit)
{
FFmpegLoader.FFmpegPath = @".\ffmpeg";
ffmpegInit = true;
}

int width = 1024;
int heigth = 1024;

VideoEncoderSettings settings = new VideoEncoderSettings(width, heigth, 30, VideoCodec.MPEG2);
settings.EncoderPreset = EncoderPreset.Medium;

mediaFile = MediaBuilder.CreateContainer(path).WithVideo(settings).Create();
}
}

/// <summary>
/// Add RecordFrame
/// </summary>
/// <param name="bitmap"></param>
public void AddRecordFrame(Bitmap bitmap)
{
if (bitmap == null)
return;

if (mediaFile == null)
return;

System.Drawing.Imaging.BitmapData bdata = bitmap.LockBits(new Rectangle(System.Drawing.Point.Empty, bitmap.Size), System.Drawing.Imaging.ImageLockMode.WriteOnly, bitmap.PixelFormat);
ImageData imgdata = ImageData.FromPointer(bdata.Scan0, ImagePixelFormat.Bgra32, bitmap.Size);
mediaFile.Video.AddFrame(imgdata);
bitmap.UnlockBits(bdata);
}

/// <summary>
/// Stop Record Video
/// </summary>
public void StopRecordVideo()
{
if (mediaFile != null)
{
mediaFile.Video.Dispose();
mediaFile.Dispose();
mediaFile = null;
}
}
}
}

동영상 포맷 변환

임시로 빌드되는 경로에 display/video 경로의 폴더를 생성합니다. 포맷 변환하는 방법은 많습니다. 다음 코드는 여러 이미지 또는 동영상을 합쳐서 새로운 동영상으로 만드는 형식의 방법을 활용하여 포맷 변환을 진행합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
using System.IO;

/// <summary>
/// End Record Video
/// </summary>
/// <param name="mp2Path">mp2 형식으로 저장된 파일경로</param>
public void EndRecordVideo(string mp2Path)
{
string videoWorkingPath = @".\display\video\";
string vedeoWorkingFolder = Directory.GetCurrentDirectory() + videoWorkingPath.Replace(".", "");
string outputVeideoPath = vedeoWorkingFolder + Path.GetFileNameWithoutExtension(mp2Path) + ".mp4"

// if exist video working directory, clean up.
if (Directory.Exists(videoWorkingPath))
ClearFolder(videoWorkingPath);

// remake working dir.
Directory.CreateDirectory(videoWorkingPath);

Task.Run(() =>
{
string lstPath = vedeoWorkingFolder + "lst.txt";

// 텍스트 파일에 앞에서 생성된 mp2 동영상 파일 경로 추가
using (StreamWriter writer = File.CreateText(lstPath))
{
writer.WriteLine("file '{0}'", mp2Path);
}

// h264 or mpeg4
string codec = "h264";

// 동영상 포맷 변환(mp2 -> mp4)
string arg = $@"-safe 0 -f concat -i ""{lstPath}"" -c copy -vcodec ""{codec}"" ""{outputVeideoPath}""";
using (Process process = Process.Start(@".\ffmpeg\ffmpeg.exe", arg))
{
process.WaitForExit();
}
});
}

/// <summary>
/// Clear Folder
/// </summary>
/// <param name="path">삭제할 경로</param>
public static void ClearFolder(string path)
{
DirectoryInfo dir = new DirectoryInfo(path);

// remove all files
foreach (FileInfo fi in dir.GetFiles())
fi.Delete();

// remove dir recursive
foreach (DirectoryInfo di in dir.GetDirectories())
{
ClearFolder(di.FullName);
di.Delete();
}
}
Share