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 }