1 /*
2 * Copyright (c) 2012-2016 Fredrik Mellbin
3 *
4 * This file is part of VapourSynth.
5 *
6 * VapourSynth is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * VapourSynth is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with VapourSynth; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20 
21 // loosely based on the relevant parts of main.cpp in avisynth
22 
23 #define INITGUID
24 #define WIN32_LEAN_AND_MEAN
25 #define NOMINMAX
26 #include <windows.h>
27 #include <vfw.h>
28 #include <aviriff.h>
29 #include <string>
30 #include <algorithm>
31 #include <mutex>
32 #include <atomic>
33 #include <vector>
34 #include <chrono>
35 #include <thread>
36 
37 #include "VSScript.h"
38 #include "VSHelper.h"
39 #include "../common/p2p_api.h"
40 #include "../common/fourcc.h"
41 #include "../common/vsutf16.h"
42 
43 static std::atomic<long> refCount(0);
44 
45 static const GUID CLSID_VapourSynth
46     = { 0x58f74ca0, 0xbd0e, 0x4664, { 0xa4, 0x9b, 0x8d, 0x10, 0xe6, 0xf0, 0xc1, 0x31 } };
47 
48 static const GUID IID_IAvisynthClipInfo
49     = { 0xe6d6b708, 0x124d, 0x11d4, {0x86, 0xf3, 0xdb, 0x80, 0xaf, 0xd9, 0x87, 0x78} };
50 
51 struct IAvisynthClipInfo : IUnknown {
52     virtual int __stdcall GetError(const char** ppszMessage) = 0;
53     virtual bool __stdcall GetParity(int n) = 0;
54     virtual bool __stdcall IsFieldBased() = 0;
55 };
56 
57 class VapourSynthFile final : public IAVIFile, public IPersistFile, public IClassFactory, public IAvisynthClipInfo {
58     friend class VapourSynthStream;
59 private:
60     int num_threads;
61     const VSAPI *vsapi;
62     VSScript *se;
63     bool enable_v210;
64     VSNodeRef *node;
65     std::atomic<long> m_refs;
66     std::string szScriptName;
67     const VSVideoInfo* vi;
68     std::string error_msg;
69     std::atomic<long> pending_requests;
70 
71     std::mutex cs_filter_graph;
72 
73     bool DelayInit();
74     bool DelayInit2();
75 
76     void Lock();
77     void Unlock();
78 public:
79 
80     VapourSynthFile(const CLSID& rclsid);
81     ~VapourSynthFile();
82 
83     static HRESULT Create(const CLSID& rclsid, const IID& riid, void **ppv);
84     static void VS_CC frameDoneCallback(void *userData, const VSFrameRef *f, int n, VSNodeRef *, const char *errorMsg);
85 
86     //////////// IUnknown
87 
88     STDMETHODIMP QueryInterface(const IID& iid, void **ppv);
89     STDMETHODIMP_(ULONG) AddRef();
90     STDMETHODIMP_(ULONG) Release();
91 
92     //////////// IClassFactory
93 
94     STDMETHODIMP CreateInstance(LPUNKNOWN pUnkOuter, REFIID riid, void * * ppvObj);
95     STDMETHODIMP LockServer(BOOL fLock);
96 
97     //////////// IPersistFile
98 
99     STDMETHODIMP GetClassID(LPCLSID lpClassID);  // IPersist
100 
101     STDMETHODIMP IsDirty();
102     STDMETHODIMP Load(LPCOLESTR lpszFileName, DWORD grfMode);
103     STDMETHODIMP Save(LPCOLESTR lpszFileName, BOOL fRemember);
104     STDMETHODIMP SaveCompleted(LPCOLESTR lpszFileName);
105     STDMETHODIMP GetCurFile(LPOLESTR *lplpszFileName);
106 
107     //////////// IAVIFile
108 
109     STDMETHODIMP CreateStream(PAVISTREAM *ppStream, AVISTREAMINFOW *psi);       // 5
110     STDMETHODIMP EndRecord();                                                   // 8
111     STDMETHODIMP GetStream(PAVISTREAM *ppStream, DWORD fccType, LONG lParam);   // 4
112     STDMETHODIMP Info(AVIFILEINFOW *psi, LONG lSize);                           // 3
113 
114     STDMETHODIMP Open(LPCSTR szFile, UINT mode, LPCOLESTR lpszFileName);        // ???
115     STDMETHODIMP Save(LPCSTR szFile, AVICOMPRESSOPTIONS FAR *lpOptions,         // ???
116         AVISAVECALLBACK lpfnCallback);
117 
118     STDMETHODIMP ReadData(DWORD fcc, LPVOID lp, LONG *lpcb);                    // 7
119     STDMETHODIMP WriteData(DWORD fcc, LPVOID lpBuffer, LONG cbBuffer);          // 6
120     STDMETHODIMP DeleteStream(DWORD fccType, LONG lParam);                      // 9
121 
122     //////////// IAvisynthClipInfo
123 
124     int __stdcall GetError(const char** ppszMessage);
125     bool __stdcall GetParity(int n);
126     bool __stdcall IsFieldBased();
127 };
128 
129 ///////////////////////////////////
130 
131 class VapourSynthStream final : public IAVIStream, public IAVIStreaming {
132 public:
133 
134     //////////// IUnknown
135 
136     STDMETHODIMP QueryInterface(const IID& iid, void **ppv);
137     STDMETHODIMP_(ULONG) AddRef();
138     STDMETHODIMP_(ULONG) Release();
139 
140     VapourSynthStream(VapourSynthFile *parentPtr, bool isAudio);
141     ~VapourSynthStream();
142 
143     //////////// IAVIStream
144 
145     STDMETHODIMP Create(LPARAM lParam1, LPARAM lParam2);
146     STDMETHODIMP Delete(LONG lStart, LONG lSamples);
147     STDMETHODIMP_(LONG) Info(AVISTREAMINFOW *psi, LONG lSize);
148     STDMETHODIMP_(LONG) FindSample(LONG lPos, LONG lFlags);
149     STDMETHODIMP Read(LONG lStart, LONG lSamples, LPVOID lpBuffer, LONG cbBuffer, LONG *plBytes, LONG *plSamples);
150     STDMETHODIMP ReadData(DWORD fcc, LPVOID lp, LONG *lpcb);
151     STDMETHODIMP ReadFormat(LONG lPos, LPVOID lpFormat, LONG *lpcbFormat);
152     STDMETHODIMP SetFormat(LONG lPos, LPVOID lpFormat, LONG cbFormat);
153     STDMETHODIMP Write(LONG lStart, LONG lSamples, LPVOID lpBuffer,
154         LONG cbBuffer, DWORD dwFlags, LONG FAR *plSampWritten,
155         LONG FAR *plBytesWritten);
156     STDMETHODIMP WriteData(DWORD fcc, LPVOID lpBuffer, LONG cbBuffer);
157     STDMETHODIMP SetInfo(AVISTREAMINFOW *psi, LONG lSize);
158 
159     //////////// IAVIStreaming
160 
161     STDMETHODIMP Begin(LONG lStart, LONG lEnd, LONG lRate);
162     STDMETHODIMP End();
163 
164 private:
165     std::atomic<long> m_refs;
166 
167     VapourSynthFile *parent;
168     std::string sName;
169 
170     //////////// internal
171 
172     bool ReadFrame(void* lpBuffer, int n);
173 
174     HRESULT Read2(LONG lStart, LONG lSamples, LPVOID lpBuffer, LONG cbBuffer, LONG *plBytes, LONG *plSamples);
175 };
176 
177 
DllMain(HANDLE hModule,ULONG ulReason,LPVOID lpReserved)178 BOOL APIENTRY DllMain(HANDLE hModule, ULONG ulReason, LPVOID lpReserved) {
179     if (ulReason == DLL_PROCESS_ATTACH) {
180         // fixme, move this where threading can't be an issue
181         vsscript_init();
182     } else if (ulReason == DLL_PROCESS_DETACH) {
183         vsscript_finalize();
184     }
185     return TRUE;
186 }
187 
DllGetClassObject(const CLSID & rclsid,const IID & riid,void ** ppv)188 STDAPI DllGetClassObject(const CLSID& rclsid, const IID& riid, void **ppv) {
189 
190     if (rclsid != CLSID_VapourSynth)
191         return CLASS_E_CLASSNOTAVAILABLE;
192     HRESULT hresult = VapourSynthFile::Create(rclsid, riid, ppv);
193     return hresult;
194 }
195 
DllCanUnloadNow()196 STDAPI DllCanUnloadNow() {
197     return refCount ? S_FALSE : S_OK;
198 }
199 
200 
201 ///////////////////////////////////////////////////////////////////////////
202 //
203 //    VapourSynthFile
204 //
205 ///////////////////////////////////////////////////////////////////////////
206 //////////// IClassFactory
207 
CreateInstance(LPUNKNOWN pUnkOuter,REFIID riid,void ** ppvObj)208 STDMETHODIMP VapourSynthFile::CreateInstance(LPUNKNOWN pUnkOuter, REFIID riid, void * * ppvObj) {
209     if (pUnkOuter)
210         return CLASS_E_NOAGGREGATION;
211     HRESULT hresult = Create(CLSID_VapourSynth, riid, ppvObj);
212     return hresult;
213 }
214 
LockServer(BOOL fLock)215 STDMETHODIMP VapourSynthFile::LockServer(BOOL fLock) {
216     return S_OK;
217 }
218 
219 ///////////////////////////////////////////////////
220 //////////// IPersistFile
221 
GetClassID(LPCLSID lpClassID)222 STDMETHODIMP VapourSynthFile::GetClassID(LPCLSID lpClassID) {  // IPersist
223     if (!lpClassID)
224         return E_POINTER;
225     *lpClassID = CLSID_VapourSynth;
226     return S_OK;
227 }
228 
IsDirty()229 STDMETHODIMP VapourSynthFile::IsDirty() {
230     return S_FALSE;
231 }
232 
Load(LPCOLESTR lpszFileName,DWORD grfMode)233 STDMETHODIMP VapourSynthFile::Load(LPCOLESTR lpszFileName, DWORD grfMode) {
234     return Open(utf16_to_utf8(lpszFileName).c_str(), grfMode, lpszFileName);
235 }
236 
Save(LPCOLESTR lpszFileName,BOOL fRemember)237 STDMETHODIMP VapourSynthFile::Save(LPCOLESTR lpszFileName, BOOL fRemember) {
238     return E_FAIL;
239 }
240 
SaveCompleted(LPCOLESTR lpszFileName)241 STDMETHODIMP VapourSynthFile::SaveCompleted(LPCOLESTR lpszFileName) {
242     return S_OK;
243 }
244 
GetCurFile(LPOLESTR * lplpszFileName)245 STDMETHODIMP VapourSynthFile::GetCurFile(LPOLESTR *lplpszFileName) {
246     if (lplpszFileName)
247         *lplpszFileName = nullptr;
248     return E_FAIL;
249 }
250 
251 ///////////////////////////////////////////////////
252 /////// static local
253 
Create(const CLSID & rclsid,const IID & riid,void ** ppv)254 HRESULT VapourSynthFile::Create(const CLSID& rclsid, const IID& riid, void **ppv) {
255     HRESULT hresult;
256     VapourSynthFile* pAVIFileSynth = new(std::nothrow)VapourSynthFile(rclsid);
257     if (!pAVIFileSynth)
258         return E_OUTOFMEMORY;
259     hresult = pAVIFileSynth->QueryInterface(riid, ppv);
260     pAVIFileSynth->Release();
261     return hresult;
262 }
263 
264 ///////////////////////////////////////////////////
265 //////////// IUnknown
266 
QueryInterface(const IID & iid,void ** ppv)267 STDMETHODIMP VapourSynthFile::QueryInterface(const IID& iid, void **ppv) {
268     if (!ppv)
269         return E_POINTER;
270 
271     if (iid == IID_IUnknown) {
272         *ppv = (IUnknown *)(IAVIFile *)this;
273     } else if (iid == IID_IClassFactory) {
274         *ppv = (IClassFactory *)this;
275     } else if (iid == IID_IPersist) {
276         *ppv = (IPersist *)this;
277     } else if (iid == IID_IPersistFile) {
278         *ppv = (IPersistFile *)this;
279     } else if (iid == IID_IAVIFile) {
280         *ppv = (IAVIFile *)this;
281     } else if (iid == IID_IAvisynthClipInfo) {
282         *ppv = (IAvisynthClipInfo *)this;
283     } else {
284         *ppv = nullptr;
285         return E_NOINTERFACE;
286     }
287 
288     AddRef();
289 
290     return S_OK;
291 }
292 
STDMETHODIMP_(ULONG)293 STDMETHODIMP_(ULONG) VapourSynthFile::AddRef() {
294     const int refs = ++m_refs;
295     ++refCount;
296     return refs;
297 }
298 
STDMETHODIMP_(ULONG)299 STDMETHODIMP_(ULONG) VapourSynthFile::Release() {
300     const int refs = --m_refs;
301     --refCount;
302     if (!refs)
303         delete this;
304     return refs;
305 }
306 
307 ////////////////////////////////////////////////////////////////////////
308 //
309 //        VapourSynthStream
310 //
311 ////////////////////////////////////////////////////////////////////////
312 //////////// IUnknown
313 
314 
QueryInterface(const IID & iid,void ** ppv)315 STDMETHODIMP VapourSynthStream::QueryInterface(const IID& iid, void **ppv) {
316     if (!ppv)
317         return E_POINTER;
318 
319     if (iid == IID_IUnknown) {
320         *ppv = (IUnknown *)(IAVIStream *)this;
321     } else if (iid == IID_IAVIStream) {
322         *ppv = (IAVIStream *)this;
323     } else if (iid == IID_IAVIStreaming) {
324         *ppv = (IAVIStreaming *)this;
325     } else {
326         *ppv = nullptr;
327         return E_NOINTERFACE;
328     }
329 
330     AddRef();
331 
332     return S_OK;
333 }
334 
STDMETHODIMP_(ULONG)335 STDMETHODIMP_(ULONG) VapourSynthStream::AddRef() {
336     const int refs = ++m_refs;
337     ++refCount;
338     return refs;
339 }
340 
STDMETHODIMP_(ULONG)341 STDMETHODIMP_(ULONG) VapourSynthStream::Release() {
342     const int refs = --m_refs;
343     --refCount;
344     if (!refs) delete this;
345     return refs;
346 }
347 
348 ////////////////////////////////////////////////////////////////////////
349 //
350 //        VapourSynthFile
351 //
352 ////////////////////////////////////////////////////////////////////////
353 //////////// IAVIFile
354 
CreateStream(PAVISTREAM * ppStream,AVISTREAMINFOW * psi)355 STDMETHODIMP VapourSynthFile::CreateStream(PAVISTREAM *ppStream, AVISTREAMINFOW *psi) {
356     *ppStream = nullptr;
357     return S_OK;
358 }
359 
EndRecord()360 STDMETHODIMP VapourSynthFile::EndRecord() {
361     return AVIERR_READONLY;
362 }
363 
Save(LPCSTR szFile,AVICOMPRESSOPTIONS FAR * lpOptions,AVISAVECALLBACK lpfnCallback)364 STDMETHODIMP VapourSynthFile::Save(LPCSTR szFile, AVICOMPRESSOPTIONS FAR *lpOptions,
365     AVISAVECALLBACK lpfnCallback) {
366     return AVIERR_READONLY;
367 }
368 
ReadData(DWORD fcc,LPVOID lp,LONG * lpcb)369 STDMETHODIMP VapourSynthFile::ReadData(DWORD fcc, LPVOID lp, LONG *lpcb) {
370     return AVIERR_NODATA;
371 }
372 
WriteData(DWORD fcc,LPVOID lpBuffer,LONG cbBuffer)373 STDMETHODIMP VapourSynthFile::WriteData(DWORD fcc, LPVOID lpBuffer, LONG cbBuffer) {
374     return AVIERR_READONLY;
375 }
376 
DeleteStream(DWORD fccType,LONG lParam)377 STDMETHODIMP VapourSynthFile::DeleteStream(DWORD fccType, LONG lParam) {
378     return AVIERR_READONLY;
379 }
380 
381 
382 ///////////////////////////////////////////////////
383 /////// local
384 
VapourSynthFile(const CLSID & rclsid)385 VapourSynthFile::VapourSynthFile(const CLSID& rclsid) : num_threads(1), vsapi(nullptr), se(nullptr), enable_v210(false), node(nullptr), m_refs(0), vi(nullptr), pending_requests(0) {
386     vsapi = vsscript_getVSApi();
387     AddRef();
388 }
389 
~VapourSynthFile()390 VapourSynthFile::~VapourSynthFile() {
391     Lock();
392     if (vi) {
393         while (pending_requests > 0) { std::this_thread::sleep_for(std::chrono::milliseconds(1)); };
394         vi = nullptr;
395         vsapi->freeNode(node);
396         vsscript_freeScript(se);
397     }
398     Unlock();
399 }
400 
Open(LPCSTR szFile,UINT mode,LPCOLESTR lpszFileName)401 STDMETHODIMP VapourSynthFile::Open(LPCSTR szFile, UINT mode, LPCOLESTR lpszFileName) {
402     if (mode & (OF_CREATE | OF_WRITE))
403         return E_FAIL;
404     szScriptName = szFile;
405     return S_OK;
406 }
407 
DelayInit()408 bool VapourSynthFile::DelayInit() {
409     Lock();
410     bool result = DelayInit2();
411     Unlock();
412     return result;
413 }
414 
415 static const char *ErrorScript1 = "\
416 import vapoursynth as vs\n\
417 import sys\n\
418 core = vs.get_core()\n\
419 w = 340\n\
420 h = 600\n\
421 red = core.std.BlankClip(width=w, height=h, format=vs.RGB24, color=[255, 0, 0])\n\
422 green = core.std.BlankClip(width=w, height=h, format=vs.RGB24, color=[0, 255, 0])\n\
423 blue = core.std.BlankClip(width=w, height=h, format=vs.RGB24, color=[0, 0, 255])\n\
424 stacked = core.std.StackHorizontal([red, green, blue])\n\
425 msg = core.text.Text(stacked, r\"\"\"";
426 
427 static const char *ErrorScript2 = "\"\"\")\n\
428 final = core.resize.Bilinear(msg, format=vs.COMPATBGR32)\n\
429 final.set_output()\n";
430 
DelayInit2()431 bool VapourSynthFile::DelayInit2() {
432     if (!szScriptName.empty() && !vi) {
433         if (!vsscript_evaluateFile(&se, szScriptName.c_str(), efSetWorkingDir)) {
434             node = vsscript_getOutput(se, 0);
435             if (!node) {
436                 error_msg = "Couldn't get output clip, no output set?";
437                 goto vpyerror;
438             }
439             vi = vsapi->getVideoInfo(node);
440             error_msg.clear();
441 
442             if (vi->width == 0 || vi->height == 0 || vi->format == nullptr || vi->numFrames == 0) {
443                 error_msg = "Cannot open clips with varying dimensions or format in vfw";
444                 goto vpyerror;
445             }
446 
447             int id = vi->format->id;
448             if (!HasSupportedFourCC(id)) {
449                 error_msg = "VFW module doesn't support ";
450                 error_msg += vi->format->name;
451                 error_msg += " output";
452                 goto vpyerror;
453             }
454 
455             // set the special options hidden in global variables
456             int error;
457             VSMap *options = vsapi->createMap();
458             vsscript_getVariable(se, "enable_v210", options);
459             enable_v210 = !!vsapi->propGetInt(options, "enable_v210", 0, &error);
460             if (error)
461                 enable_v210 = false;
462             vsapi->freeMap(options);
463 
464             VSCoreInfo info;
465             vsapi->getCoreInfo2(vsscript_getCore(se), &info);
466             num_threads = info.numThreads;
467 
468             return true;
469         } else {
470             error_msg = vsscript_getError(se);
471             vpyerror:
472             vi = nullptr;
473             vsscript_freeScript(se);
474             se = nullptr;
475             std::string error_script = ErrorScript1;
476             error_script += error_msg;
477             error_script += ErrorScript2;
478             vsscript_evaluateScript(&se, error_script.c_str(), "vfw_error.message", 0);
479             node = vsscript_getOutput(se, 0);
480             vi = vsapi->getVideoInfo(node);
481             return true;
482         }
483     } else {
484         return !!vi;
485     }
486 }
487 
Lock()488 void VapourSynthFile::Lock() {
489     cs_filter_graph.lock();
490 }
491 
Unlock()492 void VapourSynthFile::Unlock() {
493     cs_filter_graph.unlock();
494 }
495 
496 ///////////////////////////////////////////////////
497 //////////// IAVIFile
498 
Info(AVIFILEINFOW * pfi,LONG lSize)499 STDMETHODIMP VapourSynthFile::Info(AVIFILEINFOW *pfi, LONG lSize) {
500     if (!pfi)
501         return E_POINTER;
502 
503     if (!DelayInit())
504         return E_FAIL;
505 
506     AVIFILEINFOW afi = {};
507 
508     afi.dwMaxBytesPerSec = 0;
509     afi.dwFlags = AVIFILEINFO_HASINDEX | AVIFILEINFO_ISINTERLEAVED;
510     afi.dwCaps = AVIFILECAPS_CANREAD | AVIFILECAPS_ALLKEYFRAMES | AVIFILECAPS_NOCOMPRESSION;
511 
512     afi.dwStreams = 1;
513     afi.dwSuggestedBufferSize = 0;
514     afi.dwWidth = vi->width;
515     afi.dwHeight = vi->height;
516     afi.dwEditCount = 0;
517 
518     afi.dwRate = int64ToIntS(vi->fpsNum ? vi->fpsNum : 1);
519     afi.dwScale = int64ToIntS(vi->fpsDen ? vi->fpsDen : 30);
520     afi.dwLength = vi->numFrames;
521 
522     wcscpy(afi.szFileType, L"VapourSynth");
523 
524     // Maybe should return AVIERR_BUFFERTOOSMALL for lSize < sizeof(afi)
525     memset(pfi, 0, lSize);
526     memcpy(pfi, &afi, std::min(static_cast<size_t>(lSize), sizeof(afi)));
527     return S_OK;
528 }
529 
BePrintable(int ch)530 static inline char BePrintable(int ch) {
531     ch &= 0xff;
532     return isprint(ch) ? ch : '.';
533 }
534 
535 
GetStream(PAVISTREAM * ppStream,DWORD fccType,LONG lParam)536 STDMETHODIMP VapourSynthFile::GetStream(PAVISTREAM *ppStream, DWORD fccType, LONG lParam) {
537     VapourSynthStream *casr;
538     char fcc[5];
539 
540     fcc[0] = BePrintable(fccType);
541     fcc[1] = BePrintable(fccType >> 8);
542     fcc[2] = BePrintable(fccType >> 16);
543     fcc[3] = BePrintable(fccType >> 24);
544     fcc[4] = 0;
545 
546     if (!DelayInit())
547         return E_FAIL;
548 
549     *ppStream = nullptr;
550 
551     if (!fccType) {
552         if (lParam == 0)
553             fccType = streamtypeVIDEO;
554     }
555 
556     if (lParam > 0)
557         return AVIERR_NODATA;
558 
559     if (fccType == streamtypeVIDEO) {
560         if ((casr = new(std::nothrow)VapourSynthStream(this, false)) == 0)
561             return AVIERR_MEMORY;
562 
563         *ppStream = (IAVIStream *)casr;
564 
565     } else if (fccType == streamtypeAUDIO) {
566         return AVIERR_NODATA;
567 
568         if ((casr = new(std::nothrow)VapourSynthStream(this, true)) == 0)
569             return AVIERR_MEMORY;
570         *ppStream = (IAVIStream *)casr;
571     } else
572         return AVIERR_NODATA;
573 
574     return S_OK;
575 }
576 
577 
578 ////////////////////////////////////////////////////////////////////////
579 //////////// IAvisynthClipInfo
580 
GetError(const char ** ppszMessage)581 int __stdcall VapourSynthFile::GetError(const char** ppszMessage) {
582     if (!DelayInit() && error_msg.empty())
583         error_msg = "VapourSynth: script open failed!";
584 
585     if (ppszMessage)
586         *ppszMessage = error_msg.c_str();
587     return !error_msg.empty();
588 }
589 
GetParity(int n)590 bool __stdcall VapourSynthFile::GetParity(int n) {
591     if (!DelayInit())
592         return false;
593     return false;
594 }
595 
IsFieldBased()596 bool __stdcall VapourSynthFile::IsFieldBased() {
597     if (!DelayInit())
598         return false;
599     return false;
600 }
601 
602 ////////////////////////////////////////////////////////////////////////
603 //
604 //        VapourSynthStream
605 //
606 ////////////////////////////////////////////////////////////////////////
607 //////////// IAVIStreaming
608 
Begin(LONG lStart,LONG lEnd,LONG lRate)609 STDMETHODIMP VapourSynthStream::Begin(LONG lStart, LONG lEnd, LONG lRate) {
610     return S_OK;
611 }
612 
End()613 STDMETHODIMP VapourSynthStream::End() {
614     return S_OK;
615 }
616 
617 //////////// IAVIStream
618 
Create(LPARAM lParam1,LPARAM lParam2)619 STDMETHODIMP VapourSynthStream::Create(LPARAM lParam1, LPARAM lParam2) {
620     return AVIERR_READONLY;
621 }
622 
Delete(LONG lStart,LONG lSamples)623 STDMETHODIMP VapourSynthStream::Delete(LONG lStart, LONG lSamples) {
624     return AVIERR_READONLY;
625 }
626 
ReadData(DWORD fcc,LPVOID lp,LONG * lpcb)627 STDMETHODIMP VapourSynthStream::ReadData(DWORD fcc, LPVOID lp, LONG *lpcb) {
628     return AVIERR_NODATA;
629 }
630 
SetFormat(LONG lPos,LPVOID lpFormat,LONG cbFormat)631 STDMETHODIMP VapourSynthStream::SetFormat(LONG lPos, LPVOID lpFormat, LONG cbFormat) {
632     return AVIERR_READONLY;
633 }
634 
WriteData(DWORD fcc,LPVOID lpBuffer,LONG cbBuffer)635 STDMETHODIMP VapourSynthStream::WriteData(DWORD fcc, LPVOID lpBuffer, LONG cbBuffer) {
636     return AVIERR_READONLY;
637 }
638 
SetInfo(AVISTREAMINFOW * psi,LONG lSize)639 STDMETHODIMP VapourSynthStream::SetInfo(AVISTREAMINFOW *psi, LONG lSize) {
640     return AVIERR_READONLY;
641 }
642 
643 ////////////////////////////////////////////////////////////////////////
644 //////////// local
645 
VapourSynthStream(VapourSynthFile * parentPtr,bool isAudio)646 VapourSynthStream::VapourSynthStream(VapourSynthFile *parentPtr, bool isAudio) : m_refs(0), sName("video") {
647     AddRef();
648     parent = parentPtr;
649     parent->AddRef();
650 }
651 
~VapourSynthStream()652 VapourSynthStream::~VapourSynthStream() {
653     if (parent)
654         parent->Release();
655 }
656 
657 ////////////////////////////////////////////////////////////////////////
658 //////////// IAVIStream
659 
STDMETHODIMP_(LONG)660 STDMETHODIMP_(LONG) VapourSynthStream::Info(AVISTREAMINFOW *psi, LONG lSize) {
661     if (!psi)
662         return E_POINTER;
663 
664 
665     const VSVideoInfo* const vi = parent->vi;
666 
667     AVISTREAMINFOW asi = {};
668     asi.fccType = streamtypeVIDEO;
669     asi.dwQuality = DWORD(-1);
670 
671     int image_size = BMPSize(vi, (vi->format->id == pfYUV422P10 && parent->enable_v210));
672 
673     if (!GetFourCC(vi->format->id, (vi->format->id == pfYUV422P10 && parent->enable_v210), asi.fccHandler))
674         return E_FAIL;
675 
676     asi.dwScale = int64ToIntS(vi->fpsDen ? vi->fpsDen : 1);
677     asi.dwRate = int64ToIntS(vi->fpsNum ? vi->fpsNum : 30);
678     asi.dwLength = vi->numFrames;
679     asi.rcFrame.right = vi->width;
680     asi.rcFrame.bottom = vi->height;
681     asi.dwSampleSize = image_size;
682     asi.dwSuggestedBufferSize = image_size;
683     wcscpy(asi.szName, L"VapourSynth Video #1");
684 
685     // Maybe should return AVIERR_BUFFERTOOSMALL for lSize < sizeof(asi)
686     memset(psi, 0, lSize);
687     memcpy(psi, &asi, std::min(static_cast<size_t>(lSize), sizeof(asi)));
688     return S_OK;
689 }
690 
STDMETHODIMP_(LONG)691 STDMETHODIMP_(LONG) VapourSynthStream::FindSample(LONG lPos, LONG lFlags) {
692     if (lFlags & FIND_FORMAT)
693         return -1;
694 
695     if (lFlags & FIND_FROM_START)
696         return 0;
697 
698     return lPos;
699 }
700 
701 
702 ////////////////////////////////////////////////////////////////////////
703 //////////// local
704 
frameDoneCallback(void * userData,const VSFrameRef * f,int n,VSNodeRef *,const char * errorMsg)705 void VS_CC VapourSynthFile::frameDoneCallback(void *userData, const VSFrameRef *f, int n, VSNodeRef *, const char *errorMsg) {
706     VapourSynthFile *vsfile = static_cast<VapourSynthFile *>(userData);
707     vsfile->vsapi->freeFrame(f);
708     --vsfile->pending_requests;
709 }
710 
ReadFrame(void * lpBuffer,int n)711 bool VapourSynthStream::ReadFrame(void* lpBuffer, int n) {
712     const VSAPI *vsapi = parent->vsapi;
713     std::vector<char> errMsg(32 * 1024);
714     const VSFrameRef *f = vsapi->getFrame(n, parent->node, errMsg.data(), static_cast<int>(errMsg.size()));
715     VSScript *errSe = nullptr;
716     if (!f) {
717         std::string matrix;
718         if (parent->vi->format->colorFamily == cmYUV || parent->vi->format->colorFamily == cmGray || parent->vi->format->id == pfCompatYUY2)
719             matrix = ", matrix_s=\"709\"";
720 
721         std::string frameErrorScript = "import vapoursynth as vs\nimport sys\ncore = vs.get_core()\n";
722         frameErrorScript += "err_script_formatid = " + std::to_string(parent->vi->format->id) + "\n";
723         frameErrorScript += "err_script_width = " + std::to_string(parent->vi->width) + "\n";
724         frameErrorScript += "err_script_height = " + std::to_string(parent->vi->height) + "\n";
725         frameErrorScript += "err_script_background = core.std.BlankClip(width=err_script_width, height=err_script_height, format=vs.RGB24)\n";
726         frameErrorScript += "err_script_clip = core.text.Text(err_script_background, r\"\"\"";
727         frameErrorScript += errMsg.data();
728         frameErrorScript += "\"\"\")\n";
729         frameErrorScript += "err_script_clip = core.resize.Bilinear(err_script_clip, format=err_script_formatid" + matrix + ")\n";
730         frameErrorScript += "err_script_clip.set_output()\n";
731 
732         vsscript_evaluateScript(&errSe, frameErrorScript.c_str(), "vfw_error.message", 0);
733         VSNodeRef *node = vsscript_getOutput(errSe, 0);
734         f = vsapi->getFrame(0, node, nullptr, 0);
735         vsapi->freeNode(node);
736 
737         if (!f) {
738             vsscript_freeScript(errSe);
739             return false;
740         }
741     }
742 
743     const VSFormat *fi = vsapi->getFrameFormat(f);
744 
745     p2p_buffer_param p = {};
746     p.width = vsapi->getFrameWidth(f, 0);
747     p.height = vsapi->getFrameHeight(f, 0);
748     p.dst[0] = lpBuffer;
749     // Used by most
750     p.dst_stride[0] = p.width * 4 * fi->bytesPerSample;
751 
752     for (int plane = 0; plane < fi->numPlanes; plane++) {
753         p.src[plane] = vsapi->getReadPtr(f, plane);
754         p.src_stride[plane] = vsapi->getStride(f, plane);
755     }
756 
757     if (fi->id == pfRGB24) {
758         p.packing = p2p_argb32_le;
759         for (int plane = 0; plane < 3; plane++) {
760             p.src[plane] = vsapi->getReadPtr(f, plane) + vsapi->getStride(f, plane) * (vsapi->getFrameHeight(f, plane) - 1);
761             p.src_stride[plane] = -vsapi->getStride(f, plane);
762         }
763         p2p_pack_frame(&p, P2P_ALPHA_SET_ONE);
764     } else if (fi->id == pfRGB30) {
765         p.packing = p2p_rgb30_be;
766         p.dst_stride[0] = ((p.width + 63) / 64) * 256;
767         p2p_pack_frame(&p, P2P_ALPHA_SET_ONE);
768     } else if (fi->id == pfRGB48) {
769         p.packing = p2p_argb64_be;
770         p2p_pack_frame(&p, P2P_ALPHA_SET_ONE);
771     } else if (fi->id == pfYUV444P10) {
772         p.packing = p2p_y410_le;
773         p.dst_stride[0] = p.width * 2 * fi->bytesPerSample;
774         p2p_pack_frame(&p, P2P_ALPHA_SET_ONE);
775     } else if (fi->id == pfYUV444P16) {
776         p.packing = p2p_y416_le;
777         p2p_pack_frame(&p, P2P_ALPHA_SET_ONE);
778     } else if (fi->id == pfYUV422P10 && parent->enable_v210) {
779         p.packing = p2p_v210_le;
780         p.dst_stride[0] = ((16 * ((p.width + 5) / 6) + 127) & ~127);
781         p2p_pack_frame(&p, P2P_ALPHA_SET_ONE);
782     } else if ((fi->id == pfYUV420P16) || (fi->id == pfYUV422P16) || (fi->id == pfYUV420P10) || (fi->id == pfYUV422P10)) {
783         switch (fi->id) {
784         case pfYUV420P10: p.packing = p2p_p010_le; break;
785         case pfYUV422P10: p.packing = p2p_p210_le; break;
786         case pfYUV420P16: p.packing = p2p_p016_le; break;
787         case pfYUV422P16: p.packing = p2p_p216_le; break;
788         }
789         p.dst_stride[0] = p.width * fi->bytesPerSample;
790         p.dst_stride[1] = p.width * fi->bytesPerSample;
791         p.dst[1] = (uint8_t *)lpBuffer + p.dst_stride[0] * p.height;
792         p2p_pack_frame(&p, P2P_ALPHA_SET_ONE);
793     } else {
794         const int stride = vsapi->getStride(f, 0);
795         const int height = vsapi->getFrameHeight(f, 0);
796         int row_size = vsapi->getFrameWidth(f, 0) * fi->bytesPerSample;
797         if (fi->numPlanes == 1) {
798             vs_bitblt(lpBuffer, (row_size + 3) & ~3, vsapi->getReadPtr(f, 0), stride, row_size, height);
799         } else if (fi->numPlanes == 3) {
800             int row_size23 = vsapi->getFrameWidth(f, 1) * fi->bytesPerSample;
801 
802             int plane2 = (fi->id != pfYUV411P8 ? 2 : 1);
803             int plane3 = (fi->id != pfYUV411P8 ? 1 : 2);
804 
805 
806             vs_bitblt(lpBuffer, row_size, vsapi->getReadPtr(f, 0), stride, row_size, height);
807 
808             vs_bitblt((uint8_t *)lpBuffer + (row_size*height),
809                 row_size23, vsapi->getReadPtr(f, plane2),
810                 vsapi->getStride(f, plane2), vsapi->getFrameWidth(f, plane2),
811                 vsapi->getFrameHeight(f, plane2));
812 
813             vs_bitblt((uint8_t *)lpBuffer + (row_size*height + vsapi->getFrameHeight(f, plane2)*row_size23),
814                 row_size23, vsapi->getReadPtr(f, plane3),
815                 vsapi->getStride(f, plane3), vsapi->getFrameWidth(f, plane3),
816                 vsapi->getFrameHeight(f, plane3));
817         }
818     }
819 
820     vsapi->freeFrame(f);
821     vsscript_freeScript(errSe);
822 
823     if (!errSe) {
824         for (int i = n + 1; i < std::min<int>(n + parent->num_threads, parent->vi->numFrames); i++) {
825             ++parent->pending_requests;
826             vsapi->getFrameAsync(i, parent->node, VapourSynthFile::frameDoneCallback, static_cast<void *>(parent));
827         }
828     }
829 
830     return !errSe;
831 }
832 
833 ////////////////////////////////////////////////////////////////////////
834 //////////// IAVIStream
835 
Read(LONG lStart,LONG lSamples,LPVOID lpBuffer,LONG cbBuffer,LONG * plBytes,LONG * plSamples)836 STDMETHODIMP VapourSynthStream::Read(LONG lStart, LONG lSamples, LPVOID lpBuffer, LONG cbBuffer, LONG *plBytes, LONG *plSamples) {
837     parent->Lock();
838     HRESULT result = Read2(lStart, lSamples, lpBuffer, cbBuffer, plBytes, plSamples);
839     parent->Unlock();
840     return result;
841 }
842 
Read2(LONG lStart,LONG lSamples,LPVOID lpBuffer,LONG cbBuffer,LONG * plBytes,LONG * plSamples)843 HRESULT VapourSynthStream::Read2(LONG lStart, LONG lSamples, LPVOID lpBuffer, LONG cbBuffer, LONG *plBytes, LONG *plSamples) {
844     if (lStart >= parent->vi->numFrames) {
845         if (plSamples)
846             *plSamples = 0;
847         if (plBytes)
848             *plBytes = 0;
849         return S_OK;
850     }
851 
852     int image_size = BMPSize(parent->vi, (parent->vi->format->id == pfYUV422P10 && parent->enable_v210));
853     if (plSamples)
854         *plSamples = 1;
855     if (plBytes)
856         *plBytes = image_size;
857 
858     if (!lpBuffer) {
859         return S_OK;
860     } else if (cbBuffer < image_size) {
861         return AVIERR_BUFFERTOOSMALL;
862     }
863 
864     if (!ReadFrame(lpBuffer, lStart))
865         return E_FAIL;
866     return S_OK;
867 }
868 
ReadFormat(LONG lPos,LPVOID lpFormat,LONG * lpcbFormat)869 STDMETHODIMP VapourSynthStream::ReadFormat(LONG lPos, LPVOID lpFormat, LONG *lpcbFormat) {
870     if (!lpcbFormat)
871         return E_POINTER;
872 
873     if (!lpFormat) {
874         *lpcbFormat = sizeof(BITMAPINFOHEADER);
875         return S_OK;
876     }
877 
878     memset(lpFormat, 0, *lpcbFormat);
879 
880     const VSVideoInfo* const vi = parent->vi;
881 
882     BITMAPINFOHEADER bi = {};
883     bi.biSize = sizeof(bi);
884     bi.biWidth = vi->width;
885     bi.biHeight = vi->height;
886     bi.biPlanes = 1;
887     bi.biBitCount = BitsPerPixel(vi, (vi->format->id == pfYUV422P10 && parent->enable_v210));
888     if (!GetBiCompression(vi->format->id, (vi->format->id == pfYUV422P10 && parent->enable_v210), bi.biCompression))
889         return E_FAIL;
890 
891     bi.biSizeImage = BMPSize(vi, (vi->format->id == pfYUV422P10 && parent->enable_v210));
892     *lpcbFormat = std::min<LONG>(*lpcbFormat, sizeof(bi));
893     memcpy(lpFormat, &bi, static_cast<size_t>(*lpcbFormat));
894 
895     return S_OK;
896 }
897 
Write(LONG lStart,LONG lSamples,LPVOID lpBuffer,LONG cbBuffer,DWORD dwFlags,LONG FAR * plSampWritten,LONG FAR * plBytesWritten)898 STDMETHODIMP VapourSynthStream::Write(LONG lStart, LONG lSamples, LPVOID lpBuffer,
899     LONG cbBuffer, DWORD dwFlags, LONG FAR *plSampWritten,
900     LONG FAR *plBytesWritten) {
901     return AVIERR_READONLY;
902 }
903 
904