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() return 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 { 60 try 61 kill(pipes.pid); 62 catch (ProcessException e) 63 {} 64 } 65 66 version(Posix) 67 { 68 import core.sys.posix.signal : SIGKILL; 69 if (!tryWait(pipes.pid).terminated) 70 { 71 try 72 kill(pipes.pid, SIGKILL); 73 catch (ProcessException e) 74 {} 75 } 76 } 77 78 wait(pipes.pid); 79 } 80 } 81 82 private void initialize(string fn) 83 { 84 pipes = pipeProcess([ 85 "ffmpeg", 86 // Be quiet 87 "-loglevel", "panic", 88 // Specify input 89 "-i", fn, 90 // No audio 91 "-an", 92 // Specify output codec 93 "-vcodec", "bmp", 94 // Specify output format 95 "-f", "image2pipe", 96 // Specify output 97 "-" 98 ], Redirect.stdout); 99 100 frameBuf.length = Header.sizeof; 101 102 popFront(); 103 } 104 105 private: 106 import std.process; 107 108 ProcessPipes pipes; 109 bool done; 110 111 alias BitmapHeader!3 Header; 112 ubyte[] frameBuf; 113 Image!BGR frame; 114 } 115 116 struct VideoStream 117 { 118 RefCounted!VideoStreamImpl impl; 119 this(string fn) { impl.initialize(fn); } 120 @property ref Image!BGR front() return { return impl.front; } 121 @property bool empty() { return impl.empty; } 122 void popFront() { impl.popFront(); } 123 } 124 //alias RefCounted!VideoStreamImpl VideoStream; 125 126 VideoStream streamVideo(string fn) { return VideoStream(fn); } 127 128 private: 129 130 import std.stdio; 131 132 bool readExactly(ref File f, ubyte[] buf) 133 { 134 auto read = f.rawRead(buf); 135 if (read.length==0) return false; 136 enforce(read.length == buf.length, "Unexpected end of stream"); 137 return true; 138 }