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