1 #include "config.h"
2
3 #include <stdlib.h>
4 #include <stdio.h>
5 #include <string.h>
6 #include <pthread.h>
7 #include <colormodels.h>
8 #pragma GCC diagnostic ignored "-Wstrict-prototypes"
9 #include <quicktime.h>
10 #include <lqt.h>
11 #pragma GCC diagnostic warning "-Wstrict-prototypes"
12
13 #include "grab-ng.h"
14
15 /* ----------------------------------------------------------------------- */
16
17 struct qt_video_priv {
18 char fcc[5];
19 int yuvsign;
20 int libencode;
21 int cmodel;
22 };
23
24 struct qt_audio_priv {
25 char fcc[5];
26 int libencode;
27 };
28
29 struct qt_handle {
30 /* libquicktime handle */
31 quicktime_t *fh;
32
33 /* format */
34 struct ng_video_fmt video;
35 struct ng_audio_fmt audio;
36
37 /* misc */
38 int lib_video;
39 int lib_audio;
40 int yuvsign;
41 int audio_sample;
42 unsigned char **rows;
43 unsigned char *data;
44 };
45
46 /* ----------------------------------------------------------------------- */
47
48 static void*
qt_open(char * filename,char * dummy,struct ng_video_fmt * video,const void * priv_video,int fps,struct ng_audio_fmt * audio,const void * priv_audio)49 qt_open(char *filename, char *dummy,
50 struct ng_video_fmt *video, const void *priv_video, int fps,
51 struct ng_audio_fmt *audio, const void *priv_audio)
52 {
53 const struct qt_video_priv *pvideo = priv_video;
54 const struct qt_audio_priv *paudio = priv_audio;
55 struct qt_handle *h;
56
57 if (NULL == (h = malloc(sizeof(*h))))
58 return NULL;
59
60 memset(h,0,sizeof(*h));
61 h->video = *video;
62 h->audio = *audio;
63 if (h->video.fmtid != VIDEO_NONE) {
64 h->lib_video = pvideo->libencode;
65 h->yuvsign = pvideo->yuvsign;
66 }
67 if (h->audio.fmtid != AUDIO_NONE)
68 h->lib_audio = paudio->libencode;
69
70 if (NULL == (h->fh = quicktime_open(filename,0,1))) {
71 fprintf(stderr,"quicktime_open failed (%s)\n",filename);
72 goto fail;
73 }
74 if (h->lib_video)
75 if (NULL == (h->rows = malloc(h->video.height * sizeof(char*))))
76 goto fail;
77 if (h->yuvsign)
78 if (NULL == (h->data = malloc(h->video.height * h->video.width * 2)))
79 goto fail;
80
81 if (h->audio.fmtid != AUDIO_NONE) {
82 quicktime_set_audio(h->fh,
83 ng_afmt_to_channels[h->audio.fmtid],
84 h->audio.rate,
85 ng_afmt_to_bits[h->audio.fmtid],
86 (char*)paudio->fcc);
87 h->audio_sample = ng_afmt_to_channels[h->audio.fmtid] *
88 ng_afmt_to_bits[h->audio.fmtid] / 8;
89 if (h->lib_audio) {
90 if (!quicktime_supported_audio(h->fh, 0)) {
91 fprintf(stderr,"libquicktime: audio codec not supported\n");
92 goto fail;
93 }
94 }
95 }
96 if (h->video.fmtid != VIDEO_NONE) {
97 quicktime_set_video(h->fh,1,h->video.width,h->video.height,
98 (float)fps/1000,(char*)pvideo->fcc);
99 if (h->lib_video) {
100 quicktime_set_cmodel(h->fh,pvideo->cmodel);
101 if (!quicktime_supported_video(h->fh, 0)) {
102 fprintf(stderr,"libquicktime: video codec not supported\n");
103 goto fail;
104 }
105 }
106 }
107 quicktime_set_info(h->fh, "Dumme Bemerkungen gibt's hier umsonst.");
108 return h;
109
110 fail:
111 if (h->rows)
112 free(h->rows);
113 if (h->data)
114 free(h->data);
115 free(h);
116 return NULL;
117 }
118
119 static int
qt_video(void * handle,struct ng_video_buf * buf)120 qt_video(void *handle, struct ng_video_buf *buf)
121 {
122 struct qt_handle *h = handle;
123 unsigned int *src,*dest;
124 int rc,i,n;
125
126 if (h->lib_video) {
127 unsigned int row,len;
128 char *line;
129
130 /* QuickTime library expects an array of pointers to image rows (RGB) */
131 len = h->video.width * 3;
132 for (row = 0, line = buf->data; row < h->video.height; row++, line += len)
133 h->rows[row] = line;
134 rc = quicktime_encode_video(h->fh, h->rows, 0);
135
136 } else if (h->yuvsign) {
137 dest = (unsigned int *)h->data;
138 src = (unsigned int *)buf->data;
139 n = buf->size / 4;
140 /* U V values are signed but Y R G B values are unsigned. */
141 for (i = 0; i < n; i++) {
142 #if BYTE_ORDER == BIG_ENDIAN
143 *(dest++) = *(src++) ^ 0x00800080;
144 #else
145 *(dest++) = *(src++) ^ 0x80008000;
146 #endif
147 }
148 rc = quicktime_write_frame(h->fh, h->data, buf->size, 0);
149
150 } else {
151 rc = quicktime_write_frame(h->fh, buf->data, buf->size, 0);
152 }
153 return rc;
154 }
155
156 static int
qt_audio(void * handle,struct ng_audio_buf * buf)157 qt_audio(void *handle, struct ng_audio_buf *buf)
158 {
159 struct qt_handle *h = handle;
160 int16_t *ch[2];
161
162 if (h->lib_audio) {
163 /* FIXME: works for one channel (mono) only */
164 ch[0] = (int16_t*)buf->data;
165 return quicktime_encode_audio(h->fh, ch, NULL,
166 buf->size / h->audio_sample);
167 } else {
168 return quicktime_write_audio(h->fh, buf->data,
169 buf->size / h->audio_sample, 0);
170 }
171 }
172
173 static int
qt_close(void * handle)174 qt_close(void *handle)
175 {
176 struct qt_handle *h = handle;
177
178 quicktime_close(h->fh);
179 if (h->rows)
180 free(h->rows);
181 if (h->data)
182 free(h->data);
183 free(h);
184 return 0;
185 }
186
187 /* ----------------------------------------------------------------------- */
188
189 static int cmodels[] = {
190 [BC_BGR888] = VIDEO_BGR24,
191 [BC_RGB888] = VIDEO_RGB24,
192 [BC_YUV422] = VIDEO_YUYV,
193 [BC_YUV422P] = VIDEO_YUV422P,
194 [BC_YUV420P] = VIDEO_YUV420P,
195 };
196
197 static struct qt_video_priv qt_raw = {
198 .fcc = QUICKTIME_RAW,
199 .libencode = 0,
200 };
201 static struct qt_video_priv qt_yuv2 = {
202 .fcc = QUICKTIME_YUV2,
203 .yuvsign = 1,
204 .libencode = 0,
205 };
206 static struct qt_video_priv qt_yv12 = {
207 .fcc = QUICKTIME_YUV420,
208 .libencode = 0,
209 };
210 static struct qt_video_priv qt_jpeg = {
211 .fcc = QUICKTIME_JPEG,
212 .libencode = 0,
213 };
214
215 static const struct ng_format_list qt_vformats[] = {
216 {
217 .name = "raw",
218 .ext = "mov",
219 .fmtid = VIDEO_RGB24,
220 .priv = &qt_raw,
221 },{
222 .name = "yuv2",
223 .ext = "mov",
224 .fmtid = VIDEO_YUYV,
225 .priv = &qt_yuv2,
226 },{
227 .name = "yv12",
228 .ext = "mov",
229 .fmtid = VIDEO_YUV420P,
230 .priv = &qt_yv12,
231 },{
232 .name = "jpeg",
233 .ext = "mov",
234 .fmtid = VIDEO_JPEG,
235 .priv = &qt_jpeg,
236 },{
237 /* EOF */
238 }
239 };
240
241 static struct qt_audio_priv qt_mono8 = {
242 .fcc = QUICKTIME_RAW,
243 .libencode = 0,
244 };
245 static struct qt_audio_priv qt_mono16 = {
246 .fcc = QUICKTIME_TWOS,
247 .libencode = 0,
248 };
249 static struct qt_audio_priv qt_stereo = {
250 .fcc = QUICKTIME_TWOS,
251 .libencode = 0,
252 };
253 static const struct ng_format_list qt_aformats[] = {
254 {
255 .name = "mono8",
256 .ext = "mov",
257 .fmtid = AUDIO_U8_MONO,
258 .priv = &qt_mono8,
259 },{
260 .name = "mono16",
261 .ext = "mov",
262 .fmtid = AUDIO_S16_BE_MONO,
263 .priv = &qt_mono16,
264 },{
265 .name = "stereo",
266 .ext = "mov",
267 .fmtid = AUDIO_S16_BE_STEREO,
268 .priv = &qt_stereo,
269 },{
270 /* EOF */
271 }
272 };
273
274 struct ng_writer qt_writer = {
275 .name = "qt",
276 .desc = "Apple QuickTime format",
277 .combined = 1,
278 .video = qt_vformats,
279 .audio = qt_aformats,
280 .wr_open = qt_open,
281 .wr_video = qt_video,
282 .wr_audio = qt_audio,
283 .wr_close = qt_close,
284 };
285
286 /* ----------------------------------------------------------------------- */
287
288 #if 0
289 /* debug only */
290 static void dump_codecs(void)
291 {
292 lqt_codec_info_t **info;
293 int i,j;
294
295 info = lqt_query_registry(1, 1, 1, 1);
296 for (i = 0; info[i] != NULL; i++) {
297 fprintf(stderr,"lqt: %s codec: %s [%s]\n",
298 info[i]->type == LQT_CODEC_AUDIO ? "audio" : "video",
299 info[i]->name,info[i]->long_name);
300 fprintf(stderr," encode: %s\n",
301 info[i]->direction == LQT_DIRECTION_DECODE ? "no" : "yes");
302 fprintf(stderr," decode: %s\n",
303 info[i]->direction == LQT_DIRECTION_ENCODE ? "no" : "yes");
304 for (j = 0; j < info[i]->num_fourccs; j++)
305 fprintf(stderr," fcc : %s\n",info[i]->fourccs[j]);
306 for (j = 0; j < info[i]->num_encoding_colormodels; j++)
307 fprintf(stderr," cmodel: %s\n",
308 lqt_get_colormodel_string(info[i]->encoding_colormodels[j]));
309 fprintf(stderr,"\n");
310 }
311 lqt_destroy_codec_info(info);
312 }
313 #endif
314
315 static struct ng_format_list*
qt_list_add(struct ng_format_list * list,char * name,char * desc,char * ext,int fmtid,void * priv)316 qt_list_add(struct ng_format_list* list,
317 char *name, char *desc, char *ext, int fmtid, void *priv)
318 {
319 int n;
320
321 for (n = 0; list[n].name != NULL; n++)
322 /* nothing */;
323 list = realloc(list,sizeof(struct ng_format_list)*(n+2));
324 memset(list+n,0,sizeof(struct ng_format_list)*2);
325 list[n].name = strdup(name);
326 list[n].desc = strdup(desc);
327 list[n].ext = strdup(ext);
328 list[n].fmtid = fmtid;
329 list[n].priv = priv;
330 return list;
331 }
332
video_list(void)333 static struct ng_format_list* video_list(void)
334 {
335 static int debug = 0;
336 lqt_codec_info_t **info;
337 struct ng_format_list *video;
338 int i,j,k,skip,fmtid;
339 unsigned int cmodel;
340 struct qt_video_priv *vp;
341
342 /* handle video encoders */
343 video = malloc(sizeof(qt_vformats));
344 memcpy(video,qt_vformats,sizeof(qt_vformats));
345 info = lqt_query_registry(0, 1, 1, 0);
346 for (i = 0; info[i] != NULL; i++) {
347 if (debug) {
348 fprintf(stderr,"\nlqt: %s codec: %s [%s]\n",
349 info[i]->type == LQT_CODEC_AUDIO ? "audio" : "video",
350 info[i]->name,info[i]->long_name);
351 for (j = 0; j < info[i]->num_fourccs; j++)
352 fprintf(stderr," fcc : %s\n",info[i]->fourccs[j]);
353 for (j = 0; j < info[i]->num_encoding_colormodels; j++)
354 fprintf(stderr," cmodel: %d [%s]\n",
355 info[i]->encoding_colormodels[j],
356 lqt_get_colormodel_string(info[i]->encoding_colormodels[j]));
357 }
358
359 /* sanity checks */
360 if (0 == info[i]->num_fourccs) {
361 if (debug)
362 fprintf(stderr," skipping, no fourcc\n");
363 continue;
364 }
365
366 /* avoid dup entries */
367 skip = 0;
368 for (j = 0; video[j].name != NULL; j++) {
369 const struct qt_video_priv *p = video[j].priv;
370 for (k = 0; k < info[i]->num_fourccs; k++)
371 if (0 == strcmp(p->fcc,info[i]->fourccs[k]))
372 skip = 1;
373 }
374 if (skip) {
375 if (debug)
376 fprintf(stderr," skipping, fourcc already in list\n");
377 continue;
378 }
379
380 /* pick colormodel */
381 fmtid = VIDEO_NONE;
382 cmodel = 0;
383 for (j = 0; j < info[i]->num_encoding_colormodels; j++) {
384 cmodel = info[i]->encoding_colormodels[j];
385 if (cmodel>= sizeof(cmodels)/sizeof(int))
386 continue;
387 if (!cmodels[cmodel])
388 continue;
389 fmtid = cmodels[cmodel];
390 break;
391 }
392 if (VIDEO_NONE == fmtid) {
393 if (debug)
394 fprintf(stderr," skipping, can't handle color model\n");
395 continue;
396 }
397
398 /* all fine */
399 if (debug)
400 fprintf(stderr," ok, using fmtid %d [%s]\n",
401 fmtid,ng_vfmt_to_desc[fmtid]);
402 vp = malloc(sizeof(*vp));
403 memset(vp,0,sizeof(*vp));
404 strcpy(vp->fcc,info[i]->fourccs[0]);
405 vp->libencode = 1;
406 vp->cmodel = cmodel;
407 video = qt_list_add(video,vp->fcc,info[i]->long_name,"mov",fmtid,vp);
408 }
409 lqt_destroy_codec_info(info);
410 return video;
411 }
412
audio_list(void)413 static struct ng_format_list* audio_list(void)
414 {
415 static int debug = 0;
416 lqt_codec_info_t **info;
417 struct ng_format_list *audio;
418 int i,j;
419 struct qt_audio_priv *ap;
420
421 /* handle video encoders */
422 audio = malloc(sizeof(qt_aformats));
423 memcpy(audio,qt_aformats,sizeof(qt_aformats));
424 info = lqt_query_registry(1, 0, 1, 0);
425 for (i = 0; info[i] != NULL; i++) {
426 if (debug) {
427 fprintf(stderr,"\nlqt: %s codec: %s [%s]\n",
428 info[i]->type == LQT_CODEC_AUDIO ? "audio" : "video",
429 info[i]->name,info[i]->long_name);
430 for (j = 0; j < info[i]->num_fourccs; j++)
431 fprintf(stderr," fcc : %s\n",info[i]->fourccs[j]);
432 }
433
434 /* sanity checks */
435 if (0 == info[i]->num_fourccs) {
436 if (debug)
437 fprintf(stderr," skipping, no fourcc\n");
438 continue;
439 }
440
441 /* skip uncompressed formats */
442 if (0 == strcmp(info[i]->fourccs[0],QUICKTIME_RAW) ||
443 0 == strcmp(info[i]->fourccs[0],QUICKTIME_ULAW) ||
444 0 == strcmp(info[i]->fourccs[0],QUICKTIME_IMA4) || /* ??? */
445 0 == strcmp(info[i]->fourccs[0],QUICKTIME_TWOS)) {
446 if (debug)
447 fprintf(stderr," skipping, uncompressed\n");
448 continue;
449 }
450
451 /* all fine */
452 if (debug)
453 fprintf(stderr," ok\n");
454 ap = malloc(sizeof(*ap));
455 memset(ap,0,sizeof(*ap));
456 strcpy(ap->fcc,info[i]->fourccs[0]);
457 ap->libencode = 1;
458 audio = qt_list_add(audio,ap->fcc,info[i]->long_name,"mov",
459 AUDIO_S16_NATIVE_MONO,ap);
460 }
461 lqt_destroy_codec_info(info);
462 return audio;
463 }
464
465 extern void ng_plugin_init(void);
ng_plugin_init(void)466 void ng_plugin_init(void)
467 {
468 qt_writer.video = video_list();
469 qt_writer.audio = audio_list();
470 ng_writer_register(NG_PLUGIN_MAGIC,__FILE__,&qt_writer);
471 }
472