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
24 #define WIN32_LEAN_AND_MEAN
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
;
68 std::string szScriptName
;
69 const VSVideoInfo
* vi
;
70 std::string error_msg
;
71 volatile long pending_requests
;
73 CRITICAL_SECTION cs_filter_graph
;
85 VapourSynthFile(const CLSID
& rclsid
);
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
);
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
{
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
);
172 VapourSynthFile
*parent
;
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
187 } else if (ulReason
== DLL_PROCESS_DETACH
) {
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
);
205 extern "C" STDAPI
DllCanUnloadNow();
207 STDAPI
DllCanUnloadNow() {
208 return refCount
? S_FALSE
: S_OK
;
212 ///////////////////////////////////////////////////////////////////////////
216 ///////////////////////////////////////////////////////////////////////////
217 //////////// IClassFactory
219 STDMETHODIMP
VapourSynthFile::CreateInstance (LPUNKNOWN pUnkOuter
, REFIID riid
, void * * ppvObj
) {
221 return CLASS_E_NOAGGREGATION
;
222 HRESULT hresult
= Create(CLSID_VapourSynth
, riid
, ppvObj
);
226 STDMETHODIMP
VapourSynthFile::LockServer (BOOL fLock
) {
230 ///////////////////////////////////////////////////
231 //////////// IPersistFile
233 STDMETHODIMP
VapourSynthFile::GetClassID(LPCLSID lpClassID
) { // IPersist
236 *lpClassID
= CLSID_VapourSynth
;
240 STDMETHODIMP
VapourSynthFile::IsDirty() {
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
) {
254 STDMETHODIMP
VapourSynthFile::SaveCompleted(LPCOLESTR lpszFileName
) {
258 STDMETHODIMP
VapourSynthFile::GetCurFile(LPOLESTR
*lplpszFileName
) {
260 *lplpszFileName
= NULL
;
264 ///////////////////////////////////////////////////
267 HRESULT
VapourSynthFile::Create(const CLSID
& rclsid
, const IID
& riid
, void **ppv
) {
269 VapourSynthFile
* pAVIFileSynth
= new VapourSynthFile(rclsid
);
271 return E_OUTOFMEMORY
;
272 hresult
= pAVIFileSynth
->QueryInterface(riid
, ppv
);
273 pAVIFileSynth
->Release();
277 ///////////////////////////////////////////////////
278 //////////// IUnknown
280 STDMETHODIMP
VapourSynthFile::QueryInterface(const IID
& iid
, void **ppv
) {
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;
298 return E_NOINTERFACE
;
306 STDMETHODIMP_(ULONG
) VapourSynthFile::AddRef() {
307 const int refs
= InterlockedIncrement(&m_refs
);
308 InterlockedIncrement(&refCount
);
312 STDMETHODIMP_(ULONG
) VapourSynthFile::Release() {
313 const int refs
= InterlockedDecrement(&m_refs
);
314 InterlockedDecrement(&refCount
);
320 ////////////////////////////////////////////////////////////////////////
324 ////////////////////////////////////////////////////////////////////////
325 //////////// IUnknown
328 STDMETHODIMP
VapourSynthStream::QueryInterface(const IID
& iid
, void **ppv
) {
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;
340 return E_NOINTERFACE
;
348 STDMETHODIMP_(ULONG
) VapourSynthStream::AddRef() {
349 const int refs
= InterlockedIncrement(&m_refs
);
350 InterlockedIncrement(&refCount
);
354 STDMETHODIMP_(ULONG
) VapourSynthStream::Release() {
355 const int refs
= InterlockedDecrement(&m_refs
);
356 InterlockedDecrement(&refCount
);
357 if (!refs
) delete this;
361 ////////////////////////////////////////////////////////////////////////
365 ////////////////////////////////////////////////////////////////////////
366 //////////// IAVIFile
368 STDMETHODIMP
VapourSynthFile::CreateStream(PAVISTREAM
*ppStream
, AVISTREAMINFOW
*psi
) {
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 ///////////////////////////////////////////////////
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();
401 InitializeCriticalSection(&cs_filter_graph
);
404 VapourSynthFile::~VapourSynthFile() {
408 while (pending_requests
> 0) {};
409 vseval_freeScript(se
);
412 DeleteCriticalSection(&cs_filter_graph
);
415 int VapourSynthFile::ImageSize() {
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
;
430 image_size
*= vi
->height
;
431 image_size
>>= vi
->format
->subSamplingH
;
434 image_size
+= vi
->width
* vi
->format
->bytesPerSample
* vi
->height
;
439 STDMETHODIMP
VapourSynthFile::Open(LPCSTR szFile
, UINT mode
, LPCOLESTR lpszFileName
) {
440 if (mode
& (OF_CREATE
|OF_WRITE
))
442 szScriptName
= szFile
;
446 bool VapourSynthFile::DelayInit() {
448 bool result
= DelayInit2();
453 const char *ErrorScript
= "\
454 import vapoursynth as vs\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
);
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());
480 bool VapourSynthFile::DelayInit2() {
481 if (!szScriptName
.empty() && !vi
) {
483 std::string script
= get_file_contents(szScriptName
.c_str());
487 if (!vseval_evaluateScript(&se
, script
.c_str(), szScriptName
.c_str())) {
489 node
= vseval_getOutput(se
, 0);
492 vi
= vsapi
->getVideoInfo(node
);
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";
500 int id
= vi
->format
->id
;
501 if (id
!= pfCompatBGR32
502 && id
!= pfCompatYUY2
512 && id
!= pfYUV422P16
) {
513 error_msg
= "VFW module doesn't support ";
514 error_msg
+= vi
->format
->name
;
515 error_msg
+= " output";
519 // set the special options hidden in global variables
522 VSMap
*options
= vsapi
->createMap();
523 vseval_getVariable(se
, "enable_v210", options
);
524 val
= vsapi
->propGetInt(options
, "enable_v210", 0, &error
);
529 vseval_getVariable(se
, "pad_scanlines", options
);
530 val
= vsapi
->propGetInt(options
, "pad_scanlines", 0, &error
);
532 pad_scanlines
= !!val
;
534 pad_scanlines
= false;
535 vsapi
->freeMap(options
);
537 const VSCoreInfo
*info
= vsapi
->getCoreInfo(vseval_getCore(se
));
538 num_threads
= info
->numThreads
;
542 error_msg
= vseval_getError(se
);
545 vseval_freeScript(se
);
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
);
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
) {
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
;
584 afi
.dwSuggestedBufferSize
= 0;
585 afi
.dwWidth
= vi
->width
;
586 afi
.dwHeight
= vi
->height
;
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
)));
601 static inline char BePrintable(int ch
) {
603 return isprint(ch
) ? ch
: '.';
607 STDMETHODIMP
VapourSynthFile::GetStream(PAVISTREAM
*ppStream
, DWORD fccType
, LONG lParam
) {
608 VapourSynthStream
*casr
;
611 fcc
[0] = BePrintable(fccType
);
612 fcc
[1] = BePrintable(fccType
>> 8);
613 fcc
[2] = BePrintable(fccType
>> 16);
614 fcc
[3] = BePrintable(fccType
>> 24);
624 fccType
= streamtypeVIDEO
;
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
;
643 return AVIERR_NODATA
;
649 ////////////////////////////////////////////////////////////////////////
650 //////////// IAvisynthClipInfo
652 int __stdcall
VapourSynthFile::GetError(const char** ppszMessage
) {
653 if (!DelayInit() && error_msg
.empty())
654 error_msg
= "VapourSynth: script open failed!";
657 *ppszMessage
= error_msg
.c_str();
658 return !error_msg
.empty();
661 bool __stdcall
VapourSynthFile::GetParity(int n
) {
667 bool __stdcall
VapourSynthFile::IsFieldBased() {
673 ////////////////////////////////////////////////////////////////////////
677 ////////////////////////////////////////////////////////////////////////
678 //////////// IAVIStreaming
680 STDMETHODIMP
VapourSynthStream::Begin(LONG lStart
, LONG lEnd
, LONG lRate
) {
684 STDMETHODIMP
VapourSynthStream::End() {
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 ////////////////////////////////////////////////////////////////////////
717 VapourSynthStream::VapourSynthStream(VapourSynthFile
*parentPtr
, bool isAudio
) {
725 VapourSynthStream::~VapourSynthStream() {
730 ////////////////////////////////////////////////////////////////////////
731 //////////// IAVIStream
733 STDMETHODIMP_(LONG
) VapourSynthStream::Info(AVISTREAMINFOW
*psi
, LONG lSize
) {
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';
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
)));
791 STDMETHODIMP_(LONG
) VapourSynthStream::FindSample(LONG lPos
, LONG lFlags
) {
792 if (lFlags
& FIND_FORMAT
)
795 if (lFlags
& FIND_FROM_START
)
802 ////////////////////////////////////////////////////////////////////////
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);
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);
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
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));
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;
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
;
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
;
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) );
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
);
939 ////////////////////////////////////////////////////////////////////////
940 //////////// IAVIStream
942 STDMETHODIMP
VapourSynthStream::Read(LONG lStart
, LONG lSamples
, LPVOID lpBuffer
, LONG cbBuffer
, LONG
*plBytes
, LONG
*plSamples
) {
944 HRESULT result
= Read2(lStart
, lSamples
, lpBuffer
, cbBuffer
, plBytes
, plSamples
);
949 HRESULT
VapourSynthStream::Read2(LONG lStart
, LONG lSamples
, LPVOID lpBuffer
, LONG cbBuffer
, LONG
*plBytes
, LONG
*plSamples
) {
950 if (lStart
>= parent
->vi
->numFrames
) {
958 int image_size
= parent
->ImageSize();
962 *plBytes
= image_size
;
966 } else if (cbBuffer
< image_size
) {
967 return AVIERR_BUFFERTOOSMALL
;
970 if (!ReadFrame(lpBuffer
, lStart
))
975 STDMETHODIMP
VapourSynthStream::ReadFormat(LONG lPos
, LPVOID lpFormat
, LONG
*lpcbFormat
) {
980 *lpcbFormat
= sizeof(BITMAPINFOHEADER
);
984 memset(lpFormat
, 0, *lpcbFormat
);
986 const VSVideoInfo
* const vi
= parent
->vi
;
989 memset(&bi
, 0, sizeof(bi
));
990 bi
.biSize
= sizeof(bi
);
991 bi
.biWidth
= vi
->width
;
992 bi
.biHeight
= vi
->height
;
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
)
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';
1028 bi
.biSizeImage
= parent
->ImageSize();
1029 *lpcbFormat
= min(*lpcbFormat
, sizeof(bi
));
1030 memcpy(lpFormat
, &bi
, size_t(*lpcbFormat
));
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
;