1 /**
2  * Get frames from a video file by invoking ffmpeg.
3  *
4  * License:
5  *   This Source Code Form is subject to the terms of
6  *   the Mozilla Public License, v. 2.0. If a copy of
7  *   the MPL was not distributed with this file, You
8  *   can obtain one at http://mozilla.org/MPL/2.0/.
9  *
10  * Authors:
11  *   Vladimir Panteleev <vladimir@thecybershadow.net>
12  */
13 
14 module ae.utils.graphics.ffmpeg;
15 
16 import std.exception;
17 import std.typecons;
18 
19 import ae.utils.graphics.bitmap;
20 import ae.utils.graphics.color;
21 import ae.utils.graphics.image;
22 
23 private struct VideoStreamImpl
24 {
25 	@property ref Image!BGR front()
26 	{
27 		return frame;
28 	}
29 
30 	@property bool empty() { return done; }
31 
32 	void popFront()
33 	{
34 		auto stream = pipes.stdout;
35 		auto headerBuf = frameBuf[0..Header.sizeof];
36 		if (!stream.readExactly(headerBuf))
37 		{
38 			done = true;
39 			return;
40 		}
41 
42 		auto pHeader = cast(Header*)headerBuf.ptr;
43 		frameBuf.length = pHeader.bfSize;
44 		auto dataBuf = frameBuf[Header.sizeof..$];
45 		enforce(stream.readExactly(dataBuf), "Unexpected end of stream");
46 
47 		frameBuf.parseBMP!BGR(frame);
48 	}
49 
50 	@disable this(this);
51 
52 	~this()
53 	{
54 		if (done)
55 			wait(pipes.pid);
56 		else
57 		{
58 			if (!tryWait(pipes.pid).terminated)
59 				try
60 					kill(pipes.pid);
61 				catch (ProcessException e)
62 					wait(pipes.pid);
63 		}
64 	}
65 
66 	private void initialize(string fn)
67 	{
68 		pipes = pipeProcess([
69 			"ffmpeg",
70 			// Be quiet
71 			"-loglevel", "panic",
72 			// Specify input
73 			"-i", fn,
74 			// No audio
75 			"-an",
76 			// Specify output codec
77 			"-vcodec", "bmp",
78 			// Specify output format
79 			"-f", "image2pipe",
80 			// Specify output
81 			"-"
82 		], Redirect.stdout);
83 
84 		frameBuf.length = Header.sizeof;
85 
86 		popFront();
87 	}
88 
89 private:
90 	import std.process;
91 
92 	ProcessPipes pipes;
93 	bool done;
94 
95 	alias BitmapHeader!3 Header;
96 	ubyte[] frameBuf;
97 	Image!BGR frame;
98 }
99 
100 struct VideoStream
101 {
102 	RefCounted!VideoStreamImpl impl;
103 	this(string fn) { impl.initialize(fn); }
104 	@property ref Image!BGR front() { return impl.front; }
105 	@property bool empty() { return impl.empty; }
106 	void popFront() { impl.popFront(); }
107 }
108 //alias RefCounted!VideoStreamImpl VideoStream;
109 
110 VideoStream streamVideo(string fn) { return VideoStream(fn); }
111 
112 private:
113 
114 import std.stdio;
115 
116 bool readExactly(ref File f, ubyte[] buf)
117 {
118 	auto read = f.rawRead(buf);
119 	if (read.length==0) return false;
120 	enforce(read.length == buf.length, "Unexpected end of stream");
121 	return true;
122 }