32 #include <libavdevice/avdevice.h> 33 #include <libavformat/avformat.h> 34 #include <libavutil/imgutils.h> 35 #include <libswscale/swscale.h> 39 static unsigned int run = 1;
41 struct video_input_ctx {
42 AVFormatContext *format_ctx;
43 AVCodecContext *codec_ctx;
44 int video_stream_index;
47 static int video_input_init(
struct video_input_ctx *input_ctx,
48 const char *input_format_string,
49 const char *input_path,
50 AVDictionary **input_options)
52 AVInputFormat *input_format = NULL;
53 AVFormatContext *input_format_ctx;
54 AVCodecParameters *input_codec_params;
55 AVCodecContext *input_codec_ctx;
60 avdevice_register_all();
61 avcodec_register_all();
64 if (input_format_string) {
66 input_format = av_find_input_format(input_format_string);
67 if (input_format == NULL) {
68 fprintf(stderr,
"cannot find input format\n");
74 if (input_path == NULL) {
75 fprintf(stderr,
"input_path must not be NULL!\n");
81 input_format_ctx = NULL;
82 ret = avformat_open_input(&input_format_ctx,
87 fprintf(stderr,
"cannot open input format/device\n");
92 ret = avformat_find_stream_info(input_format_ctx, NULL);
94 fprintf(stderr,
"cannot get information on the stream\n");
99 av_dump_format(input_format_ctx, 0, input_path, 0);
102 video_index = av_find_best_stream(input_format_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, &input_codec, 0);
103 if (video_index < 0) {
104 fprintf(stderr,
"cannot find any video streams\n");
109 input_codec_ctx = avcodec_alloc_context3(input_codec);
110 if (input_codec_ctx == NULL) {
111 fprintf(stderr,
"failed to allocate the input codec context\n");
116 input_codec_params = input_format_ctx->streams[video_index]->codecpar;
117 ret = avcodec_parameters_to_context(input_codec_ctx, input_codec_params);
119 fprintf(stderr,
"cannot copy parameters to input codec context\n");
124 ret = avcodec_open2(input_codec_ctx, input_codec, NULL);
126 fprintf(stderr,
"cannot open input codec\n");
130 input_ctx->format_ctx = input_format_ctx;
131 input_ctx->codec_ctx = input_codec_ctx;
132 input_ctx->video_stream_index = video_index;
138 avcodec_free_context(&input_codec_ctx);
140 avformat_close_input(&input_format_ctx);
142 av_dict_free(input_options);
143 *input_options = NULL;
148 struct video_output_ctx {
149 AVCodecContext *codec_ctx;
153 static int video_output_init(
struct video_output_ctx *output_ctx,
154 struct video_input_ctx *input_ctx,
155 unsigned int upscale,
156 unsigned int quality,
160 AVCodecContext *output_codec_ctx;
161 AVCodec *output_codec;
162 unsigned int new_output_width;
163 unsigned int new_output_height;
166 if (input_ctx == NULL) {
167 fprintf(stderr,
"input_ctx must not be NULL!\n");
173 output_codec_ctx = avcodec_alloc_context3(NULL);
174 if (output_codec_ctx == NULL) {
175 fprintf(stderr,
"cannot allocate output codec context!\n");
184 (input_ctx->codec_ctx)->width,
185 (input_ctx->codec_ctx)->height,
189 fprintf(stderr,
"cannot calculate output dimension\n");
194 output_codec_ctx->bit_rate = (input_ctx->codec_ctx)->bit_rate;
195 output_codec_ctx->width = new_output_width;
196 output_codec_ctx->height = new_output_height;
197 output_codec_ctx->time_base.num =
198 (input_ctx->format_ctx)->streams[input_ctx->video_stream_index]->time_base.num;
199 output_codec_ctx->time_base.den =
200 (input_ctx->format_ctx)->streams[input_ctx->video_stream_index]->time_base.den;
206 fprintf(stdout,
"using raw output format\n");
207 output_codec_ctx->pix_fmt = AV_PIX_FMT_NV12;
208 output_ctx->codec_ctx = output_codec_ctx;
209 output_ctx->raw_output = 1;
215 output_codec_ctx->pix_fmt = AV_PIX_FMT_YUVJ420P;
216 output_codec_ctx->codec_id = AV_CODEC_ID_MJPEG;
217 output_codec_ctx->codec_type = AVMEDIA_TYPE_VIDEO;
226 output_codec_ctx->qmin = output_codec_ctx->qmax = ((100 - (quality - 1)) * FF_QUALITY_SCALE) / 100;
227 output_codec_ctx->mb_lmin = output_codec_ctx->qmin * FF_QP2LAMBDA;
228 output_codec_ctx->mb_lmax = output_codec_ctx->qmax * FF_QP2LAMBDA;
229 output_codec_ctx->flags |= AV_CODEC_FLAG_QSCALE;
230 output_codec_ctx->global_quality = output_codec_ctx->qmin * FF_QP2LAMBDA;
233 output_codec = avcodec_find_encoder(output_codec_ctx->codec_id);
234 if (output_codec == NULL) {
235 fprintf(stderr,
"cannot find output codec!\n");
241 ret = avcodec_open2(output_codec_ctx, output_codec, NULL);
243 fprintf(stderr,
"could not open output codec!\n");
247 output_ctx->codec_ctx = output_codec_ctx;
248 output_ctx->raw_output = 0;
254 avcodec_free_context(&output_codec_ctx);
275 static int decode(AVCodecContext *avctx, AVFrame *frame,
int *got_frame, AVPacket *pkt)
282 ret = avcodec_send_packet(avctx, pkt);
289 return ret == AVERROR_EOF ? 0 : ret;
292 ret = avcodec_receive_frame(avctx, frame);
293 if (ret < 0 && ret != AVERROR(EAGAIN) && ret != AVERROR_EOF)
301 static int encode(AVCodecContext *avctx, AVPacket *pkt,
int *got_packet, AVFrame *frame)
307 ret = avcodec_send_frame(avctx, frame);
311 ret = avcodec_receive_packet(avctx, pkt);
314 if (ret == AVERROR(EAGAIN))
321 static int am7xxx_play(
const char *input_format_string,
322 AVDictionary **input_options,
323 const char *input_path,
324 unsigned int rescale_method,
325 unsigned int upscale,
326 unsigned int quality,
331 struct video_input_ctx input_ctx;
332 struct video_output_ctx output_ctx;
334 AVFrame *frame_scaled;
339 struct SwsContext *sw_scale_ctx;
346 ret = video_input_init(&input_ctx, input_format_string, input_path, input_options);
348 fprintf(stderr,
"cannot initialize input\n");
352 ret = video_output_init(&output_ctx, &input_ctx, upscale, quality, image_format, dev);
354 fprintf(stderr,
"cannot initialize input\n");
359 frame_raw = av_frame_alloc();
360 if (frame_raw == NULL) {
361 fprintf(stderr,
"cannot allocate the raw frame!\n");
367 frame_scaled = av_frame_alloc();
368 if (frame_scaled == NULL) {
369 fprintf(stderr,
"cannot allocate the scaled frame!\n");
371 goto cleanup_frame_raw;
373 frame_scaled->format = (output_ctx.codec_ctx)->pix_fmt;
374 frame_scaled->width = (output_ctx.codec_ctx)->width;
375 frame_scaled->height = (output_ctx.codec_ctx)->height;
378 out_buf_size = av_image_get_buffer_size((output_ctx.codec_ctx)->pix_fmt,
379 (output_ctx.codec_ctx)->width,
380 (output_ctx.codec_ctx)->height,
382 out_buf = av_malloc(out_buf_size *
sizeof(uint8_t));
383 if (out_buf == NULL) {
384 fprintf(stderr,
"cannot allocate output data buffer!\n");
386 goto cleanup_frame_scaled;
390 av_image_fill_arrays(frame_scaled->data,
391 frame_scaled->linesize,
393 (output_ctx.codec_ctx)->pix_fmt,
394 (output_ctx.codec_ctx)->width,
395 (output_ctx.codec_ctx)->height,
398 sw_scale_ctx = sws_getCachedContext(NULL,
399 (input_ctx.codec_ctx)->width,
400 (input_ctx.codec_ctx)->height,
401 (input_ctx.codec_ctx)->pix_fmt,
402 (output_ctx.codec_ctx)->width,
403 (output_ctx.codec_ctx)->height,
404 (output_ctx.codec_ctx)->pix_fmt,
407 if (sw_scale_ctx == NULL) {
408 fprintf(stderr,
"cannot set up the rescaling context!\n");
410 goto cleanup_out_buf;
416 ret = av_read_frame(input_ctx.format_ctx, &in_packet);
418 if (ret == (
int)AVERROR_EOF || input_ctx.format_ctx->pb->eof_reached)
421 fprintf(stderr,
"av_read_frame failed, EOF?\n");
426 if (in_packet.stream_index != input_ctx.video_stream_index) {
434 ret = decode(input_ctx.codec_ctx, frame_raw, &got_frame, &in_packet);
436 fprintf(stderr,
"cannot decode video\n");
448 sws_scale(sw_scale_ctx,
449 (
const uint8_t *
const *)frame_raw->data,
452 (input_ctx.codec_ctx)->height,
454 frame_scaled->linesize);
456 if (output_ctx.raw_output) {
458 out_frame_size = out_buf_size;
460 frame_scaled->quality = (output_ctx.codec_ctx)->global_quality;
461 av_init_packet(&out_packet);
462 out_packet.data = NULL;
465 ret = encode(output_ctx.codec_ctx,
469 if (ret < 0 || !got_packet) {
470 fprintf(stderr,
"cannot encode video\n");
475 out_frame = out_packet.data;
476 out_frame_size = out_packet.size;
481 char filename[NAME_MAX];
483 if (!output_ctx.raw_output)
484 snprintf(filename, NAME_MAX,
"out_q%03d.jpg", quality);
486 snprintf(filename, NAME_MAX,
"out.raw");
487 file = fopen(filename,
"wb");
488 fwrite(out_frame, 1, out_frame_size, file);
497 (output_ctx.codec_ctx)->width,
498 (output_ctx.codec_ctx)->height,
502 perror(
"am7xxx_send_image_async");
508 if (!output_ctx.raw_output && got_packet)
509 av_packet_unref(&out_packet);
510 av_packet_unref(&in_packet);
513 sws_freeContext(sw_scale_ctx);
516 cleanup_frame_scaled:
517 av_frame_free(&frame_scaled);
519 av_frame_free(&frame_raw);
525 avcodec_close(output_ctx.codec_ctx);
526 avcodec_free_context(&(output_ctx.codec_ctx));
529 avcodec_close(input_ctx.codec_ctx);
530 avcodec_free_context(&(input_ctx.codec_ctx));
531 avformat_close_input(&(input_ctx.format_ctx));
539 static int x_get_screen_dimensions(
const char *displayname,
int *width,
int *height)
541 int i, screen_number;
542 xcb_connection_t *connection;
543 const xcb_setup_t *setup;
544 xcb_screen_iterator_t iter;
546 connection = xcb_connect(displayname, &screen_number);
547 if (xcb_connection_has_error(connection)) {
548 fprintf(stderr,
"Cannot open a connection to %s\n", displayname);
552 setup = xcb_get_setup(connection);
554 fprintf(stderr,
"Cannot get setup for %s\n", displayname);
555 xcb_disconnect(connection);
559 iter = xcb_setup_roots_iterator(setup);
560 for (i = 0; i < screen_number; ++i) {
561 xcb_screen_next(&iter);
564 xcb_screen_t *screen = iter.data;
566 *width = screen->width_in_pixels;
567 *height = screen->height_in_pixels;
569 xcb_disconnect(connection);
574 static char *get_x_screen_size(
const char *input_path)
582 ret = x_get_screen_dimensions(input_path, &width, &height);
584 fprintf(stderr,
"Cannot get screen dimensions for %s\n", input_path);
588 len = snprintf(NULL, 0,
"%dx%d", width, height);
590 screen_size = malloc((len + 1) *
sizeof(
char));
591 if (screen_size == NULL) {
596 len = snprintf(screen_size, len + 1,
"%dx%d", width, height);
605 static char *get_x_screen_size(
const char *input_path)
608 fprintf(stderr,
"%s: fallback implementation, assuming a vga screen\n", __func__);
609 return strdup(
"vga");
613 static void unset_run(
int signo)
619 #ifdef HAVE_SIGACTION 620 static int set_signal_handler(
void (*signal_handler)(
int))
622 struct sigaction new_action;
623 struct sigaction old_action;
626 new_action.sa_handler = signal_handler;
627 sigemptyset(&new_action.sa_mask);
628 new_action.sa_flags = 0;
630 ret = sigaction(SIGINT, NULL, &old_action);
632 perror(
"sigaction on old_action");
636 if (old_action.sa_handler != SIG_IGN) {
637 ret = sigaction(SIGINT, &new_action, NULL);
639 perror(
"sigaction on new_action");
648 static int set_signal_handler(
void (*signal_handler)(
int))
650 (void)signal_handler;
651 fprintf(stderr,
"set_signal_handler() not implemented, sigaction not available\n");
657 static void usage(
char *name)
659 printf(
"usage: %s [OPTIONS]\n\n", name);
660 printf(
"OPTIONS:\n");
661 printf(
"\t-d <index>\t\tthe device index (default is 0)\n");
663 printf(
"\t-D \t\t\tdump the last frame to a file (only active in DEBUG mode)\n");
665 printf(
"\t-f <input format>\tthe input device format\n");
666 printf(
"\t-i <input path>\t\tthe input path\n");
667 printf(
"\t-o <options>\t\ta comma separated list of input format options\n");
668 printf(
"\t\t\t\tEXAMPLE:\n");
669 printf(
"\t\t\t\t\t-o draw_mouse=1,framerate=100,video_size=800x480\n");
670 printf(
"\t-s <scaling method>\tthe rescaling method (see swscale.h)\n");
671 printf(
"\t-u \t\t\tupscale the image if smaller than the display dimensions\n");
672 printf(
"\t-F <format>\t\tthe image format to use (default is JPEG)\n");
673 printf(
"\t\t\t\tSUPPORTED FORMATS:\n");
674 printf(
"\t\t\t\t\t1 - JPEG\n");
675 printf(
"\t\t\t\t\t2 - NV12\n");
676 printf(
"\t-q <quality>\t\tquality of jpeg sent to the device, between 1 and 100\n");
677 printf(
"\t-l <log level>\t\tthe verbosity level of libam7xxx output (0-5)\n");
678 printf(
"\t-p <power mode>\t\tthe power mode of device, between %d (off) and %d (turbo)\n",
680 printf(
"\t\t\t\tWARNING: Level 2 and greater require the master AND\n");
681 printf(
"\t\t\t\t the slave connector to be plugged in.\n");
682 printf(
"\t-z <zoom mode>\t\tthe display zoom mode, between %d (original) and %d (tele)\n",
684 printf(
"\t-h \t\t\tthis help message\n");
685 printf(
"\n\nEXAMPLES OF USE:\n");
686 printf(
"\t%s -f x11grab -i :0.0 -o video_size=800x480\n", name);
687 printf(
"\t%s -f fbdev -i /dev/fb0\n", name);
688 printf(
"\t%s -f video4linux2 -i /dev/video0 -o video_size=320x240,frame_rate=100 -u -q 90\n", name);
689 printf(
"\t%s -i http://download.blender.org/peach/bigbuckbunny_movies/BigBuckBunny_640x360.m4v\n", name);
692 int main(
int argc,
char *argv[])
699 char *input_format_string = NULL;
700 AVDictionary *options = NULL;
701 char *input_path = NULL;
702 unsigned int rescale_method = SWS_BICUBIC;
703 unsigned int upscale = 0;
704 unsigned int quality = 95;
706 int device_index = 0;
714 while ((opt = getopt(argc, argv,
"d:Df:i:o:s:uF:q:l:p:z:h")) != -1) {
717 device_index = atoi(optarg);
718 if (device_index < 0) {
719 fprintf(stderr,
"Unsupported device index\n");
727 fprintf(stderr,
"Warning: the -D option is only active in DEBUG mode.\n");
731 input_format_string = strdup(optarg);
734 input_path = strdup(optarg);
743 subopts = subopts_saved = strdup(optarg);
744 while ((subopt = strtok_r(subopts,
",", &subopts))) {
745 char *subopt_name = strtok_r(subopt,
"=", &subopt);
746 char *subopt_value = strtok_r(NULL,
"", &subopt);
747 if (subopt_value == NULL) {
748 fprintf(stderr,
"invalid suboption: %s\n", subopt_name);
751 av_dict_set(&options, subopt_name, subopt_value, 0);
755 fprintf(stderr,
"Option '-o' not implemented\n");
759 rescale_method = atoi(optarg);
760 switch(rescale_method) {
761 case SWS_FAST_BILINEAR:
774 fprintf(stderr,
"Unsupported rescale method\n");
783 format = atoi(optarg);
786 fprintf(stdout,
"JPEG format\n");
789 fprintf(stdout,
"NV12 format\n");
792 fprintf(stderr,
"Unsupported format\n");
798 quality = atoi(optarg);
799 if (quality < 1 || quality > 100) {
800 fprintf(stderr,
"Invalid quality value, must be between 1 and 100\n");
806 log_level = atoi(optarg);
808 fprintf(stderr,
"Unsupported log level, falling back to AM7XXX_LOG_ERROR\n");
813 power_mode = atoi(optarg);
820 fprintf(stdout,
"Power mode: %d\n", power_mode);
823 fprintf(stderr,
"Invalid power mode value, must be between %d and %d\n",
837 fprintf(stdout,
"Zoom: %d\n", zoom);
840 fprintf(stderr,
"Invalid zoom mode value, must be between %d and %d\n",
857 if (input_path == NULL) {
858 fprintf(stderr,
"The -i option must always be passed\n\n");
868 if (input_format_string && strcmp(input_format_string,
"x11grab") == 0) {
871 video_size = get_x_screen_size(input_path);
873 if (!av_dict_get(options,
"video_size", NULL, 0))
874 av_dict_set(&options,
"video_size", video_size, 0);
876 if (!av_dict_get(options,
"framerate", NULL, 0))
877 av_dict_set(&options,
"framerate",
"60", 0);
879 if (!av_dict_get(options,
"draw_mouse", NULL, 0))
880 av_dict_set(&options,
"draw_mouse",
"1", 0);
885 ret = set_signal_handler(unset_run);
893 perror(
"am7xxx_init");
901 perror(
"am7xxx_open_device");
907 perror(
"am7xxx_set_zoom_mode");
913 perror(
"am7xxx_set_power_mode");
921 ret = am7xxx_play(input_format_string,
931 fprintf(stderr,
"am7xxx_play failed\n");
938 av_dict_free(&options);
940 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.