From ae69a754244c4e475c8d2591772ca8e005071d83 Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Mon, 18 Feb 2019 11:55:41 +0100 Subject: Update to 5.42 --- hacks/analogtv-cli.c | 967 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 967 insertions(+) create mode 100644 hacks/analogtv-cli.c (limited to 'hacks/analogtv-cli.c') diff --git a/hacks/analogtv-cli.c b/hacks/analogtv-cli.c new file mode 100644 index 0000000..d70db1b --- /dev/null +++ b/hacks/analogtv-cli.c @@ -0,0 +1,967 @@ +/* xanalogtv-cli, Copyright (c) 2018 Jamie Zawinski + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation. No representations are made about the suitability of this + * software for any purpose. It is provided "as is" without express or + * implied warranty. + * + * Performs the "Analog TV" transform on an image file, converting it to + * an MP4. The MP4 file will have the same dimensions as the input image. + * Requires 'ffmpeg' on $PATH. + * + * --duration Length in seconds of MP4. + * --powerup Do the power-on animation at the beginning. + * --logo FILE Small image overlayed onto the colorbars image. + * --audio FILE Add a soundtrack. Must be as long or longer. + * + * Created: 10-Dec-2018 by jwz. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "resources.h" +#include "visual.h" +#include "yarandom.h" +#include "font-retry.h" +#include "ximage-loader.h" +#include "thread_util.h" +#include "xshm.h" +#include "analogtv.h" + +#ifdef HAVE_LIBPNG +# include +#endif + +const char *progname; +const char *progclass; +int mono_p = 0; +static Bool verbose_p = 0; + +#define RANDSIGN() ((random() & 1) ? 1 : -1) + +enum { + N_CHANNELS=12, + MAX_MULTICHAN=2, + MAX_STATIONS=11 +}; + +typedef struct chansetting_s { + analogtv_reception recs[MAX_MULTICHAN]; + double noise_level; +} chansetting; + +struct state { + XImage *output_frame; + int frames_written; + char *framefile_fmt; + + Display *dpy; + Window window; + analogtv *tv; + analogtv_font ugly_font; + + int n_stations; + analogtv_input *stations[MAX_STATIONS]; + Bool image_loading_p; + XImage *logo, *logo_mask; + + int curinputi; + chansetting chansettings[N_CHANNELS]; + chansetting *cs; +}; + +static struct state global_state; + + +/* Since this program does not connect to an X server, or in fact link + with Xlib, we need stubs for the few X11 routines that analogtv.c calls. + Most are unused. It seems like I am forever implementing subsets of X11. + */ + +Status +XAllocColor (Display *dpy, Colormap cmap, XColor *c) +{ + abort(); +} + +int +XClearArea (Display *dpy, Window win, int x, int y, + unsigned int w, unsigned int h, Bool exp) +{ + return 0; +} + +int +XClearWindow (Display *dpy, Window window) +{ + return 0; +} + +GC +XCreateGC(Display *dpy, Drawable d, unsigned long mask, XGCValues *gcv) +{ + return 0; +} + +XImage * +XCreateImage (Display *dpy, Visual *v, unsigned int depth, + int format, int offset, char *data, + unsigned int width, unsigned int height, + int bitmap_pad, int bytes_per_line) +{ + XImage *ximage = (XImage *) calloc (1, sizeof(*ximage)); + unsigned long r, g, b; + + if (depth == 0) depth = 32; + + ximage->width = width; + ximage->height = height; + ximage->format = format; + ximage->data = data; + ximage->bitmap_unit = 8; + ximage->byte_order = LSBFirst; + ximage->bitmap_bit_order = ximage->byte_order; + ximage->bitmap_pad = bitmap_pad; + ximage->depth = depth; + visual_rgb_masks (0, v, &r, &g, &b); + ximage->red_mask = (depth == 1 ? 0 : r); + ximage->green_mask = (depth == 1 ? 0 : g); + ximage->blue_mask = (depth == 1 ? 0 : b); + ximage->bits_per_pixel = (depth == 1 ? 1 : visual_pixmap_depth (0, v)); + ximage->bytes_per_line = bytes_per_line; + + XInitImage (ximage); + if (! ximage->f.put_pixel) abort(); + return ximage; +} + +Pixmap +XCreatePixmap (Display *dpy, Drawable d, unsigned int width, + unsigned int height, unsigned int depth) +{ + abort(); +} + +Pixmap +XCreatePixmapFromBitmapData (Display *dpy, Drawable d, char *data, + unsigned int w, unsigned int h, + unsigned long fg, unsigned long bg, + unsigned int depth) +{ + abort(); +} + +int +XDrawString (Display *dpy, Drawable d, GC gc, int x, int y, const char *s, + int len) +{ + abort(); +} + +int +XFillRectangle (Display *dpy, Drawable d, GC gc, int x, int y, + unsigned int width, unsigned int height) +{ + abort(); +} + +int +XFreeColors (Display *dpy, Colormap cmap, unsigned long *px, int n, + unsigned long planes) +{ + abort(); +} + +int +XFreeGC (Display *dpy, GC gc) +{ + abort(); +} + +int +XFreePixmap (Display *dpy, Pixmap p) +{ + abort(); +} + +XImage * +XGetImage (Display *dpy, Drawable d, int x, int y, + unsigned int w, unsigned int h, + unsigned long pm, int fmt) +{ + abort(); +} + +Status +XGetWindowAttributes (Display *dpy, Window w, XWindowAttributes *xgwa) +{ + struct state *st = &global_state; + memset (xgwa, 0, sizeof(*xgwa)); + xgwa->width = st->output_frame->width; + xgwa->height = st->output_frame->height; + return True; +} + +int +XPutImage (Display *dpy, Drawable d, GC gc, XImage *image, + int src_x, int src_y, int dest_x, int dest_y, + unsigned int w, unsigned int h) +{ + struct state *st = &global_state; + XImage *out = st->output_frame; + int x, y; + for (y = 0; y < h; y++) { + int iy = src_y + y; + int oy = dest_y + y; + if (iy >= 0 && + oy >= 0 && + iy < image->height && + oy < out->height) + for (x = 0; x < w; x++) { + int ix = src_x + x; + int ox = dest_x + x; + if (ix >= 0 && + ox >= 0 && + ix < image->width && + ox < out->width) { + XPutPixel (out, ox, oy, XGetPixel (image, ix, iy)); + } + } + } + return 0; +} + +int +XQueryColor (Display *dpy, Colormap cmap, XColor *color) +{ + uint16_t r = (color->pixel & 0x00FF0000L) >> 16; + uint16_t g = (color->pixel & 0x0000FF00L) >> 8; + uint16_t b = (color->pixel & 0x000000FFL); + color->red = r | (r<<8); + color->green = g | (g<<8); + color->blue = b | (b<<8); + color->flags = DoRed|DoGreen|DoBlue; + return 0; +} + +int +XQueryColors (Display *dpy, Colormap cmap, XColor *c, int n) +{ + int i; + for (i = 0; i < n; i++) + XQueryColor (dpy, cmap, &c[i]); + return 0; +} + +int +XSetForeground (Display *dpy, GC gc, unsigned long fg) +{ + abort(); +} + +int +XSetWindowBackground (Display *dpy, Window win, unsigned long bg) +{ + return 0; +} + +XImage * +create_xshm_image (Display *dpy, Visual *visual, + unsigned int depth, + int format, XShmSegmentInfo *shm_info, + unsigned int width, unsigned int height) +{ +# undef BitmapPad +# define BitmapPad(dpy) 8 + XImage *image = XCreateImage (dpy, visual, depth, format, 0, NULL, + width, height, BitmapPad(dpy), 0); + int error = thread_malloc ((void **)&image->data, dpy, + image->height * image->bytes_per_line); + if (error) { + XDestroyImage (image); + image = NULL; + } else { + memset (image->data, 0, image->height * image->bytes_per_line); + } + + return image; +} + +void +destroy_xshm_image (Display *dpy, XImage *image, XShmSegmentInfo *shm_info) +{ + thread_free (image->data); + image->data = NULL; + XDestroyImage (image); +} + +Bool +get_boolean_resource (Display *dpy, char *name, char *class) +{ + if (!strcmp(name, "useThreads")) return True; + abort(); +} + +double +get_float_resource (Display *dpy, char *name, char *class) +{ + if (!strcmp(name, "TVTint")) return 5; + if (!strcmp(name, "TVColor")) return 70; + if (!strcmp(name, "TVBrightness")) return -15; + if (!strcmp(name, "TVContrast")) return 150; + abort(); +} + +int +get_integer_resource (Display *dpy, char *name, char *class) +{ + if (!strcmp(name, "use_cmap")) return 0; + abort(); +} + +unsigned int +get_pixel_resource (Display *dpy, Colormap cmap, char *name, char *class) +{ + if (!strcmp(name, "background")) return 0; + abort(); +} + +XFontStruct * +load_font_retry (Display *dpy, const char *xlfd) +{ + abort(); +} + +Bool +put_xshm_image (Display *dpy, Drawable d, GC gc, XImage *image, + int src_x, int src_y, int dest_x, int dest_y, + unsigned int width, unsigned int height, + XShmSegmentInfo *shm_info) +{ + return XPutImage (dpy, d, gc, image, src_x, src_y, dest_x, dest_y, + width, height); +} + +int +visual_class (Screen *s, Visual *v) +{ + return TrueColor; +} + +int +visual_pixmap_depth (Screen *s, Visual *v) +{ + return 32; +} + +void +visual_rgb_masks (Screen *screen, Visual *visual, + unsigned long *red_mask, + unsigned long *green_mask, + unsigned long *blue_mask) +{ + *red_mask = 0x00FF0000L; + *green_mask = 0x0000FF00L; + *blue_mask = 0x000000FFL; +} + + +static void +update_smpte_colorbars(analogtv_input *input) +{ + struct state *st = (struct state *) input->client_data; + int col; + int black_ntsc[4]; + + /* + SMPTE is the society of motion picture and television engineers, and + these are the standard color bars in the US. Following the partial spec + at http://broadcastengineering.com/ar/broadcasting_inside_color_bars/ + These are luma, chroma, and phase numbers for each of the 7 bars. + */ + double top_cb_table[7][3]={ + {75, 0, 0.0}, /* gray */ + {69, 31, 167.0}, /* yellow */ + {56, 44, 283.5}, /* cyan */ + {48, 41, 240.5}, /* green */ + {36, 41, 60.5}, /* magenta */ + {28, 44, 103.5}, /* red */ + {15, 31, 347.0} /* blue */ + }; + double mid_cb_table[7][3]={ + {15, 31, 347.0}, /* blue */ + {7, 0, 0}, /* black */ + {36, 41, 60.5}, /* magenta */ + {7, 0, 0}, /* black */ + {56, 44, 283.5}, /* cyan */ + {7, 0, 0}, /* black */ + {75, 0, 0.0} /* gray */ + }; + + analogtv_lcp_to_ntsc(0.0, 0.0, 0.0, black_ntsc); + + analogtv_setup_sync(input, 1, 0); + + for (col=0; col<7; col++) { + analogtv_draw_solid_rel_lcp(input, col*(1.0/7.0), (col+1)*(1.0/7.0), 0.00, 0.68, + top_cb_table[col][0], + top_cb_table[col][1], top_cb_table[col][2]); + + analogtv_draw_solid_rel_lcp(input, col*(1.0/7.0), (col+1)*(1.0/7.0), 0.68, 0.75, + mid_cb_table[col][0], + mid_cb_table[col][1], mid_cb_table[col][2]); + } + + analogtv_draw_solid_rel_lcp(input, 0.0, 1.0/6.0, + 0.75, 1.00, 7, 40, 303); /* -I */ + analogtv_draw_solid_rel_lcp(input, 1.0/6.0, 2.0/6.0, + 0.75, 1.00, 100, 0, 0); /* white */ + analogtv_draw_solid_rel_lcp(input, 2.0/6.0, 3.0/6.0, + 0.75, 1.00, 7, 40, 33); /* +Q */ + analogtv_draw_solid_rel_lcp(input, 3.0/6.0, 4.0/6.0, + 0.75, 1.00, 7, 0, 0); /* black */ + analogtv_draw_solid_rel_lcp(input, 12.0/18.0, 13.0/18.0, + 0.75, 1.00, 3, 0, 0); /* black -4 */ + analogtv_draw_solid_rel_lcp(input, 13.0/18.0, 14.0/18.0, + 0.75, 1.00, 7, 0, 0); /* black */ + analogtv_draw_solid_rel_lcp(input, 14.0/18.0, 15.0/18.0, + 0.75, 1.00, 11, 0, 0); /* black +4 */ + analogtv_draw_solid_rel_lcp(input, 5.0/6.0, 6.0/6.0, + 0.75, 1.00, 7, 0, 0); /* black */ + if (st->logo) + { + double aspect = (double) + st->output_frame->width / st->output_frame->height; + int w2 = st->tv->xgwa.width * 0.35; + int h2 = st->tv->xgwa.height * 0.35 * aspect; + analogtv_load_ximage (st->tv, input, st->logo, st->logo_mask, + (st->tv->xgwa.width - w2) / 2, + st->tv->xgwa.height * 0.20, + w2, h2); + } + + input->next_update_time += 1.0; +} + + + +static void +analogtv_save_frame (struct state *st, const char *outfile, + unsigned long frame) +{ + char *pngfile = malloc (strlen (st->framefile_fmt) + 40); + FILE *f; + + sprintf (pngfile, st->framefile_fmt, (int) frame); + f = fopen (pngfile, "wb"); + if (! f) { + fprintf (stderr, "%s: unable to write %s\n", progname, pngfile); + exit (1); + } + +# ifdef HAVE_LIBPNG + { + png_structp png_ptr; + png_infop info_ptr; + png_bytep row; + XImage *img = st->output_frame; + int x, y; + + png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, 0, 0, 0); + if (!png_ptr) abort(); + info_ptr = png_create_info_struct (png_ptr); + if (!info_ptr) abort(); + if (setjmp (png_jmpbuf (png_ptr))) abort(); + + png_init_io (png_ptr, f); + + png_set_IHDR (png_ptr, info_ptr, img->width, img->height, 8, + PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); + png_write_info (png_ptr, info_ptr); + + row = (png_bytep) malloc (3 * img->width * sizeof(png_byte)); + if (!row) abort(); + for (y = 0 ; y < img->height ; y++) { + for (x = 0 ; x < img->width ; x++) { + unsigned long p = XGetPixel (img, x, y); + row[x*3+0] = (p & 0x000000FFL); + row[x*3+1] = (p & 0x0000FF00L) >> 8; + row[x*3+2] = (p & 0x00FF0000L) >> 16; + } + png_write_row (png_ptr, row); + } + + png_write_end (png_ptr, 0); + + png_free_data (png_ptr, info_ptr, PNG_FREE_ALL, -1); + png_destroy_write_struct (&png_ptr, 0); + free (row); + } +#else /* ! HAVE_LIBPNG */ +# error libpng required +# endif /* ! HAVE_LIBPNG */ + + fclose (f); + if (verbose_p > 1) + fprintf (stderr, "%s: wrote %s\n", progname, pngfile); + free (pngfile); +} + + +static void +delete_tmp_files(void) +{ + struct state *st = &global_state; + char outfile[2048]; + int i; + for (i = 0; i <= st->frames_written; i++) + { + sprintf (outfile, st->framefile_fmt, i); + if (verbose_p > 1) + fprintf (stderr, "%s: rm %s\n", progname, outfile); + unlink (outfile); + } +} + + +static RETSIGTYPE +analogtv_signal (int sig) +{ + signal (sig, SIG_DFL); + delete_tmp_files(); + kill (getpid (), sig); +} + + +static void +analogtv_write_mp4 (struct state *st, const char *outfile, + const char *audiofile, + unsigned long frames) +{ + char cmd[1024]; + struct stat ss; + + sprintf (cmd, + "ffmpeg" + " -hide_banner" + " -v 16" + " -framerate 30" /* rate of input: must be before -i */ + " -i '%s'" + " -r 30", /* rate of output: must be after -i */ + st->framefile_fmt); + if (audiofile) + sprintf (cmd + strlen(cmd), + " -i '%s'" + " -map 0:v:0" + " -map 1:a:0" + " -acodec aac" + " -shortest", + audiofile); + sprintf (cmd + strlen(cmd), + " -c:v libx264" + " -profile:v high" + " -crf 24" /* 18 is very high; 24 is good enough */ + " -pix_fmt yuv420p" + " '%s'" + " &-"*/, + outfile); + + if (verbose_p > 1) + fprintf (stderr, "%s: exec: %s\n", progname, cmd); + unlink (outfile); + system (cmd); + + delete_tmp_files(); + + if (stat (outfile, &ss)) + { + fprintf (stderr, "%s: %s was not created\n", progname, outfile); + exit (1); + } + + if (verbose_p) + fprintf (stderr, "%s: wrote %s (%dx%d, %lu sec, %.0f MB)\n", + progname, outfile, + st->output_frame->width, st->output_frame->height, + frames/30, + (float) ss.st_size / (1024*1024)); +} + + +static void +flip_ximage (XImage *ximage) +{ + char *data2, *in, *out; + int y; + + if (!ximage) return; + data2 = malloc (ximage->bytes_per_line * ximage->height); + if (!data2) abort(); + in = ximage->data; + out = data2 + ximage->bytes_per_line * (ximage->height - 1); + for (y = 0; y < ximage->height; y++) + { + memcpy (out, in, ximage->bytes_per_line); + in += ximage->bytes_per_line; + out -= ximage->bytes_per_line; + } + free (ximage->data); + ximage->data = data2; +} + + +static void +analogtv_convert (const char *infile, const char *outfile, + const char *audiofile, const char *logofile, + int duration, Bool powerp) +{ + struct state *st = &global_state; + XImage *ximage = file_to_ximage (0, 0, infile); + Display *dpy = 0; + Window window = 0; + int i; + unsigned long curticks = 0; + time_t lastlog = time((time_t *)0); + int frames_left; + int fps = 30; + + if (verbose_p) + fprintf (stderr, "%s: progname: loaded %s %dx%d\n", + progname, infile, ximage->width, ximage->height); + + flip_ximage (ximage); + + memset (st, 0, sizeof(*st)); + st->dpy = dpy; + st->window = window; + + st->output_frame = XCreateImage (dpy, 0, ximage->depth, ximage->format, 0, + NULL, + ximage->width & ~1, /* can't be odd */ + ximage->height & ~1, + ximage->bitmap_pad, 0); + st->output_frame->data = (char *) + calloc (st->output_frame->height, st->output_frame->bytes_per_line); + + { + char *s1, *s2; + st->framefile_fmt = malloc (strlen(outfile) + 100); + strcpy (st->framefile_fmt, outfile); + s1 = strrchr (st->framefile_fmt, '/'); + s2 = strrchr (st->framefile_fmt, '.'); + if (s2 && s2 > s1) *s2 = 0; + sprintf (st->framefile_fmt + strlen(st->framefile_fmt), + ".%08x.%%06d.png", (random() % 0xFFFFFFFF)); + } + + if (logofile) { + int x, y; + st->logo = file_to_ximage (0, 0, logofile); + if (verbose_p) + fprintf (stderr, "%s: progname: loaded %s %dx%d\n", + progname, logofile, st->logo->width, st->logo->height); + flip_ximage (st->logo); + /* Pull the alpha out of the logo and make a separate mask ximage. */ + st->logo_mask = XCreateImage (dpy, 0, st->logo->depth, st->logo->format, 0, + NULL, st->logo->width, st->logo->height, + st->logo->bitmap_pad, 0); + st->logo_mask->data = (char *) + calloc (st->logo_mask->height, st->logo_mask->bytes_per_line); + + for (y = 0; y < st->logo->height; y++) + for (x = 0; x < st->logo->width; x++) { + unsigned long p = XGetPixel (st->logo, x, y); + uint8_t a = (p & 0xFF000000L) >> 24; + XPutPixel (st->logo, x, y, (p & 0x00FFFFFFL)); + XPutPixel (st->logo_mask, x, y, (a ? 0x00FFFFFFL : 0)); + } + } + + + if (audiofile) { + struct stat ss; + if (stat (audiofile, &ss)) + { + fprintf (stderr, "%s: %s does not exist\n", progname, audiofile); + exit (1); + } + } + + /* Catch signals to delete tmp files before we start writing them. */ + + signal (SIGHUP, analogtv_signal); + signal (SIGINT, analogtv_signal); + signal (SIGQUIT, analogtv_signal); + signal (SIGILL, analogtv_signal); + signal (SIGTRAP, analogtv_signal); +# ifdef SIGIOT + signal (SIGIOT, analogtv_signal); +# endif + signal (SIGABRT, analogtv_signal); +# ifdef SIGEMT + signal (SIGEMT, analogtv_signal); +# endif + signal (SIGFPE, analogtv_signal); + signal (SIGBUS, analogtv_signal); + signal (SIGSEGV, analogtv_signal); +# ifdef SIGSYS + signal (SIGSYS, analogtv_signal); +# endif + signal (SIGTERM, analogtv_signal); +# ifdef SIGXCPU + signal (SIGXCPU, analogtv_signal); +# endif +# ifdef SIGXFSZ + signal (SIGXFSZ, analogtv_signal); +# endif +# ifdef SIGDANGER + signal (SIGDANGER, analogtv_signal); +# endif + + st->tv=analogtv_allocate(dpy, window); + + while (st->n_stations < MAX_STATIONS) { + analogtv_input *input=analogtv_input_allocate(); + st->stations[st->n_stations++]=input; + input->client_data = st; + } + + analogtv_set_defaults(st->tv, ""); + st->tv->need_clear=1; + + if (random()%4==0) { + st->tv->tint_control += pow(frand(2.0)-1.0, 7) * 180.0; + } + if (1) { + st->tv->color_control += frand(0.3) * RANDSIGN(); + } + if (random()%4==0) { + st->tv->brightness_control += frand(0.15); + } + if (random()%4==0) { + st->tv->contrast_control += frand(0.2) * RANDSIGN(); + } + + for (i=0; ichansettings[i], 0, sizeof(chansetting)); + + st->chansettings[i].noise_level = 0.06; + { + int last_station=42; + int stati; + for (stati=0; statichansettings[i].recs[stati]; + int station; + while (1) { + station=random()%st->n_stations; + if (station!=last_station) break; + if ((random()%10)==0) break; + } + last_station=station; + rec->input = st->stations[station]; + rec->level = pow(frand(1.0), 3.0) * 2.0 + 0.05; + rec->ofs=random()%ANALOGTV_SIGNAL_LEN; + if (random()%3) { + rec->multipath = frand(1.0); + } else { + rec->multipath=0.0; + } + if (stati) { + /* We only set a frequency error for ghosting stations, + because it doesn't matter otherwise */ + rec->freqerr = (frand(2.0)-1.0) * 3.0; + } + + if (rec->level > 0.3) break; + if (random()%4) break; + } + } + } + + st->curinputi=0; + st->cs = &st->chansettings[st->curinputi]; + frames_left = fps * (2 + frand(1.5)); + + st->tv->powerup=0.0; + + /* load_station_images() */ + + for (i = 0; i < MAX_STATIONS; i++) { + analogtv_input *input = st->stations[i]; + + if (i == 1) { /* station 0 is the unadulterated image. + station 1 is colorbars. */ + input->updater = update_smpte_colorbars; + } else { + int w = ximage->width * 0.815; /* underscan */ + int h = ximage->height * 0.970; + int x = (ximage->width - w) / 2; + int y = (ximage->height - h) / 2; + analogtv_input *input = st->stations[i]; + analogtv_setup_sync(input, 1, (random()%20)==0); + analogtv_load_ximage (st->tv, input, ximage, 0, x, y, w, h); + } + } + + + /* xanalogtv_draw() */ + + while (1) { + const analogtv_reception *recs[MAX_MULTICHAN]; + unsigned rec_count = 0; + double curtime=curticks*0.001; + + frames_left--; + if (frames_left <= 0) { + frames_left = fps * (0.5 + frand(2.5)); + + if (st->curinputi != 0 && !(random() % 3)) { + st->curinputi = 0; /* unadulterated image */ + } else { + st->curinputi = 1 + (random() % (N_CHANNELS - 1)); + } + + st->cs = &st->chansettings[st->curinputi]; + /* Set channel change noise flag */ + st->tv->channel_change_cycles=200000; + } + + for (i=0; ics->recs[i]; + analogtv_input *inp=rec->input; + if (!inp) continue; + + if (inp->updater) { + inp->next_update_time = curtime; + (inp->updater)(inp); + } + rec->ofs += rec->freqerr; + } + + st->tv->powerup=(powerp ? curtime : 9999); + + if (st->curinputi == 0) { + XPutImage (dpy, 0, 0, ximage, 0, 0, 0, 0, + ximage->width, ximage->height); + } else { + for (i=0; ics->recs[i]; + if (rec->input) { + analogtv_reception_update(rec); + recs[rec_count] = rec; + ++rec_count; + } + } + analogtv_draw (st->tv, st->cs->noise_level, recs, rec_count); + } + + analogtv_save_frame (st, outfile, st->frames_written); + + if (curtime >= duration) break; + + curticks += 1000/fps; + st->frames_written++; + + if (verbose_p) { + unsigned long now = time((time_t *)0); + if (now > lastlog + 5) { + fprintf (stderr, "%s: %2d%%...\n", progname, + (int) (curtime * 100 / duration)); + lastlog = now; + } + } + } + + analogtv_write_mp4 (st, outfile, audiofile, st->frames_written); +} + + +static void +usage(const char *err) +{ + if (err) fprintf (stderr, "%s: %s unknown\n", progname, err); + fprintf (stderr, "usage: %s [--verbose] [--duration secs]" + " [--audio mp3-file] [--no-powerup] infile.png outfile.mp4\n", + progname); + exit (1); +} + +int +main (int argc, char **argv) +{ + int i; + const char *infile = 0; + const char *outfile = 0; + int duration = 30; + Bool powerp = False; + char *audio = 0; + char *logo = 0; + + char *s = strrchr (argv[0], '/'); + progname = s ? s+1 : argv[0]; + progclass = progname; + + for (i = 1; i < argc; i++) + { + if (argv[i][0] == '-' && argv[i][1] == '-') + argv[i]++; + if (!strcmp(argv[i], "-v") || + !strcmp(argv[i], "-verbose")) + verbose_p++; + else if (!strcmp(argv[i], "-vv")) verbose_p += 2; + else if (!strcmp(argv[i], "-vvv")) verbose_p += 3; + else if (!strcmp(argv[i], "-vvvv")) verbose_p += 4; + else if (!strcmp(argv[i], "-vvvvv")) verbose_p += 5; + else if (!strcmp(argv[i], "-duration") && argv[i+1]) + { + char dummy; + i++; + if (1 != sscanf (argv[i], " %d %c", &duration, &dummy)) + usage(argv[i]); + } + else if (!strcmp(argv[i], "-audio") && argv[i+1]) + audio = argv[++i]; + else if (!strcmp(argv[i], "-logo") && argv[i+1]) + logo = argv[++i]; + else if (!strcmp(argv[i], "-powerup") || + !strcmp(argv[i], "-power")) + powerp = True; + else if (!strcmp(argv[i], "-no-powerup") || + !strcmp(argv[i], "-no-power")) + powerp = False; + else if (argv[i][0] == '-') + usage(argv[i]); + else if (!infile) + infile = argv[i]; + else if (!outfile) + outfile = argv[i]; + else + usage(argv[i]); + } + if (!infile) usage("input file"); + if (!outfile) usage("output file"); + +# undef ya_rand_init + ya_rand_init (0); + analogtv_convert (infile, outfile, audio, logo, duration, powerp); + exit (0); +} -- cgit v1.2.3-55-g7522