1 /** 2 Module implements common utilities for video I/O 3 4 Copyright: Copyright Relja Ljubobratovic 2016. 5 6 Authors: Relja Ljubobratovic 7 8 License: $(LINK3 http://www.boost.org/LICENSE_1_0.txt, Boost Software License - Version 1.0). 9 */ 10 module dcv.io.video.common; 11 12 import std.stdio : writeln; 13 import std.exception : enforce; 14 15 import ffmpeg.libavcodec.avcodec; 16 import ffmpeg.libavformat.avformat; 17 import ffmpeg.libavutil.avutil; 18 import ffmpeg.libavutil.frame; 19 import ffmpeg.libavutil.opt; 20 import ffmpeg.libswscale.swscale; 21 import ffmpeg.libavdevice.avdevice; 22 import ffmpeg.libavfilter.avfilter; 23 24 public import dcv.io.image; 25 26 /** 27 Exception related to streaming operations. 28 */ 29 class StreamException : Exception 30 { 31 @safe pure nothrow this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable next = null) 32 { 33 super(msg, file, line, next); 34 } 35 } 36 37 /** 38 Exception thrown on failed video stream opening. 39 */ 40 class StreamNotOpenException : StreamException 41 { 42 @safe pure nothrow this(string file = __FILE__, size_t line = __LINE__, Throwable next = null) 43 { 44 super("Stream is not opened.", file, line, next); 45 } 46 } 47 48 /** 49 Video codec identifiers. 50 */ 51 enum CodecID 52 { 53 NONE = 0, /// No codec. 54 RAW = AVCodecID.AV_CODEC_ID_RAWVIDEO, /// Raw video. 55 MPEG1VIDEO = AVCodecID.AV_CODEC_ID_MPEG1VIDEO, /// MPEG 1 codec. 56 MPEG2VIDEO = AVCodecID.AV_CODEC_ID_MPEG2VIDEO, /// MPEG 2 codec. 57 MPEG4 = AVCodecID.AV_CODEC_ID_MPEG4, /// MPEG 4 codec. 58 H263 = AVCodecID.AV_CODEC_ID_H263, /// h263 codec. 59 H264 = AVCodecID.AV_CODEC_ID_H264 /// h264 codec. 60 } 61 62 package: 63 64 string getCodecString(CodecID codec) 65 { 66 switch (codec) 67 { 68 case CodecID.NONE: 69 return ""; 70 case CodecID.RAW: 71 return "rawvideo"; 72 case CodecID.MPEG1VIDEO: 73 return "mpeg1video"; 74 case CodecID.MPEG2VIDEO: 75 return "mpeg2video"; 76 case CodecID.MPEG4: 77 return "mp4"; 78 case CodecID.H263: 79 return "h263"; 80 case CodecID.H264: 81 return "h264"; 82 default: 83 return ""; 84 } 85 } 86 87 class AVStarter 88 { 89 private static AVStarter _instance = null; 90 static AVStarter instance() 91 { 92 if (AVStarter._instance is null) 93 AVStarter._instance = new AVStarter; 94 return AVStarter._instance; 95 } 96 97 this() 98 { 99 av_register_all(); 100 avformat_network_init(); 101 avcodec_register_all(); 102 avfilter_register_all(); 103 avdevice_register_all(); 104 } 105 } 106 107 immutable IF_MONO_TYPES = [AVPixelFormat.AV_PIX_FMT_GRAY8]; 108 109 immutable IF_MONO_ALPHA_TYPES = [AVPixelFormat.AV_PIX_FMT_GRAY8A]; 110 111 immutable IF_YUV_TYPES = [ 112 AVPixelFormat.AV_PIX_FMT_YUV410P, AVPixelFormat.AV_PIX_FMT_YUV411P, 113 AVPixelFormat.AV_PIX_FMT_YUV420P, AVPixelFormat.AV_PIX_FMT_YUV422P, 114 AVPixelFormat.AV_PIX_FMT_YUYV422, AVPixelFormat.AV_PIX_FMT_YUV440P, AVPixelFormat.AV_PIX_FMT_YUV444P 115 ]; 116 117 immutable IF_RGB_TYPES = [ 118 AVPixelFormat.AV_PIX_FMT_RGB0, AVPixelFormat.AV_PIX_FMT_RGB24, AVPixelFormat.AV_PIX_FMT_RGB4, 119 AVPixelFormat.AV_PIX_FMT_RGB8 120 ]; 121 122 immutable IF_RGB_ALPHA_TYPES = [AVPixelFormat.AV_PIX_FMT_ARGB, AVPixelFormat.AV_PIX_FMT_RGBA]; 123 124 immutable IF_BGR_TYPES = [ 125 AVPixelFormat.AV_PIX_FMT_BGR0, AVPixelFormat.AV_PIX_FMT_BGR24, AVPixelFormat.AV_PIX_FMT_BGR4, 126 AVPixelFormat.AV_PIX_FMT_BGR8 127 ]; 128 129 immutable IF_BGR_ALPHA_TYPES = [AVPixelFormat.AV_PIX_FMT_ABGR, AVPixelFormat.AV_PIX_FMT_BGRA]; 130 131 alias IF_MONO_PREFERED = AVPixelFormat.AV_PIX_FMT_GRAY8; 132 alias IF_MONO_ALPHA_PREFERED = AVPixelFormat.AV_PIX_FMT_GRAY8A; 133 alias IF_RGB_PREFERED = AVPixelFormat.AV_PIX_FMT_RGB24; 134 alias IF_RGB_ALPHA_PREFERED = AVPixelFormat.AV_PIX_FMT_RGBA; 135 alias IF_BGR_PREFERED = AVPixelFormat.AV_PIX_FMT_BGR24; 136 alias IF_BGR_ALPHA_PREFERED = AVPixelFormat.AV_PIX_FMT_BGRA; 137 alias IF_YUV_PREFERED = AVPixelFormat.AV_PIX_FMT_YUV444P; 138 139 AVPixelFormat convertDepricatedPixelFormat(AVPixelFormat pix) 140 { 141 AVPixelFormat pixFormat = pix; 142 switch (pix) 143 { 144 case AVPixelFormat.AV_PIX_FMT_YUVJ420P: 145 pixFormat = AVPixelFormat.AV_PIX_FMT_YUV420P; 146 break; 147 case AVPixelFormat.AV_PIX_FMT_YUVJ422P: 148 pixFormat = AVPixelFormat.AV_PIX_FMT_YUV422P; 149 break; 150 case AVPixelFormat.AV_PIX_FMT_YUVJ444P: 151 pixFormat = AVPixelFormat.AV_PIX_FMT_YUV444P; 152 break; 153 case AVPixelFormat.AV_PIX_FMT_YUVJ440P: 154 pixFormat = AVPixelFormat.AV_PIX_FMT_YUV440P; 155 break; 156 default: 157 break; 158 } 159 return pixFormat; 160 } 161 162 ImageFormat AVPixelFormat_to_ImageFormat(AVPixelFormat format) 163 { 164 import std.exception : enforce; 165 import std.algorithm.searching : find; 166 167 if (IF_YUV_TYPES.find(format)) 168 { 169 return ImageFormat.IF_YUV; 170 } 171 else if (IF_RGB_TYPES.find(format)) 172 { 173 return ImageFormat.IF_RGB; 174 } 175 else if (IF_BGR_TYPES.find(format)) 176 { 177 return ImageFormat.IF_BGR; 178 } 179 else if (IF_RGB_ALPHA_TYPES.find(format)) 180 { 181 return ImageFormat.IF_RGB_ALPHA; 182 } 183 else if (IF_BGR_ALPHA_TYPES.find(format)) 184 { 185 return ImageFormat.IF_BGR_ALPHA; 186 } 187 else if (IF_MONO_TYPES.find(format)) 188 { 189 return ImageFormat.IF_MONO; 190 } 191 else if (IF_MONO_ALPHA_TYPES.find(format)) 192 { 193 return ImageFormat.IF_MONO_ALPHA; 194 } 195 else 196 { 197 enforce(0, "Format type is not supported"); 198 } 199 return ImageFormat.IF_UNASSIGNED; 200 } 201 202 AVPixelFormat ImageFormat_to_AVPixelFormat(ImageFormat format) 203 { 204 switch (format) 205 { 206 case ImageFormat.IF_MONO: 207 return IF_MONO_PREFERED; 208 case ImageFormat.IF_MONO_ALPHA: 209 return IF_MONO_ALPHA_PREFERED; 210 case ImageFormat.IF_BGR: 211 return IF_BGR_PREFERED; 212 case ImageFormat.IF_BGR_ALPHA: 213 return IF_BGR_ALPHA_PREFERED; 214 case ImageFormat.IF_RGB: 215 return IF_RGB_PREFERED; 216 case ImageFormat.IF_RGB_ALPHA: 217 return IF_RGB_ALPHA_PREFERED; 218 case ImageFormat.IF_YUV: 219 return IF_YUV_PREFERED; 220 default: 221 assert(0); 222 } 223 } 224 225 void adoptFormat(AVPixelFormat format, AVFrame* frame, ubyte[] data) 226 { 227 228 import std.exception : enforce; 229 import std.algorithm.searching : find; 230 231 if (IF_YUV_TYPES.find(format)) 232 { 233 adoptYUV(format, frame, data); 234 } 235 else if (IF_RGB_TYPES.find(format)) 236 { 237 throw new Exception("Not implemented"); 238 } 239 else if (IF_BGR_TYPES.find(format)) 240 { 241 throw new Exception("Not implemented"); 242 } 243 else if (IF_RGB_ALPHA_TYPES.find(format)) 244 { 245 throw new Exception("Not implemented"); 246 } 247 else if (IF_BGR_ALPHA_TYPES.find(format)) 248 { 249 throw new Exception("Not implemented"); 250 } 251 else if (IF_MONO_TYPES.find(format)) 252 { 253 throw new Exception("Not implemented"); 254 } 255 else if (IF_MONO_ALPHA_TYPES.find(format)) 256 { 257 throw new Exception("Not implemented"); 258 } 259 else 260 { 261 enforce(0, "Format type is not supported"); 262 } 263 } 264 265 void adoptYUV(AVPixelFormat format, AVFrame* frame, ubyte[] data) 266 { 267 switch (format) 268 { 269 case AVPixelFormat.AV_PIX_FMT_YUV410P, AVPixelFormat.AV_PIX_FMT_YUV420P, 270 AVPixelFormat.AV_PIX_FMT_YUV440P: 271 adoptYUVGrouped(frame, data); 272 break; 273 case AVPixelFormat.AV_PIX_FMT_YUV411P: 274 adoptYUV411P(frame, data); 275 break; 276 case AVPixelFormat.AV_PIX_FMT_YUV422P: 277 adoptYUV422P(frame, data); 278 break; 279 case AVPixelFormat.AV_PIX_FMT_YUYV422: 280 adoptYUYV422(frame, data); 281 break; 282 case AVPixelFormat.AV_PIX_FMT_YUV444P: 283 adoptYUV444P(frame, data); 284 break; 285 default: 286 assert(0); 287 } 288 } 289 290 void adoptYUVGrouped(AVFrame* frame, ubyte[] data) 291 { 292 auto ysize = frame.linesize[0]; 293 auto usize = frame.linesize[1]; 294 auto vsize = frame.linesize[2]; 295 296 auto udiv = ysize / usize; 297 auto vdiv = ysize / vsize; 298 299 int w = frame.width; 300 int h = frame.height; 301 302 if (data.length != w * h * 3) 303 data.length = w * h * 3; 304 305 auto ydata = frame.data[0]; 306 auto udata = frame.data[1]; 307 auto vdata = frame.data[2]; 308 309 foreach (r; 0 .. h) 310 { 311 foreach (c; 0 .. w) 312 { 313 auto pixpos = r * w * 3 + c * 3; 314 auto ypos = r * w + c; 315 auto uvpos = r / 2 * w / 2 + c / 2; 316 data[pixpos + 0] = ydata[ypos]; 317 data[pixpos + 1] = udata[uvpos]; 318 data[pixpos + 2] = vdata[uvpos]; 319 } 320 } 321 } 322 323 void adoptYUV411P(AVFrame* frame, ubyte[] data) 324 { 325 326 int w = frame.width; 327 int h = frame.height; 328 int s = (w * h) / 4; 329 330 auto ydata = frame.data[0]; 331 auto udata = frame.data[1]; 332 auto vdata = frame.data[2]; 333 334 foreach (i; 0 .. s) 335 { 336 auto y1 = ydata[i * 4]; 337 auto y2 = ydata[i * 4 + 1]; 338 auto y3 = ydata[i * 4 + 2]; 339 auto y4 = ydata[i * 4 + 3]; 340 auto u = udata[i]; 341 auto v = vdata[i]; 342 343 data[i * 12 + 0] = y1; 344 data[i * 12 + 1] = u; 345 data[i * 12 + 2] = v; 346 347 data[i * 12 + 3] = y2; 348 data[i * 12 + 4] = u; 349 data[i * 12 + 5] = v; 350 351 data[i * 12 + 3] = y3; 352 data[i * 12 + 4] = u; 353 data[i * 12 + 5] = v; 354 355 data[i * 12 + 3] = y4; 356 data[i * 12 + 4] = u; 357 data[i * 12 + 5] = v; 358 } 359 } 360 361 void adoptYUV422P(AVFrame* frame, ubyte[] data) 362 { 363 364 int w = frame.width; 365 int h = frame.height; 366 int s = (w * h) / 2; 367 368 auto ydata = frame.data[0]; 369 auto udata = frame.data[1]; 370 auto vdata = frame.data[2]; 371 372 foreach (i; 0 .. s) 373 { 374 auto y1 = ydata[i * 2]; 375 auto y2 = ydata[i * 2 + 1]; 376 auto u = udata[i]; 377 auto v = vdata[i]; 378 379 data[i * 6 + 0] = y1; 380 data[i * 6 + 1] = u; 381 data[i * 6 + 2] = v; 382 383 data[i * 6 + 3] = y2; 384 data[i * 6 + 4] = u; 385 data[i * 6 + 5] = v; 386 } 387 } 388 389 void adoptYUYV422(AVFrame* frame, ubyte[] data) 390 { 391 392 int w = frame.width; 393 int h = frame.height; 394 int s = (w * h) / 2; 395 396 auto yuyvdata = frame.data[0]; 397 398 size_t dataIter = 0; 399 size_t yuyvIter = 0; 400 401 foreach (i; 0 .. s) 402 { 403 auto y1 = yuyvdata[yuyvIter++]; 404 auto u = yuyvdata[yuyvIter++]; 405 auto y2 = yuyvdata[yuyvIter++]; 406 auto v = yuyvdata[yuyvIter++]; 407 408 data[dataIter++] = y1; 409 data[dataIter++] = u; 410 data[dataIter++] = v; 411 412 data[dataIter++] = y2; 413 data[dataIter++] = u; 414 data[dataIter++] = v; 415 } 416 } 417 418 void adoptYUV444P(AVFrame* frame, ubyte[] data) 419 { 420 foreach (i; 0 .. frame.width * frame.height) 421 { 422 data[i * 3 + 0] = frame.data[0][i]; 423 data[i * 3 + 1] = frame.data[1][i]; 424 data[i * 3 + 2] = frame.data[2][i]; 425 } 426 }