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 Libav; 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 files of main.cpp in avisynth
24 #define WIN32_LEAN_AND_MEAN
33 #include "VapourSynth.h"
34 #include "vapoursynthpp_api.h"
36 void BitBlt(uint8_t* dstp
, int dst_pitch
, const uint8_t* srcp
, int src_pitch
, int row_size
, int height
) {
37 for (int i
= 0; i
< height
; i
++) {
38 memcpy(dstp
, srcp
, row_size
);
44 static long refCount
=0;
46 static int BMPSize(int height
, int rowsize
, int planar
) {
48 int p
= height
* ((rowsize
+3) & ~3);
52 return height
* ((rowsize
+3) & ~3);
55 // {58F74CA0-BD0E-4664-A49B-8D10E6F0C131}
56 extern "C" const GUID CLSID_VapourSynth
=
57 { 0x58f74ca0, 0xbd0e, 0x4664, { 0xa4, 0x9b, 0x8d, 0x10, 0xe6, 0xf0, 0xc1, 0x31 } };
59 extern "C" const GUID IID_IAvisynthClipInfo
// {E6D6B708-124D-11D4-86F3-DB80AFD98778}
60 = {0xe6d6b708, 0x124d, 0x11d4, {0x86, 0xf3, 0xdb, 0x80, 0xaf, 0xd9, 0x87, 0x78}};
62 struct IAvisynthClipInfo
: IUnknown
{
63 virtual int __stdcall
GetError(const char** ppszMessage
) = 0;
64 virtual bool __stdcall
GetParity(int n
) = 0;
65 virtual bool __stdcall
IsFieldBased() = 0;
68 class VapourSynthFile
: public IAVIFile
, public IPersistFile
, public IClassFactory
, public IAvisynthClipInfo
{
69 friend class VapourSynthStream
;
73 std::string szScriptName
;
75 const VSVideoInfo
* vi
;
76 std::string error_msg
;
77 volatile long pending_requests
;
79 CRITICAL_SECTION cs_filter_graph
;
91 VapourSynthFile(const CLSID
& rclsid
);
94 static HRESULT
Create(const CLSID
& rclsid
, const IID
& riid
, void **ppv
);
95 static void VS_CC
frameDoneCallback(void *userData
, const VSFrameRef
*f
, int n
, const VSNodeRef
*, const char *errorMsg
);
99 STDMETHODIMP
QueryInterface(const IID
& iid
, void **ppv
);
100 STDMETHODIMP_(ULONG
) AddRef();
101 STDMETHODIMP_(ULONG
) Release();
103 //////////// IClassFactory
105 STDMETHODIMP
CreateInstance (LPUNKNOWN pUnkOuter
, REFIID riid
, void * * ppvObj
) ;
106 STDMETHODIMP
LockServer (BOOL fLock
) ;
108 //////////// IPersistFile
110 STDMETHODIMP
GetClassID(LPCLSID lpClassID
); // IPersist
112 STDMETHODIMP
IsDirty();
113 STDMETHODIMP
Load(LPCOLESTR lpszFileName
, DWORD grfMode
);
114 STDMETHODIMP
Save(LPCOLESTR lpszFileName
, BOOL fRemember
);
115 STDMETHODIMP
SaveCompleted(LPCOLESTR lpszFileName
);
116 STDMETHODIMP
GetCurFile(LPOLESTR
*lplpszFileName
);
118 //////////// IAVIFile
120 STDMETHODIMP
CreateStream(PAVISTREAM
*ppStream
, AVISTREAMINFOW
*psi
); // 5
121 STDMETHODIMP
EndRecord(); // 8
122 STDMETHODIMP
GetStream(PAVISTREAM
*ppStream
, DWORD fccType
, LONG lParam
); // 4
123 STDMETHODIMP
Info(AVIFILEINFOW
*psi
, LONG lSize
); // 3
125 STDMETHODIMP
Open(LPCSTR szFile
, UINT mode
, LPCOLESTR lpszFileName
); // ???
126 STDMETHODIMP
Save(LPCSTR szFile
, AVICOMPRESSOPTIONS FAR
*lpOptions
, // ???
127 AVISAVECALLBACK lpfnCallback
);
129 STDMETHODIMP
ReadData(DWORD fcc
, LPVOID lp
, LONG
*lpcb
); // 7
130 STDMETHODIMP
WriteData(DWORD fcc
, LPVOID lpBuffer
, LONG cbBuffer
); // 6
131 STDMETHODIMP
DeleteStream(DWORD fccType
, LONG lParam
); // 9
133 //////////// IAvisynthClipInfo
135 int __stdcall
GetError(const char** ppszMessage
);
136 bool __stdcall
GetParity(int n
);
137 bool __stdcall
IsFieldBased();
140 ///////////////////////////////////
142 class VapourSynthStream
: public IAVIStream
, public IAVIStreaming
{
145 //////////// IUnknown
147 STDMETHODIMP
QueryInterface(const IID
& iid
, void **ppv
);
148 STDMETHODIMP_(ULONG
) AddRef();
149 STDMETHODIMP_(ULONG
) Release();
151 VapourSynthStream(VapourSynthFile
*parentPtr
, bool isAudio
);
152 ~VapourSynthStream();
154 //////////// IAVIStream
156 STDMETHODIMP
Create(LONG lParam1
, LONG lParam2
);
157 STDMETHODIMP
Delete(LONG lStart
, LONG lSamples
);
158 STDMETHODIMP_(LONG
) Info(AVISTREAMINFOW
*psi
, LONG lSize
);
159 STDMETHODIMP_(LONG
) FindSample(LONG lPos
, LONG lFlags
);
160 STDMETHODIMP
Read(LONG lStart
, LONG lSamples
, LPVOID lpBuffer
, LONG cbBuffer
, LONG
*plBytes
, LONG
*plSamples
);
161 STDMETHODIMP
ReadData(DWORD fcc
, LPVOID lp
, LONG
*lpcb
);
162 STDMETHODIMP
ReadFormat(LONG lPos
, LPVOID lpFormat
, LONG
*lpcbFormat
);
163 STDMETHODIMP
SetFormat(LONG lPos
, LPVOID lpFormat
, LONG cbFormat
);
164 STDMETHODIMP
Write(LONG lStart
, LONG lSamples
, LPVOID lpBuffer
,
165 LONG cbBuffer
, DWORD dwFlags
, LONG FAR
*plSampWritten
,
166 LONG FAR
*plBytesWritten
);
167 STDMETHODIMP
WriteData(DWORD fcc
, LPVOID lpBuffer
, LONG cbBuffer
);
168 STDMETHODIMP
SetInfo(AVISTREAMINFOW
*psi
, LONG lSize
);
170 //////////// IAVIStreaming
172 STDMETHODIMP
Begin(LONG lStart
, LONG lEnd
, LONG lRate
);
178 VapourSynthFile
*parent
;
181 //////////// internal
183 bool ReadFrame(void* lpBuffer
, int n
);
185 HRESULT
Read2(LONG lStart
, LONG lSamples
, LPVOID lpBuffer
, LONG cbBuffer
, LONG
*plBytes
, LONG
*plSamples
);
189 BOOL APIENTRY
DllMain(HANDLE hModule
, ULONG ulReason
, LPVOID lpReserved
) {
190 if (ulReason
== DLL_PROCESS_ATTACH
) {
192 import_vapoursynth();
193 } else if (ulReason
== DLL_PROCESS_DETACH
) {
199 // From the Microsoft AVIFile docs. Dense code...
201 extern "C" STDAPI
DllGetClassObject(const CLSID
& rclsid
, const IID
& riid
, void **ppv
);
203 STDAPI
DllGetClassObject(const CLSID
& rclsid
, const IID
& riid
, void **ppv
) {
205 if (rclsid
!= CLSID_VapourSynth
)
206 return CLASS_E_CLASSNOTAVAILABLE
;
207 HRESULT hresult
= VapourSynthFile::Create(rclsid
, riid
, ppv
);
211 extern "C" STDAPI
DllCanUnloadNow();
213 STDAPI
DllCanUnloadNow() {
214 return refCount
? S_FALSE
: S_OK
;
218 ///////////////////////////////////////////////////////////////////////////
222 ///////////////////////////////////////////////////////////////////////////
223 //////////// IClassFactory
225 STDMETHODIMP
VapourSynthFile::CreateInstance (LPUNKNOWN pUnkOuter
, REFIID riid
, void * * ppvObj
) {
227 return CLASS_E_NOAGGREGATION
;
228 HRESULT hresult
= Create(CLSID_VapourSynth
, riid
, ppvObj
);
232 STDMETHODIMP
VapourSynthFile::LockServer (BOOL fLock
) {
236 ///////////////////////////////////////////////////
237 //////////// IPersistFile
239 STDMETHODIMP
VapourSynthFile::GetClassID(LPCLSID lpClassID
) { // IPersist
242 *lpClassID
= CLSID_VapourSynth
;
246 STDMETHODIMP
VapourSynthFile::IsDirty() {
250 STDMETHODIMP
VapourSynthFile::Load(LPCOLESTR lpszFileName
, DWORD grfMode
) {
251 char filename
[MAX_PATH
*2];
252 WideCharToMultiByte(CP_UTF8
, 0, lpszFileName
, -1, filename
, sizeof(filename
), NULL
, NULL
);
253 return Open(filename
, grfMode
, lpszFileName
);
256 STDMETHODIMP
VapourSynthFile::Save(LPCOLESTR lpszFileName
, BOOL fRemember
) {
260 STDMETHODIMP
VapourSynthFile::SaveCompleted(LPCOLESTR lpszFileName
) {
264 STDMETHODIMP
VapourSynthFile::GetCurFile(LPOLESTR
*lplpszFileName
) {
266 *lplpszFileName
= NULL
;
270 ///////////////////////////////////////////////////
273 HRESULT
VapourSynthFile::Create(const CLSID
& rclsid
, const IID
& riid
, void **ppv
) {
275 VapourSynthFile
* pAVIFileSynth
= new VapourSynthFile(rclsid
);
277 return E_OUTOFMEMORY
;
278 hresult
= pAVIFileSynth
->QueryInterface(riid
, ppv
);
279 pAVIFileSynth
->Release();
283 ///////////////////////////////////////////////////
284 //////////// IUnknown
286 STDMETHODIMP
VapourSynthFile::QueryInterface(const IID
& iid
, void **ppv
) {
290 if (iid
== IID_IUnknown
) {
291 *ppv
= (IUnknown
*)(IAVIFile
*)this;
292 } else if (iid
== IID_IClassFactory
) {
293 *ppv
= (IClassFactory
*)this;
294 } else if (iid
== IID_IPersist
) {
295 *ppv
= (IPersist
*)this;
296 } else if (iid
== IID_IPersistFile
) {
297 *ppv
= (IPersistFile
*)this;
298 } else if (iid
== IID_IAVIFile
) {
299 *ppv
= (IAVIFile
*)this;
300 } else if (iid
== IID_IAvisynthClipInfo
) {
301 *ppv
= (IAvisynthClipInfo
*)this;
304 return E_NOINTERFACE
;
312 STDMETHODIMP_(ULONG
) VapourSynthFile::AddRef() {
313 const int refs
= InterlockedIncrement(&m_refs
);
314 InterlockedIncrement(&refCount
);
318 STDMETHODIMP_(ULONG
) VapourSynthFile::Release() {
319 const int refs
= InterlockedDecrement(&m_refs
);
320 InterlockedDecrement(&refCount
);
326 ////////////////////////////////////////////////////////////////////////
330 ////////////////////////////////////////////////////////////////////////
331 //////////// IUnknown
334 STDMETHODIMP
VapourSynthStream::QueryInterface(const IID
& iid
, void **ppv
) {
338 if (iid
== IID_IUnknown
) {
339 *ppv
= (IUnknown
*)(IAVIStream
*)this;
340 } else if (iid
== IID_IAVIStream
) {
341 *ppv
= (IAVIStream
*)this;
342 } else if (iid
== IID_IAVIStreaming
) {
343 *ppv
= (IAVIStreaming
*)this;
346 return E_NOINTERFACE
;
354 STDMETHODIMP_(ULONG
) VapourSynthStream::AddRef() {
355 const int refs
= InterlockedIncrement(&m_refs
);
356 InterlockedIncrement(&refCount
);
360 STDMETHODIMP_(ULONG
) VapourSynthStream::Release() {
361 const int refs
= InterlockedDecrement(&m_refs
);
362 InterlockedDecrement(&refCount
);
363 if (!refs
) delete this;
367 ////////////////////////////////////////////////////////////////////////
371 ////////////////////////////////////////////////////////////////////////
372 //////////// IAVIFile
374 STDMETHODIMP
VapourSynthFile::CreateStream(PAVISTREAM
*ppStream
, AVISTREAMINFOW
*psi
) {
379 STDMETHODIMP
VapourSynthFile::EndRecord() {
380 return AVIERR_READONLY
;
383 STDMETHODIMP
VapourSynthFile::Save(LPCSTR szFile
, AVICOMPRESSOPTIONS FAR
*lpOptions
,
384 AVISAVECALLBACK lpfnCallback
) {
385 return AVIERR_READONLY
;
388 STDMETHODIMP
VapourSynthFile::ReadData(DWORD fcc
, LPVOID lp
, LONG
*lpcb
) {
389 return AVIERR_NODATA
;
392 STDMETHODIMP
VapourSynthFile::WriteData(DWORD fcc
, LPVOID lpBuffer
, LONG cbBuffer
) {
393 return AVIERR_READONLY
;
396 STDMETHODIMP
VapourSynthFile::DeleteStream(DWORD fccType
, LONG lParam
) {
397 return AVIERR_READONLY
;
401 ///////////////////////////////////////////////////
404 VapourSynthFile::VapourSynthFile(const CLSID
& rclsid
) : m_refs(0), vi(NULL
), pending_requests(0) {
406 InitializeCriticalSection(&cs_filter_graph
);
409 VapourSynthFile::~VapourSynthFile() {
413 while (pending_requests
> 0);
414 vpy_free_script(&se
);
417 DeleteCriticalSection(&cs_filter_graph
);
420 int VapourSynthFile::ImageSize() {
425 if (vi
->format
->id
== pfYUV422P10
&& se
.enable_v210
) {
426 image_size
= ((16*((vi
->width
+ 5) / 6) + 63) & ~63);
427 image_size
*= vi
->height
;
428 } else if (vi
->format
->numPlanes
== 1) {
429 image_size
= BMPSize(vi
->height
, vi
->width
* vi
->format
->bytesPerSample
, 0);
430 } else { // Packed size
431 image_size
= (vi
->width
* vi
->format
->bytesPerSample
) >> vi
->format
->subSamplingW
;
433 image_size
*= vi
->height
;
434 image_size
>>= vi
->format
->subSamplingH
;
437 image_size
+= vi
->width
* vi
->format
->bytesPerSample
* vi
->height
;
442 STDMETHODIMP
VapourSynthFile::Open(LPCSTR szFile
, UINT mode
, LPCOLESTR lpszFileName
) {
443 if (mode
& (OF_CREATE
|OF_WRITE
))
445 szScriptName
= szFile
;
449 bool VapourSynthFile::DelayInit() {
451 bool result
= DelayInit2();
456 const char *ErrorScript
= "\
457 import vapoursynth as vs\n\
459 core = vs.Core(threads=1)\n\
460 red = core.std.BlankClip(width=240, height=480, format=vs.RGB24, color=[255, 0, 0])\n\
461 green = core.std.BlankClip(width=240, height=480, format=vs.RGB24, color=[0, 255, 0])\n\
462 blue = core.std.BlankClip(width=240, height=480, format=vs.RGB24, color=[0, 0, 255])\n\
463 stacked = core.std.StackHorizontal([red, green, blue])\n\
464 last = core.resize.Bicubic(stacked, format=vs.COMPATBGR32)\n";
466 bool VapourSynthFile::DelayInit2() {
467 if (!szScriptName
.empty() && !vi
) {
468 // this ugly cast is needed because cython doesn't understand the const keyword
469 if (!vpy_evaluate_file((char *)szScriptName
.c_str(), &se
)) {
470 vi
= se
.vsapi
->getVideoInfo(se
.node
);
473 if (vi
->width
== 0 || vi
->height
== 0 || vi
->format
== NULL
|| vi
->numFrames
== 0) {
474 error_msg
= "Cannot open clips with varying dimensions or format in vfw";
478 int id
= vi
->format
->id
;
479 if (id
!= pfCompatBGR32
480 && id
!= pfCompatYUY2
490 && id
!= pfYUV422P16
) {
491 error_msg
= "VFW module doesn't support ";
492 error_msg
+= vi
->format
->name
;
493 error_msg
+= " output";
499 error_msg
= se
.error
;
502 vpy_free_script(&se
);
503 vpy_evaluate_text((char *)ErrorScript
, "vfw_error.bleh", &se
);
504 vi
= se
.vsapi
->getVideoInfo(se
.node
);
512 void VapourSynthFile::Lock() {
513 EnterCriticalSection(&cs_filter_graph
);
516 void VapourSynthFile::Unlock() {
517 LeaveCriticalSection(&cs_filter_graph
);
520 ///////////////////////////////////////////////////
521 //////////// IAVIFile
523 STDMETHODIMP
VapourSynthFile::Info(AVIFILEINFOW
*pfi
, LONG lSize
) {
531 memset(&afi
, 0, sizeof(afi
));
533 afi
.dwMaxBytesPerSec
= 0;
534 afi
.dwFlags
= AVIFILEINFO_HASINDEX
| AVIFILEINFO_ISINTERLEAVED
;
535 afi
.dwCaps
= AVIFILECAPS_CANREAD
| AVIFILECAPS_ALLKEYFRAMES
| AVIFILECAPS_NOCOMPRESSION
;
538 afi
.dwSuggestedBufferSize
= 0;
539 afi
.dwWidth
= vi
->width
;
540 afi
.dwHeight
= vi
->height
;
543 afi
.dwRate
= vi
->fpsNum
? vi
->fpsNum
: 1;
544 afi
.dwScale
= vi
->fpsDen
? vi
->fpsDen
: 30;
545 afi
.dwLength
= vi
->numFrames
;
547 wcscpy(afi
.szFileType
, L
"VapourSynth");
549 // Maybe should return AVIERR_BUFFERTOOSMALL for lSize < sizeof(afi)
550 memset(pfi
, 0, lSize
);
551 memcpy(pfi
, &afi
, min(size_t(lSize
), sizeof(afi
)));
555 static inline char BePrintable(int ch
) {
557 return isprint(ch
) ? ch
: '.';
561 STDMETHODIMP
VapourSynthFile::GetStream(PAVISTREAM
*ppStream
, DWORD fccType
, LONG lParam
) {
562 VapourSynthStream
*casr
;
565 fcc
[0] = BePrintable(fccType
);
566 fcc
[1] = BePrintable(fccType
>> 8);
567 fcc
[2] = BePrintable(fccType
>> 16);
568 fcc
[3] = BePrintable(fccType
>> 24);
578 fccType
= streamtypeVIDEO
;
582 return AVIERR_NODATA
;
584 if (fccType
== streamtypeVIDEO
) {
585 if ((casr
= new VapourSynthStream(this, false)) == 0)
586 return AVIERR_MEMORY
;
588 *ppStream
= (IAVIStream
*)casr
;
590 } else if (fccType
== streamtypeAUDIO
) {
591 return AVIERR_NODATA
;
593 if ((casr
= new VapourSynthStream(this, true)) == 0)
594 return AVIERR_MEMORY
;
595 *ppStream
= (IAVIStream
*)casr
;
597 return AVIERR_NODATA
;
603 ////////////////////////////////////////////////////////////////////////
604 //////////// IAvisynthClipInfo
606 int __stdcall
VapourSynthFile::GetError(const char** ppszMessage
) {
607 if (!DelayInit() && error_msg
.empty())
608 error_msg
= "VapourSynth: script open failed!";
611 *ppszMessage
= error_msg
.c_str();
612 return !error_msg
.empty();
615 bool __stdcall
VapourSynthFile::GetParity(int n
) {
621 bool __stdcall
VapourSynthFile::IsFieldBased() {
627 ////////////////////////////////////////////////////////////////////////
631 ////////////////////////////////////////////////////////////////////////
632 //////////// IAVIStreaming
634 STDMETHODIMP
VapourSynthStream::Begin(LONG lStart
, LONG lEnd
, LONG lRate
) {
638 STDMETHODIMP
VapourSynthStream::End() {
642 //////////// IAVIStream
644 STDMETHODIMP
VapourSynthStream::Create(LONG lParam1
, LONG lParam2
) {
645 return AVIERR_READONLY
;
648 STDMETHODIMP
VapourSynthStream::Delete(LONG lStart
, LONG lSamples
) {
649 return AVIERR_READONLY
;
652 STDMETHODIMP
VapourSynthStream::ReadData(DWORD fcc
, LPVOID lp
, LONG
*lpcb
) {
653 return AVIERR_NODATA
;
656 STDMETHODIMP
VapourSynthStream::SetFormat(LONG lPos
, LPVOID lpFormat
, LONG cbFormat
) {
657 return AVIERR_READONLY
;
660 STDMETHODIMP
VapourSynthStream::WriteData(DWORD fcc
, LPVOID lpBuffer
, LONG cbBuffer
) {
661 return AVIERR_READONLY
;
664 STDMETHODIMP
VapourSynthStream::SetInfo(AVISTREAMINFOW
*psi
, LONG lSize
) {
665 return AVIERR_READONLY
;
668 ////////////////////////////////////////////////////////////////////////
671 VapourSynthStream::VapourSynthStream(VapourSynthFile
*parentPtr
, bool isAudio
) {
679 VapourSynthStream::~VapourSynthStream() {
684 ////////////////////////////////////////////////////////////////////////
685 //////////// IAVIStream
687 STDMETHODIMP_(LONG
) VapourSynthStream::Info(AVISTREAMINFOW
*psi
, LONG lSize
) {
693 const VSVideoInfo
* const vi
= parent
->vi
;
695 memset(&asi
, 0, sizeof(asi
));
696 asi
.fccType
= streamtypeVIDEO
;
697 asi
.dwQuality
= DWORD(-1);
699 const int image_size
= parent
->ImageSize();
700 asi
.fccHandler
= 'UNKN';
701 if (vi
->format
->id
== pfCompatBGR32
)
702 asi
.fccHandler
= ' BID';
703 else if (vi
->format
->id
== pfCompatYUY2
)
704 asi
.fccHandler
= '2YUY';
705 else if (vi
->format
->id
== pfYUV420P8
)
706 asi
.fccHandler
= '21VY';
707 else if (vi
->format
->id
== pfGray8
)
708 asi
.fccHandler
= '008Y';
709 else if (vi
->format
->id
== pfYUV444P8
)
710 asi
.fccHandler
= '42VY';
711 else if (vi
->format
->id
== pfYUV422P8
)
712 asi
.fccHandler
= '61VY';
713 else if (vi
->format
->id
== pfYUV411P8
)
714 asi
.fccHandler
= 'B14Y';
715 else if (vi
->format
->id
== pfYUV410P8
)
716 asi
.fccHandler
= '9UVY';
717 else if (vi
->format
->id
== pfYUV420P10
)
718 asi
.fccHandler
= '010P';
719 else if (vi
->format
->id
== pfYUV420P16
)
720 asi
.fccHandler
= '610P';
721 else if (vi
->format
->id
== pfYUV422P10
&& parent
->se
.enable_v210
)
722 asi
.fccHandler
= '012v';
723 else if (vi
->format
->id
== pfYUV422P10
)
724 asi
.fccHandler
= '012P';
725 else if (vi
->format
->id
== pfYUV422P16
)
726 asi
.fccHandler
= '612P';
730 asi
.dwScale
= vi
->fpsDen
? vi
->fpsDen
: 1;
731 asi
.dwRate
= vi
->fpsNum
? vi
->fpsNum
: 30;
732 asi
.dwLength
= vi
->numFrames
;
733 asi
.rcFrame
.right
= vi
->width
;
734 asi
.rcFrame
.bottom
= vi
->height
;
735 asi
.dwSampleSize
= image_size
;
736 asi
.dwSuggestedBufferSize
= image_size
;
737 wcscpy(asi
.szName
, L
"VapourSynth video #1");
739 // Maybe should return AVIERR_BUFFERTOOSMALL for lSize < sizeof(asi)
740 memset(psi
, 0, lSize
);
741 memcpy(psi
, &asi
, min(size_t(lSize
), sizeof(asi
)));
745 STDMETHODIMP_(LONG
) VapourSynthStream::FindSample(LONG lPos
, LONG lFlags
) {
746 if (lFlags
& FIND_FORMAT
)
749 if (lFlags
& FIND_FROM_START
)
756 ////////////////////////////////////////////////////////////////////////
759 void VS_CC
VapourSynthFile::frameDoneCallback(void *userData
, const VSFrameRef
*f
, int n
, const VSNodeRef
*, const char *errorMsg
) {
760 VapourSynthFile
*vsfile
= (VapourSynthFile
*)userData
;
761 vsfile
->se
.vsapi
->freeFrame(f
);
762 InterlockedDecrement(&vsfile
->pending_requests
);
765 bool VapourSynthStream::ReadFrame(void* lpBuffer
, int n
) {
766 const VSAPI
*vsapi
= parent
->se
.vsapi
;
767 const VSFrameRef
*f
= vsapi
->getFrame(n
, parent
->se
.node
, 0, 0);
771 const VSFormat
*fi
= vsapi
->getFrameFormat(f
);
772 const int pitch
= vsapi
->getStride(f
, 0);
773 const int row_size
= vsapi
->getFrameWidth(f
, 0) * fi
->bytesPerSample
;
774 const int height
= vsapi
->getFrameHeight(f
, 0);
779 bool semi_packed_p10
= (fi
->id
== pfYUV420P10
) || (fi
->id
== pfYUV422P10
);
780 bool semi_packed_p16
= (fi
->id
== pfYUV420P16
) || (fi
->id
== pfYUV422P16
);
782 // BMP scanlines are dword-aligned
783 if (fi
->numPlanes
== 1) {
784 out_pitch
= (row_size
+3) & ~3;
785 out_pitchUV
= (vsapi
->getFrameWidth(f
, 1) * fi
->bytesPerSample
+3) & ~3;
787 // Planar scanlines are packed
789 out_pitch
= row_size
;
790 out_pitchUV
= vsapi
->getFrameWidth(f
, 1) * fi
->bytesPerSample
;
793 if (fi
->id
== pfYUV422P10
&& parent
->se
.enable_v210
) {
794 int width
= vsapi
->getFrameWidth(f
, 0);
795 int pstride_y
= vsapi
->getStride(f
, 0)/2;
796 int pstride_uv
= vsapi
->getStride(f
, 1)/2;
797 const uint16_t *yptr
= (const uint16_t *)vsapi
->getReadPtr(f
, 0);
798 const uint16_t *uptr
= (const uint16_t *)vsapi
->getReadPtr(f
, 1);
799 const uint16_t *vptr
= (const uint16_t *)vsapi
->getReadPtr(f
, 2);
800 uint32_t *outbuf
= (uint32_t *)lpBuffer
;
801 out_pitch
= ((16*((width
+ 5) / 6) + 63) & ~63)/4;
802 for (int y
= 0; y
< height
; y
++) {
803 const uint16_t *yline
= yptr
;
804 const uint16_t *uline
= uptr
;
805 const uint16_t *vline
= vptr
;
806 uint32_t *out_line
= outbuf
;
807 for (int x
= 0; x
< width
+ 5; x
+= 6) {
808 out_line
[0] = (uline
[0] | (yline
[0] << 10) | (vline
[0] << 20));
809 out_line
[1] = (yline
[1] | (uline
[1] << 10) | (yline
[2] << 20));
810 out_line
[2] = (vline
[1] | (yline
[3] << 10) | (uline
[2] << 20));
811 out_line
[3] = (yline
[4] | (vline
[2] << 10) | (yline
[5] << 20));
822 } else if (semi_packed_p10
) {
823 int pwidth
= vsapi
->getFrameWidth(f
, 0);
824 int pstride
= vsapi
->getStride(f
, 0) / 2;
825 uint16_t *outbuf
= (uint16_t *)lpBuffer
;
826 const uint16_t *yptr
= (const uint16_t *)vsapi
->getReadPtr(f
, 0);
828 for (int y
= 0; y
< height
; y
++) {
829 for (int x
= 0; x
< pwidth
; x
++) {
830 outbuf
[x
] = yptr
[x
] << 6;
832 outbuf
+= out_pitch
/2;
836 BitBlt((BYTE
*)lpBuffer
, out_pitch
, vsapi
->getReadPtr(f
, 0), pitch
, row_size
, height
);
839 if (fi
->id
== pfYUV422P10
&& parent
->se
.enable_v210
) {
840 // intentionally empty
841 } else if (semi_packed_p10
|| semi_packed_p16
) {
842 int pheight
= vsapi
->getFrameHeight(f
, 1);
843 int pwidth
= vsapi
->getFrameWidth(f
, 1);
844 int pstride
= vsapi
->getStride(f
, 1) / 2;
845 BYTE
*outadj
= (BYTE
*)lpBuffer
+ out_pitch
*height
;
846 uint16_t *outbuf
= (uint16_t *)outadj
;
847 const uint16_t *uptr
= (const uint16_t *)vsapi
->getReadPtr(f
, 1);
848 const uint16_t *vptr
= (const uint16_t *)vsapi
->getReadPtr(f
, 2);
850 if (semi_packed_p16
) {
851 for (int y
= 0; y
< pheight
; y
++) {
852 for (int x
= 0; x
< pwidth
; x
++) {
853 outbuf
[2*x
] = uptr
[x
];
854 outbuf
[2*x
+ 1] = vptr
[x
];
856 outbuf
+= out_pitchUV
;
861 for (int y
= 0; y
< pheight
; y
++) {
862 for (int x
= 0; x
< pwidth
; x
++) {
863 outbuf
[2*x
] = uptr
[x
] << 6;
864 outbuf
[2*x
+ 1] = vptr
[x
] << 6;
866 outbuf
+= out_pitchUV
;
871 } else if (fi
->numPlanes
== 3) {
872 BitBlt((BYTE
*)lpBuffer
+ (out_pitch
*height
),
873 out_pitchUV
, vsapi
->getReadPtr(f
, 2),
874 vsapi
->getStride(f
, 2), vsapi
->getFrameWidth(f
, 2),
875 vsapi
->getFrameHeight(f
, 2) );
877 BitBlt((BYTE
*)lpBuffer
+ (out_pitch
*height
+ vsapi
->getFrameHeight(f
, 1)*out_pitchUV
),
878 out_pitchUV
, vsapi
->getReadPtr(f
, 1),
879 vsapi
->getStride(f
, 1), vsapi
->getFrameWidth(f
, 1),
880 vsapi
->getFrameHeight(f
, 1) );
885 for (int i
= n
+ 1; i
< std::min
<int>(n
+ parent
->se
.num_threads
, parent
->vi
->numFrames
); i
++) {
886 InterlockedIncrement(&parent
->pending_requests
);
887 vsapi
->getFrameAsync(i
, parent
->se
.node
, VapourSynthFile::frameDoneCallback
, (void *)parent
);
893 ////////////////////////////////////////////////////////////////////////
894 //////////// IAVIStream
896 STDMETHODIMP
VapourSynthStream::Read(LONG lStart
, LONG lSamples
, LPVOID lpBuffer
, LONG cbBuffer
, LONG
*plBytes
, LONG
*plSamples
) {
898 HRESULT result
= Read2(lStart
, lSamples
, lpBuffer
, cbBuffer
, plBytes
, plSamples
);
903 HRESULT
VapourSynthStream::Read2(LONG lStart
, LONG lSamples
, LPVOID lpBuffer
, LONG cbBuffer
, LONG
*plBytes
, LONG
*plSamples
) {
904 if (lStart
>= parent
->vi
->numFrames
) {
912 int image_size
= parent
->ImageSize();
916 *plBytes
= image_size
;
920 } else if (cbBuffer
< image_size
) {
921 return AVIERR_BUFFERTOOSMALL
;
924 if (!ReadFrame(lpBuffer
, lStart
))
929 STDMETHODIMP
VapourSynthStream::ReadFormat(LONG lPos
, LPVOID lpFormat
, LONG
*lpcbFormat
) {
934 *lpcbFormat
= sizeof(BITMAPINFOHEADER
);
938 memset(lpFormat
, 0, *lpcbFormat
);
940 const VSVideoInfo
* const vi
= parent
->vi
;
943 memset(&bi
, 0, sizeof(bi
));
944 bi
.biSize
= sizeof(bi
);
945 bi
.biWidth
= vi
->width
;
946 bi
.biHeight
= vi
->height
;
948 bi
.biBitCount
= vi
->format
->bytesPerSample
* 8;
949 if (vi
->format
->numPlanes
== 3)
950 bi
.biBitCount
+= (bi
.biBitCount
* 2) >> (vi
->format
->subSamplingH
+ vi
->format
->subSamplingW
);
951 if (vi
->format
->id
== pfCompatBGR32
)
952 bi
.biCompression
= BI_RGB
;
953 else if (vi
->format
->id
== pfCompatYUY2
)
954 bi
.biCompression
= '2YUY';
955 else if (vi
->format
->id
== pfYUV420P8
)
956 bi
.biCompression
= '21VY';
957 else if (vi
->format
->id
== pfGray8
)
958 bi
.biCompression
= '008Y';
959 else if (vi
->format
->id
== pfYUV444P8
)
960 bi
.biCompression
= '42VY';
961 else if (vi
->format
->id
== pfYUV422P8
)
962 bi
.biCompression
= '61VY';
963 else if (vi
->format
->id
== pfYUV411P8
)
964 bi
.biCompression
= 'B14Y';
965 else if (vi
->format
->id
== pfYUV410P8
)
966 bi
.biCompression
= '9UVY';
967 else if (vi
->format
->id
== pfYUV420P10
)
968 bi
.biCompression
= '010P';
969 else if (vi
->format
->id
== pfYUV420P16
)
970 bi
.biCompression
= '610P';
971 else if (vi
->format
->id
== pfYUV422P10
&& parent
->se
.enable_v210
)
972 bi
.biCompression
= '012v';
973 else if (vi
->format
->id
== pfYUV422P10
)
974 bi
.biCompression
= '012P';
975 else if (vi
->format
->id
== pfYUV422P16
)
976 bi
.biCompression
= '612P';
980 bi
.biSizeImage
= parent
->ImageSize();
981 *lpcbFormat
= min(*lpcbFormat
, sizeof(bi
));
982 memcpy(lpFormat
, &bi
, size_t(*lpcbFormat
));
987 STDMETHODIMP
VapourSynthStream::Write(LONG lStart
, LONG lSamples
, LPVOID lpBuffer
,
988 LONG cbBuffer
, DWORD dwFlags
, LONG FAR
*plSampWritten
,
989 LONG FAR
*plBytesWritten
) {
990 return AVIERR_READONLY
;