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.color;
20 import ae.utils.graphics.image;
21 
22 private struct VideoStreamImpl
23 {
24 	@property ref Image!BGR front()
25 	{
26 		return frame;
27 	}
28 
29 	@property bool empty() { return done; }
30 
31 	void popFront()
32 	{
33 		auto stream = pipes.stdout;
34 		auto headerBuf = frameBuf[0..Header.sizeof];
35 		if (!stream.readExactly(headerBuf))
36 		{
37 			done = true;
38 			return;
39 		}
40 
41 		auto pHeader = cast(Header*)headerBuf.ptr;
42 		frameBuf.length = pHeader.bfSize;
43 		auto dataBuf = frameBuf[Header.sizeof..$];
44 		enforce(stream.readExactly(dataBuf), "Unexpected end of stream");
45 
46 		frameBuf.parseBMP!BGR(frame);
47 	}
48 
49 	@disable this(this);
50 
51 	~this()
52 	{
53 		if (done)
54 			wait(pipes.pid);
55 		else
56 			kill(pipes.pid);
57 	}
58 
59 	private void initialize(string fn)
60 	{
61 		pipes = pipeProcess([
62 			"ffmpeg",
63 			// Be quiet
64 			"-loglevel", "panic",
65 			// Specify input
66 			"-i", fn,
67 			// No audio
68 			"-an",
69 			// Specify output codec
70 			"-vcodec", "bmp",
71 			// Specify output format
72 			"-f", "image2pipe",
73 			// Specify output
74 			"-"
75 		], Redirect.stdout);
76 
77 		frameBuf.length = Header.sizeof;
78 
79 		popFront();
80 	}
81 
82 private:
83 	import std.process;
84 
85 	ProcessPipes pipes;
86 	bool done;
87 
88 	alias BitmapHeader!3 Header;
89 	ubyte[] frameBuf;
90 	Image!BGR frame;
91 }
92 
93 struct VideoStream
94 {
95 	RefCounted!VideoStreamImpl impl;
96 	this(string fn) { impl.initialize(fn); }
97 	@property ref Image!BGR front() { return impl.front; }
98 	@property bool empty() { return impl.empty; }
99 	void popFront() { impl.popFront(); }
100 }
101 //alias RefCounted!VideoStreamImpl VideoStream;
102 
103 VideoStream streamVideo(string fn) { return VideoStream(fn); }
104 
105 private:
106 
107 import std.stdio;
108 
109 bool readExactly(ref File f, ubyte[] buf)
110 {
111 	auto read = f.rawRead(buf);
112 	if (read.length==0) return false;
113 	enforce(read.length == buf.length, "Unexpected end of stream");
114 	return true;
115 }