32 #include <libavdevice/avdevice.h>
33 #include <libavformat/avformat.h>
34 #include <libswscale/swscale.h>
45 static unsigned int run = 1;
47 struct video_input_ctx {
48 AVFormatContext *format_ctx;
49 AVCodecContext *codec_ctx;
50 int video_stream_index;
53 static int video_input_init(
struct video_input_ctx *input_ctx,
54 const char *input_format_string,
55 const char *input_path,
56 AVDictionary **input_options)
58 AVInputFormat *input_format = NULL;
59 AVFormatContext *input_format_ctx;
60 AVCodecContext *input_codec_ctx;
66 avdevice_register_all();
67 avcodec_register_all();
70 if (input_format_string) {
72 input_format = av_find_input_format(input_format_string);
73 if (input_format == NULL) {
74 fprintf(stderr,
"cannot find input format\n");
80 if (input_path == NULL) {
81 fprintf(stderr,
"input_path must not be NULL!\n");
87 input_format_ctx = NULL;
88 ret = avformat_open_input(&input_format_ctx,
93 fprintf(stderr,
"cannot open input format/device\n");
98 ret = avformat_find_stream_info(input_format_ctx, NULL);
100 fprintf(stderr,
"cannot get information on the stream\n");
105 av_dump_format(input_format_ctx, 0, input_path, 0);
109 for (i = 0; i < input_format_ctx->nb_streams; i++)
110 if (input_format_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
114 if (video_index == -1) {
115 fprintf(stderr,
"cannot find any video streams\n");
121 input_codec_ctx = input_format_ctx->streams[video_index]->codec;
122 if (input_codec_ctx == NULL) {
123 fprintf(stderr,
"input codec context is not valid\n");
129 input_codec = avcodec_find_decoder(input_codec_ctx->codec_id);
130 if (input_codec == NULL) {
131 fprintf(stderr,
"input_codec is NULL!\n");
137 ret = avcodec_open2(input_codec_ctx, input_codec, NULL);
139 fprintf(stderr,
"cannot open input codec\n");
144 input_ctx->format_ctx = input_format_ctx;
145 input_ctx->codec_ctx = input_codec_ctx;
146 input_ctx->video_stream_index = video_index;
152 avformat_close_input(&input_format_ctx);
154 av_dict_free(input_options);
155 *input_options = NULL;
160 struct video_output_ctx {
161 AVCodecContext *codec_ctx;
165 static int video_output_init(
struct video_output_ctx *output_ctx,
166 struct video_input_ctx *input_ctx,
167 unsigned int upscale,
168 unsigned int quality,
172 AVCodecContext *output_codec_ctx;
173 AVCodec *output_codec;
174 unsigned int new_output_width;
175 unsigned int new_output_height;
178 if (input_ctx == NULL) {
179 fprintf(stderr,
"input_ctx must not be NULL!\n");
185 output_codec_ctx = avcodec_alloc_context3(NULL);
186 if (output_codec_ctx == NULL) {
187 fprintf(stderr,
"cannot allocate output codec context!\n");
196 (input_ctx->codec_ctx)->width,
197 (input_ctx->codec_ctx)->height,
201 fprintf(stderr,
"cannot calculate output dimension\n");
206 output_codec_ctx->bit_rate = (input_ctx->codec_ctx)->bit_rate;
207 output_codec_ctx->width = new_output_width;
208 output_codec_ctx->height = new_output_height;
209 output_codec_ctx->time_base.num =
210 (input_ctx->format_ctx)->streams[input_ctx->video_stream_index]->time_base.num;
211 output_codec_ctx->time_base.den =
212 (input_ctx->format_ctx)->streams[input_ctx->video_stream_index]->time_base.den;
218 fprintf(stdout,
"using raw output format\n");
219 output_codec_ctx->pix_fmt = PIX_FMT_NV12;
220 output_ctx->codec_ctx = output_codec_ctx;
221 output_ctx->raw_output = 1;
226 output_codec_ctx->pix_fmt = AV_PIX_FMT_YUVJ420P;
227 output_codec_ctx->codec_id = AV_CODEC_ID_MJPEG;
228 output_codec_ctx->codec_type = AVMEDIA_TYPE_VIDEO;
237 output_codec_ctx->qmin = output_codec_ctx->qmax = ((100 - (quality - 1)) * FF_QUALITY_SCALE) / 100;
238 output_codec_ctx->mb_lmin = output_codec_ctx->lmin = output_codec_ctx->qmin * FF_QP2LAMBDA;
239 output_codec_ctx->mb_lmax = output_codec_ctx->lmax = output_codec_ctx->qmax * FF_QP2LAMBDA;
240 output_codec_ctx->flags |= CODEC_FLAG_QSCALE;
241 output_codec_ctx->global_quality = output_codec_ctx->qmin * FF_QP2LAMBDA;
244 output_codec = avcodec_find_encoder(output_codec_ctx->codec_id);
245 if (output_codec == NULL) {
246 fprintf(stderr,
"cannot find output codec!\n");
252 ret = avcodec_open2(output_codec_ctx, output_codec, NULL);
254 fprintf(stderr,
"could not open output codec!\n");
258 output_ctx->codec_ctx = output_codec_ctx;
259 output_ctx->raw_output = 0;
265 avcodec_close(output_codec_ctx);
266 av_free(output_codec_ctx);
272 static int am7xxx_play(
const char *input_format_string,
273 AVDictionary **input_options,
274 const char *input_path,
275 unsigned int rescale_method,
276 unsigned int upscale,
277 unsigned int quality,
281 struct video_input_ctx input_ctx;
282 struct video_output_ctx output_ctx;
283 AVFrame *picture_raw;
284 AVFrame *picture_scaled;
287 int out_picture_size;
288 uint8_t *out_picture;
289 struct SwsContext *sw_scale_ctx;
296 ret = video_input_init(&input_ctx, input_format_string, input_path, input_options);
298 fprintf(stderr,
"cannot initialize input\n");
302 ret = video_output_init(&output_ctx, &input_ctx, upscale, quality, image_format, dev);
304 fprintf(stderr,
"cannot initialize input\n");
309 picture_raw = av_frame_alloc();
310 if (picture_raw == NULL) {
311 fprintf(stderr,
"cannot allocate the raw picture frame!\n");
317 picture_scaled = av_frame_alloc();
318 if (picture_scaled == NULL) {
319 fprintf(stderr,
"cannot allocate the scaled picture!\n");
321 goto cleanup_picture_raw;
325 out_buf_size = avpicture_get_size((output_ctx.codec_ctx)->pix_fmt,
326 (output_ctx.codec_ctx)->width,
327 (output_ctx.codec_ctx)->height);
328 out_buf = av_malloc(out_buf_size *
sizeof(uint8_t));
329 if (out_buf == NULL) {
330 fprintf(stderr,
"cannot allocate output data buffer!\n");
332 goto cleanup_picture_scaled;
336 avpicture_fill((AVPicture *)picture_scaled,
338 (output_ctx.codec_ctx)->pix_fmt,
339 (output_ctx.codec_ctx)->width,
340 (output_ctx.codec_ctx)->height);
342 sw_scale_ctx = sws_getCachedContext(NULL,
343 (input_ctx.codec_ctx)->width,
344 (input_ctx.codec_ctx)->height,
345 (input_ctx.codec_ctx)->pix_fmt,
346 (output_ctx.codec_ctx)->width,
347 (output_ctx.codec_ctx)->height,
348 (output_ctx.codec_ctx)->pix_fmt,
351 if (sw_scale_ctx == NULL) {
352 fprintf(stderr,
"cannot set up the rescaling context!\n");
354 goto cleanup_out_buf;
359 ret = av_read_frame(input_ctx.format_ctx, &in_packet);
361 if (ret == (
int)AVERROR_EOF || input_ctx.format_ctx->pb->eof_reached)
364 fprintf(stderr,
"av_read_frame failed, EOF?\n");
369 if (in_packet.stream_index != input_ctx.video_stream_index) {
377 ret = avcodec_decode_video2(input_ctx.codec_ctx, picture_raw, &got_picture, &in_packet);
379 fprintf(stderr,
"cannot decode video\n");
387 sws_scale(sw_scale_ctx,
388 (
const uint8_t *
const*)picture_raw->data,
389 picture_raw->linesize,
391 (input_ctx.codec_ctx)->height,
392 picture_scaled->data,
393 picture_scaled->linesize);
395 if (output_ctx.raw_output) {
396 out_picture = out_buf;
397 out_picture_size = out_buf_size;
399 picture_scaled->quality = (output_ctx.codec_ctx)->global_quality;
400 av_init_packet(&out_packet);
401 out_packet.data = NULL;
404 ret = avcodec_encode_video2(output_ctx.codec_ctx,
408 if (ret < 0 || !got_packet) {
409 fprintf(stderr,
"cannot encode video\n");
414 out_picture = out_packet.data;
415 out_picture_size = out_packet.size;
419 char filename[NAME_MAX];
421 if (!output_ctx.raw_output)
422 snprintf(filename, NAME_MAX,
"out_q%03d.jpg", quality);
424 snprintf(filename, NAME_MAX,
"out.raw");
425 file = fopen(filename,
"wb");
426 fwrite(out_picture, 1, out_picture_size, file);
432 (output_ctx.codec_ctx)->width,
433 (output_ctx.codec_ctx)->height,
437 perror(
"am7xxx_send_image");
443 if (!output_ctx.raw_output && got_packet)
444 av_free_packet(&out_packet);
445 av_free_packet(&in_packet);
448 sws_freeContext(sw_scale_ctx);
451 cleanup_picture_scaled:
452 av_frame_free(&picture_scaled);
454 av_frame_free(&picture_raw);
460 avcodec_close(output_ctx.codec_ctx);
461 av_free(output_ctx.codec_ctx);
464 avcodec_close(input_ctx.codec_ctx);
465 avformat_close_input(&(input_ctx.format_ctx));
473 static int x_get_screen_dimensions(
const char *displayname,
int *width,
int *height)
475 int i, screen_number;
476 xcb_connection_t *connection;
477 const xcb_setup_t *setup;
478 xcb_screen_iterator_t iter;
480 connection = xcb_connect(displayname, &screen_number);
481 if (xcb_connection_has_error(connection)) {
482 fprintf(stderr,
"Cannot open a connection to %s\n", displayname);
486 setup = xcb_get_setup(connection);
488 fprintf(stderr,
"Cannot get setup for %s\n", displayname);
489 xcb_disconnect(connection);
493 iter = xcb_setup_roots_iterator(setup);
494 for (i = 0; i < screen_number; ++i) {
495 xcb_screen_next(&iter);
498 xcb_screen_t *screen = iter.data;
500 *width = screen->width_in_pixels;
501 *height = screen->height_in_pixels;
503 xcb_disconnect(connection);
508 static char *get_x_screen_size(
const char *input_path)
516 ret = x_get_screen_dimensions(input_path, &width, &height);
518 fprintf(stderr,
"Cannot get screen dimensions for %s\n", input_path);
522 len = snprintf(NULL, 0,
"%dx%d", width, height);
524 screen_size = malloc((len + 1) *
sizeof(
char));
525 if (screen_size == NULL) {
530 len = snprintf(screen_size, len + 1,
"%dx%d", width, height);
539 static char *get_x_screen_size(
const char *input_path)
542 fprintf(stderr,
"%s: fallback implementation\n", __func__);
543 return strdup(
"vga");
547 static void unset_run(
int signo)
553 #ifdef HAVE_SIGACTION
554 static int set_signal_handler(
void (*signal_handler)(
int))
556 struct sigaction new_action;
557 struct sigaction old_action;
560 new_action.sa_handler = signal_handler;
561 sigemptyset(&new_action.sa_mask);
562 new_action.sa_flags = 0;
564 ret = sigaction(SIGINT, NULL, &old_action);
566 perror(
"sigaction on old_action");
570 if (old_action.sa_handler != SIG_IGN) {
571 ret = sigaction(SIGINT, &new_action, NULL);
573 perror(
"sigaction on new_action");
582 static int set_signal_handler(
void (*signal_handler)(
int))
584 (void)signal_handler;
585 fprintf(stderr,
"set_signal_handler() not implemented, sigaction not available\n");
591 static void usage(
char *name)
593 printf(
"usage: %s [OPTIONS]\n\n", name);
594 printf(
"OPTIONS:\n");
595 printf(
"\t-d <index>\t\tthe device index (default is 0)\n");
596 printf(
"\t-f <input format>\tthe input device format\n");
597 printf(
"\t-i <input path>\t\tthe input path\n");
598 printf(
"\t-o <options>\t\ta comma separated list of input format options\n");
599 printf(
"\t\t\t\tEXAMPLE:\n");
600 printf(
"\t\t\t\t\t-o draw_mouse=1,framerate=100,video_size=800x480\n");
601 printf(
"\t-s <scaling method>\tthe rescaling method (see swscale.h)\n");
602 printf(
"\t-u \t\t\tupscale the image if smaller than the display dimensions\n");
603 printf(
"\t-F <format>\t\tthe image format to use (default is JPEG)\n");
604 printf(
"\t\t\t\tSUPPORTED FORMATS:\n");
605 printf(
"\t\t\t\t\t1 - JPEG\n");
606 printf(
"\t\t\t\t\t2 - NV12\n");
607 printf(
"\t-q <quality>\t\tquality of jpeg sent to the device, between 1 and 100\n");
608 printf(
"\t-l <log level>\t\tthe verbosity level of libam7xxx output (0-5)\n");
609 printf(
"\t-p <power mode>\t\tthe power mode of device, between %d (off) and %d (turbo)\n",
611 printf(
"\t\t\t\tWARNING: Level 2 and greater require the master AND\n");
612 printf(
"\t\t\t\t the slave connector to be plugged in.\n");
613 printf(
"\t-z <zoom mode>\t\tthe display zoom mode, between %d (original) and %d (tele)\n",
615 printf(
"\t-h \t\t\tthis help message\n");
616 printf(
"\n\nEXAMPLES OF USE:\n");
617 printf(
"\t%s -f x11grab -i :0.0 -o video_size=800x480\n", name);
618 printf(
"\t%s -f fbdev -i /dev/fb0\n", name);
619 printf(
"\t%s -f video4linux2 -i /dev/video0 -o video_size=320x240,frame_rate=100 -u -q 90\n", name);
620 printf(
"\t%s -i http://download.blender.org/peach/bigbuckbunny_movies/BigBuckBunny_640x360.m4v\n", name);
623 int main(
int argc,
char *argv[])
630 char *input_format_string = NULL;
631 AVDictionary *options = NULL;
632 char *input_path = NULL;
633 unsigned int rescale_method = SWS_BICUBIC;
634 unsigned int upscale = 0;
635 unsigned int quality = 95;
637 int device_index = 0;
644 while ((opt = getopt(argc, argv,
"d:f:i:o:s:uF:q:l:p:z:h")) != -1) {
647 device_index = atoi(optarg);
648 if (device_index < 0) {
649 fprintf(stderr,
"Unsupported device index\n");
655 input_format_string = strdup(optarg);
658 input_path = strdup(optarg);
667 subopts = subopts_saved = strdup(optarg);
668 while ((subopt = strtok_r(subopts,
",", &subopts))) {
669 char *subopt_name = strtok_r(subopt,
"=", &subopt);
670 char *subopt_value = strtok_r(NULL,
"", &subopt);
671 if (subopt_value == NULL) {
672 fprintf(stderr,
"invalid suboption: %s\n", subopt_name);
675 av_dict_set(&options, subopt_name, subopt_value, 0);
679 fprintf(stderr,
"Option '-o' not implemented\n");
683 rescale_method = atoi(optarg);
684 switch(rescale_method) {
685 case SWS_FAST_BILINEAR:
698 fprintf(stderr,
"Unsupported rescale method\n");
707 format = atoi(optarg);
710 fprintf(stdout,
"JPEG format\n");
713 fprintf(stdout,
"NV12 format\n");
716 fprintf(stderr,
"Unsupported format\n");
722 quality = atoi(optarg);
723 if (quality < 1 || quality > 100) {
724 fprintf(stderr,
"Invalid quality value, must be between 1 and 100\n");
730 log_level = atoi(optarg);
732 fprintf(stderr,
"Unsupported log level, falling back to AM7XXX_LOG_ERROR\n");
737 power_mode = atoi(optarg);
744 fprintf(stdout,
"Power mode: %d\n", power_mode);
747 fprintf(stderr,
"Invalid power mode value, must be between %d and %d\n",
761 fprintf(stdout,
"Zoom: %d\n", zoom);
764 fprintf(stderr,
"Invalid zoom mode value, must be between %d and %d\n",
781 if (input_path == NULL) {
782 fprintf(stderr,
"The -i option must always be passed\n\n");
792 if (input_format_string && strcmp(input_format_string,
"x11grab") == 0) {
795 video_size = get_x_screen_size(input_path);
797 if (!av_dict_get(options,
"video_size", NULL, 0))
798 av_dict_set(&options,
"video_size", video_size, 0);
800 if (!av_dict_get(options,
"framerate", NULL, 0))
801 av_dict_set(&options,
"framerate",
"60", 0);
803 if (!av_dict_get(options,
"draw_mouse", NULL, 0))
804 av_dict_set(&options,
"draw_mouse",
"1", 0);
809 ret = set_signal_handler(unset_run);
817 perror(
"am7xxx_init");
825 perror(
"am7xxx_open_device");
831 perror(
"am7xxx_set_zoom_mode");
837 perror(
"am7xxx_set_power_mode");
845 ret = am7xxx_play(input_format_string,
854 fprintf(stderr,
"am7xxx_play failed\n");
861 av_dict_free(&options);
863 free(input_format_string);
Zoom test screen, the firmware version is shown as well.
Zoom 1: H Scale (changes aspect ratio).
struct _am7xxx_context am7xxx_context
An opaque data type representing a context.
Zoom Tele: available on some PicoPix models.
int am7xxx_init(am7xxx_context **ctx)
Initialize the library context and data structures, and scan for devices.
Original Size, as retrieved via am7xxx_device_info.
Max brightness and power consumption.
Middle level of brightness.
am7xxx_image_format
The image formats accepted by the device.
int am7xxx_open_device(am7xxx_context *ctx, am7xxx_device **dev, unsigned int device_index)
Open an am7xxx_device according to a index.
Zoom 2: H/V Scale (changes aspect ratio).
More brightness, but more power consumption.
Error messages, typically they describe API functions failures.
int am7xxx_set_power_mode(am7xxx_device *dev, am7xxx_power_mode power)
Set the power mode of an am7xxx device.
void am7xxx_set_log_level(am7xxx_context *ctx, am7xxx_log_level log_level)
Set verbosity level of log messages.
int am7xxx_calc_scaled_image_dimensions(am7xxx_device *dev, unsigned int upscale, unsigned int original_width, unsigned int original_height, unsigned int *scaled_width, unsigned int *scaled_height)
Calculate the dimensions of an image to be shown on an am7xxx device.
int am7xxx_send_image_async(am7xxx_device *dev, am7xxx_image_format format, unsigned int width, unsigned int height, unsigned char *image, unsigned int image_size)
Queue transfer of an image for display on an am7xxx device and return immediately.
Informations about the device operations.
int am7xxx_set_zoom_mode(am7xxx_device *dev, am7xxx_zoom_mode zoom)
Set the zoom mode of an am7xxx device.
Raw YUV in the NV12 variant.
Verbose informations about the communication with the hardware.
Low power consumption but also low brightness.
void am7xxx_shutdown(am7xxx_context *ctx)
Cleanup the library data structures and free the context.
struct _am7xxx_device am7xxx_device
An opaque data type representing an am7xxx device.
Display is powered off, no image shown.