1 
2 
3 #include "avicodecrestrictions.h"
4 #include "tconvert.h"
5 #include <windows.h>
6 #include <vfw.h>
7 
8 namespace {
9 
getCodec(const std::wstring & codecName,int & bpp)10 HIC getCodec(const std::wstring &codecName, int &bpp) {
11   HIC hic = 0;
12   ICINFO icinfo;
13   memset(&icinfo, 0, sizeof(ICINFO));
14   bool found = false;
15 
16   char descr[2048], name[2048];
17   DWORD fccType = 0;
18 
19   BITMAPINFO inFmt;
20   memset(&inFmt, 0, sizeof(BITMAPINFO));
21 
22   inFmt.bmiHeader.biSize  = sizeof(BITMAPINFOHEADER);
23   inFmt.bmiHeader.biWidth = inFmt.bmiHeader.biHeight = 100;
24   inFmt.bmiHeader.biPlanes                           = 1;
25   inFmt.bmiHeader.biCompression                      = BI_RGB;
26   for (bpp = 32; (bpp >= 24) && !found; bpp -= 8) {
27     // find the codec.
28     inFmt.bmiHeader.biBitCount = bpp;
29     for (int i = 0; ICInfo(fccType, i, &icinfo); i++) {
30       hic = ICOpen(icinfo.fccType, icinfo.fccHandler, ICMODE_COMPRESS);
31 
32       ICGetInfo(hic, &icinfo, sizeof(ICINFO));  // Find out the compressor name
33       WideCharToMultiByte(CP_ACP, 0, icinfo.szDescription, -1, descr,
34                           sizeof(descr), 0, 0);
35       WideCharToMultiByte(CP_ACP, 0, icinfo.szName, -1, name, sizeof(name), 0,
36                           0);
37 
38       std::string compressorName;
39       compressorName = std::string(name) + " '" + std::to_string(bpp) + "' " +
40                        std::string(descr);
41 
42       if (hic) {
43         if (ICCompressQuery(hic, &inFmt, NULL) != ICERR_OK) {
44           ICClose(hic);
45           continue;  // Skip this compressor if it can't handle the format.
46         }
47         if (::to_wstring(compressorName) == codecName) {
48           found = true;
49           break;
50         }
51         ICClose(hic);
52       }
53     }
54     if (found) break;
55   }
56   return hic;
57 }
58 
59 //-----------------------------------------------------------------------------
60 
canWork(const HIC & hic,const TDimension & resolution,int bpp)61 bool canWork(const HIC &hic, const TDimension &resolution, int bpp) {
62   int lx = resolution.lx, ly = resolution.ly;
63 
64   BITMAPINFO bi;
65   bi.bmiHeader.biSize          = sizeof(BITMAPINFOHEADER);
66   bi.bmiHeader.biPlanes        = 1;
67   bi.bmiHeader.biCompression   = BI_RGB;
68   bi.bmiHeader.biXPelsPerMeter = 80;
69   bi.bmiHeader.biYPelsPerMeter = 72;
70   bi.bmiHeader.biClrUsed       = 0;
71   bi.bmiHeader.biClrImportant  = 0;
72   bi.bmiHeader.biBitCount      = bpp;
73   bi.bmiHeader.biWidth         = lx;
74   bi.bmiHeader.biHeight        = ly;
75 
76   return ICERR_OK == ICCompressQuery(hic, &bi.bmiHeader, NULL);
77 }
78 }  // namespace
79 
80 //-----------------------------------------------------------------------------
81 
getRestrictions(const std::wstring & codecName,QString & restrictions)82 void AviCodecRestrictions::getRestrictions(const std::wstring &codecName,
83                                            QString &restrictions) {
84   restrictions.clear();
85   if (codecName == L"Uncompressed") {
86     restrictions = QObject::tr("No restrictions for uncompressed avi video");
87     return;
88   }
89   // find the codec
90   int bpp;
91   HIC hic = getCodec(codecName, bpp);
92   if (!hic) {
93     restrictions = QObject::tr(
94         "It is not possible to communicate with the codec.\n Probably the "
95         "codec cannot work correctly.");
96     return;
97   }
98 
99   BITMAPINFO bi;
100   bi.bmiHeader.biSize          = sizeof(BITMAPINFOHEADER);
101   bi.bmiHeader.biPlanes        = 1;
102   bi.bmiHeader.biCompression   = BI_RGB;
103   bi.bmiHeader.biXPelsPerMeter = 80;
104   bi.bmiHeader.biYPelsPerMeter = 72;
105   bi.bmiHeader.biClrUsed       = 0;
106   bi.bmiHeader.biClrImportant  = 0;
107 
108   int lx = 640, ly = 480;
109   bi.bmiHeader.biWidth  = lx;
110   bi.bmiHeader.biHeight = ly;
111 
112   // Loop until we can find a width, height, and depth that works!
113   int i;
114 
115   // check the x length
116   bi.bmiHeader.biBitCount = bpp;
117   for (i = 3; i >= 0; i--) {
118     bi.bmiHeader.biWidth = lx + (1 << i);
119     bi.bmiHeader.biSizeImage =
120         ((bi.bmiHeader.biWidth * bi.bmiHeader.biBitCount + 31) / 32) * 4 * ly;
121 
122     if (ICERR_OK != ICCompressQuery(hic, &bi.bmiHeader, NULL)) break;
123   }
124   if (i >= 0)
125     restrictions = QObject::tr("video width must be a multiple of %1")
126                        .arg(QString::number(1 << (i + 1)));
127 
128   // check the y length
129   bi.bmiHeader.biWidth = 640;
130   for (i = 3; i >= 0; i--) {
131     bi.bmiHeader.biHeight = ly + (1 << i);
132     bi.bmiHeader.biSizeImage =
133         ((lx * bi.bmiHeader.biBitCount + 31) / 32) * 4 * bi.bmiHeader.biHeight;
134 
135     if (ICERR_OK != ICCompressQuery(hic, &bi.bmiHeader, NULL)) break;
136   }
137   if (i >= 0)
138     restrictions = restrictions + "\n" +
139                    QObject::tr("video length must be a multiple of %1")
140                        .arg(QString::number(1 << (i + 1)));
141 
142   ICClose(hic);
143 
144   if (restrictions.isEmpty())
145     restrictions = QObject::tr("No restrictions for this codec");
146   else
147     restrictions.prepend(QObject::tr("Resolution restrictions:") + "\n");
148 }
149 
150 //-----------------------------------------------------------------------------
151 
canWriteMovie(const std::wstring & codecName,const TDimension & resolution)152 bool AviCodecRestrictions::canWriteMovie(const std::wstring &codecName,
153                                          const TDimension &resolution) {
154   if (codecName == L"Uncompressed") {
155     return true;
156   }
157   // find the codec
158   int bpp;
159   HIC hic = getCodec(codecName, bpp);
160   if (!hic) return false;
161 
162   bool test = canWork(hic, resolution, bpp);
163 
164   ICClose(hic);
165   return test;
166 }
167 
168 //-----------------------------------------------------------------------------
169 
canBeConfigured(const std::wstring & codecName)170 bool AviCodecRestrictions::canBeConfigured(const std::wstring &codecName) {
171   if (codecName == L"Uncompressed") return false;
172 
173   // find the codec
174   int bpp;
175   HIC hic = getCodec(codecName, bpp);
176   if (!hic) return false;
177 
178   bool test = ICQueryConfigure(hic);
179   ICClose(hic);
180   return test;
181 }
182 
183 //-----------------------------------------------------------------------------
184 
openConfiguration(const std::wstring & codecName,void * winId)185 void AviCodecRestrictions::openConfiguration(const std::wstring &codecName,
186                                              void *winId) {
187   if (codecName == L"Uncompressed") return;
188 
189   // find the codec
190   int bpp;
191   HIC hic = getCodec(codecName, bpp);
192   if (!hic) return;
193 
194   ICConfigure(hic, winId);
195   ICClose(hic);
196 }
197 
198 //-----------------------------------------------------------------------------
199 
getUsableCodecs(const TDimension & resolution)200 QMap<std::wstring, bool> AviCodecRestrictions::getUsableCodecs(
201     const TDimension &resolution) {
202   QMap<std::wstring, bool> codecs;
203 
204   HIC hic = 0;
205   ICINFO icinfo;
206   memset(&icinfo, 0, sizeof(ICINFO));
207 
208   char descr[2048], name[2048];
209   DWORD fccType = 0;
210 
211   BITMAPINFO inFmt;
212   memset(&inFmt, 0, sizeof(BITMAPINFO));
213 
214   inFmt.bmiHeader.biSize  = sizeof(BITMAPINFOHEADER);
215   inFmt.bmiHeader.biWidth = inFmt.bmiHeader.biHeight = 100;
216   inFmt.bmiHeader.biPlanes                           = 1;
217   inFmt.bmiHeader.biCompression                      = BI_RGB;
218   int bpp;
219   for (bpp = 32; (bpp >= 24); bpp -= 8) {
220     // find the codec.
221     inFmt.bmiHeader.biBitCount = bpp;
222     for (int i = 0; ICInfo(fccType, i, &icinfo); i++) {
223       hic = ICOpen(icinfo.fccType, icinfo.fccHandler, ICMODE_COMPRESS);
224 
225       ICGetInfo(hic, &icinfo, sizeof(ICINFO));  // Find out the compressor name
226       WideCharToMultiByte(CP_ACP, 0, icinfo.szDescription, -1, descr,
227                           sizeof(descr), 0, 0);
228       WideCharToMultiByte(CP_ACP, 0, icinfo.szName, -1, name, sizeof(name), 0,
229                           0);
230       // Give up to load codecs once the blackmagic codec is found -
231       // as it seems to cause crash for unknown reasons (issue #138)
232       if (strstr(descr, "Blackmagic") != 0) break;
233 
234       std::wstring compressorName;
235       compressorName =
236           ::to_wstring(std::string(name) + " '" + std::to_string(bpp) + "' " +
237                        std::string(descr));
238 
239       if (hic) {
240         if (ICCompressQuery(hic, &inFmt, NULL) != ICERR_OK) {
241           ICClose(hic);
242           continue;  // Skip this compressor if it can't handle the format.
243         }
244         codecs[compressorName] = canWork(hic, resolution, bpp);
245         ICClose(hic);
246       }
247     }
248   }
249   return codecs;
250 }
251