1 module dcv.io.video;
2
3 public import dcv.io.video.input, dcv.io.video.output;
4
5 /+
6 unittest
7 {
8 /*
9 Only a temp solution for a test. More complete and correct form of testing should be provided in future.
10 */
11 import std.stdio;
12 import std.file;
13 import std.math : abs;
14
15 import mir.ndslice;
16
17 import dcv.imgproc.color : yuv2rgb;
18
19 immutable ubyte[][] frameColorMap = [[255, 255, 255], [255, 0, 0], [
20 0, 255, 0
21 ], [0, 0, 255], [0, 0, 0], [255, 255, 255], [85, 170, 255], [255, 170, 85], [255, 255, 255]];
22
23 // output the dummy image of certain format
24
25 immutable width = 352;
26 immutable height = 288;
27 immutable filePath = "test.mkv";
28
29 scope (exit)
30 {
31 try
32 {
33 remove(filePath);
34 }
35 finally
36 {
37 }
38 }
39
40 Image frameImage = new Image(width, height, ImageFormat.IF_RGB); // define the frame image
41 auto frameSlice = frameImage.sliced; // slice the image for editing
42
43 OutputStream outputStream = new OutputStream; // define the output video outputStream.
44
45 OutputDefinition props;
46
47 props.width = width;
48 props.height = height;
49 props.imageFormat = ImageFormat.IF_RGB;
50 props.bitRate = 1_400_000;
51 /*
52 * Use the h263 because the mpeg repeats the first frame.
53 * This looks like a known bug:
54 * https://trac.ffmpeg.org/ticket/2324
55 */
56 props.codecId = CodecID.H263; //
57
58 outputStream.open(filePath, props);
59
60 if (!outputStream.isOpen)
61 {
62 writeln("Cannot open H263 stream");
63 return;
64 }
65
66 foreach (pixel; frameSlice.pack!1.flattened)
67 {
68 pixel[0] = cast(ubyte)0;
69 pixel[1] = cast(ubyte)0;
70 pixel[2] = cast(ubyte)0;
71 }
72
73 foreach (frameColor; frameColorMap)
74 {
75 foreach (pixel; frameSlice.pack!1.flattened)
76 {
77 pixel[0] = frameColor[0];
78 pixel[1] = frameColor[1];
79 pixel[2] = frameColor[2];
80 }
81 outputStream.writeFrame(frameImage);
82 }
83
84 outputStream.close();
85
86 // define an input stream, read the video we've just written, and find if its of the same content
87 InputStream inputStream = new InputStream;
88
89 inputStream.open(filePath, InputStreamType.FILE);
90
91 if (!inputStream.isOpen)
92 {
93 writeln("Cannot open input stream");
94 return;
95 }
96
97 size_t i = 0;
98 while (inputStream.readFrame(frameImage))
99 {
100 frameSlice = yuv2rgb!ubyte(frameImage.sliced);
101
102 // compare
103 auto frameColor = frameColorMap[i++];
104
105 foreach (c; 0 .. 3)
106 {
107 auto error = abs(cast(int)frameSlice[height / 2, width / 2, c] - cast(int)frameColor[c]);
108 assert(error < 2); // encoding error
109 }
110 }
111
112 // seek some frames
113 auto seekFramesIds = [0, 3, 5, 2]; // last should throw SeekFrameException
114 foreach (index; seekFramesIds)
115 {
116 try
117 {
118 inputStream.seekFrame(index);
119 }
120 catch
121 {
122 assert(0); // should not throw!
123 }
124
125 assert(inputStream.readFrame(frameImage));
126 frameSlice = yuv2rgb!ubyte(frameImage.sliced);
127
128 foreach (c; 0 .. 3)
129 {
130 auto error = abs(cast(int)frameSlice[height / 2, width / 2, c] - cast(int)frameColorMap[index][c]);
131 assert(error < 2); // encoding error
132 }
133 }
134
135 try
136 {
137 inputStream.seekFrame(size_t.max); // should throw
138 }
139 catch (SeekFrameException e)
140 {
141 // should be caught here
142 }
143 catch
144 {
145 assert(0);
146 }
147
148 try
149 {
150 inputStream.seekTime(0.0);
151 }
152 catch
153 {
154 assert(0); // should not throw!
155 }
156
157 try
158 {
159 inputStream.seekTime(size_t.max); // should throw
160 }
161 catch (SeekTimeException e)
162 {
163 // should be caught here
164 }
165 catch
166 {
167 assert(0);
168 }
169
170 inputStream.close();
171 }
172 +/
173