1 /*
2 * import_framegen.c -- generate an infinite stream of synthetic frames
3 * for testing purposes.
4 * (C) 2009-2010 Francesco Romani <fromani at gmail dot com>
5 * using some video frame filling code which is
6 * Copyright (C) Thomas Oestreich - June 2001
7 * some code derived by alsa-utils/speakertest, which is
8 * Copyright (C) 2005 Nathan Hurst
9 *
10 * This file is part of transcode, a video stream processing tool.
11 *
12 * transcode is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * transcode is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program. If not, see <http://www.gnu.org/licenses/>.
24 */
25
26 /*%*
27 *%* DESCRIPTION
28 *%* This module produces an infinite stream of raw synthetic frames.
29 *%* It is intended for testing purposes.
30 *%*
31 *%* #BUILD-DEPENDS
32 *%*
33 *%* #DEPENDS
34 *%*
35 *%* PROCESSING
36 *%* import/demuxer
37 *%*
38 *%* MEDIA
39 *%* video, audio
40 *%*
41 *%* #INPUT
42 *%*
43 *%* OUTPUT
44 *%* YUV420P, PCM
45 *%*
46 *%* #OPTION
47 *%*/
48
49 /*
50 * TODO (unordered):
51 * - add more generators (at least a video/RGB one)
52 * - review internal generator API
53 * -- to emit cooked frames instead of raw bytes it is better?
54 * -- need to explicitely separate A/V generators?
55 * -- need to distinguish the function to call for A/V?
56 * - accepts (and use!) random seed as module options
57 */
58
59 #include "src/transcode.h"
60 #include "aclib/imgconvert.h"
61 #include "libtc/optstr.h"
62 #include "libtc/tcmodule-plugin.h"
63
64 #include "pink.h"
65
66 #include "config.h"
67
68 #define LEGACY 1
69
70 #ifdef LEGACY
71 # define MOD_NAME "import_framegen.so"
72 #else
73 # define MOD_NAME "demultiplex_framegen.so"
74 #endif
75
76
77 #define MOD_VERSION "v0.1.0 (2009-06-21)"
78 #define MOD_CAP "generate stream of testframes"
79 #define MOD_AUTHOR "Francesco Romani"
80
81 #define MOD_FEATURES \
82 TC_MODULE_FEATURE_DEMULTIPLEX|TC_MODULE_FEATURE_AUDIO|TC_MODULE_FEATURE_VIDEO
83 #define MOD_FLAGS \
84 TC_MODULE_FLAG_RECONFIGURABLE
85
86 /*************************************************************************/
87
88 typedef struct tcframegensource_ TCFrameGenSource;
89
90 struct tcframegensource_ {
91 void *priv;
92 const char *name;
93 const char *media;
94
95 int32_t seed;
96
97 int (*get_data)(TCFrameGenSource *handle,
98 uint8_t *data, int maxdata, int *datalen);
99
100 int (*close)(TCFrameGenSource *handle);
101 };
102
tc_framegen_source_get_data(TCFrameGenSource * handle,uint8_t * data,int maxdata,int * datalen)103 static int tc_framegen_source_get_data(TCFrameGenSource *handle,
104 uint8_t *data, int maxdata,
105 int *datalen)
106 {
107 return handle->get_data(handle, data, maxdata, datalen);
108 }
109
tc_framegen_source_close(TCFrameGenSource * handle)110 static int tc_framegen_source_close(TCFrameGenSource *handle)
111 {
112 return handle->close(handle);
113 }
114
framegen_generic_close(TCFrameGenSource * handle)115 static int framegen_generic_close(TCFrameGenSource *handle)
116 {
117 tc_free(handle);
118 return TC_OK;
119 }
120
121 /*************************************************************************/
122
123 typedef struct pinknoisedata_ PinkNoiseData;
124 struct pinknoisedata_ {
125 pink_noise_t pink;
126 };
127
128 /* only signed 16 bits LE samples supported so far */
framegen_pink_noise_get_data(TCFrameGenSource * handle,uint8_t * data,int maxdata,int * datalen)129 static int framegen_pink_noise_get_data(TCFrameGenSource *handle,
130 uint8_t *data, int maxdata, int *datalen)
131 {
132 PinkNoiseData *PN = handle->priv;
133 int16_t *samples = (int16_t*)data;
134 int i;
135
136 for (i = 0; i < maxdata; i++) {
137 int32_t res = generate_pink_noise_sample(&(PN->pink)) * 0x03fffffff; /* Don't use MAX volume */
138 samples[i] = res >> 16;
139 }
140
141 return TC_OK;
142 }
143
framegen_pink_noise_init(PinkNoiseData * PN,vob_t * vob)144 static int framegen_pink_noise_init(PinkNoiseData *PN, vob_t *vob)
145 {
146 int ret = TC_ERROR;
147 if (vob->a_bits == 16) {
148 initialize_pink_noise(&(PN->pink), 16);
149 /* this doesn't depend to the sample size */
150
151 ret = TC_OK;
152 }
153 return ret;
154 }
155
tc_framegen_source_open_audio_pink_noise(vob_t * vob,int32_t seed)156 static TCFrameGenSource *tc_framegen_source_open_audio_pink_noise(vob_t *vob,
157 int32_t seed)
158 {
159 uint8_t *p = tc_zalloc(sizeof(TCFrameGenSource) + sizeof(PinkNoiseData));
160 TCFrameGenSource *FG = (TCFrameGenSource*)p;
161 PinkNoiseData *PN = (PinkNoiseData*)(p + sizeof(TCFrameGenSource));
162 if (FG) {
163 int ret = framegen_pink_noise_init(PN, vob);
164 if (ret != TC_OK) {
165 tc_free(p);
166 FG = NULL;
167 PN = NULL;
168 } else {
169 FG->priv = PN;
170 FG->name = "pink noise generator";
171 FG->media = "audio";
172
173 FG->get_data = framegen_pink_noise_get_data;
174 FG->close = framegen_generic_close;
175 }
176 }
177 return FG;
178 }
179
180 /*************************************************************************/
181
182 typedef struct colorwavedata_ ColorWaveData;
183 struct colorwavedata_ {
184 int width;
185 int height;
186 int index;
187 };
188
framegen_color_wave_get_data(TCFrameGenSource * handle,uint8_t * data,int maxdata,int * datalen)189 static int framegen_color_wave_get_data(TCFrameGenSource *handle,
190 uint8_t *data, int maxdata, int *datalen)
191
192 {
193 ColorWaveData *CW = handle->priv;
194 uint8_t *planes[3] = { NULL, NULL, NULL };
195 int size = CW->width * CW->height * 3 / 2;
196 int x, y;
197
198 if (maxdata < size) {
199 return TC_ERROR;
200 }
201
202 YUV_INIT_PLANES(planes, data, IMG_YUV_DEFAULT, CW->width, CW->height);
203
204 memset(data, 0x80, size); /* FIXME */
205
206 for (y = 0; y < CW->height; y++) {
207 for (x = 0; x < CW->width; x++) {
208 planes[0][y * CW->width + x] = x + y + CW->index * 3;
209 }
210 }
211
212 for (y = 0; y < CW->height/2; y++) {
213 for (x = 0; x < CW->width/2; x++) {
214 planes[1][y * CW->width/2 + x] = 128 + y + CW->index * 2;
215 planes[2][y * CW->width/2 + x] = 64 + x + CW->index * 5;
216 }
217 }
218
219 CW->index++;
220 *datalen = size;
221
222 return TC_OK;
223 }
224
framegen_color_wave_init(ColorWaveData * CW,vob_t * vob)225 static int framegen_color_wave_init(ColorWaveData *CW, vob_t *vob)
226 {
227 int ret = TC_ERROR;
228 if (vob->im_v_codec == TC_CODEC_YUV420P || vob->im_v_codec == CODEC_YUV) {
229 CW->index = 0;
230 CW->width = vob->im_v_width;
231 CW->height = vob->im_v_height;
232 ret = TC_OK;
233 }
234 return ret;
235 }
236
tc_framegen_source_open_video_color_wave(vob_t * vob,int32_t seed)237 static TCFrameGenSource *tc_framegen_source_open_video_color_wave(vob_t *vob,
238 int32_t seed)
239 {
240 uint8_t *p = tc_zalloc(sizeof(TCFrameGenSource) + sizeof(ColorWaveData));
241 TCFrameGenSource *FG = (TCFrameGenSource*)p;
242 ColorWaveData *CW = (ColorWaveData*)(p + sizeof(TCFrameGenSource));
243 if (FG) {
244 int ret = framegen_color_wave_init(CW, vob);
245 if (ret != TC_OK) {
246 tc_free(p);
247 FG = NULL;
248 CW = NULL;
249 } else {
250 FG->priv = CW;
251 FG->name = "color wave generator";
252 FG->media = "video";
253
254 FG->get_data = framegen_color_wave_get_data;
255 FG->close = framegen_generic_close;
256 }
257 }
258 return FG;
259 }
260
261 /*************************************************************************/
262
263 #define RETURN_IF_FAILED(RET, MSG) do { \
264 if ((RET) < 0) { \
265 tc_log_error(MOD_NAME, "%s", (MSG)); \
266 return TC_ERROR; \
267 } \
268 } while (0)
269
270 #define RETURN_IF_ERROR(RET, MSG) do { \
271 if ((RET) != TC_OK) { \
272 tc_log_error(MOD_NAME, "%s", (MSG)); \
273 return RET; \
274 } \
275 } while (0)
276
277 #define RETURN_IF_NULL(PTR, MSG) do { \
278 if (!(PTR)) { \
279 tc_log_error(MOD_NAME, "%s", (MSG)); \
280 return TC_ERROR; \
281 } \
282 } while (0)
283
284
285 /*************************************************************************/
286 /* New-Style module interface */
287
288 static const char tc_framegen_help[] = ""
289 "Overview:\n"
290 " This module reads audio samples from an ALSA device using libalsa.\n"
291 "Options:\n"
292 " device=dev selects ALSA device to use\n"
293 " help produce module overview and options explanations\n";
294
295
296
297 typedef struct tcframegenprivatedata_ TCFrameGenPrivateData;
298 struct tcframegenprivatedata_ {
299 TCFrameGenSource *video_gen;
300 TCFrameGenSource *audio_gen;
301 };
302
303
TC_MODULE_GENERIC_INIT(tc_framegen,TCFrameGenPrivateData)304 TC_MODULE_GENERIC_INIT(tc_framegen, TCFrameGenPrivateData)
305
306 TC_MODULE_GENERIC_FINI(tc_framegen)
307
308 static int tc_framegen_configure(TCModuleInstance *self,
309 const char *options, vob_t *vob)
310 {
311 TCFrameGenPrivateData *priv = NULL;
312
313 TC_MODULE_SELF_CHECK(self, "configure");
314
315 priv = self->userdata;
316
317 /* FIXME: add proper option handling/mode selection */
318
319 priv->video_gen = tc_framegen_source_open_video_color_wave(vob, 0);
320 RETURN_IF_NULL((priv->video_gen),
321 "configure: failed to open the video frame generator");
322
323 priv->audio_gen = tc_framegen_source_open_audio_pink_noise(vob, 0);
324 RETURN_IF_NULL((priv->audio_gen),
325 "configure: failed to open the audio frame generator");
326
327 return TC_OK;
328 }
329
tc_framegen_inspect(TCModuleInstance * self,const char * param,const char ** value)330 static int tc_framegen_inspect(TCModuleInstance *self,
331 const char *param, const char **value)
332 {
333 TC_MODULE_SELF_CHECK(self, "inspect");
334
335 if (optstr_lookup(param, "help")) {
336 *value = tc_framegen_help;
337 }
338
339 return TC_OK;
340 }
341
tc_framegen_stop(TCModuleInstance * self)342 static int tc_framegen_stop(TCModuleInstance *self)
343 {
344 TCFrameGenPrivateData *priv = NULL;
345 int ret = 0;
346
347 TC_MODULE_SELF_CHECK(self, "stop");
348
349 priv = self->userdata;
350
351 ret = tc_framegen_source_close(priv->video_gen);
352 RETURN_IF_ERROR(ret, "stop: failed to close the video frame generator");
353
354 ret = tc_framegen_source_close(priv->audio_gen);
355 RETURN_IF_ERROR(ret, "stop: failed to close the audio frame generator");
356
357 return TC_OK;
358 }
359
tc_framegen_demultiplex(TCModuleInstance * self,TCFrameVideo * vframe,TCFrameAudio * aframe)360 static int tc_framegen_demultiplex(TCModuleInstance *self,
361 TCFrameVideo *vframe, TCFrameAudio *aframe)
362 {
363 int vret = 0, aret = 0;
364 TCFrameGenPrivateData *priv = NULL;
365
366 TC_MODULE_SELF_CHECK(self, "demultiplex");
367
368 priv = self->userdata;
369
370 if (vframe != NULL) {
371 vret = tc_framegen_source_get_data(priv->video_gen,
372 vframe->video_buf,
373 vframe->video_size,
374 &vframe->video_len);
375 RETURN_IF_FAILED(vret, "demux: failed to pull a new video frame");
376 }
377
378 if (aframe != NULL) {
379 aret = tc_framegen_source_get_data(priv->audio_gen,
380 aframe->audio_buf,
381 aframe->audio_size,
382 &aframe->audio_len);
383 RETURN_IF_FAILED(aret, "demux: failed to pull a new audio frame");
384 }
385 return (vret + aret);
386 }
387
388 /*************************************************************************/
389
390 static const TCCodecID tc_framegen_codecs_in[] = { TC_CODEC_ERROR };
391
392 /* a demultiplexor is at the begin of the pipeline */
393 static const TCCodecID tc_framegen_codecs_out[] = {
394 TC_CODEC_YUV420P,
395 TC_CODEC_PCM,
396 TC_CODEC_ERROR,
397 };
398
399 static const TCFormatID tc_framegen_formats_in[] = {
400 TC_FORMAT_ERROR,
401 };
402
403 static const TCFormatID tc_framegen_formats_out[] = { TC_FORMAT_ERROR };
404
405 static const TCModuleInfo tc_framegen_info = {
406 .features = MOD_FEATURES,
407 .flags = MOD_FLAGS,
408 .name = MOD_NAME,
409 .version = MOD_VERSION,
410 .description = MOD_CAP,
411 .codecs_in = tc_framegen_codecs_in,
412 .codecs_out = tc_framegen_codecs_out,
413 .formats_in = tc_framegen_formats_in,
414 .formats_out = tc_framegen_formats_out
415 };
416
417 static const TCModuleClass tc_framegen_class = {
418 TC_MODULE_CLASS_HEAD(tc_framegen),
419
420 .init = tc_framegen_init,
421 .fini = tc_framegen_fini,
422 .configure = tc_framegen_configure,
423 .stop = tc_framegen_stop,
424 .inspect = tc_framegen_inspect,
425
426 .demultiplex = tc_framegen_demultiplex,
427 };
428
429 TC_MODULE_ENTRY_POINT(tc_framegen)
430
431 /*************************************************************************/
432 /* Old-Style module interface */
433 /*************************************************************************/
434
435
436 static TCFrameGenPrivateData mod_framegen;
437
438 static int verbose_flag = TC_QUIET;
439 static int capability_flag = TC_CAP_YUV|TC_CAP_PCM;
440
441 #define MOD_PRE framegen
442 #define MOD_CODEC "(video) YUV | (audio) PCM"
443
444 #include "import_def.h"
445
446 /*************************************************************************/
447
448 #define RETURN_WITH_MSG(RET, MSG) do { \
449 if ((RET) != TC_OK) { \
450 tc_log_error(MOD_NAME, "%s", (MSG)); \
451 return RET; \
452 } \
453 return TC_OK; \
454 } while (0)
455
456
457
458 MOD_open
459 {
460 if(param->flag == TC_AUDIO) {
461 param->fd = NULL;
462 mod_framegen.audio_gen = tc_framegen_source_open_audio_pink_noise(vob, 0);
463
464 RETURN_IF_NULL((mod_framegen.audio_gen),
465 "MOD_open: failed to open the audio frame generator");
466 return TC_OK;
467 }
468
469 if(param->flag == TC_VIDEO) {
470 param->fd = NULL;
471 mod_framegen.video_gen = tc_framegen_source_open_video_color_wave(vob, 0);
472
473 RETURN_IF_NULL((mod_framegen.video_gen),
474 "configure: failed to open the video frame generator");
475 return TC_OK;
476 }
477
478 return TC_ERROR;
479 }
480
481 MOD_decode
482 {
483 int ret;
484
485 if (param->flag == TC_AUDIO) {
486 ret = tc_framegen_source_get_data(mod_framegen.audio_gen,
487 param->buffer,
488 param->size,
489 ¶m->size);
490 RETURN_WITH_MSG(ret, "MOD_decode: failed to pull a new audio frame");
491 }
492
493 if(param->flag == TC_VIDEO) {
494 ret = tc_framegen_source_get_data(mod_framegen.video_gen,
495 param->buffer,
496 param->size,
497 ¶m->size);
498 RETURN_WITH_MSG(ret, "MOD_decode: failed to pull a new video frame");
499 }
500
501 return TC_ERROR;
502 }
503
504 MOD_close
505 {
506 int ret;
507
508 if(param->flag == TC_AUDIO) {
509 ret = tc_framegen_source_close(mod_framegen.audio_gen);
510 RETURN_WITH_MSG(ret,
511 "MOD_close: failed to close the audio frame generator");
512 }
513
514 if(param->flag == TC_VIDEO) {
515 ret = tc_framegen_source_close(mod_framegen.video_gen);
516 RETURN_WITH_MSG(ret,
517 "MOD_close: failed to close the video frame generator");
518 }
519
520 return TC_ERROR;
521 }
522
523 /*************************************************************************/
524
525 /*
526 * Local variables:
527 * c-file-style: "stroustrup"
528 * c-file-offsets: ((case-label . *) (statement-case-intro . *))
529 * indent-tabs-mode: nil
530 * End:
531 *
532 * vim: expandtab shiftwidth=4:
533 */
534
535