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                                           &param->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                                           &param->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