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