Add note about Sphinx to INSTALL.
[vapoursynth-svn.git] / src / vfw / vsvfw.cpp
blob98ce0a9b248b1cd2ee7f07eebe17f9f56ea00b6c
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 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
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>
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);
39 srcp += src_pitch;
40 dstp += dst_pitch;
44 static long refCount=0;
46 static int BMPSize(int height, int rowsize, int planar) {
47 if (planar) {
48 int p = height * ((rowsize+3) & ~3);
49 p+=p>>1;
50 return p;
51 } else
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;
70 private:
71 long m_refs;
73 std::string szScriptName;
74 VPYScriptExport se;
75 const VSVideoInfo* vi;
76 std::string error_msg;
77 volatile long pending_requests;
79 CRITICAL_SECTION cs_filter_graph;
81 bool DelayInit();
82 bool DelayInit2();
84 void Lock();
85 void Unlock();
87 int ImageSize();
89 public:
91 VapourSynthFile(const CLSID& rclsid);
92 ~VapourSynthFile();
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);
97 //////////// IUnknown
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 {
143 public:
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);
173 STDMETHODIMP End();
175 private:
176 long m_refs;
178 VapourSynthFile *parent;
179 std::string sName;
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) {
191 Py_Initialize();
192 import_vapoursynth();
193 } else if (ulReason == DLL_PROCESS_DETACH) {
194 Py_Finalize();
196 return TRUE;
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);
208 return hresult;
211 extern "C" STDAPI DllCanUnloadNow();
213 STDAPI DllCanUnloadNow() {
214 return refCount ? S_FALSE : S_OK;
218 ///////////////////////////////////////////////////////////////////////////
220 // VapourSynthFile
222 ///////////////////////////////////////////////////////////////////////////
223 //////////// IClassFactory
225 STDMETHODIMP VapourSynthFile::CreateInstance (LPUNKNOWN pUnkOuter, REFIID riid, void * * ppvObj) {
226 if (pUnkOuter)
227 return CLASS_E_NOAGGREGATION;
228 HRESULT hresult = Create(CLSID_VapourSynth, riid, ppvObj);
229 return hresult;
232 STDMETHODIMP VapourSynthFile::LockServer (BOOL fLock) {
233 return S_OK;
236 ///////////////////////////////////////////////////
237 //////////// IPersistFile
239 STDMETHODIMP VapourSynthFile::GetClassID(LPCLSID lpClassID) { // IPersist
240 if (!lpClassID)
241 return E_POINTER;
242 *lpClassID = CLSID_VapourSynth;
243 return S_OK;
246 STDMETHODIMP VapourSynthFile::IsDirty() {
247 return S_FALSE;
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) {
257 return E_FAIL;
260 STDMETHODIMP VapourSynthFile::SaveCompleted(LPCOLESTR lpszFileName) {
261 return S_OK;
264 STDMETHODIMP VapourSynthFile::GetCurFile(LPOLESTR *lplpszFileName) {
265 if (lplpszFileName)
266 *lplpszFileName = NULL;
267 return E_FAIL;
270 ///////////////////////////////////////////////////
271 /////// static local
273 HRESULT VapourSynthFile::Create(const CLSID& rclsid, const IID& riid, void **ppv) {
274 HRESULT hresult;
275 VapourSynthFile* pAVIFileSynth = new VapourSynthFile(rclsid);
276 if (!pAVIFileSynth)
277 return E_OUTOFMEMORY;
278 hresult = pAVIFileSynth->QueryInterface(riid, ppv);
279 pAVIFileSynth->Release();
280 return hresult;
283 ///////////////////////////////////////////////////
284 //////////// IUnknown
286 STDMETHODIMP VapourSynthFile::QueryInterface(const IID& iid, void **ppv) {
287 if (!ppv)
288 return E_POINTER;
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;
302 } else {
303 *ppv = NULL;
304 return E_NOINTERFACE;
307 AddRef();
309 return S_OK;
312 STDMETHODIMP_(ULONG) VapourSynthFile::AddRef() {
313 const int refs = InterlockedIncrement(&m_refs);
314 InterlockedIncrement(&refCount);
315 return refs;
318 STDMETHODIMP_(ULONG) VapourSynthFile::Release() {
319 const int refs = InterlockedDecrement(&m_refs);
320 InterlockedDecrement(&refCount);
321 if (!refs)
322 delete this;
323 return refs;
326 ////////////////////////////////////////////////////////////////////////
328 // VapourSynthStream
330 ////////////////////////////////////////////////////////////////////////
331 //////////// IUnknown
334 STDMETHODIMP VapourSynthStream::QueryInterface(const IID& iid, void **ppv) {
335 if (!ppv)
336 return E_POINTER;
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;
344 } else {
345 *ppv = NULL;
346 return E_NOINTERFACE;
349 AddRef();
351 return S_OK;
354 STDMETHODIMP_(ULONG) VapourSynthStream::AddRef() {
355 const int refs = InterlockedIncrement(&m_refs);
356 InterlockedIncrement(&refCount);
357 return refs;
360 STDMETHODIMP_(ULONG) VapourSynthStream::Release() {
361 const int refs = InterlockedDecrement(&m_refs);
362 InterlockedDecrement(&refCount);
363 if (!refs) delete this;
364 return refs;
367 ////////////////////////////////////////////////////////////////////////
369 // VapourSynthFile
371 ////////////////////////////////////////////////////////////////////////
372 //////////// IAVIFile
374 STDMETHODIMP VapourSynthFile::CreateStream(PAVISTREAM *ppStream, AVISTREAMINFOW *psi) {
375 *ppStream = NULL;
376 return S_OK;
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 ///////////////////////////////////////////////////
402 /////// local
404 VapourSynthFile::VapourSynthFile(const CLSID& rclsid) : m_refs(0), vi(NULL), pending_requests(0) {
405 AddRef();
406 InitializeCriticalSection(&cs_filter_graph);
409 VapourSynthFile::~VapourSynthFile() {
410 Lock();
411 if (vi) {
412 vi = NULL;
413 while (pending_requests > 0);
414 vpy_free_script(&se);
416 Unlock();
417 DeleteCriticalSection(&cs_filter_graph);
420 int VapourSynthFile::ImageSize() {
421 if (!vi)
422 return 0;
423 int image_size;
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;
432 if (image_size) {
433 image_size *= vi->height;
434 image_size >>= vi->format->subSamplingH;
435 image_size *= 2;
437 image_size += vi->width * vi->format->bytesPerSample * vi->height;
439 return image_size;
442 STDMETHODIMP VapourSynthFile::Open(LPCSTR szFile, UINT mode, LPCOLESTR lpszFileName) {
443 if (mode & (OF_CREATE|OF_WRITE))
444 return E_FAIL;
445 szScriptName = szFile;
446 return S_OK;
449 bool VapourSynthFile::DelayInit() {
450 Lock();
451 bool result = DelayInit2();
452 Unlock();
453 return result;
456 const char *ErrorScript = "\
457 import vapoursynth as vs\n\
458 import sys\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);
471 error_msg.clear();
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";
475 goto vpyerror;
478 int id = vi->format->id;
479 if (id != pfCompatBGR32
480 && id != pfCompatYUY2
481 && id != pfYUV420P8
482 && id != pfGray8
483 && id != pfYUV444P8
484 && id != pfYUV422P8
485 && id != pfYUV411P8
486 && id != pfYUV410P8
487 && id != pfYUV420P10
488 && id != pfYUV420P16
489 && id != pfYUV422P10
490 && id != pfYUV422P16) {
491 error_msg = "VFW module doesn't support ";
492 error_msg += vi->format->name;
493 error_msg += " output";
494 goto vpyerror;
497 return true;
498 } else {
499 error_msg = se.error;
500 vpyerror:
501 vi = NULL;
502 vpy_free_script(&se);
503 vpy_evaluate_text((char *)ErrorScript, "vfw_error.bleh", &se);
504 vi = se.vsapi->getVideoInfo(se.node);
505 return true;
507 } else {
508 return !!vi;
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) {
524 if (!pfi)
525 return E_POINTER;
527 if (!DelayInit())
528 return E_FAIL;
530 AVIFILEINFOW afi;
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;
537 afi.dwStreams = 1;
538 afi.dwSuggestedBufferSize = 0;
539 afi.dwWidth = vi->width;
540 afi.dwHeight = vi->height;
541 afi.dwEditCount = 0;
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)));
552 return S_OK;
555 static inline char BePrintable(int ch) {
556 ch &= 0xff;
557 return isprint(ch) ? ch : '.';
561 STDMETHODIMP VapourSynthFile::GetStream(PAVISTREAM *ppStream, DWORD fccType, LONG lParam) {
562 VapourSynthStream *casr;
563 char fcc[5];
565 fcc[0] = BePrintable(fccType );
566 fcc[1] = BePrintable(fccType >> 8);
567 fcc[2] = BePrintable(fccType >> 16);
568 fcc[3] = BePrintable(fccType >> 24);
569 fcc[4] = 0;
571 if (!DelayInit())
572 return E_FAIL;
574 *ppStream = NULL;
576 if (!fccType) {
577 if (lParam==0)
578 fccType = streamtypeVIDEO;
581 if (lParam > 0)
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;
596 } else
597 return AVIERR_NODATA;
599 return S_OK;
603 ////////////////////////////////////////////////////////////////////////
604 //////////// IAvisynthClipInfo
606 int __stdcall VapourSynthFile::GetError(const char** ppszMessage) {
607 if (!DelayInit() && error_msg.empty())
608 error_msg = "VapourSynth: script open failed!";
610 if (ppszMessage)
611 *ppszMessage = error_msg.c_str();
612 return !error_msg.empty();
615 bool __stdcall VapourSynthFile::GetParity(int n) {
616 if (!DelayInit())
617 return false;
618 return false;
621 bool __stdcall VapourSynthFile::IsFieldBased() {
622 if (!DelayInit())
623 return false;
624 return false;
627 ////////////////////////////////////////////////////////////////////////
629 // VapourSynthStream
631 ////////////////////////////////////////////////////////////////////////
632 //////////// IAVIStreaming
634 STDMETHODIMP VapourSynthStream::Begin(LONG lStart, LONG lEnd, LONG lRate) {
635 return S_OK;
638 STDMETHODIMP VapourSynthStream::End() {
639 return S_OK;
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 ////////////////////////////////////////////////////////////////////////
669 //////////// local
671 VapourSynthStream::VapourSynthStream(VapourSynthFile *parentPtr, bool isAudio) {
672 sName = "video";
673 m_refs = 0;
674 AddRef();
675 parent = parentPtr;
676 parent->AddRef();
679 VapourSynthStream::~VapourSynthStream() {
680 if (parent)
681 parent->Release();
684 ////////////////////////////////////////////////////////////////////////
685 //////////// IAVIStream
687 STDMETHODIMP_(LONG) VapourSynthStream::Info(AVISTREAMINFOW *psi, LONG lSize) {
688 if (!psi)
689 return E_POINTER;
691 AVISTREAMINFOW asi;
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';
727 else
728 return E_FAIL;
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)));
742 return S_OK;
745 STDMETHODIMP_(LONG) VapourSynthStream::FindSample(LONG lPos, LONG lFlags) {
746 if (lFlags & FIND_FORMAT)
747 return -1;
749 if (lFlags & FIND_FROM_START)
750 return 0;
752 return lPos;
756 ////////////////////////////////////////////////////////////////////////
757 //////////// local
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);
768 if (!f)
769 return false;
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);
776 int out_pitch;
777 int out_pitchUV;
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
788 else {
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));
812 out_line += 4;
813 yline += 6;
814 uline += 3;
815 vline += 3;
817 outbuf += out_pitch;
818 yptr += pstride_y;
819 uptr += pstride_uv;
820 vptr += pstride_uv;
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;
833 yptr += pstride;
835 } else {
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;
857 uptr += pstride;
858 vptr += pstride;
860 } else {
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;
867 uptr += pstride;
868 vptr += pstride;
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) );
883 vsapi->freeFrame(f);
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);
890 return true;
893 ////////////////////////////////////////////////////////////////////////
894 //////////// IAVIStream
896 STDMETHODIMP VapourSynthStream::Read(LONG lStart, LONG lSamples, LPVOID lpBuffer, LONG cbBuffer, LONG *plBytes, LONG *plSamples) {
897 parent->Lock();
898 HRESULT result = Read2(lStart, lSamples, lpBuffer, cbBuffer, plBytes, plSamples);
899 parent->Unlock();
900 return result;
903 HRESULT VapourSynthStream::Read2(LONG lStart, LONG lSamples, LPVOID lpBuffer, LONG cbBuffer, LONG *plBytes, LONG *plSamples) {
904 if (lStart >= parent->vi->numFrames) {
905 if (plSamples)
906 *plSamples = 0;
907 if (plBytes)
908 *plBytes = 0;
909 return S_OK;
912 int image_size = parent->ImageSize();
913 if (plSamples)
914 *plSamples = 1;
915 if (plBytes)
916 *plBytes = image_size;
918 if (!lpBuffer) {
919 return S_OK;
920 } else if (cbBuffer < image_size) {
921 return AVIERR_BUFFERTOOSMALL;
924 if (!ReadFrame(lpBuffer, lStart))
925 return E_FAIL;
926 return S_OK;
929 STDMETHODIMP VapourSynthStream::ReadFormat(LONG lPos, LPVOID lpFormat, LONG *lpcbFormat) {
930 if (!lpcbFormat)
931 return E_POINTER;
933 if (!lpFormat) {
934 *lpcbFormat = sizeof(BITMAPINFOHEADER);
935 return S_OK;
938 memset(lpFormat, 0, *lpcbFormat);
940 const VSVideoInfo* const vi = parent->vi;
942 BITMAPINFOHEADER bi;
943 memset(&bi, 0, sizeof(bi));
944 bi.biSize = sizeof(bi);
945 bi.biWidth = vi->width;
946 bi.biHeight = vi->height;
947 bi.biPlanes = 1;
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';
977 else
978 return E_FAIL;
980 bi.biSizeImage = parent->ImageSize();
981 *lpcbFormat = min(*lpcbFormat, sizeof(bi));
982 memcpy(lpFormat, &bi, size_t(*lpcbFormat));
984 return S_OK;
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;