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 }