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