Fix things on case-insensitive file systems.
[vapoursynth-svn.git] / src / vfw / vsvfw.cpp
blob093ee20bdebaea265bd80403b14b5eb69a680460
1 /*
2 * Copyright (c) 2012 Fredrik Mellbin
4 * This file is part of VapourSynth.
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.
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.
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
21 // loosely based on the relevant parts of main.cpp in avisynth
23 #define INITGUID
24 #define WIN32_LEAN_AND_MEAN
25 #include <objbase.h>
26 #include <vfw.h>
27 #include <windows.h>
28 #include <cstdio>
29 #include <cassert>
30 #include <string>
31 #include <algorithm>
32 #include <fstream>
33 #include <string>
34 #include <cerrno>
36 #include "VSScript.h"
37 #include "VSHelper.h"
39 static long refCount=0;
41 static int BMPSize(int height, int rowsize) {
42 return height * ((rowsize+3) & ~3);
45 // {58F74CA0-BD0E-4664-A49B-8D10E6F0C131}
46 extern "C" const GUID CLSID_VapourSynth =
47 { 0x58f74ca0, 0xbd0e, 0x4664, { 0xa4, 0x9b, 0x8d, 0x10, 0xe6, 0xf0, 0xc1, 0x31 } };
49 extern "C" const GUID IID_IAvisynthClipInfo // {E6D6B708-124D-11D4-86F3-DB80AFD98778}
50 = {0xe6d6b708, 0x124d, 0x11d4, {0x86, 0xf3, 0xdb, 0x80, 0xaf, 0xd9, 0x87, 0x78}};
52 struct IAvisynthClipInfo : IUnknown {
53 virtual int __stdcall GetError(const char** ppszMessage) = 0;
54 virtual bool __stdcall GetParity(int n) = 0;
55 virtual bool __stdcall IsFieldBased() = 0;
58 class VapourSynthFile: public IAVIFile, public IPersistFile, public IClassFactory, public IAvisynthClipInfo {
59 friend class VapourSynthStream;
60 private:
61 int num_threads;
62 const VSAPI *vsapi;
63 VSScript *se;
64 bool enable_v210;
65 bool pad_scanlines;
66 VSNodeRef *node;
67 long m_refs;
68 std::string szScriptName;
69 const VSVideoInfo* vi;
70 std::string error_msg;
71 volatile long pending_requests;
73 CRITICAL_SECTION cs_filter_graph;
75 bool DelayInit();
76 bool DelayInit2();
78 void Lock();
79 void Unlock();
81 int ImageSize();
83 public:
85 VapourSynthFile(const CLSID& rclsid);
86 ~VapourSynthFile();
88 static HRESULT Create(const CLSID& rclsid, const IID& riid, void **ppv);
89 static void VS_CC frameDoneCallback(void *userData, const VSFrameRef *f, int n, VSNodeRef *, const char *errorMsg);
91 //////////// IUnknown
93 STDMETHODIMP QueryInterface(const IID& iid, void **ppv);
94 STDMETHODIMP_(ULONG) AddRef();
95 STDMETHODIMP_(ULONG) Release();
97 //////////// IClassFactory
99 STDMETHODIMP CreateInstance (LPUNKNOWN pUnkOuter, REFIID riid, void * * ppvObj) ;
100 STDMETHODIMP LockServer (BOOL fLock) ;
102 //////////// IPersistFile
104 STDMETHODIMP GetClassID(LPCLSID lpClassID); // IPersist
106 STDMETHODIMP IsDirty();
107 STDMETHODIMP Load(LPCOLESTR lpszFileName, DWORD grfMode);
108 STDMETHODIMP Save(LPCOLESTR lpszFileName, BOOL fRemember);
109 STDMETHODIMP SaveCompleted(LPCOLESTR lpszFileName);
110 STDMETHODIMP GetCurFile(LPOLESTR *lplpszFileName);
112 //////////// IAVIFile
114 STDMETHODIMP CreateStream(PAVISTREAM *ppStream, AVISTREAMINFOW *psi); // 5
115 STDMETHODIMP EndRecord(); // 8
116 STDMETHODIMP GetStream(PAVISTREAM *ppStream, DWORD fccType, LONG lParam); // 4
117 STDMETHODIMP Info(AVIFILEINFOW *psi, LONG lSize); // 3
119 STDMETHODIMP Open(LPCSTR szFile, UINT mode, LPCOLESTR lpszFileName); // ???
120 STDMETHODIMP Save(LPCSTR szFile, AVICOMPRESSOPTIONS FAR *lpOptions, // ???
121 AVISAVECALLBACK lpfnCallback);
123 STDMETHODIMP ReadData(DWORD fcc, LPVOID lp, LONG *lpcb); // 7
124 STDMETHODIMP WriteData(DWORD fcc, LPVOID lpBuffer, LONG cbBuffer); // 6
125 STDMETHODIMP DeleteStream(DWORD fccType, LONG lParam); // 9
127 //////////// IAvisynthClipInfo
129 int __stdcall GetError(const char** ppszMessage);
130 bool __stdcall GetParity(int n);
131 bool __stdcall IsFieldBased();
134 ///////////////////////////////////
136 class VapourSynthStream: public IAVIStream , public IAVIStreaming {
137 public:
139 //////////// IUnknown
141 STDMETHODIMP QueryInterface(const IID& iid, void **ppv);
142 STDMETHODIMP_(ULONG) AddRef();
143 STDMETHODIMP_(ULONG) Release();
145 VapourSynthStream(VapourSynthFile *parentPtr, bool isAudio);
146 ~VapourSynthStream();
148 //////////// IAVIStream
150 STDMETHODIMP Create(LONG lParam1, LONG lParam2);
151 STDMETHODIMP Delete(LONG lStart, LONG lSamples);
152 STDMETHODIMP_(LONG) Info(AVISTREAMINFOW *psi, LONG lSize);
153 STDMETHODIMP_(LONG) FindSample(LONG lPos, LONG lFlags);
154 STDMETHODIMP Read(LONG lStart, LONG lSamples, LPVOID lpBuffer, LONG cbBuffer, LONG *plBytes, LONG *plSamples);
155 STDMETHODIMP ReadData(DWORD fcc, LPVOID lp, LONG *lpcb);
156 STDMETHODIMP ReadFormat(LONG lPos, LPVOID lpFormat, LONG *lpcbFormat);
157 STDMETHODIMP SetFormat(LONG lPos, LPVOID lpFormat, LONG cbFormat);
158 STDMETHODIMP Write(LONG lStart, LONG lSamples, LPVOID lpBuffer,
159 LONG cbBuffer, DWORD dwFlags, LONG FAR *plSampWritten,
160 LONG FAR *plBytesWritten);
161 STDMETHODIMP WriteData(DWORD fcc, LPVOID lpBuffer, LONG cbBuffer);
162 STDMETHODIMP SetInfo(AVISTREAMINFOW *psi, LONG lSize);
164 //////////// IAVIStreaming
166 STDMETHODIMP Begin(LONG lStart, LONG lEnd, LONG lRate);
167 STDMETHODIMP End();
169 private:
170 long m_refs;
172 VapourSynthFile *parent;
173 std::string sName;
175 //////////// internal
177 bool ReadFrame(void* lpBuffer, int n);
179 HRESULT Read2(LONG lStart, LONG lSamples, LPVOID lpBuffer, LONG cbBuffer, LONG *plBytes, LONG *plSamples);
183 BOOL APIENTRY DllMain(HANDLE hModule, ULONG ulReason, LPVOID lpReserved) {
184 if (ulReason == DLL_PROCESS_ATTACH) {
185 // fixme, move this where threading can't be an issue
186 vseval_init();
187 } else if (ulReason == DLL_PROCESS_DETACH) {
188 vseval_finalize();
190 return TRUE;
193 // From the Microsoft AVIFile docs. Dense code...
195 extern "C" STDAPI DllGetClassObject(const CLSID& rclsid, const IID& riid, void **ppv);
197 STDAPI DllGetClassObject(const CLSID& rclsid, const IID& riid, void **ppv) {
199 if (rclsid != CLSID_VapourSynth)
200 return CLASS_E_CLASSNOTAVAILABLE;
201 HRESULT hresult = VapourSynthFile::Create(rclsid, riid, ppv);
202 return hresult;
205 extern "C" STDAPI DllCanUnloadNow();
207 STDAPI DllCanUnloadNow() {
208 return refCount ? S_FALSE : S_OK;
212 ///////////////////////////////////////////////////////////////////////////
214 // VapourSynthFile
216 ///////////////////////////////////////////////////////////////////////////
217 //////////// IClassFactory
219 STDMETHODIMP VapourSynthFile::CreateInstance (LPUNKNOWN pUnkOuter, REFIID riid, void * * ppvObj) {
220 if (pUnkOuter)
221 return CLASS_E_NOAGGREGATION;
222 HRESULT hresult = Create(CLSID_VapourSynth, riid, ppvObj);
223 return hresult;
226 STDMETHODIMP VapourSynthFile::LockServer (BOOL fLock) {
227 return S_OK;
230 ///////////////////////////////////////////////////
231 //////////// IPersistFile
233 STDMETHODIMP VapourSynthFile::GetClassID(LPCLSID lpClassID) { // IPersist
234 if (!lpClassID)
235 return E_POINTER;
236 *lpClassID = CLSID_VapourSynth;
237 return S_OK;
240 STDMETHODIMP VapourSynthFile::IsDirty() {
241 return S_FALSE;
244 STDMETHODIMP VapourSynthFile::Load(LPCOLESTR lpszFileName, DWORD grfMode) {
245 char filename[MAX_PATH*2];
246 WideCharToMultiByte(CP_UTF8, 0, lpszFileName, -1, filename, sizeof(filename), NULL, NULL);
247 return Open(filename, grfMode, lpszFileName);
250 STDMETHODIMP VapourSynthFile::Save(LPCOLESTR lpszFileName, BOOL fRemember) {
251 return E_FAIL;
254 STDMETHODIMP VapourSynthFile::SaveCompleted(LPCOLESTR lpszFileName) {
255 return S_OK;
258 STDMETHODIMP VapourSynthFile::GetCurFile(LPOLESTR *lplpszFileName) {
259 if (lplpszFileName)
260 *lplpszFileName = NULL;
261 return E_FAIL;
264 ///////////////////////////////////////////////////
265 /////// static local
267 HRESULT VapourSynthFile::Create(const CLSID& rclsid, const IID& riid, void **ppv) {
268 HRESULT hresult;
269 VapourSynthFile* pAVIFileSynth = new VapourSynthFile(rclsid);
270 if (!pAVIFileSynth)
271 return E_OUTOFMEMORY;
272 hresult = pAVIFileSynth->QueryInterface(riid, ppv);
273 pAVIFileSynth->Release();
274 return hresult;
277 ///////////////////////////////////////////////////
278 //////////// IUnknown
280 STDMETHODIMP VapourSynthFile::QueryInterface(const IID& iid, void **ppv) {
281 if (!ppv)
282 return E_POINTER;
284 if (iid == IID_IUnknown) {
285 *ppv = (IUnknown *)(IAVIFile *)this;
286 } else if (iid == IID_IClassFactory) {
287 *ppv = (IClassFactory *)this;
288 } else if (iid == IID_IPersist) {
289 *ppv = (IPersist *)this;
290 } else if (iid == IID_IPersistFile) {
291 *ppv = (IPersistFile *)this;
292 } else if (iid == IID_IAVIFile) {
293 *ppv = (IAVIFile *)this;
294 } else if (iid == IID_IAvisynthClipInfo) {
295 *ppv = (IAvisynthClipInfo *)this;
296 } else {
297 *ppv = NULL;
298 return E_NOINTERFACE;
301 AddRef();
303 return S_OK;
306 STDMETHODIMP_(ULONG) VapourSynthFile::AddRef() {
307 const int refs = InterlockedIncrement(&m_refs);
308 InterlockedIncrement(&refCount);
309 return refs;
312 STDMETHODIMP_(ULONG) VapourSynthFile::Release() {
313 const int refs = InterlockedDecrement(&m_refs);
314 InterlockedDecrement(&refCount);
315 if (!refs)
316 delete this;
317 return refs;
320 ////////////////////////////////////////////////////////////////////////
322 // VapourSynthStream
324 ////////////////////////////////////////////////////////////////////////
325 //////////// IUnknown
328 STDMETHODIMP VapourSynthStream::QueryInterface(const IID& iid, void **ppv) {
329 if (!ppv)
330 return E_POINTER;
332 if (iid == IID_IUnknown) {
333 *ppv = (IUnknown *)(IAVIStream *)this;
334 } else if (iid == IID_IAVIStream) {
335 *ppv = (IAVIStream *)this;
336 } else if (iid == IID_IAVIStreaming) {
337 *ppv = (IAVIStreaming *)this;
338 } else {
339 *ppv = NULL;
340 return E_NOINTERFACE;
343 AddRef();
345 return S_OK;
348 STDMETHODIMP_(ULONG) VapourSynthStream::AddRef() {
349 const int refs = InterlockedIncrement(&m_refs);
350 InterlockedIncrement(&refCount);
351 return refs;
354 STDMETHODIMP_(ULONG) VapourSynthStream::Release() {
355 const int refs = InterlockedDecrement(&m_refs);
356 InterlockedDecrement(&refCount);
357 if (!refs) delete this;
358 return refs;
361 ////////////////////////////////////////////////////////////////////////
363 // VapourSynthFile
365 ////////////////////////////////////////////////////////////////////////
366 //////////// IAVIFile
368 STDMETHODIMP VapourSynthFile::CreateStream(PAVISTREAM *ppStream, AVISTREAMINFOW *psi) {
369 *ppStream = NULL;
370 return S_OK;
373 STDMETHODIMP VapourSynthFile::EndRecord() {
374 return AVIERR_READONLY;
377 STDMETHODIMP VapourSynthFile::Save(LPCSTR szFile, AVICOMPRESSOPTIONS FAR *lpOptions,
378 AVISAVECALLBACK lpfnCallback) {
379 return AVIERR_READONLY;
382 STDMETHODIMP VapourSynthFile::ReadData(DWORD fcc, LPVOID lp, LONG *lpcb) {
383 return AVIERR_NODATA;
386 STDMETHODIMP VapourSynthFile::WriteData(DWORD fcc, LPVOID lpBuffer, LONG cbBuffer) {
387 return AVIERR_READONLY;
390 STDMETHODIMP VapourSynthFile::DeleteStream(DWORD fccType, LONG lParam) {
391 return AVIERR_READONLY;
395 ///////////////////////////////////////////////////
396 /////// local
398 VapourSynthFile::VapourSynthFile(const CLSID& rclsid) : num_threads(1), node(NULL), se(NULL), vsapi(NULL), enable_v210(false), pad_scanlines(false), m_refs(0), vi(NULL), pending_requests(0) {
399 vsapi = vseval_getVSApi();
400 AddRef();
401 InitializeCriticalSection(&cs_filter_graph);
404 VapourSynthFile::~VapourSynthFile() {
405 Lock();
406 if (vi) {
407 vi = NULL;
408 while (pending_requests > 0) {};
409 vseval_freeScript(se);
411 Unlock();
412 DeleteCriticalSection(&cs_filter_graph);
415 int VapourSynthFile::ImageSize() {
416 if (!vi)
417 return 0;
418 int image_size;
420 if (vi->format->id == pfYUV422P10 && enable_v210) {
421 image_size = ((16*((vi->width + 5) / 6) + 127) & ~127);
422 image_size *= vi->height;
423 } else if (vi->format->numPlanes == 1 || pad_scanlines) {
424 image_size = BMPSize(vi->height, vi->width * vi->format->bytesPerSample);
425 if (vi->format->numPlanes == 3)
426 image_size += 2 * BMPSize(vi->height >> vi->format->subSamplingH, (vi->width >> vi->format->subSamplingW) * vi->format->bytesPerSample);
427 } else { // Packed size
428 image_size = (vi->width * vi->format->bytesPerSample) >> vi->format->subSamplingW;
429 if (image_size) {
430 image_size *= vi->height;
431 image_size >>= vi->format->subSamplingH;
432 image_size *= 2;
434 image_size += vi->width * vi->format->bytesPerSample * vi->height;
436 return image_size;
439 STDMETHODIMP VapourSynthFile::Open(LPCSTR szFile, UINT mode, LPCOLESTR lpszFileName) {
440 if (mode & (OF_CREATE|OF_WRITE))
441 return E_FAIL;
442 szScriptName = szFile;
443 return S_OK;
446 bool VapourSynthFile::DelayInit() {
447 Lock();
448 bool result = DelayInit2();
449 Unlock();
450 return result;
453 const char *ErrorScript = "\
454 import vapoursynth as vs\n\
455 import sys\n\
456 core = vs.get_core()\n\
457 red = core.std.BlankClip(width=240, height=480, format=vs.RGB24, color=[255, 0, 0])\n\
458 green = core.std.BlankClip(width=240, height=480, format=vs.RGB24, color=[0, 255, 0])\n\
459 blue = core.std.BlankClip(width=240, height=480, format=vs.RGB24, color=[0, 0, 255])\n\
460 stacked = core.std.StackHorizontal([red, green, blue])\n\
461 last = core.resize.Bicubic(stacked, format=vs.COMPATBGR32)\n\
462 last.set_output()\n";
464 std::string get_file_contents(const char *filename)
466 std::ifstream in(filename, std::ios::in | std::ios::binary);
467 if (in)
469 std::string contents;
470 in.seekg(0, std::ios::end);
471 contents.resize(in.tellg());
472 in.seekg(0, std::ios::beg);
473 in.read(&contents[0], contents.size());
474 in.close();
475 return(contents);
477 return "";;
480 bool VapourSynthFile::DelayInit2() {
481 if (!szScriptName.empty() && !vi) {
483 std::string script = get_file_contents(szScriptName.c_str());
484 if (script.empty())
485 goto vpyerror;
487 if (!vseval_evaluateScript(&se, script.c_str(), szScriptName.c_str())) {
489 node = vseval_getOutput(se, 0);
490 if (!node)
491 goto vpyerror;
492 vi = vsapi->getVideoInfo(node);
493 error_msg.clear();
495 if (vi->width == 0 || vi->height == 0 || vi->format == NULL || vi->numFrames == 0) {
496 error_msg = "Cannot open clips with varying dimensions or format in vfw";
497 goto vpyerror;
500 int id = vi->format->id;
501 if (id != pfCompatBGR32
502 && id != pfCompatYUY2
503 && id != pfYUV420P8
504 && id != pfGray8
505 && id != pfYUV444P8
506 && id != pfYUV422P8
507 && id != pfYUV411P8
508 && id != pfYUV410P8
509 && id != pfYUV420P10
510 && id != pfYUV420P16
511 && id != pfYUV422P10
512 && id != pfYUV422P16) {
513 error_msg = "VFW module doesn't support ";
514 error_msg += vi->format->name;
515 error_msg += " output";
516 goto vpyerror;
519 // set the special options hidden in global variables
520 int error;
521 int64_t val;
522 VSMap *options = vsapi->createMap();
523 vseval_getVariable(se, "enable_v210", options);
524 val = vsapi->propGetInt(options, "enable_v210", 0, &error);
525 if (!error)
526 enable_v210 = !!val;
527 else
528 enable_v210 = false;
529 vseval_getVariable(se, "pad_scanlines", options);
530 val = vsapi->propGetInt(options, "pad_scanlines", 0, &error);
531 if (!error)
532 pad_scanlines = !!val;
533 else
534 pad_scanlines = false;
535 vsapi->freeMap(options);
537 const VSCoreInfo *info = vsapi->getCoreInfo(vseval_getCore(se));
538 num_threads = info->numThreads;
540 return true;
541 } else {
542 error_msg = vseval_getError(se);
543 vpyerror:
544 vi = NULL;
545 vseval_freeScript(se);
546 se = NULL;
547 int res = vseval_evaluateScript(&se, ErrorScript, "vfw_error.bleh");
548 const char *et = vseval_getError(se);
549 node = vseval_getOutput(se, 0);
550 vi = vsapi->getVideoInfo(node);
551 return true;
553 } else {
554 return !!vi;
558 void VapourSynthFile::Lock() {
559 EnterCriticalSection(&cs_filter_graph);
562 void VapourSynthFile::Unlock() {
563 LeaveCriticalSection(&cs_filter_graph);
566 ///////////////////////////////////////////////////
567 //////////// IAVIFile
569 STDMETHODIMP VapourSynthFile::Info(AVIFILEINFOW *pfi, LONG lSize) {
570 if (!pfi)
571 return E_POINTER;
573 if (!DelayInit())
574 return E_FAIL;
576 AVIFILEINFOW afi;
577 memset(&afi, 0, sizeof(afi));
579 afi.dwMaxBytesPerSec = 0;
580 afi.dwFlags = AVIFILEINFO_HASINDEX | AVIFILEINFO_ISINTERLEAVED;
581 afi.dwCaps = AVIFILECAPS_CANREAD | AVIFILECAPS_ALLKEYFRAMES | AVIFILECAPS_NOCOMPRESSION;
583 afi.dwStreams = 1;
584 afi.dwSuggestedBufferSize = 0;
585 afi.dwWidth = vi->width;
586 afi.dwHeight = vi->height;
587 afi.dwEditCount = 0;
589 afi.dwRate = int64ToIntS(vi->fpsNum ? vi->fpsNum : 1);
590 afi.dwScale = int64ToIntS(vi->fpsDen ? vi->fpsDen : 30);
591 afi.dwLength = vi->numFrames;
593 wcscpy(afi.szFileType, L"VapourSynth");
595 // Maybe should return AVIERR_BUFFERTOOSMALL for lSize < sizeof(afi)
596 memset(pfi, 0, lSize);
597 memcpy(pfi, &afi, min(size_t(lSize), sizeof(afi)));
598 return S_OK;
601 static inline char BePrintable(int ch) {
602 ch &= 0xff;
603 return isprint(ch) ? ch : '.';
607 STDMETHODIMP VapourSynthFile::GetStream(PAVISTREAM *ppStream, DWORD fccType, LONG lParam) {
608 VapourSynthStream *casr;
609 char fcc[5];
611 fcc[0] = BePrintable(fccType );
612 fcc[1] = BePrintable(fccType >> 8);
613 fcc[2] = BePrintable(fccType >> 16);
614 fcc[3] = BePrintable(fccType >> 24);
615 fcc[4] = 0;
617 if (!DelayInit())
618 return E_FAIL;
620 *ppStream = NULL;
622 if (!fccType) {
623 if (lParam==0)
624 fccType = streamtypeVIDEO;
627 if (lParam > 0)
628 return AVIERR_NODATA;
630 if (fccType == streamtypeVIDEO) {
631 if ((casr = new VapourSynthStream(this, false)) == 0)
632 return AVIERR_MEMORY;
634 *ppStream = (IAVIStream *)casr;
636 } else if (fccType == streamtypeAUDIO) {
637 return AVIERR_NODATA;
639 if ((casr = new VapourSynthStream(this, true)) == 0)
640 return AVIERR_MEMORY;
641 *ppStream = (IAVIStream *)casr;
642 } else
643 return AVIERR_NODATA;
645 return S_OK;
649 ////////////////////////////////////////////////////////////////////////
650 //////////// IAvisynthClipInfo
652 int __stdcall VapourSynthFile::GetError(const char** ppszMessage) {
653 if (!DelayInit() && error_msg.empty())
654 error_msg = "VapourSynth: script open failed!";
656 if (ppszMessage)
657 *ppszMessage = error_msg.c_str();
658 return !error_msg.empty();
661 bool __stdcall VapourSynthFile::GetParity(int n) {
662 if (!DelayInit())
663 return false;
664 return false;
667 bool __stdcall VapourSynthFile::IsFieldBased() {
668 if (!DelayInit())
669 return false;
670 return false;
673 ////////////////////////////////////////////////////////////////////////
675 // VapourSynthStream
677 ////////////////////////////////////////////////////////////////////////
678 //////////// IAVIStreaming
680 STDMETHODIMP VapourSynthStream::Begin(LONG lStart, LONG lEnd, LONG lRate) {
681 return S_OK;
684 STDMETHODIMP VapourSynthStream::End() {
685 return S_OK;
688 //////////// IAVIStream
690 STDMETHODIMP VapourSynthStream::Create(LONG lParam1, LONG lParam2) {
691 return AVIERR_READONLY;
694 STDMETHODIMP VapourSynthStream::Delete(LONG lStart, LONG lSamples) {
695 return AVIERR_READONLY;
698 STDMETHODIMP VapourSynthStream::ReadData(DWORD fcc, LPVOID lp, LONG *lpcb) {
699 return AVIERR_NODATA;
702 STDMETHODIMP VapourSynthStream::SetFormat(LONG lPos, LPVOID lpFormat, LONG cbFormat) {
703 return AVIERR_READONLY;
706 STDMETHODIMP VapourSynthStream::WriteData(DWORD fcc, LPVOID lpBuffer, LONG cbBuffer) {
707 return AVIERR_READONLY;
710 STDMETHODIMP VapourSynthStream::SetInfo(AVISTREAMINFOW *psi, LONG lSize) {
711 return AVIERR_READONLY;
714 ////////////////////////////////////////////////////////////////////////
715 //////////// local
717 VapourSynthStream::VapourSynthStream(VapourSynthFile *parentPtr, bool isAudio) {
718 sName = "video";
719 m_refs = 0;
720 AddRef();
721 parent = parentPtr;
722 parent->AddRef();
725 VapourSynthStream::~VapourSynthStream() {
726 if (parent)
727 parent->Release();
730 ////////////////////////////////////////////////////////////////////////
731 //////////// IAVIStream
733 STDMETHODIMP_(LONG) VapourSynthStream::Info(AVISTREAMINFOW *psi, LONG lSize) {
734 if (!psi)
735 return E_POINTER;
737 AVISTREAMINFOW asi;
739 const VSVideoInfo* const vi = parent->vi;
741 memset(&asi, 0, sizeof(asi));
742 asi.fccType = streamtypeVIDEO;
743 asi.dwQuality = DWORD(-1);
745 const int image_size = parent->ImageSize();
746 asi.fccHandler = 'UNKN';
747 if (vi->format->id == pfCompatBGR32)
748 asi.fccHandler = ' BID';
749 else if (vi->format->id == pfCompatYUY2)
750 asi.fccHandler = '2YUY';
751 else if (vi->format->id == pfYUV420P8)
752 asi.fccHandler = '21VY';
753 else if (vi->format->id == pfGray8)
754 asi.fccHandler = '008Y';
755 else if (vi->format->id == pfYUV444P8)
756 asi.fccHandler = '42VY';
757 else if (vi->format->id == pfYUV422P8)
758 asi.fccHandler = '61VY';
759 else if (vi->format->id == pfYUV411P8)
760 asi.fccHandler = 'B14Y';
761 else if (vi->format->id == pfYUV410P8)
762 asi.fccHandler = '9UVY';
763 else if (vi->format->id == pfYUV420P10)
764 asi.fccHandler = '010P';
765 else if (vi->format->id == pfYUV420P16)
766 asi.fccHandler = '610P';
767 else if (vi->format->id == pfYUV422P10 && parent->enable_v210)
768 asi.fccHandler = '012v';
769 else if (vi->format->id == pfYUV422P10)
770 asi.fccHandler = '012P';
771 else if (vi->format->id == pfYUV422P16)
772 asi.fccHandler = '612P';
773 else
774 return E_FAIL;
776 asi.dwScale = int64ToIntS(vi->fpsDen ? vi->fpsDen : 1);
777 asi.dwRate = int64ToIntS(vi->fpsNum ? vi->fpsNum : 30);
778 asi.dwLength = vi->numFrames;
779 asi.rcFrame.right = vi->width;
780 asi.rcFrame.bottom = vi->height;
781 asi.dwSampleSize = image_size;
782 asi.dwSuggestedBufferSize = image_size;
783 wcscpy(asi.szName, L"VapourSynth video #1");
785 // Maybe should return AVIERR_BUFFERTOOSMALL for lSize < sizeof(asi)
786 memset(psi, 0, lSize);
787 memcpy(psi, &asi, min(size_t(lSize), sizeof(asi)));
788 return S_OK;
791 STDMETHODIMP_(LONG) VapourSynthStream::FindSample(LONG lPos, LONG lFlags) {
792 if (lFlags & FIND_FORMAT)
793 return -1;
795 if (lFlags & FIND_FROM_START)
796 return 0;
798 return lPos;
802 ////////////////////////////////////////////////////////////////////////
803 //////////// local
805 void VS_CC VapourSynthFile::frameDoneCallback(void *userData, const VSFrameRef *f, int n, VSNodeRef *, const char *errorMsg) {
806 VapourSynthFile *vsfile = (VapourSynthFile *)userData;
807 vsfile->vsapi->freeFrame(f);
808 InterlockedDecrement(&vsfile->pending_requests);
811 bool VapourSynthStream::ReadFrame(void* lpBuffer, int n) {
812 const VSAPI *vsapi = parent->vsapi;
813 const VSFrameRef *f = vsapi->getFrame(n, parent->node, 0, 0);
814 if (!f)
815 return false;
817 const VSFormat *fi = vsapi->getFrameFormat(f);
818 const int pitch = vsapi->getStride(f, 0);
819 const int row_size = vsapi->getFrameWidth(f, 0) * fi->bytesPerSample;
820 const int height = vsapi->getFrameHeight(f, 0);
822 int out_pitch;
823 int out_pitchUV;
825 bool semi_packed_p10 = (fi->id == pfYUV420P10) || (fi->id == pfYUV422P10);
826 bool semi_packed_p16 = (fi->id == pfYUV420P16) || (fi->id == pfYUV422P16);
828 // BMP scanlines are dword-aligned
829 if (fi->numPlanes == 1) {
830 out_pitch = (row_size+3) & ~3;
831 out_pitchUV = (vsapi->getFrameWidth(f, 1) * fi->bytesPerSample+3) & ~3;
833 // Planar scanlines are packed
834 else {
835 out_pitch = row_size;
836 out_pitchUV = vsapi->getFrameWidth(f, 1) * fi->bytesPerSample;
839 if (fi->id == pfYUV422P10 && parent->enable_v210) {
840 int width = vsapi->getFrameWidth(f, 0);
841 int pstride_y = vsapi->getStride(f, 0)/2;
842 int pstride_uv = vsapi->getStride(f, 1)/2;
843 const uint16_t *yptr = (const uint16_t *)vsapi->getReadPtr(f, 0);
844 const uint16_t *uptr = (const uint16_t *)vsapi->getReadPtr(f, 1);
845 const uint16_t *vptr = (const uint16_t *)vsapi->getReadPtr(f, 2);
846 uint32_t *outbuf = (uint32_t *)lpBuffer;
847 out_pitch = ((16*((width + 5) / 6) + 127) & ~127)/4;
848 for (int y = 0; y < height; y++) {
849 const uint16_t *yline = yptr;
850 const uint16_t *uline = uptr;
851 const uint16_t *vline = vptr;
852 uint32_t *out_line = outbuf;
853 for (int x = 0; x < width + 5; x += 6) {
854 out_line[0] = (uline[0] | (yline[0] << 10) | (vline[0] << 20));
855 out_line[1] = (yline[1] | (uline[1] << 10) | (yline[2] << 20));
856 out_line[2] = (vline[1] | (yline[3] << 10) | (uline[2] << 20));
857 out_line[3] = (yline[4] | (vline[2] << 10) | (yline[5] << 20));
858 out_line += 4;
859 yline += 6;
860 uline += 3;
861 vline += 3;
863 outbuf += out_pitch;
864 yptr += pstride_y;
865 uptr += pstride_uv;
866 vptr += pstride_uv;
868 } else if (semi_packed_p10) {
869 int pwidth = vsapi->getFrameWidth(f, 0);
870 int pstride = vsapi->getStride(f, 0) / 2;
871 uint16_t *outbuf = (uint16_t *)lpBuffer;
872 const uint16_t *yptr = (const uint16_t *)vsapi->getReadPtr(f, 0);
874 for (int y = 0; y < height; y++) {
875 for (int x = 0; x < pwidth; x++) {
876 outbuf[x] = yptr[x] << 6;
878 outbuf += out_pitch/2;
879 yptr += pstride;
881 } else {
882 vs_bitblt(lpBuffer, out_pitch, vsapi->getReadPtr(f, 0), pitch, row_size, height);
885 if (fi->id == pfYUV422P10 && parent->enable_v210) {
886 // intentionally empty
887 } else if (semi_packed_p10 || semi_packed_p16) {
888 int pheight = vsapi->getFrameHeight(f, 1);
889 int pwidth = vsapi->getFrameWidth(f, 1);
890 int pstride = vsapi->getStride(f, 1) / 2;
891 BYTE *outadj = (BYTE*)lpBuffer + out_pitch*height;
892 uint16_t *outbuf = (uint16_t *)outadj;
893 const uint16_t *uptr = (const uint16_t *)vsapi->getReadPtr(f, 1);
894 const uint16_t *vptr = (const uint16_t *)vsapi->getReadPtr(f, 2);
896 if (semi_packed_p16) {
897 for (int y = 0; y < pheight; y++) {
898 for (int x = 0; x < pwidth; x++) {
899 outbuf[2*x] = uptr[x];
900 outbuf[2*x + 1] = vptr[x];
902 outbuf += out_pitchUV;
903 uptr += pstride;
904 vptr += pstride;
906 } else {
907 for (int y = 0; y < pheight; y++) {
908 for (int x = 0; x < pwidth; x++) {
909 outbuf[2*x] = uptr[x] << 6;
910 outbuf[2*x + 1] = vptr[x] << 6;
912 outbuf += out_pitchUV;
913 uptr += pstride;
914 vptr += pstride;
917 } else if (fi->numPlanes == 3) {
918 vs_bitblt((BYTE *)lpBuffer + (out_pitch*height),
919 out_pitchUV, vsapi->getReadPtr(f, 2),
920 vsapi->getStride(f, 2), vsapi->getFrameWidth(f, 2),
921 vsapi->getFrameHeight(f, 2) );
923 vs_bitblt((BYTE *)lpBuffer + (out_pitch*height + vsapi->getFrameHeight(f, 1)*out_pitchUV),
924 out_pitchUV, vsapi->getReadPtr(f, 1),
925 vsapi->getStride(f, 1), vsapi->getFrameWidth(f, 1),
926 vsapi->getFrameHeight(f, 1) );
929 vsapi->freeFrame(f);
931 for (int i = n + 1; i < std::min<int>(n + parent->num_threads, parent->vi->numFrames); i++) {
932 InterlockedIncrement(&parent->pending_requests);
933 vsapi->getFrameAsync(i, parent->node, VapourSynthFile::frameDoneCallback, (void *)parent);
936 return true;
939 ////////////////////////////////////////////////////////////////////////
940 //////////// IAVIStream
942 STDMETHODIMP VapourSynthStream::Read(LONG lStart, LONG lSamples, LPVOID lpBuffer, LONG cbBuffer, LONG *plBytes, LONG *plSamples) {
943 parent->Lock();
944 HRESULT result = Read2(lStart, lSamples, lpBuffer, cbBuffer, plBytes, plSamples);
945 parent->Unlock();
946 return result;
949 HRESULT VapourSynthStream::Read2(LONG lStart, LONG lSamples, LPVOID lpBuffer, LONG cbBuffer, LONG *plBytes, LONG *plSamples) {
950 if (lStart >= parent->vi->numFrames) {
951 if (plSamples)
952 *plSamples = 0;
953 if (plBytes)
954 *plBytes = 0;
955 return S_OK;
958 int image_size = parent->ImageSize();
959 if (plSamples)
960 *plSamples = 1;
961 if (plBytes)
962 *plBytes = image_size;
964 if (!lpBuffer) {
965 return S_OK;
966 } else if (cbBuffer < image_size) {
967 return AVIERR_BUFFERTOOSMALL;
970 if (!ReadFrame(lpBuffer, lStart))
971 return E_FAIL;
972 return S_OK;
975 STDMETHODIMP VapourSynthStream::ReadFormat(LONG lPos, LPVOID lpFormat, LONG *lpcbFormat) {
976 if (!lpcbFormat)
977 return E_POINTER;
979 if (!lpFormat) {
980 *lpcbFormat = sizeof(BITMAPINFOHEADER);
981 return S_OK;
984 memset(lpFormat, 0, *lpcbFormat);
986 const VSVideoInfo* const vi = parent->vi;
988 BITMAPINFOHEADER bi;
989 memset(&bi, 0, sizeof(bi));
990 bi.biSize = sizeof(bi);
991 bi.biWidth = vi->width;
992 bi.biHeight = vi->height;
993 bi.biPlanes = 1;
994 bi.biBitCount = vi->format->bytesPerSample * 8;
995 if (vi->format->numPlanes == 3)
996 bi.biBitCount += (bi.biBitCount * 2) >> (vi->format->subSamplingH + vi->format->subSamplingW);
997 if (parent->enable_v210 && vi->format->id == pfYUV422P10)
998 bi.biBitCount = 20;
999 if (vi->format->id == pfCompatBGR32)
1000 bi.biCompression = BI_RGB;
1001 else if (vi->format->id == pfCompatYUY2)
1002 bi.biCompression = '2YUY';
1003 else if (vi->format->id == pfYUV420P8)
1004 bi.biCompression = '21VY';
1005 else if (vi->format->id == pfGray8)
1006 bi.biCompression = '008Y';
1007 else if (vi->format->id == pfYUV444P8)
1008 bi.biCompression = '42VY';
1009 else if (vi->format->id == pfYUV422P8)
1010 bi.biCompression = '61VY';
1011 else if (vi->format->id == pfYUV411P8)
1012 bi.biCompression = 'B14Y';
1013 else if (vi->format->id == pfYUV410P8)
1014 bi.biCompression = '9UVY';
1015 else if (vi->format->id == pfYUV420P10)
1016 bi.biCompression = '010P';
1017 else if (vi->format->id == pfYUV420P16)
1018 bi.biCompression = '610P';
1019 else if (vi->format->id == pfYUV422P10 && parent->enable_v210)
1020 bi.biCompression = '012v';
1021 else if (vi->format->id == pfYUV422P10)
1022 bi.biCompression = '012P';
1023 else if (vi->format->id == pfYUV422P16)
1024 bi.biCompression = '612P';
1025 else
1026 return E_FAIL;
1028 bi.biSizeImage = parent->ImageSize();
1029 *lpcbFormat = min(*lpcbFormat, sizeof(bi));
1030 memcpy(lpFormat, &bi, size_t(*lpcbFormat));
1032 return S_OK;
1035 STDMETHODIMP VapourSynthStream::Write(LONG lStart, LONG lSamples, LPVOID lpBuffer,
1036 LONG cbBuffer, DWORD dwFlags, LONG FAR *plSampWritten,
1037 LONG FAR *plBytesWritten) {
1038 return AVIERR_READONLY;